Qwen3.8-27B 的权重名字怎么对上框架:三类映射规则各管一段

2026-08-24

加载一个模型,直觉上是把 safetensors 里的张量按名字塞进对应的模块。名字对上了就装进去,装完就能跑。

实际上没这么顺。checkpoint 里的张量名和框架里的模块名经常对不上——有的多了前缀,有的被拆成了几份,有的框架压根不想要。

vLLM 用三类规则来处理这段差距。读懂它们,能解释几个只看模型仓库想不通的现象:为什么某些权重会被直接丢掉、为什么四个张量会被拼成两个、为什么社区的纯文本版本也能正常加载。

这篇的依据

来源是 vLLM 主干仓库的 vllm/model_executor/models/qwen3_5.py,采集时间 2026-08-24

我们没有加载过权重、没有跑过模型。 下面讲的是代码里声明的映射规则,不是加载过程的观察记录。

规则一:前缀改写,包括改成「不要」

第一类规则叫 orig_to_new_prefix,顾名思义是改前缀。Qwen3.8-27B 相关的声明是这样的:

hf_to_vllm_mapper = WeightsMapper(
    orig_to_new_prefix={"model.language_model.": "model.", "mtp.": None},
)

两条规则,做的事完全不同。

第一条是改名。 凡是以 model.language_model. 开头的张量,前缀替换成 model.

代码上方的注释解释了缘由:某些社区的纯文本 checkpoint,保留了从多模态训练栈继承下来的多余 model.language_model. 前缀。剥掉这层前缀,带前缀的和不带前缀的 checkpoint 就都能正确加载了。

这是个很实际的兼容处理。多模态模型被社区剥成纯文本版本时,权重名往往会留下痕迹,框架这边顺手把两种情况都接住。

第二条是丢弃。 映射目标是 None,意思是凡是 mtp. 前缀的权重,一律不要

这条规则值得重视,因为它解释了一个容易困惑的现象:你下载的 checkpoint 里明明有 MTP 的权重,主模型加载完却好像没有 MTP。不是加载失败,是框架主动丢弃的——MTP 在 vLLM 里是独立注册的另一个模型,主模型这边不该管它。

规则二:堆叠融合,把分开的拼起来

第二类规则叫 orig_to_new_stacked,它做的事比改名复杂——把 checkpoint 里分开存的几个张量,拼成框架里的一个

hf_to_vllm_mapper = Qwen3NextModel.hf_to_vllm_mapper | WeightsMapper(
    orig_to_new_stacked={
        ".in_proj_qkv": (".in_proj_qkvz", (0, 1, 2)),
        ".in_proj_z": (".in_proj_qkvz", 3),
        ".in_proj_b": (".in_proj_ba", 0),
        ".in_proj_a": (".in_proj_ba", 1),
    }
)

读法是:箭头左边是 checkpoint 里的名字,右边是目标名字加它在目标里占的位置。

前两条说:in_proj_qkvin_proj_qkvz 的第 0、1、2 位,in_proj_z 去第 3 位。合起来正好凑齐 qkvz 四个字母代表的四份内容——q、k、v 来自前者(它自己就是三合一的),z 来自后者。

后两条同理:in_proj_bin_proj_a 拼成 in_proj_ba

为什么要拼? 代码上方的注释给了答案:Qwen3.5 把 Gated DeltaNet 的 in_proj 权重分开发布,而 qwen3-next 是预先融合好的。vLLM 的实现继承自 qwen3-next,模块结构按融合后的样子搭,所以加载分开存的 checkpoint 时得先拼一遍。

融合本身是有意义的——几个投影合成一个大矩阵,一次矩阵乘法就能算完,比分几次算要省事。这类融合是推理框架的常规做法,代价就是要维护这种映射表。

注意开头那个 | 运算符:它把 qwen3-next 已有的映射规则和这几条新规则合并起来。Qwen3.5 的映射是在前代基础上叠加的,不是重写。

规则三:打包映射,为量化和 LoRA 服务

第三类是 packed_modules_mapping,声明哪些原始模块被打包进了哪个融合模块:

packed_modules_mapping = {
    "qkv_proj": ["q_proj", "k_proj", "v_proj"],
    "gate_up_proj": ["gate_proj", "up_proj"],
    "in_proj_qkvz": ["in_proj_qkv", "in_proj_z"],
    "in_proj_ba": ["in_proj_b", "in_proj_a"],
}

前两条是标准操作:注意力的 Q、K、V 三个投影打包成 qkv_proj,前馈网络的 gate 和 up 两个投影打包成 gate_up_proj。几乎所有 transformer 实现都会这么做。

后两条就是这个模型特有的了,对应线性注意力那一支。

这份映射和规则二看起来重复,用途却不同。 规则二管的是加载时怎么拼张量;这一份管的是加载之后,别的功能怎么知道这个融合模块里装着哪几个原始模块

两个典型用途:

量化。 量化配置通常按原始模块名来指定哪些层用什么精度。融合之后模块名变了,得靠这份映射反查回去。

LoRA。 LoRA 的适配器是针对原始模块训练的(比如只对 q_projv_proj 加),加载到融合后的 qkv_proj 上时,需要知道 Q、K、V 各占哪一段才能正确地把增量放进去。

顺带一提,这个类还声明了嵌入层的映射:

embedding_modules = {
    "embed_tokens": "input_embeddings",
    "lm_head": "output_embeddings",
}

注释说这是把 PEFT 的嵌入与输出头 LoRA 目标对应到 vLLM 的嵌入包装器上。也就是说这个模型是支持对嵌入层和输出头做 LoRA 的——这两个位置能不能加 LoRA,不同实现之间差别不小。

多模态版本还要再叠一层

前面几条是纯文本主干上的映射。多模态的顶层类还会再叠:

hf_to_vllm_mapper = (
    Qwen3VLForConditionalGeneration.hf_to_vllm_mapper
    | WeightsMapper(orig_to_new_prefix={"mtp.": None})
)

从 Qwen3-VL 那边继承一整套,再加上这个模型特有的部分。 丢弃 mtp. 这条在这里又声明了一遍——因为多模态类的继承链走的是 Qwen3-VL,拿不到纯文本类里那条规则。

packed_modules_mapping 也是同样的叠加方式:先取 Qwen3-VL 的那份,再并上 in_proj_qkvzin_proj_ba 这两条。

这再次印证了这个模型的双重血统:混合注意力的骨架来自 Qwen3-Next,多模态外壳来自 Qwen3-VL,连映射表都是从两边分别继承再叠加的。

这些对使用者意味着什么

三类规则读完,有几条能落到实处的认识:

看到「权重未加载」的提示先别慌。 有些权重是被规则明确丢弃的(比如 mtp.),属于预期行为,不是文件损坏也不是版本不对。

自己转换或微调时,别假设名字一一对应。 checkpoint 里的 in_proj_qkv 在框架内部是 in_proj_qkvz 的一部分,直接按名字去找是找不到的。这类问题在写自定义加载逻辑时特别容易撞上。

社区的纯文本版本能用,靠的是前缀改写规则。 如果你自己剥一个版本出来,命名规则得跟社区一致,否则加载会出问题。

要加 LoRA 就得看 packed_modules_mapping 你想适配的那个模块是不是被打包了、打包进了哪个、占第几段——这些决定了适配器能不能正确装上。

小结

  • orig_to_new_prefix 管前缀改写,映射到 None 就是丢弃mtp. 就是这么被扔掉的)
  • orig_to_new_stacked堆叠融合,把 checkpoint 里分开存的投影拼成框架里的融合矩阵
  • packed_modules_mapping反查,让量化和 LoRA 知道融合模块里装着哪些原始模块
  • Qwen3.5 的映射是在 qwen3-next 基础上用 | 叠加的,多模态版还要再从 Qwen3-VL 叠一层
  • 上游分开发布、框架融合加载,这个差异是映射表存在的主要原因

理解这三类规则的实际价值在于:模型加载出问题时,你能分清是「文件不对」还是「名字没对上」。 这两类问题的排查方向完全不同,混淆了会浪费很多时间。

还有一层更一般的启示。这套映射表的存在说明:模型的发布方和推理框架,对权重该怎么组织并没有统一约定。 上游按训练时的模块结构存,框架按推理时的计算效率存,两边各有各的道理,中间这段差距只能靠映射表填。

所以当你看到某个框架对某个新模型的支持迟迟不来,除了「要写建模代码」之外,往往还有这一层看不见的活儿要干——得把每一个张量名对上,一个都不能错。

更多拆解在 Qwen3.8-27B 专题

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。