Prhub

#29042 [NPU] Fix the DeepSeek-V2-Coder model accuracy issue

原始 PR 作者 zhsurpass 合并时间 2026-06-25 09:14 文件变更 3 提交数 4 评论 5 代码增减 +4 / -1

执行摘要

修复 NPU 上 DeepSeek-V2-Coder MoE 路由精度

DeepSeek-V2-Coder 模型(如 DeepSeek-Coder-V2-Lite-Instruct)在 NPU 上运行时,MoE 路由权重的 norm_type 被硬编码为 1(sigmoid),但该模型实际使用 softmax 评分函数,导致路由权重计算错误,模型输出精度下降。PR 描述中附带了精度对比截图(未在材料中展示)表明修复后精度恢复正常。

建议阅读 fused_topk_npu 函数中 norm_type 的计算方式,以及在 deepseek_v2.pyllada2.pyscoring_func 的透传方式,以理解 NPU MoE 门控的配置传递链路。对于 NNPU 后端维护者,建议后续为该逻辑添加单元测试。

讨论亮点

机器人 gemini-code-assist[bot] 在 review 中指出了代码中的一个潜在隐患:norm_type 定义时末尾多了一个逗号,可能导致其变成一个单元素元组而不是整数,从而引起 npu_moe_gating_top_k 算子的类型错误。该评论在原始 diff 中显示的是多行版本,但合并后的最终代码(head_excerpt)中已移除该逗号,因此该问题已在合并前解决。无人类 reviewer 参与讨论。

实现拆解

  1. 修改 NPU MoE 门控内核的 norm_type 判断python/sglang/srt/hardware_backend/npu/moe/topk.py
    - 将原来固定为 1(sigmoid)的 norm_type 替换为条件表达式:若 topk_config.scoring_func == "softmax" 则设为 0,否则设为 1(sigmoid)。
    - 这确保了在 NPU 专用 npu_moe_gating_top_k 算子中,路由权重的归一化方式与模型配置一致。

  2. 在 DeepSeek-V2 MoE 初始化中透传 scoring_funcpython/sglang/srt/models/deepseek_v2.py
    - 在构造 TopK 对象的 topk_kwargs 字典中新增 scoring_func=config.scoring_func 键值对,使得 fused_topk_npu 能通过 topk_config.scoring_func 访问到实际评分函数。

  3. 在 LLaDA2 MoE 初始化中透传 scoring_funcpython/sglang/srt/models/llada2.py
    - 类似地,在 LLaDA2MoE.__init__TopK 构造参数中新增 scoring_func=self.score_function,确保 LLaDA2 模型在未来 NPU 运行时也能正确选择归一化方式。

  4. 无测试或配置变更:提交历史显示作者在 4 次提交中完成了功能实现与 lint 修复,但未包含单元测试或 CI 配置改动。

文件 模块 状态 重要度
python/sglang/srt/hardware_backend/npu/moe/topk.py NPU MoE modified 5.56
python/sglang/srt/models/deepseek_v2.py DeepSeek 模型 modified 4.96
python/sglang/srt/models/llada2.py LLaDA2 模型 modified 4.56

关键符号

fused_topk_npu DeepseekV2MoE.__init__ LLaDA2MoE.__init__

关键源码片段

python/sglang/srt/hardware_backend/npu/moe/topk.py core-logic

核心修复文件:将 MoE 门控的 `norm_type` 从固定值改为动态判断,直接解决了精度问题。

# python/sglang/srt/hardware_backend/npu/moe/topk.py
# 关键修改 : 根据 scoring_func 动态选择 norm_type
# 原代码 : norm_type=1, # 1 for sigmoid, 0 for softmax
# 新代码 :
elif (
    correction_bias is not None
    or topk_config.scoring_func == "sigmoid"
    or num_token_non_padded is not None
):
    topk_weights, topk_ids, _ = torch.ops.npu.npu_moe_gating_top_k(
        router_logits.to(torch.float32),
        k=topk_config.top_k,
        bias=(
            correction_bias.to(torch.float32)
            if correction_bias is not None
            else None
        ),
        k_group=topk_config.topk_group if use_grouped_topk else 1,
        group_count=topk_config.num_expert_group if use_grouped_topk else 1,
        group_select_mode=(1 if use_grouped_topk else 0),
        renorm=0,
        # 1 for sigmoid, 0 for softmax
        norm_type=(0 if topk_config.scoring_func == "softmax" else 1),
        routed_scaling_factor=(
            1 if renormalize else topk_config.routed_scaling_factor
        ),
        eps=float(1e-20),
    )

评论区精华

norm_type 末尾逗号导致的类型错误风险 正确性

review 机器人指出 `norm_type=(0 if topk_config.scoring_func == "softmax" else 1,)` 中末尾逗号使表达式变成元组,可能导致 `npu_moe_gating_top_k` 算子收到错误类型参数。

结论:该问题在合并后的最终代码中已解决(head_excerpt 中无逗号),但在提交历史中未见对应修复提交,可能已通过 force push 或合并时修正。 · 已解决

风险与影响

  1. 回归风险低scoring_func 仅在构造 TopK 时通过参数传递,不影响原有的 correction_bias 或 grouped topk 分支逻辑;且变更仅影响 NPU 后端(topk.py 中的 npu_moe_gating_top_k 调用路径),不影响 GPU 或其他硬件。
  2. LLaDA2 模型暂无 NPU 测试覆盖:虽然 LLaDA2 的修改是防御性的,但由于缺乏 NPU 上的 LLaDA2 测试,该变更在 NPU 上可能被错误激活,需关注后续测试结果。
  3. 无测试配套:PR 未增加单元测试或集成测试,长期维护时可能难以回归验证。
  1. 用户影响:修复了 NPU 上 DeepSeek-V2-Coder 系列模型的推理精度问题,用户可直接受益于正确的模型输出。
  2. 系统影响:仅影响 NPU 硬件后端的 MoE 路由逻辑,不影响 GPU 或其他后端。
  3. 团队影响:变更较小且已合并,维护成本低;但缺少测试可能给未来重构带来隐患。
缺少测试覆盖 防御性修改可能引入未验证路径

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论