Prhub

#44132 [Quantization] add online fp8 ptpc

原始 PR 作者 walterbm 合并时间 2026-06-08 22:42 文件变更 6 提交数 4 评论 15 代码增减 +385 / -8

执行摘要

新增在线 FP8 per-channel 量化方法

用户在使用在线量化时缺乏 per-channel 粒度的 weight scale + per-token activation 方案。llmcompressor 的 FP8_DYNAMIC 配方精度良好,但要求预量化检查点。本 PR 提供了同等的量化布局,无需预量化,只需指定 --quantization fp8_per_channel。参考 PR body 和 docstring。

值得精读,展示了在线量化框架的扩展方式,包括量化键(QuantKey)、调度表注册、MoE 集成以及测试策略。关注 MarlinFP8 兼容性检查和 ROCm 缺失问题。

讨论亮点
  • 测试位置:AndreasKaratzas 建议将精度测试合入已有文件,作者解释 tests/models/quantization/ 是质量测试的规范位置。
  • 精度验证:AndreasKaratzas 质疑 BF16 与 FP8 的 allclose,作者确认仅在 B200 上通过,reviewer 建议添加小容差避免脆性,但最终未修改。
  • ROCm 支持:divakar-amd 指出 fp8_per_channel 未在 ROCm 支持列表中,要求跳过测试。作者采纳并修改了 skip 条件。
  • 后端兼容性:AndreasKaratzas 询问 AITER 后端是否兼容,作者未回复。

实现拆解

  1. 新增量化方法类:在 vllm/model_executor/layers/quantization/online/fp8.py 中添加 Fp8PtpcOnlineLinearMethodFp8PtpcOnlineMoEMethod,分别继承 _Fp8OnlineLinearBase_Fp8OnlineMoEBase。使用 kFp8StaticChannelSym (权重) 和 kFp8DynamicTokenSym (激活) 量化键。
  2. 配置注册:在 vllm/config/quantization.py 中添加 fp8_per_channel 缩写,映射到 QuantSpec(weight=kFp8StaticChannelSym),并注册 QUANT_KEY_NAMES
  3. 调度表更新:在 vllm/model_executor/layers/quantization/online/base.py 中将 kFp8StaticChannelSym 映射到新方法类,更新 _ONLINE_LINEAR_METHODS_ONLINE_MOE_METHODS
  4. 入口注册:在 vllm/model_executor/layers/quantization/__init__.py_ONLINE_SHORTHANDS 校验列表中添加 "fp8_per_channel"
  5. 测试配套:新增 tests/quantization/test_fp8_per_channel.py 测试注册表一致性和 kernel 行为;新增 tests/models/quantization/test_fp8_per_channel.py 进行端到端 logprobs 对比(使用 dense 和 MoE 模型)。
  6. ROCm 兼容处理:测试中使用 is_quant_method_supported("fp8_per_channel") 跳过不支持平台(如 ROCm)。
文件 模块 状态 重要度
vllm/model_executor/layers/quantization/online/fp8.py 量化方法 modified 8.98
vllm/model_executor/layers/quantization/online/base.py 量化框架 modified 5.74
vllm/config/quantization.py 配置层 modified 5.27
vllm/model_executor/layers/quantization/__init__.py 量化入口 modified 4.39
tests/quantization/test_fp8_per_channel.py 量化测试 added 7.46
tests/models/quantization/test_fp8_per_channel.py 端到端测试 added 6.89

关键符号

Fp8PtpcOnlineLinearMethod.create_weights Fp8PtpcOnlineLinearMethod.process_weights_after_loading Fp8PtpcOnlineLinearMethod.apply Fp8PtpcOnlineMoEMethod.__init__ _Fp8OnlineMoEBase.__init__ ( 重构 )

关键源码片段

vllm/model_executor/layers/quantization/online/fp8.py core-logic

核心实现,新增 Fp8PtpcOnlineLinearMethod 和 Fp8PtpcOnlineMoEMethod,包含权重创建、处理和应用逻辑。

class Fp8PtpcOnlineLinearMethod(_Fp8OnlineLinearBase):
    """Online PTPC FP8 linear quantization.    Per-output-channel weight scale + dynamic per-token activation scale. The
    layout matches the llmcompressor's FP8_DYNAMIC recipe, so accuracy
    is comparable but no pre-quantized checkpoint is required.
    """
​
    weight_quant_key = kFp8StaticChannelSym
    activation_quant_key = kFp8DynamicTokenSym
​
    def create_weights(
        self,
        layer: torch.nn.Module,
        input_size_per_partition: int,
        output_partition_sizes: list[int],
        input_size: int,
        output_size: int,
        params_dtype: torch.dtype,
        **extra_weight_attrs,
    ):
        # 调用父类创建原始权重(meta device 上的 fp16/bf16)
        super().create_weights(
            layer,
            input_size_per_partition,
            output_partition_sizes,
            input_size,
            output_size,
            params_dtype,
            **extra_weight_attrs,
        )
​
        # 根据量化键初始化 FP8 线性 kernel(自动选择 Cutlass / Rocm / Triton)
        self.fp8_linear = init_fp8_linear_kernel(
            activation_quant_key=self.activation_quant_key,
            weight_quant_key=self.weight_quant_key,
            weight_shape=layer.weight.shape,
            input_dtype=self.input_dtype,
            out_dtype=self.out_dtype,
            module_name=self.__class__.__name__,
        )
        # PTPC 需要 per-token activation 量化,MarlinFP8 是 W8A16 仅权重量化,不允许
        if isinstance(self.fp8_linear, MarlinFP8ScaledMMLinearKernel):
            raise ValueError(
                "FP8 PTPC online quant requires a kernel that honors "
                "per-token activation quantization; MarlinFP8 is W8A16 "
                "weight-only. Requires SM89+ for Cutlass FP8 or ROCm MI3xx "
                "for rowwise scaled_mm."
            )
​
    def process_weights_after_loading(self, layer: Module) -> None:
        # 防止重复处理(如权重重载时)
        if getattr(layer, "_already_called_process_weights_after_loading", False):
            return
​
        layer.input_scale = None
        # 对权重进行 per-channel 量化:scale 维度为 [out_channels, 1]
        qweight, weight_scale = ops.scaled_fp8_quant(
            layer.weight, scale=None, use_per_token_if_dynamic=True
        )
​
        replace_parameter(layer, "weight", qweight.t())
        replace_parameter(layer, "weight_scale", weight_scale)
​
        self.fp8_linear.process_weights_after_loading(layer)
​
        layer._already_called_process_weights_after_loading = True
​
    def apply(
        self,
        layer: torch.nn.Module,
        x: torch.Tensor,
        bias: torch.Tensor | None = None,
    ) -> torch.Tensor:
        # 批处理不变性已在 apply_weights 中处理
        return self.fp8_linear.apply_weights(layer, x, bias)

评论区精华

测试文件位置 style

AndreasKaratzas 建议将精度测试合入已有 `tests/quantization/test_fp8_ptpc.py`,认为新文件多余。作者解释 `tests/models/quantization/` 是质量测试的规范位置。

结论:AndreasKaratzas 收回建议,同意保持新文件。 · 已解决

精度验证方法 测试

AndreasKaratzas 质疑 BF16 和 FP8 如何 allclose,作者回应仅测试 B200 且 `check_logprobs_close` 只比较 top-k 候选 token,不检查实际概率值。AndreasKaratzas 建议添加小容差避免脆性,作者未修改。

结论:保持现有方式,未添加额外容差。 · 已解决

ROCm 兼容性 设计

divakar-amd 指出测试在 ROCm 上失败,因为 `fp8_per_channel` 未在 `rocm.py` 支持列表中。建议跳过测试。

结论:作者采纳建议,将测试 skipif 条件从 `is_quant_method_supported("fp8")` 改为 `is_quant_method_supported("fp8_per_channel")`。 · 已解决

AITER 后端兼容性 question

AndreasKaratzas 询问 AITER 后端是否兼容新 MoE 方法(cc @divakar-amd)。

结论:未得到明确回复,可能需后续跟进。 · unresolved

风险与影响

  • ROCm 兼容性fp8_per_channel 未在 rocm.py 注册,测试已跳过,但若用户强行使用会导致运行时错误。
  • MarlinFP8 检测:在 create_weights 中显式检查 MarlinFP8ScaledMMLinearKernel 并抛出 ValueError,防止误用。
  • 精度风险:仅使用 logprobs top-k 对比(非实际数值 allclose),可能掩盖细微精度损失。
  • 硬件覆盖:精度测试仅在 B200 (NVIDIA) 上通过,其他 GPU 上未验证。
  • 用户:可通过 --quantization fp8_per_channel 使用新的在线量化方案,提升 FP8 权重量化粒度。
  • 系统:新增两个量化方法类,扩展在线量化框架,对现有功能无影响(未修改任何已有方法)。
  • 团队:为后续 per-channel 量化方案提供了可复用的基类和注册模式。
ROCm 兼容性待验证 MarlinFP8 不兼容运行时检测 精度测试仅比较 top-k 而非数值 AITER 后端兼容性未确认

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论