Prhub

#47874 [ROCm][CI][MoE] Fix double-transpose of fused w3 expert weights

原始 PR 作者 stefankoncarevic 合并时间 2026-07-09 07:59 文件变更 1 提交数 5 评论 2 代码增减 +10 / -6

执行摘要

修复融合 MoE 专家权重 w3 双重转置崩溃

修复加载 Qwen/Qwen3-VL-30B-A3B-Instruct-FP8 等模型时,融合 MoE 专家权重 gate_up_proj 因双重转置导致 RuntimeError 的崩溃问题。PR body 明确指出 w3 分支看到的是 w1 已转置后的张量,导致第二次转置后 chunk 维度错误。

值得精读。这是一个典型的共享变量误用 bug,修复手法简洁高效,适合作为代码审查教学案例。建议开发者关注变量作用域和可变性对迭代逻辑的影响。

讨论亮点

无 review 讨论。Approver AndreasKaratzas 确认测试通过,并请求 mgoin 审阅;zyongye 直接批准。

实现拆解

  1. 共享变量加载问题:在 vllm/model_executor/layers/fused_moe/routed_experts.pyload_weights 方法中,融合张量 loaded_weight 在 expert_mapping 中会匹配两次(分别对应 w1 和 w3)。第一次匹配时,如果张量形状需要转置,loaded_weight 被直接赋值修改,导致第二次匹配时看到的是已转置的版本。
  2. 局部变量修复:在 is_fused 分支开头引入局部变量 fused_weight = loaded_weight,后续转置和 chunk 操作均使用 fused_weight,确保 loaded_weight 保持原始状态。这样 w1 和 w3 的转置逻辑完全对称,互不干扰。
  3. 非融合路径检查:w2(down_proj)分支只匹配一次,非融合路径(is_fused=False)使用 loaded_weight.unsqueeze(0),两者均不受双重转置影响,因此无需修改。
  4. 测试验证:PR 通过 pytest tests/v1/spec_decode/test_acceptance_length.py -k 'qwen3-30b-moe-vl-eagle3' 在 ROCm MI300 上通过端到端测试。
文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/routed_experts.py 模型执行器 modified 6.41

关键符号

load_weights

关键源码片段

vllm/model_executor/layers/fused_moe/routed_experts.py data-contract

修复的核心文件,修改了 load_weights 方法中 fused 分支的变量作用域,避免 loaded_weight 被多次转置。

def load_weights(
    self, weights: Iterable[tuple[str, torch.Tensor]]
) -> Iterable[str]:
    expert_mapping = self.get_expert_mapping(include_fused=True)
    unpadded_hidden = self.moe_config.hidden_dim_unpadded
    for expert_name, loaded_weight in weights:
        # ... 省略前面的匹配逻辑
        if is_fused:
            # w1 和 w3 共享同一个 fused 张量;使用局部变量 fused_weight
            # 防止下面的转置操作修改 loaded_weight,否则 w3 会看到
            # 已被 w1 转置过的版本,导致第二次转置和 chunk 维度错误
            fused_weight = loaded_weight
            if shard_id in {"w1", "w3"}:
                if fused_weight.shape[-1] != unpadded_hidden:
                    # [..., hidden, intermediate] -> [..., intermediate, hidden]
                    fused_weight = fused_weight.transpose(-1, -2)
                # 复用 expert_id 来解耦 w1 和 w3
                experts_shard = fused_weight.chunk(2, dim=1)[expert_id]
            else:
                # w2 分支,只需转置一次
                if fused_weight.shape[-2] != unpadded_hidden:
                    fused_weight = fused_weight.transpose(-1, -2)
                experts_shard = fused_weight
            # ... 后续统一加载逻辑

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

回归风险低:变更仅限于 fused MoE 权重的加载逻辑,引入了局部变量 fused_weight 替代直接修改 loaded_weight。该变量仅在 is_fused 分支中使用,不影响非融合路径和 w2 分支。PR 已通过目标模型端到端测试。唯一潜在风险是如果未来有代码依赖 loaded_weight 在迭代中被修改的行为,但当前代码无此依赖。

正向影响:修复了 ROCm 平台上加载 Qwen3-VL 等 FP8 MoE 模型的崩溃问题,使这些模型在 ROCm 上可正常运行。影响范围限于融合 MoE 权重的加载路径,对 NVIDIA 和其他平台无负面影响。

无实质性风险

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论