Prhub

#29761 [Bugfix] compressed-tensors WNA16 MoE: don't assume a "Linear" config group

原始 PR 作者 joerowell 合并时间 2026-07-02 02:08 文件变更 4 提交数 6 评论 7 代码增减 +145 / -8

执行摘要

修复 compressed-tensors WNA16 MoE 加载无 Linear 组的检查点时 KeyError

CompressedTensorsWNA16MoE.init 读取 target_scheme_map["Linear"],对于 config_groups 使用 regex 或每层目标(例如混合精度 INT4/INT8 MoE)且没有命名为 "Linear" 的组的检查点,会抛出 KeyError: 'Linear'。测试在混合精度 int4/int8 MoE 检查点上验证修复前加载失败,修复后正常加载并生成。

该 PR 修复了一个重要的加载阻塞 bug,建议阅读者关注其设计模式:将每层 weight_quant 的解析逻辑收敛在 get_moe_scheme 中,而不是分散在各方案构造函数中。这符合 DRY 原则,也便于未来扩展新的 MoE 量化方案。新增的回归测试同样值得参考。

讨论亮点

BBuf 在代码审查中建议为没有 "Linear" 组的 MoE 配置添加回归测试,以防止 target_scheme_map["Linear"] 回退再次引入。joerowell 采纳了建议并在后续提交中添加了测试文件。

实现拆解

  1. 修改 WNA16 MoE 方案构造函数(compressed_tensors_wNa16_moe.py):添加 weight_quant: QuantizationArgs 参数,替换原本从 self.quant_config.target_scheme_map["Linear"] 读取 weights 的逻辑,直接从 weight_quant 获取每层权重量化参数。
  2. 修改 MxInt4 MoE 方案构造函数(compressed_tensors_w4a4_mxint4_moe.py):同样添加 weight_quant 参数并替换 target_scheme_map["Linear"] 查找。
  3. 更新调用起点(compressed_tensors.py):在 get_moe_scheme 方法中,将已经通过层名匹配解析的 weight_quant 显式传递给各个 MoE 方案构造函数(包括 WNA16 Marlin、Triton 和 MxInt4),而不是仅传入 self
  4. 添加回归测试(test/.../test_compressed_tensors_wna16_moe_no_linear.py):构造没有 "Linear" 组的 MoE 量化配置(使用 regex 或每层 FQN 目标),断言 get_moe_scheme 正确返回 WNA16 方案且不抛出异常。测试运行在 CPU,无权重创建和 kernel 执行。
文件 模块 状态 重要度
python/sglang/srt/layers/quantization/compressed_tensors/schemes/compressed_tensors_wNa16_moe.py 量化层 modified 6.97
test/registered/unit/layers/quantization/test_compressed_tensors_wna16_moe_no_linear.py 测试 added 7.51
python/sglang/srt/layers/quantization/compressed_tensors/compressed_tensors.py 量化层 modified 5.63
python/sglang/srt/layers/quantization/compressed_tensors/schemes/compressed_tensors_w4a4_mxint4_moe.py 量化层 modified 6.31

关键符号

CompressedTensorsWNA16MoE.__init__ CompressedTensorsMxInt4MoE.__init__ CompressedTensorsConfig.get_moe_scheme

关键源码片段

python/sglang/srt/layers/quantization/compressed_tensors/schemes/compressed_tensors_wNa16_moe.py core-logic

核心修复文件:修改 __init__ 方法,添加 weight_quant 参数并移除 target_scheme_map['Linear'] 查找。

from __future__ import annotations
# ... ( 其他导入省略 )class CompressedTensorsWNA16MoE(CompressedTensorsMoEScheme):
​
    def __init__(
        self,
        quant_config: CompressedTensorsConfig,
        weight_quant: QuantizationArgs, # 新增参数:由 get_moe_scheme 预先解析的每层权重量化参数
        num_gpu_experts: int = -1,
    ):
        self.quant_config = quant_config
        # 替换原有 self.quant_config.target_scheme_map["Linear"].get("weights")
        # 混合精度 MoE 检查点可能没有 "Linear" 组,使用已解析的 weight_quant 直接赋值
        config = weight_quant
        self.num_bits = config.num_bits
        self.packed_factor = 32 // config.num_bits
        self.strategy = config.strategy
        self.group_size = config.group_size
        self.actorder = config.actorder
        self.sym = config.symmetric
​
        if not (
            self.quant_config.quant_format == CompressionFormat.pack_quantized.value
            and self.num_bits in WNA16_SUPPORTED_BITS
        ):
            raise ValueError(
                "For Fused MoE layers, only ",
                f"{CompressionFormat.pack_quantized.value} ",
                "is supported for the following bits: ",
                f"{WNA16_SUPPORTED_BITS}",
            )
        self.num_gpu_experts = num_gpu_experts
​
    # ... ( 其他方法不变 )
test/registered/unit/layers/quantization/test_compressed_tensors_wna16_moe_no_linear.py test-coverage

新增回归测试文件,测试没有 Linear 组的 MoE 配置能否正确解析方案,覆盖 INT4/INT8、regex/FQN 目标变体。

"""CPU 回归测试:WNA16 compressed-tensors MoE 在没有 'Linear' 组时能正常工作"""from sglang.srt.layers.quantization.compressed_tensors.compressed_tensors import (
    CompressedTensorsConfig,
)
from sglang.srt.layers.quantization.compressed_tensors.schemes import (
    CompressedTensorsWNA16MoE,
    CompressedTensorsWNA16TritonMoE,
)
from sglang.test.test_utils import CustomTestCase# 允许的 WNA16 MoE 方案(Marlin 或 Triton 后端均可)
_WNA16_MOE_SCHEMES = (CompressedTensorsWNA16MoE, CompressedTensorsWNA16TritonMoE)
EXPERTS_LAYER = "model.layers.0.mlp.experts"
​
​
def _make_wna16_moe_config(targets, num_bits):
    """构造不含 'Linear' 组的 WNA16 MoE 量化配置"""
    return {
        "quant_method": "compressed-tensors",
        "format": "pack-quantized",
        "config_groups": {
            "group_0": {
                "targets": targets,
                "weights": {
                    "num_bits": num_bits,
                    "type": "int",
                    "symmetric": True,
                    "strategy": "group",
                    "group_size": 128,
                },
                "input_activations": None,
            }
        },
        "ignore": ["lm_head", "re:.*self_attn.*", "re:.*mlp.gate$"],
    }
​
​
class TestWNA16MoENoLinearGroup(CustomTestCase):
    """回归测试:确保没有 'Linear' 组时也能正确解析 MoE 方案"""
​
    def _assert_wna16_moe(self, config_dict, expected_bits):
        quant_config = CompressedTensorsConfig.from_config(config_dict)
        # 验证没有 "Linear" 组(预条件,否则就失去了测试意义)
        self.assertNotIn("Linear", quant_config.target_scheme_map)
​
        layer = torch.nn.Module()
        # 调用 get_moe_scheme —— 修复前会抛出 KeyError: 'Linear'
        scheme = quant_config.get_moe_scheme(layer, layer_name=EXPERTS_LAYER)
​
        self.assertIsInstance(scheme, _WNA16_MOE_SCHEMES)
        self.assertEqual(scheme.num_bits, expected_bits)
        self.assertEqual(scheme.group_size, 128)
​
    def test_regex_expert_targets_int4(self):
        config = _make_wna16_moe_config(["re:.*mlp.experts.*"], num_bits=4)
        self._assert_wna16_moe(config, expected_bits=4)
​
    def test_regex_expert_targets_int8(self):
        config = _make_wna16_moe_config(["re:.*mlp.experts.*"], num_bits=8)
        self._assert_wna16_moe(config, expected_bits=8)
​
    def test_per_layer_fqn_expert_targets_int4(self):
        config = _make_wna16_moe_config(
            [f"{EXPERTS_LAYER}.0.gate_proj",
             f"{EXPERTS_LAYER}.0.up_proj",
             f"{EXPERTS_LAYER}.0.down_proj"],
            num_bits=4
        )
        self._assert_wna16_moe(config, expected_bits=4)if __name__ == "__main__":
    unittest.main()

评论区精华

建议添加回归测试 测试

BBuf 建议添加一个针对没有 'Linear' 组的 MoE 配置的回归测试,以防止 future reintroduction。

结论:joerowell 同意并添加了测试文件 test_compressed_tensors_wna16_moe_no_linear.py。 · 已解决

风险与影响

风险较低。主要风险在于方案构造函数签名增加了 weight_quant 参数,但所有调用点已在 get_moe_scheme 中同步更新。如果未来有新的 MoE 方案没有相应更新,可能会导致 TypeError。但该风险通过显式参数传递和静态类型检查可降低。另外,混合精度 MoE 检查点的加载路径以前未经过充分测试,新增的回归测试覆盖了主要变体(INT4/INT8、regex/FQN 目标),减少了回归风险。

直接影响使用 compressed-tensors 量化且包含 MoE 层的用户,特别是使用混合精度(如部分 INT4 部分 INT8)或通过 regex/per-layer 目标指定配置组而没有 "Linear" 组的检查点。之前这些用户会遇到 KeyError 无法加载模型,现在可以正常加载。其他用户(如使用均匀量化和有 "Linear" 组的检查点)不受影响。

内部 API 变更 需要同步其他 MoE 方案

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论