Prhub

#24540 [NPU] [Bugfix] Wan quantization fix

原始 PR 作者 OrangeRedeng 合并时间 2026-05-11 13:32 文件变更 5 提交数 22 评论 11 代码增减 +201 / -183

执行摘要

修复 Wan 模型在 NPU 上的量化方案识别

Issue #24518 报告在合并 PR #23625 后,NPU 上运行 Wan2.2-T2V-A14B-Diffusers-w8a8 量化模型出现 No modelslim compatible scheme 错误。根本原因是 ModelSlim 的 _get_scheme_from_parts 使用模型的内部层名(如 blocks.0.self_attn.to_q)去查询量化配置,但量化配置文件使用架构规范名(如 blocks.0.attn1.to_q)。WanVideo 架构配置中已有 reverse_param_names_mapping 映射,但 ModelSlim 初始化路径未使用该映射,导致查询失败。

该 PR 修复了关键 regression,改动虽小但涉及核心量化配置路径,值得精读。设计上通过 reverse_param_names_mapping 解耦内部命名与规范命名的做法值得关注,可在未来支持更多量化模型时复用。

讨论亮点
  • gemini-code-assist 指出两个关键问题:
    1. from_config 新增参数后,其他调用点未传递该参数可能导致 TypeError(已通过添加默认值 None 解决)。
    2. 在 _get_scheme_from_parts 的 fallback 路径中,mapped_name 未包含 .weight 后缀导致配置查询失败(已通过统一使用 full_weight_name 修复)。
      - ping1jing2 建议将 import get_param_names_mapping 移到 TYPE_CHECKING 块之上,但因循环导入问题未能实现,作者回复 “Tried, not worked”。
      所有讨论已在最终提交中得到解决或说明。

实现拆解

  1. 在 ModelSlimConfig 中接受名称映射:在 init 和 from_config 方法中新增 reverse_param_names_mapping 参数,并利用 get_param_names_mapping 构建内部名称到规范名称的映射器 _name_mapper。
  2. 使用映射器查询量化方案:在 _get_scheme_from_parts 中,先调用映射器将层名转为规范名(若映射器存在),然后基于规范名从 quant_model_description.json 中获取量化类型,并确定正确的 prefix 传递给具体量化方案。
  3. 在加载路径中传递映射:在 transformer_load_utils.py 的 _resolve_quant_config 中,从架构配置读取 reverse_param_names_mapping,并作为参数传入 get_quant_config。
  4. 在量化工具函数中转发:在 quantization_utils.py 的 get_quant_config 中,新增 reverse_param_names_mapping 参数,并在找到 modelslim 配置时传递给 ModelSlimConfig.from_config。
  5. 补充 FSDP 加载支持:在 fsdp_load.py 中添加缺失的量化参数后缀(input_offset、quant_bias、deq_scale)到 sharding 白名单,并设置 requires_grad=False 避免元张量拷贝错误。
  6. 更新性能基线和警告信息:更新 perf_baselines_npu.json 中的基线数据(反映修复后的实际延迟变化),并在 npu/utils.py 中改进 CPU 卸载的性能警告提示。
文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/layers/quantization/modelslim.py 量化配置 modified 7.74
python/sglang/multimodal_gen/runtime/loader/transformer_load_utils.py 模型加载 modified 5.63
python/sglang/multimodal_gen/runtime/utils/quantization_utils.py 量化工具 modified 4.59
python/sglang/srt/hardware_backend/npu/utils.py NPU 工具 modified 3.83
python/sglang/multimodal_gen/test/server/ascend/perf_baselines_npu.json 性能基线 modified 5.36

关键符号

ModelSlimConfig.__init__ ModelSlimConfig.from_config ModelSlimConfig._get_scheme_from_parts get_quant_config _resolve_quant_config

关键源码片段

python/sglang/multimodal_gen/runtime/layers/quantization/modelslim.py data-contract

核心修复文件:接受 reverse_param_names_mapping 并在 _get_scheme_from_parts 中使用映射器转换层名,使量化方案查询能正确匹配架构规范名。

# modelslim.py - 核心变更:接受 reverse_param_names_mapping 用于名称映射
from sglang.multimodal_gen.runtime.loader.utils import get_param_names_mappingclass ModelSlimConfig(QuantizationConfig):
    def __init__(
        self,
        quant_config: Dict[str, Any] = {},
        reverse_param_names_mapping: dict = None, # 新增参数,默认 None 保持兼容
    ):
        super().__init__()
        self.quant_description = quant_config
        # ... 其他初始化 ...
        # 构建映射器:如果提供了映射字典,则生成从内部名到规范名的映射函数
        self._name_mapper = (
            get_param_names_mapping(reverse_param_names_mapping)
            if reverse_param_names_mapping is not None
            else None
        )
​
    @classmethod
    def from_config(
        cls, config: Dict[str, Any], reverse_param_names_mapping: dict = None
    ) -> ModelSlimConfig:
        # 现在将映射参数传递到构造函数
        return cls(config, reverse_param_names_mapping)
​
    # ... 在查询量化方案的方法中:
    def _get_scheme_from_parts(self, layer_name: str) -> ModelSlimLinearScheme:
        full_weight_name = layer_name + ".weight"
        if self._name_mapper is not None:
            # 使用映射器将内部层名转为规范名(例如 blocks.0.self_attn.to_q -> blocks.0.attn1.to_q)
            mapped_name, _, _ = self._name_mapper(full_weight_name)
        else:
            mapped_name = full_weight_name
​
        quant_type = self.quant_description.get(mapped_name, "")
        prefix = mapped_name.removesuffix(".weight")
​
        if quant_type == "W8A8_DYNAMIC" or quant_type == "W8A8":
            return ModelSlimW8A8Int8(quant_config=self.quant_description, prefix=prefix)
        elif quant_type == "W4A4_DYNAMIC":
            return ModelSlimW4A4Int4(quant_config=self.quant_description, prefix=prefix)
        # ... 其他方案

评论区精华

from_config 新增参数可能破坏其他调用点 设计

gemini-code-assist 指出 from_config 签名变化可能导致未传递新参数的调用点出错。

结论:作者为 reverse_param_names_mapping 添加默认值 None,保持向后兼容。已解决。 · 已解决

查询量化配置时缺少 .weight 后缀 正确性

gemini-code-assist 指出在 _name_mapper 为 None 的 fallback 路径中,mapped_name 缺少 .weight 后缀,导致配置查找失败。

结论:作者通过统一使用 full_weight_name = layer_name + '.weight' 修复。 · 已解决

get_param_names_mapping 导入位置 style

ping1jing2 建议将 import 移到 TYPE_CHECKING 块之上,但作者尝试后因循环导入无法实现。

结论:保留当前位置,因循环导入限制。 · 已解决

风险与影响

  • 回归风险:修改 ModelSlimConfig 初始化签名,但通过默认参数保持向后兼容;其他量化模型加载不受影响。
  • 性能风险:名称映射仅在模型加载时执行一次,对运行时性能无影响。
  • 兼容性:仅影响 NPU 后端的 ModelSlim 量化加载流程,GPU 端不受影响。
  • 测试覆盖:缺乏新增单元测试,仅更新了性能基线(该基线会随模型输出变化,但未验证错误场景)。
  • 用户影响:修复了 Wan2.2-W8A8 量化模型在 NPU 上的加载问题,受影响的用户可直接使用该模型。
  • 系统影响:无。
  • 团队影响:明确了量化配置中名称映射的重要性,为后续类似模型的量化支持提供参考模式。
量化配置路径变更 NPU 专用修复 未添加新测试

关联 Issue

#23625 Flux2 nvfp4 quantization correctness on Blackwell (B200)
#24518 [Bug] [NPU] Wan2.2-T2V-A14B-Diffusers-w8a8 does not work

完整报告

参与讨论