Prhub

#40708 [BugFix] Fix Gemma4 'layers.0.moe.experts.0.down_proj_packed' KeyError issue

原始 PR 作者 SoluMilken 合并时间 2026-05-10 01:20 文件变更 2 提交数 4 评论 14 代码增减 +55 / -18

执行摘要

修复 Gemma4 AWQ 权重加载 KeyError,支持下划线命名

Issue #40247 和 #40591 报告了 Gemma4 26B AWQ 模型在 v0.19.1 版本加载时崩溃,错误信息 KeyError: 'layers.0.moe.experts.0.down_proj_packed'。该问题由 expert_params_mapping 仅支持点号后缀(如 .qweight ),未处理 compressed-tensors 使用的下划线后缀(如 _packed )导致。

值得阅读。该 PR 清晰展示了如何处理不同量化框架的命名约定差异,并合理利用已有的工具函数 fused_moe_make_expert_params_mapping 减少重复代码。测试添加也体现了回归预防的实践,适合作为类似权重加载问题的参考。

讨论亮点

Review 中有两个主要讨论:

  • Isotr0py 建议使用 fused_moe_make_expert_params_mapping 替代手动构造映射,减少冗余代码。作者采纳。
  • 作者最初创建了单独测试文件 test_gemma4_weight_loading.py,但 Isotr0py 认为无需独立文件,且现有 test_awq.py 更适合 CI GPU 限制,作者随后将测试合并到 test_awq.py。

实现拆解

  1. 引入 fused_moe_make_expert_params_mapping 工具函数(从 vllm.model_executor.layers.fused_moe 导入),用于生成标准点号后缀的 expert_params_mapping。
  2. 基于 dot_suffix_expert_params_mapping 构建 underscore_suffix_expert_params_mapping:将 weight_name 中的 '.' 替换为 '' 并附加 weight 前缀,以匹配 compressed-tensors 的命名规范(如 experts.0.gate_proj. → experts.0.gate_proj_weight_)。
  3. 合并两个映射列表作为新的 expert_params_mapping,替代原有的手动映射生成代码。
  4. 在 tests/models/quantization/test_awq.py 中新增 test_awq_load 测试函数,参数化测试两个模型:标准 AWQ 格式(dot-suffix)和 compressed-tensors 格式(underscore-suffix),验证加载不抛出异常且能生成输出。
  5. (辅助)在 .gitignore 中添加 .codex 忽略。
文件 模块 状态 重要度
vllm/model_executor/models/gemma4.py 模型加载 modified 6.88
tests/models/quantization/test_awq.py 量化测试 modified 5.65

关键符号

load_weights test_awq_load

关键源码片段

vllm/model_executor/models/gemma4.py data-contract

核心修复文件,修改了 load_weights 方法中的 expert_params_mapping 生成逻辑,添加下划线后缀命名支持。

# 文件 : vllm/model_executor/models/gemma4.py# 在文件顶部的导入部分,新增了以下导入
from vllm.model_executor.layers.fused_moe import (
    FusedMoE,
    GateLinear,
    fused_moe_make_expert_params_mapping, # 新增
)# 在 load_weights 方法中,变更后的核心逻辑:
def load_weights(self, weights):
    # ... ( 之前的 stacked_params_mapping 等 )
​
    num_experts = getattr(self.config, 'num_experts', None) or 0
​
    # Strategy A: dot-separated suffix ( 标准 AWQ/GPTQ, 如 .qweight, .scales)
    dot_suffix_expert_params_mapping = fused_moe_make_expert_params_mapping(
        self,
        ckpt_gate_proj_name='gate_proj',
        ckpt_down_proj_name='down_proj',
        ckpt_up_proj_name='up_proj',
        num_experts=num_experts,
    )
​
    # Strategy B: underscore-separated suffix
    # (CompressedTensors 格式 , 如 _packed, _scale)
    underscore_suffix_expert_params_mapping = [
        (
            f'{param_name}weight_', # 参数名 , 如 experts.w13_weight_
            f'{weight_name.rstrip(".")}_', # 权重名 , 如 experts.0.gate_proj_
            expert_id,
            shard_id,
        )
        for (param_name, weight_name, expert_id, shard_id)
        in dot_suffix_expert_params_mapping
    ]
​
    expert_params_mapping = (
        dot_suffix_expert_params_mapping + underscore_suffix_expert_params_mapping
    )
​
    # 后续加载逻辑不变 ...
tests/models/quantization/test_awq.py test-coverage

添加了 test_awq_load 回归测试,覆盖两种 AWQ 命名约定。

# 文件 : tests/models/quantization/test_awq.py# 新增的测试函数
@pytest.mark.parametrize(
    ('model', 'quantization', 'dtype'),
    [
        ('mattbucci/gemma-4-26B-AWQ', 'awq', 'float16'),
        ('cyankiwi/gemma-4-26B-A4B-it-AWQ-4bit', 'compressed-tensors', 'bfloat16'),
    ],
    ids=[
        'gemma4-moe-standard-awq-dot-suffix',
        'gemma4-moe-compressed-tensors-underscore-suffix',
    ],
)
@torch.inference_mode()
def test_awq_load(
    vllm_runner: type[VllmRunner],
    example_prompts: list[str],
    model: str,
    quantization: str,
    dtype: str,
) -> None:
    '''Regression test: AWQ weight loading must not KeyError.'''
    with vllm_runner(
        model,
        quantization=quantization,
        dtype=dtype,
        max_model_len=128,
        enforce_eager=True,
    ) as vllm_model:
        outputs = vllm_model.generate_greedy(example_prompts[:2], max_tokens=32)
    assert len(outputs) == 2

评论区精华

使用 fused_moe_make_expert_params_mapping 重构专家参数映射 设计

Isotr0py 建议使用已有的 fused_moe_make_expert_params_mapping 工具函数代替手动构建映射,以减少重复代码和潜在错误。

结论:作者采纳,改用工具函数生成点号后缀映射,并基于此构造下划线后缀映射。 · 已解决

测试用例位置选择 测试

作者最初创建了单独测试文件 test_gemma4_weight_loading.py,但 Isotr0py 认为不需要自包含测试,建议加入现有 test_awq.py 以适配 CI 24GB L4 GPU 内存限制。

结论:作者接受,将测试合并到 test_awq.py。 · 已解决

风险与影响

主要风险是新引入的下划线后缀映射可能仍无法覆盖所有 compressed-tensors 命名变体(如 weight 之外的 scale 参数)。但通过遍历 dot_suffix_expert_params_mapping 并动态生成,其覆盖面与标准映射一致;测试覆盖了两种主流模型,降低了遗漏风险。对性能无影响,因为修改仅在模型加载阶段执行一次。

直接影响使用 Gemma4 系列模型并采用 AWQ/compressed-tensors 量化的用户,修复了此前无法加载模型的 bug。对其他模型和功能无影响。该修复有助于稳定 Gemma4 在 v0.19.1 中的回归问题。

专家权重命名兼容性

关联 Issue

#40247 [Bug]: Gemma 4 26b-a4b AWQ INT4 is broken in 0.19.1, but working in latest gemma4 tag on Dockerhub
#40591 [Bug]: Regression in 0.19.1 - Gemma 4 26B MoE fails to load packed experts (KeyError: down_proj_packed). Worked in dev6.

完整报告

参与讨论