Prhub

#48451 [feature]Add int4 quantization support for emulation moe backend

原始 PR 作者 qli88 合并时间 2026-07-16 10:00 文件变更 9 提交数 7 评论 9 代码增减 +1653 / -10

执行摘要

为 ROCm 添加 int4 量化模拟 MoE 后端

ROCm 平台缺乏支持非对称 int4 量化的 MoE 后端,因此需要模拟后端以运行 int4 量化模型(如 cyankiwi/MiniMax-M3-AWQ-INT4),见 PR body 描述。

值得精读,尤其是 Int4EmulationTritonExperts 的反量化设计模式(加载时一次性转换,前向保持纯浮点),以及如何无侵入地在现有路由系统中插入新后端。对需要在无原生 int4 内核平台上运行量化模型的场景有参考价值。

讨论亮点
  • 测试代码风格:tjtanaa 要求遵循函数式测试而非类风格,qli88 重构为纯函数。
  • 变量命名:tjtanaa 建议列表变量加 _list 后缀,qli88 采纳。
  • 专家并行(EP)支持:tjtanaa 询问是否支持 EP,若支持需添加测试;qli88 后来添加了 EP 测试用例。
    • 最终由 tjtanaa 批准(LGTM)。

实现拆解

  1. 新增专家类vllm/model_executor/layers/fused_moe/experts/int4_emulation_moe.py 中定义 Int4EmulationTritonExperts,继承 TritonExperts,在 __init__ 中清除 quant_config 中的 int4 dtype 和 scale 字段,使基类认为权重是浮点;apply() 中校验权重类型并直接调用父类方法。
  2. 扩展 Oracle 层vllm/model_executor/layers/fused_moe/oracle/int_wna16.py 中新增 WNA16MoEBackend.EMULATION 枚举,并在 backend_to_kernel_cls_get_priority_backendsmap_wna16_backendmake_wna16_moe_kernel 等关键路由函数中串联 emulation 后端;同时实现 _unpack_and_dequant_int4_gptq_unpack_and_dequant_int4_awq 等反量化辅助函数供 weight loading 调用。
  3. 压缩张量方法适配:在 compressed_tensors_moe.py 中拦截 moe_backend == "emulation" 分支,路由到 CompressedTensorsWNA16MarlinMoEMethod;并在 compressed_tensors_moe_wna16_marlin.py 中调整 process_weights_after_loading 以跳过 Marlin 专属的 workspace 分配、增加 g_idx 等参数的 None 保护。
  4. 配置扩展config.py 中为 int4_w4a16_moe_quant_configint8_w8a16_moe_quant_config 增加 gemm1_clamp_limit/alpha/beta 参数,支持 SWIGLU 的 clamp 配置。
  5. 测试配套:新增 tests/kernels/quantization/test_int4_emulation_moe.py,包含反量化单元测试(对照参考实现)和端到端 MoE 前向测试,覆盖对称/非对称量化以及专家并行(EP)场景。
文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/experts/int4_emulation_moe.py MoE 层 added 9.23
vllm/model_executor/layers/fused_moe/oracle/int_wna16.py MoE 层 modified 9.21
tests/kernels/quantization/test_int4_emulation_moe.py 测试 added 8.14
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16_marlin.py 量化 modified 6.6
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe.py 量化 modified 6.43
vllm/model_executor/layers/fused_moe/config.py 配置 modified 6.02

关键符号

Int4EmulationTritonExperts.__init__ Int4EmulationTritonExperts.apply Int4EmulationTritonExperts._supports_quant_scheme _unpack_and_dequant_int4_gptq _unpack_and_dequant_int4_awq _process_weights_emulation_gptq _process_weights_emulation_awq backend_to_kernel_cls map_wna16_backend make_wna16_moe_kernel

关键源码片段

vllm/model_executor/layers/fused_moe/oracle/int_wna16.py core-logic

核心路由文件,新增 EMULATION 后端枚举、反量化辅助函数以及后端选择逻辑。

# 以下展示 WNA16MoEBackend 枚举新增 EMULATION 以及 backend_to_kernel_cls 中的分发逻辑class WNA16MoEBackend(Enum):
    MARLIN = "MARLIN"
    BATCHED_MARLIN = "BATCHED_MARLIN"
    HUMMING = "HUMMING"
    CPU = "CPU"
    FLASHINFER_TRTLLM = "FLASHINFER_TRTLLM"
    XPU = "XPU"
    EMULATION = "EMULATION" # 新增枚举值
​
​
def backend_to_kernel_cls(
    backend: WNA16MoEBackend,
) -> list[type[mk.FusedMoEExperts]]:
    if backend == WNA16MoEBackend.EMULATION:
        from vllm.model_executor.layers.fused_moe.experts.int4_emulation_moe import (
            Int4EmulationTritonExperts,
        )
        return [Int4EmulationTritonExperts]
    # 其他后端保持不变 ...# 反量化辅助函数示例:GPTQ 格式 unpack+dequantdef _unpack_and_dequant_int4_gptq(
    w_int32: torch.Tensor, # [n_groups, N//8, 1] int32 — packed 权重
    scale: torch.Tensor, # [n_groups, N] float — 每组缩放因子
    qzeros: torch.Tensor, # [n_groups, N//8] int32 — packed 零点
) -> torch.Tensor:
    """Unpack GPTQ int4 权重并反量化为 BF16."""
    # 实际实现略,流程:解包 -> 应用零点 -> 乘以 scale -> 返回 BF16
    ...
tests/kernels/quantization/test_int4_emulation_moe.py test-coverage

新增完整测试套件,覆盖反量化单元测试和端到端前向测试(含 EP)。

# 测试反量化函数 _unpack_and_dequant_int4_gptq 的正确性import torch
import pytest
from vllm.model_executor.layers.fused_moe.oracle.int_wna16 import (
    _unpack_and_dequant_int4_gptq,
)def _dequantize_ref(w_uint, scale, zero=None, dtype=torch.bfloat16):
    """参考反量化:手动计算 (unpacked_weights - zero) * scale"""
    K, N = w_uint.shape
    n_groups = scale.shape[0]
    group_size = K // n_groups
    w = w_uint.reshape(n_groups, group_size, N).to(dtype)
    s = scale.unsqueeze(1).to(dtype)
    if zero is None:
        return ((w - 8) * s).reshape(K, N)
    z = zero.unsqueeze(1).to(dtype)
    return ((w - z) * s).reshape(K, N)
​
​
class TestUnpackDequantGPTQ:
    @pytest.mark.parametrize("E,K,N,group_size", [
        (2, 64, 32, 32),
        (4, 128, 64, 64),
    ])
    def test_gptq(self, E, K, N, group_size):
        # 构造随机 int4 权重并打包
        w_fp = torch.randn(E, K, N, dtype=torch.bfloat16, device="cuda")
        # 模拟 GPTQ 打包 + 反量化 ...
        result = _unpack_and_dequant_int4_gptq(packed, scale, zeros)
        expected = _dequantize_ref(unpacked, scale, zeros)
        torch.testing.assert_close(result, expected, atol=1e-2, rtol=1e-2)

评论区精华

测试代码风格:类风格 vs 函数风格 style

tjtanaa 在 review 中要求“遵循常规语法,不要引入类风格的单元测试”,原始测试使用了 unittest.TestCase 类风格。

结论:qli88 移除了类风格测试,重构为纯函数式 pytest 测试。 · 已解决

变量命名:添加 _list 后缀 style

tjtanaa 建议“保持这些变量明确使用 `_list` 以获得更好的语义”,针对测试中的 list 变量。

结论:qli88 添加了 `_list` 后缀,如 `shapes_list` 等。 · 已解决

专家并行(EP)支持 设计

tjtanaa 询问“是否支持 EP?如果不支持请定义 / 覆盖相关方法;如果支持请添加单元测试”。

结论:qli88 回复“添加了 EP 测试用例”,后续提交增加了 EP 端到端测试。 · 已解决

风险与影响

  • 精度风险:反量化后再以 BF16 前向 vs 原生 int4 内核存在数值差异,GSM8K 91.2% 可接受,但其他任务可能需要验证。
  • 性能开销:反量化在加载时一次性完成,但权重存储膨胀为 BF16(2x 显存),可能限制 batch size。
  • 兼容性Int4EmulationTritonExperts._supports_current_device 仅返回 is_cuda_alike(),在非 CUDA/ROCm 平台测试会被跳过。
  • 耦合风险:该实现依赖 TritonExperts 的接口,若基类变更需同步维护。
  • 用户:ROCm 用户可通过 --moe-backend emulation 运行 int4 量化 MoE 模型(如 MiniMax-M3-AWQ-INT4),此前不可用。
  • 系统:新增一个后端选择分支,不影响现有 Marlin/Humming 等后端,但压缩张量方法中 emulation 路由暂未与 --moe-backend 完全解耦(见 TODO 注释)。
  • 团队:需要维护新的模拟路径,但代码量小且模型层单一(继承自 TritonExperts),维护成本可控。
精度风险 显存翻倍 仅 CUDA-like 设备 新增维护分支 压缩张量路由未完全解耦

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论