Prhub

#46254 [Bugfix] Fix NVFP4/OCP MX MoE emulation

原始 PR 作者 mawong-amd 合并时间 2026-06-21 12:13 文件变更 5 提交数 3 评论 3 代码增减 +3 / -21

执行摘要

修复 NVFP4/OCP MX MoE 模拟双重量化问题

PR #42120 修改了TritonExperts.apply使其在expects_unquantized_inputs=True时调用moe_kernel_quantize_input,但NVFP4/OCP MX模拟类在调用super().apply()之前已经调用了同样的量化函数,导致重复量化。作者在PR描述中指出:"The end-result is that moe_kernel_quantize_input is erroneously called twice on activations when NVFP4/OCP MX emulation is active." 问题影响了AMD CI中AMD: LM Eval Large Models (H200) (mi300_8)测试组的NVFP4模拟测试。

该PR值得关注,因为它揭示了PR #42120 引入的隐蔽bug,以及review过程中对scale选取逻辑的深入讨论。建议合并并密切跟踪相关的OCP MX修复PR #46142。

讨论亮点

fxmarty-amd 在 review 中指出,该PR的修改不完整,特别是 self.a1_scale 在模拟场景下仍可能被错误使用,并在评论中引用了相关issue #44667 和 #46142 中的讨论。 reviewer 认为应该同时考虑 OCP MX 和 NVFP4 模拟场景下 scale 的取值问题。作者随后采纳了建议,修改了 triton_moe.py 中的 scale 选取逻辑。

实现拆解

该PR通过以下步骤修复问题:

  1. 移除OCP MX模拟中的重复量化调用:在ocp_mx_emulation_moe.py中,删除apply方法中对moe_kernel_quantize_input的调用,并移除相关的导入语句。原先在进入super().apply()之前进行量化,现在由父类统一处理。

  2. 移除NVFP4模拟中的重复量化调用:在nvfp4_emulation_moe.py中,同样删除apply方法中对moe_kernel_quantize_input的调用及其导入。原先使用self.quant_config.a1_gscale作为scale,现在改由父类处理。

  3. 修正父类中的scale选择:在triton_moe.py中,将self.a1_scale改为self.a1_scale or self.a1_gscale,确保在模拟场景下(self.a1_scaleNone)能正确fallback到self.a1_gscale

  4. 增加断言保护:在utils.pynvfp4模拟分支中,添加assert A_scale is not None,确保模拟量化时scale非空,避免潜在的空指针错误。

  5. CI配置优化:在lm_eval.yaml中增加环境变量PYTORCH_ROCM_ARCH=gfx942,限制Quark编译到特定GPU架构以节省CI时间。

文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/experts/ocp_mx_emulation_moe.py MoE 专家层 modified 6.61
vllm/model_executor/layers/fused_moe/experts/nvfp4_emulation_moe.py MoE 专家层 modified 6.38
vllm/model_executor/layers/fused_moe/experts/triton_moe.py MoE 专家层 modified 5.33
vllm/model_executor/layers/fused_moe/utils.py MoE 工具层 modified 4.79
.buildkite/test_areas/lm_eval.yaml CI 配置 modified 2.36

关键符号

apply moe_kernel_quantize_input

关键源码片段

vllm/model_executor/layers/fused_moe/experts/ocp_mx_emulation_moe.py core-logic

移除了重复的激活量化调用,是修复核心文件之一

# 修改前:手动量化激活后调用父类
hidden_states, _ = moe_kernel_quantize_input(
    A=hidden_states,
    A_scale=None,
    quant_dtype=self.quant_config.quant_dtype,
    per_act_token_quant=False,
    ocp_mx_scheme=self.ocp_mx_scheme,
    quantization_emulation=True,
)
# 激活量化 / 反量化将延迟到 TritonExperts.apply 中的 moe_kernel_quantize_input 处理
super().apply(...)# 修改后:直接调用父类,由父类统一处理量化
# Activation quantization/dequantization is deferred to
# `moe_kernel_quantize_input` in TritonExperts.apply.
super().apply(...)
vllm/model_executor/layers/fused_moe/experts/nvfp4_emulation_moe.py core-logic

移除了重复的激活量化调用,是修复核心文件之一

# 修改前:手动量化激活后调用父类
hidden_states, _ = moe_kernel_quantize_input(
    A=hidden_states,
    A_scale=self.quant_config.a1_gscale,
    quant_dtype="nvfp4",
    per_act_token_quant=False,
    quantization_emulation=True,
)
# 激活量化 / 反量化将延迟到 TritonExperts.apply 中的 moe_kernel_quantize_input 处理
super().apply(...)# 修改后:直接调用父类,由父类统一处理量化
# Activation quantization/dequantization is deferred to
# `moe_kernel_quantize_input` in TritonExperts.apply.
super().apply(...)
vllm/model_executor/layers/fused_moe/experts/triton_moe.py core-logic

父类的 scale 选取逻辑是修复的关键:使用 `self.a1_scale or self.a1_gscale` 确保模拟场景下能正确取值

# 修改前:直接使用 self.a1_scale,在模拟场景下可能为 None
hidden_states, a1q_scale = moe_kernel_quantize_input(
    hidden_states,
    self.a1_scale,
    ...
)# 修改后:优先使用 self.a1_scale,否则回退到 self.a1_gscale
# 这样 NVFP4/OCP MX 模拟场景下也能正确获取 scale
hidden_states, a1q_scale = moe_kernel_quantize_input(
    hidden_states,
    self.a1_scale or self.a1_gscale,
    ...
)

评论区精华

scale 选取逻辑仍不完整 正确性

fxmarty-amd 指出,`self.a1_scale` 在模拟场景下可能仍被错误使用,并引用了相关 issue #44667 和 #46142 中的讨论。

结论:作者随后修改了 `triton_moe.py`,将 `self.a1_scale` 改为 `self.a1_scale or self.a1_gscale`,解决了该问题。 · 已解决

风险与影响

风险较低,因为:

1) 修改集中在模拟代码路径,不影响原生量化路径;
2) 删除了冗余的量化调用,理论上只会修复问题而不会引入新bug;
3) CI测试(NVFP4模拟测试)通过。潜在风险:如果未来其他子类也依赖在super().apply()之前主动调用moe_kernel_quantize_input,此修改可能导致这些子类缺少必要的量化。但当前仅NVFP4和OCP MX两个子类有此行为,且已统一修复。

影响范围:仅影响NVFP4和OCP MX模拟模式下的MoE计算,这类场景用于在不支持原生NVFP4/OCP MX的硬件(如AMD gfx942)上运行量化模型。修复后,模拟模式下的推理结果应恢复正常,消除了双重量化导致的数值错误。对其他硬件(如NVIDIA Blackwell)原生模式无影响。

缺少测试覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论