Ming-3.0 的权重先按模块拆开看、推理脚本里的路径别写死

文章导读
换机器或换目录后脚本找不到权重,通常不是模型本身的问题,而是脚本里把权重路径写成了某个固定的绝对路径,且没人清楚目录里各文件分别喂给哪一部分。处理顺序建议是:先列目录、按体积和命名把文件归到模块,再把加载路径抽成命令行参数或环境变量,加载前加存在性校验,最后用相对路径和绝对路径各跑一次做交叉确认。这样换环境时出错信息会落在“哪个模块缺哪个文件”,而不是加载到一半才中断。
📋 目录
  1. A 按体积和命名规律列出权重目录里的每个文件
  2. B 把加载路径抽成命令行参数或环境变量
  3. C 在加载前加一段文件存在性校验
  4. D 分别用相对路径和绝对路径各跑一次
A A

换机器或换目录后脚本找不到权重,通常不是模型本身的问题,而是脚本里把权重路径写成了某个固定的绝对路径,且没人清楚目录里各文件分别喂给哪一部分。处理顺序建议是:先列目录、按体积和命名把文件归到模块,再把加载路径抽成命令行参数或环境变量,加载前加存在性校验,最后用相对路径和绝对路径各跑一次做交叉确认。这样换环境时出错信息会落在“哪个模块缺哪个文件”,而不是加载到一半才中断。

先拆清单再改路径:用 ls -lh 的输出把权重目录里每个文件对应到文本、视觉、音频等模块,形成一张对照表;脚本把目录改成 `--model-dir` 加环境变量兜底,覆盖顺序为命令行 > 环境变量 > 默认值;加载前跑一遍 glob 校验,缺文件在启动阶段直接退出并打印缺失模块名;最后分别用相对路径和绝对路径各跑一次,以日志里每个模块的首个文件路径和进程退出码作为判断依据。整个过程只依赖配置、日志和命令输出,不涉及任何性能结论。

按体积和命名规律列出权重目录里的每个文件

这一步的目的不是清理目录,而是建立“文件 → 模块”的对应关系,后面改路径时才知道每个路径该指向什么。建议先看整体体积分布,再看文件名的公共前缀。

# 1) 目录内每个文件的体积与时间,按体积倒序
ls -lh `--sort`=size /path/to/ming3-weights

# 2) 只看文件名,快速找公共前缀和分片编号
ls -1 /path/to/ming3-weights | sort

# 3) 统计各前缀的文件数量,判断是否分片
ls -1 /path/to/ming3-weights | sed 's/[0-9]\+\..*$//' | sort | uniq -c

输出骨架通常长这样,具体行数以实际目录为准:

-rw-r`--r--` 1 u u  xxxM Jan  1 00:00 text_model-00001-of-0000N.safetensors
-rw-r`--r--` 1 u u  xxxM Jan  1 00:00 text_model-00002-of-0000N.safetensors
-rw-r`--r--` 1 u u  xxxM Jan  1 00:00 vision_encoder.safetensors
-rw-r`--r--` 1 u u  xxxK Jan  1 00:00 tokenizer.json
-rw-r`--r--` 1 u u  xxxK Jan  1 00:00 config.json

把上面的输出填进下面这张对照表。体积区间一列先用目录内的相对位次描述,等真实 ls -lh 结果出来再替换成实际区间;命名特征一列写你目录里真实的前缀,不要照抄示例。

Ming-3.0 的权重先按模块拆开看、推理脚本里的路径别写死
  • 命名特征:带 -0000X-of-0000N 后缀的通常是同一模块的分片,前缀相同即同一模块。
  • 体积位次:目录内体积最大的一批一般是主干权重;体积很小(几十到几百 KB)的多为配置或分词器文件,路径写错时同样会让脚本报错。
  • 对应模块:按前缀归到文本、视觉、音频等模块;无法从前缀判断的文件,先在加载代码里搜索该文件名,再回填到表里。
  • 确认方式:文件能否被加载代码引用,是判断归属的唯一硬依据;体积和命名只是线索。

把加载路径抽成命令行参数或环境变量

路径失效的根因是路径写进了代码。做法是把“根目录”和“按模块覆盖”分开:根目录给一个默认值,模块级参数留空,为空时回落到根目录下的约定子目录。覆盖顺序建议固定为命令行 > 环境变量 > 脚本默认值,这样临时换盘只加一个参数,长期换盘则设环境变量。

import argparse, os

def build_args():
    p = argparse.ArgumentParser()
    # 默认值优先读环境变量,读不到才用脚本内置默认
    p.add_argument("`--model-dir`",
                   default=os.environ.get("MING3_MODEL_DIR", "./weights"))
    # 模块级覆盖,留空表示沿用 model-dir 下的约定子目录
    p.add_argument("`--text-dir`", default=os.environ.get("MING3_TEXT_DIR"))
    p.add_argument("`--vision-dir`", default=os.environ.get("MING3_VISION_DIR"))
    p.add_argument("`--dtype`", default="auto")
    return p.parse_args()

def resolve_dir(model_dir, override, sub):
    return override if override else os.path.join(model_dir, sub)

使用时的写法:临时指定用 `--model-dir` /data/ming3-weights;固定机器上把 MING3_MODEL_DIR 写进启动脚本或服务配置。需要注意 argparse 的 default 本身就会被命令行覆盖,所以不必再写一层 if args.model_dir is None 的分支,少一层判断也少一个出错的点。相对路径会按脚本的当前工作目录解析,如果脚本由 systemd、cron 或容器拉起,工作目录往往不是你以为的那个,这一条要在最后一步验证。

在加载前加一段文件存在性校验

目标是让“缺文件”在启动阶段以非零退出码结束,而不是等加载到第三个模块才抛异常。校验只需要模块名、匹配模式和目录三样东西。

Ming-3.0 的权重先按模块拆开看、推理脚本里的路径别写死
import glob, os, sys

REQUIRED = {
    "text":   "text_model-*.safetensors",
    "vision": "vision_*.safetensors",
    "config": "config.json",
}

def check_required(model_dir, required=REQUIRED):
    missing = []
    for module, pattern in required.items():
        hits = sorted(glob.glob(os.path.join(model_dir, pattern)))
        if not hits:
            missing.append((module, pattern))
        else:
            print(f"[weight-check] ok     module={module} files={len(hits)} "
                  f"first={os.path.basename(hits[0])}")
    if missing:
        for module, pattern in missing:
            print(f"[weight-check] MISSING module={module} "
                  f"pattern={pattern} dir={model_dir}", file=sys.stderr)
        raise SystemExit(2)
    return True

缺文件时的输出大致如下,模块名和模式都直接可读,方便对照上一步的对照表定位:

[weight-check] ok     module=text files=4 first=text_model-00001-of-00004.safetensors
[weight-check] MISSING module=vision pattern=vision_*.safetensors dir=./weights

校验函数放在模型对象构造之前调用,别放在 load_state_dict 附近。分片文件这里只做数量与首个文件检查,分片是否完整(比如 1 到 N 是否连续)属于另一层校验,可以在确认目录稳定后再补。

分别用相对路径和绝对路径各跑一次

两次运行的参数只有路径形式不同,其余保持一致,这样日志差异只可能来自路径解析。第一次在权重目录的上一级执行,用相对路径:

Ming-3.0 的权重先按模块拆开看、推理脚本里的路径别写死
cd /data
python infer.py `--model-dir` ./ming3-weights `--dtype` auto
# 退出码应为 0

第二次在任意目录执行,用绝对路径:

cd /tmp
python /data/infer.py `--model-dir` /data/ming3-weights `--dtype` auto
# 退出码应为 0

日志对照看三处:其一,两次的 [weight-check] 行里 dir= 是否分别显示相对与绝对形式,而 files= 数量一致;其二,每个模块的 first= 文件名两次应完全相同,不同就说明某次落到了别的目录(常见于默认值生效);其三,加载完成后脚本自己打印的模型目录一行,是否与传入值一致。

判断加载成功的依据建议定为:所有模块都有 ok 行、没有 MISSING 行、进程退出码为 0,且推理输出正常返回。如果相对路径那次失败而绝对路径成功,问题基本落在工作目录,而不是权重本身;这种情况优先把启动脚本里的 cd 或服务配置的 WorkingDirectory 补齐,而不是把绝对路径再写回代码里。