Prhub

#25755 [Fix][NPU] Preserve existing packed_modules_mapping when merging model-level fused module mappings

原始 PR 作者 litmei 合并时间 2026-05-29 09:11 文件变更 3 提交数 6 评论 4 代码增减 +8 / -2

执行摘要

修复 NPU 上 DeepSeek 模型加载时 quant_config.packed_modules_mapping 被覆盖

修复 Commit abe2ec2af 引入的 bug:DeepseekV2ForCausalLM.__init__ 中直接赋值 quant_config.packed_modules_mapping = self.packed_modules_mapping 覆盖了 ModelSlim 加载器预先填充的嵌套字典,导致 get_linear_scheme 返回 NoneAttributeError

该 PR 是一个精确的 bugfix,值得精读以理解量化配置的契约。设计上引入多态方法而非条件判断,是良好的重构方向。建议为新增方法补充单元测试。

讨论亮点

无 review 评论,仅由 iforgetmyname 批准合并。

实现拆解

  1. 基类添加统一接口:在 python/sglang/srt/layers/quantization/base_config.pyQuantizationConfig 基类中新增 update_packed_modules_mapping(self, mapping) 方法,默认行为为直接替换 self.packed_modules_mapping(保持与其他量化方法的兼容性)。
  2. ModelSlim 覆盖实现:在 python/sglang/srt/layers/quantization/modelslim/modelslim.pyModelSlimConfig 中覆盖 update_packed_modules_mapping,使用 self.packed_modules_mapping.update(mapping) 来合并,而非替换,从而保留嵌套结构。
  3. 调用点变更:在 python/sglang/srt/models/deepseek_v2.pyDeepseekV2ForCausalLM.__init__ 中,将条件判断从 if quant_config is not None and hasattr(quant_config, "packed_modules_mapping") 简化为 if quant_config is not None,并调用 quant_config.update_packed_modules_mapping(self.packed_modules_mapping)
文件 模块 状态 重要度
python/sglang/srt/models/deepseek_v2.py 模型加载 modified 5.84
python/sglang/srt/layers/quantization/modelslim/modelslim.py 量化配置 modified 5.83
python/sglang/srt/layers/quantization/base_config.py 量化配置 modified 5.38

关键符号

update_packed_modules_mapping

关键源码片段

python/sglang/srt/models/deepseek_v2.py data-contract

修改了 DeepSeekV2 模型初始化时对 quant_config 的操作,是 bug 的根源。

# python/sglang/srt/models/deepseek_v2.py 第 2462-2476 行class DeepseekV2ForCausalLM(nn.Module, DeepseekV2WeightLoaderMixin):
    # ...
    def __init__(self, config, quant_config=None, prefix=""):
        super().__init__()
        # ...
​
        # 如果条件是 fuse_qkv_a_proj,则向 self.packed_modules_mapping 添加 fusion 条目
        if self.fuse_qkv_a_proj:
            self.packed_modules_mapping["fused_qkv_a_proj_with_mqa"] = [
                "q_a_proj",
                "kv_a_proj_with_mqa",
            ]
​
        # 使用统一接口,而非直接赋值
        # 基类默认替换,ModelSlim 合并,避免丢失嵌套结构
        if quant_config is not None:
            quant_config.update_packed_modules_mapping(self.packed_modules_mapping)
​
        self.pp_group = get_pp_group()
        self.config = config
        # ...
python/sglang/srt/layers/quantization/modelslim/modelslim.py data-contract

为 ModelSlimConfig 覆盖 update_packed_modules_mapping 方法,实现合并语义。

# python/sglang/srt/layers/quantization/modelslim/modelslim.py 第 111-112 行class ModelSlimConfig(QuantizationConfig):
    # ...
    def update_packed_modules_mapping(self, mapping: Dict[str, List[str]]) -> None:
        # 合并而非替换,以保留加载器设置的嵌套结构
        self.packed_modules_mapping.update(mapping)
    # ...
python/sglang/srt/layers/quantization/base_config.py core-logic

基类新增 update_packed_modules_mapping 方法,提供默认实现(直接替换),确保与其他量化方法兼容。

# python/sglang/srt/layers/quantization/base_config.py 第 126-135 行class QuantizationConfig(ABC):
    def __init__(self):
        super().__init__()
        # 此映射会在模型初始化时由模型更新
        self.packed_modules_mapping: Dict[str, List[str]] = dict()
​
    def update_packed_modules_mapping(
        self, mapping: Dict[str, List[str]]
    ) -> None:
        # 默认行为:直接替换(适用于大多数量化方法)
        self.packed_modules_mapping = mapping
    # ...

评论区精华

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

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

风险与影响

该变更影响 DeepSeek V2/V3 在 NPU(ModelSlim 量化)上的模型加载流程。风险较低,因为基类的默认行为(直接替换)与非 NPU 量化路径一致。但缺少单元测试覆盖新增的 update_packed_modules_mapping 方法,未来重构可能忽略此行为差异。

直接影响 NPU 上使用 ModelSlim 量化加载 DeepSeek 模型的用户,修复了模型加载崩溃。对 GPU 上使用其他量化方法(如 GPTQ、FP4)的用户无影响,因为基类默认行为不变。

NPU 特定修复 缺少测试覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论