Prhub

#29593 [CPU][QUANT] add amx cpu support for auto-round

原始 PR 作者 WeiweiZhang1 合并时间 2026-08-13 15:51 文件变更 6 提交数 32 评论 7 代码增减 +288 / -31

执行摘要

AutoRound INT4 模型 CPU 推理支持上线(Intel AMX)

issue #27568 明确提出“Support auto-round INT4 model offline inference on CPU”,PR body 说明“Currently supports offline inference for CUDA devices with auto-round quantized models, but CPU inference is not yet supported. This is a blocker for users who want to run INT4 quantized models on CPU-only machines or in CPU-based serving pipelines using SGLang as the backend.”即该变更直接服务于无 GPU 环境下的 INT4 模型离线推理与服务化部署诉求。

值得精读,尤其是 auto_round.py 里“配置默认假设显式化 + 一次性日志 + 守卫校验”的组合是量化后端接入的样板:先约束平台/位宽/act-order,再透传完整配置。建议关注三点:get_gptq_config_kwargs 的默认值机制、CPU 分支复用 AWQCPUConfig/CPUGPTQConfig 的接线方式、以及 register_cpu_ci(suite="base-b-test-cpu") 的 CI 分层约定。对要在其他平台(如 NPU/XPU)接入 AutoRound 的开发者有直接借鉴意义。

讨论亮点

讨论集中在三处:

  • jianan-gu 对 CPU GPTQ 分支中硬编码默认值(lm_head_quantized=False, desc_act=False)提出“Shall we have some logs (or assertion) for this default assumptions?”——这直接催生了 log_gptq_default_assumptions_onceget_gptq_config_kwargs 的设计。
  • mingfeima 在文档 diff 上指出实现只支持 INT4 + AMX(指向 check_cpu_support 的 R281-R289),要求文档不得暗示通用 x86/AVX512 支持;作者回复“nice catch”并修正文档,明确仅 Intel AMX。
  • mingfeima 建议将 CPU CI 注册从 base-a-test-cpu 改为 base-b-test-cpu(base-b 由 Intel 托管且为 AMX CPU),测试文件的最终版本已采纳。
    此外 review 状态经历了 mingfeima 的 CHANGES_REQUESTED 到 APPROVED。

实现拆解

1. 扩展 AutoRoundConfig 配置契约

python/sglang/srt/layers/quantization/auto_round.py 中为 AutoRoundConfig 新增 lm_head_quantizeddesc_actdynamiccheckpoint_formattrue_sequentialstatic_groups 等 GPTQ 风格字段,from_config 通过局部函数 has_any_key 检测 checkpoint 配置中缺失的键并记录到 gptq_defaulted_config_keys,用于后续一次性日志提示默认假设。

2. 新增 CPU/AMX 支持守卫与 GPTQ 配置聚合

新增 check_cpu_support(仅允许 4-bit 且要求 cpu_has_amx_support())、check_gptq_support(拒绝 desc_act=True)、log_gptq_default_assumptions_onceget_gptq_config_kwargs。在 apply_awq_quant_layerapply_gptq_quant_layer 中分别插入 _is_cpu 分支,将权重位宽与后端检查通过后,复用 AWQCPUConfig/CPUGPTQConfig 及其 Method 完成 CPU 加载。

3. 注册 CPU 量化方法并修正 MoE/marlin 分支

python/sglang/srt/layers/quantization/__init__.pyCPU_QUANTIZATION_METHODS 注册 auto-round,在 python/sglang/srt/configs/model_config.pyoptimized_quantization_methods 加入 auto-round;同时修正 FusedMoE 层使用 marlin 后端时误走 else: use_marlin = False 的分支判断,并补上 amx_utils.pyis_conv_weight 初始化。

4. 测试与文档配套

新增 test/registered/cpu/quant/test_autoround.pyTestAutoRoundCPUConfig 验证 GPTQ 默认值与 desc_act 拒绝逻辑,TestAutoRoundCPU 以 AMX 为条件(skipUnless(cpu_has_amx_support()))启动 server 跑 MMLU(32 例,阈值 0.25),并注册到 base-b-test-cpu CI suite。文档 docs/docs/advanced_features/quantization.mdx 补充 CPU serving 示例命令与“仅限 Intel AMX 4-bit”的边界说明。

文件 模块 状态 重要度
python/sglang/srt/layers/quantization/auto_round.py 量化层 modified 8.77
test/registered/cpu/quant/test_autoround.py 量化测试 added 7.6
python/sglang/srt/layers/quantization/__init__.py 量化层 modified 4.75
python/sglang/srt/configs/model_config.py 模型配置 modified 4.59
python/sglang/srt/layers/amx_utils.py AMX 工具 modified 4.55
docs/docs/advanced_features/quantization.mdx 量化文档 modified 3.47

关键符号

AutoRoundConfig.from_config AutoRoundConfig.check_cpu_support AutoRoundConfig.check_gptq_support AutoRoundConfig.log_gptq_default_assumptions_once AutoRoundConfig.get_gptq_config_kwargs AutoRoundConfig.apply_awq_quant_layer AutoRoundConfig.apply_gptq_quant_layer

关键源码片段

python/sglang/srt/layers/quantization/auto_round.py core-logic

核心实现:新增 GPTQ 风格配置透传、CPU/AMX 守卫、CPU 分支复用 AWQCPUConfig/CPUGPTQConfig,并修正 FusedMoE+marlin 分支逻辑

# python/sglang/srt/layers/quantization/auto_round.py
# 模块级一次性探测 CPU / AMX 能力,避免在每个 layer 上重复调用
_is_cpu = is_cpu()
_is_cpu_amx_available = cpu_has_amx_support()# AutoRound 的 auto_gptq 导出通常不写全 GPTQ 字段,
# 这里集中定义 SGLang 侧采用的默认值,便于在日志中一次性说明假设
_GPTQ_DEFAULTS = {
    "lm_head_quantized": False,
    "desc_act": False,
    "dynamic": {},
    "checkpoint_format": "",
    "true_sequential": False,
    "static_groups": False,
}
​
​
class AutoRoundConfig(QuantizationConfig):
​
    @classmethod
    def from_config(cls, config: dict[str, Any]) -> "AutoRoundConfig":
        # 记录 checkpoint 配置中缺失的 GPTQ 键,
        # 后续用 log_gptq_default_assumptions_once 提示用户“这里用了默认值”
        def has_any_key(keys: list[str]) -> bool:
            return any(key in config for key in keys)
​
        gptq_config_keys = {
            "lm_head_quantized": ["lm_head", "lm_head_quantized"],
            "desc_act": ["desc_act"],
            "dynamic": ["dynamic"],
            "checkpoint_format": ["checkpoint_format"],
            "true_sequential": ["true_sequential"],
            "static_groups": ["static_groups"],
        }
        gptq_defaulted_config_keys = tuple(
            name for name, keys in gptq_config_keys.items() if not has_any_key(keys)
        )
        # ... 其余字段解析与原来一致,仅新增以下透传项 ...
        return cls(
            # ... 原有 bits/group_size/sym/packing_format/backend 等 ...
            lm_head_quantized=cls.get_from_keys_or(
                config, ["lm_head", "lm_head_quantized"], False
            ),
            desc_act=cls.get_from_keys_or(config, ["desc_act"], False),
            dynamic=cls.get_from_keys_or(config, ["dynamic"], {}) or {},
            gptq_defaulted_config_keys=gptq_defaulted_config_keys,
        )
​
    def check_cpu_support(self, weight_bits: int) -> None:
        # CPU 推理路径当前只支持 4-bit,且必须是 Intel AMX;
        # 其他位宽或非 AMX CPU(含 AMD CPU)直接拒绝,避免错误结果
        if weight_bits != 4:
            raise ValueError(
                "SGLang's AutoRound CPU inference path currently supports "
                "only 4-bit AWQ/GPTQ checkpoints because it uses the Intel "
                f"AMX INT4 backend, but got {weight_bits}-bit."
            )
        if not _is_cpu_amx_available:
            raise ValueError(_CPU_AMX_REQUIRED_MSG)
​
    def get_gptq_config_kwargs(self, weight_bits: int, group_size: int) -> dict[str, Any]:
        # 把“默认假设日志”和“desc_act 守卫”收敛到一处,
        # CPU / NPU 分支统一从这里拿参数,避免各分支硬编码默认值
        self.log_gptq_default_assumptions_once()
        self.check_gptq_support()
        return {
            "weight_bits": weight_bits,
            "group_size": group_size,
            "lm_head_quantized": self.lm_head_quantized,
            "desc_act": self.desc_act,
            "dynamic": self.dynamic,
            "checkpoint_format": self.checkpoint_format,
            "true_sequential": self.true_sequential,
            "static_groups": self.static_groups,
        }
​
    def apply_gptq_quant_layer(self, layer, prefix: str, backend: str = "auto"):
        # ... 前面先解析 weight_bits/group_size/sym ...
        if _is_cpu:
            # CPU 分支:限 4-bit + AMX,直接复用 CPUGPTQConfig 与对应 Method
            self.check_cpu_support(weight_bits)
            from sglang.srt.layers.quantization.gptq import CPUGPTQConfig
​
            quant_args = CPUGPTQConfig(
                **self.get_gptq_config_kwargs(weight_bits, group_size),
            )
            quant_args.sym = sym
            # ... 设置 layer.scheme 并返回 GPTQLinearMethod / GPTQMoEMethod ...
test/registered/cpu/quant/test_autoround.py test-coverage

新增 CPU AutoRound 精度测试,覆盖 GPTQ 默认值 /desc_act 拒绝的单元断言与端到端 MMLU 推理,并注册 Intel base-b CI suite

# test/registered/cpu/quant/test_autoround.py
# 仅注册到 base-b-test-cpu:该 suite 由 Intel 托管且为 AMX CPU,
# 与 base-a(sglang 托管、x86 无 AMX)区分开
register_cpu_ci(est_time=330, suite="base-b-test-cpu")
​
​
class TestAutoRoundCPUConfig(CustomTestCase):
    # 验证缺失字段时 SGLang 采用显式默认值,而不是静默传入 None
    def test_gptq_defaults_are_explicit(self):
        quant_config = AutoRoundConfig.from_config(
            {
                "bits": 4,
                "group_size": 128,
                "sym": True,
                "packing_format": "auto_round:auto_gptq",
            }
        )
        gptq_kwargs = quant_config.get_gptq_config_kwargs(4, 128)
        self.assertFalse(gptq_kwargs["desc_act"])
        self.assertFalse(gptq_kwargs["lm_head_quantized"])
        self.assertEqual(gptq_kwargs["dynamic"], {})
​
    # AutoRound 的 auto_gptq 导出不支持 act-order,
    # 遇到 desc_act=True 必须报错并引导用户改用 gptq/gptq_marlin
    def test_gptq_desc_act_is_rejected(self):
        quant_config = AutoRoundConfig.from_config(
            {
                "bits": 4,
                "group_size": 128,
                "sym": True,
                "packing_format": "auto_round:auto_gptq",
                "desc_act": True,
            }
        )
        with self.assertRaisesRegex(ValueError, "desc_act=False only"):
            quant_config.get_gptq_config_kwargs(4, 128)
​
​
# 非 AMX CPU(如 AMD CPU)直接跳过,避免在错误的硬件上误报失败
@unittest.skipUnless(
    cpu_has_amx_support(),
    "AutoRound INT4 CPU inference requires the Intel AMX CPU backend.",
)
class TestAutoRoundCPU(CustomTestCase):
    @classmethod
    def setUpClass(cls):
        cls.base_url = DEFAULT_URL_FOR_TEST
​
    # 端到端:拉起 CPU server,跑 32 例 MMLU,score 阈值 0.25
    def test_mmlu(self):
        device = "cpu"
        for model in DEFAULT_AUTOROUND_MODEL_NAME_FOR_TEST:
            with self.subTest(model=model):
                process = popen_launch_server(
                    model,
                    self.base_url,
                    timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
                    other_args=["--trust-remote-code", "--quantization", "auto-round"],
                    device=device,
                )
                try:
                    args = SimpleNamespace(
                        base_url=self.base_url,
                        model=model,
                        eval_name="mmlu",
                        num_examples=32,
                        num_threads=32,
                        device=device,
                    )
                    metrics = run_eval(args)
                    self.assertGreaterEqual(metrics["score"], 0.25)
                finally:
                    kill_process_tree(process.pid)

评论区精华

GPTQ 默认假设是否需要日志或断言 设计

jianan-gu 在 CPU GPTQ 分支看到硬编码的 lm_head_quantized=False、desc_act=False 等默认值,询问是否需要日志或断言说明这些默认假设。

结论:作者新增 log_gptq_default_assumptions_once 与 get_gptq_config_kwargs,集中管理默认值并在首次使用时输出日志;同时 check_gptq_support 对 desc_act=True 抛错并引导用户改用 gptq/gptq_marlin。 · 已解决

文档声称通用 x86 支持与实现(仅 AMX)不一致 documentation

mingfeima 指出文档写“Dense layers run on any x86 CPU...AVX512 fallback”,但代码 check_cpu_support 只支持 INT4 + AMX,要求修正文档。

结论:作者承认“nice catch”,将文档改为仅支持 Intel AMX CPU 的 4-bit AutoRound checkpoint,与非 AMX CPU 路径划清界限。 · 已解决

CPU CI suite 选择(base-a vs base-b) infra

mingfeima 解释 base-a 由 sglang 托管且无 AMX,base-b 由 Intel 托管且为 AMX CPU,建议将测试注册到 base-b-test-cpu。

结论:采纳建议,最终 register_cpu_ci(est_time=330, suite="base-b-test-cpu")。 · 已解决

风险与影响

  1. 平台限定风险:CPU 路径硬性要求 Intel AMX(check_cpu_support_is_cpu_amx_available 不满足即抛错),文档已对齐,但用户若误用非 AMX CPU 会得到启动期 ValueError,属预期内但体验较硬。
  2. 行为默认值风险:get_gptq_config_kwargs 会把缺失的 desc_act/lm_head_quantized/dynamic 等按 _GPTQ_DEFAULTS 静默补全,虽然加了“仅记录一次”的日志,但如果 checkpoint 实际是 act-order 的 GPTQModel 导出,check_gptq_support 能拦截 desc_act=True,而 lm_head_quantized 之类的默认 False 并不会校验,仍需依赖测试覆盖。
  3. CPU/MoE 组合风险:文档与代码都限定 MoE 专家仍需 AMX 路径;amx_utils.pyis_conv_weight 初始化修复涉及权重复包路径,若 CPU 上加载含卷积权重的模型(如 Mamba/混合架构),行为可能受影响,改动仅 1 行但需留意回归。
  4. 测试覆盖面:新增测试仅在 AMX CPU 上运行(非 AMX 直接 skip),且注册在 Intel 托管的 CI suite;SGLang 侧 CI 无法兜底验证,回归检测依赖 Intel 侧 CI 稳定性。
  5. 分支耦合:auto_round.py_is_cpu 分支与现有 CUDA/NPU 分支并存,后续若 AWQ/GPTQ CPU 后端接口变化,需要同步维护。

影响范围中等且明确:为 CPU-only 部署(Intel AMX)解锁 AutoRound INT4 推理与 serving 能力,覆盖 auto_round:auto_gptqauto_round:auto_awq 两种 packing 格式;对 CUDA/NPU 现有路径不改变默认行为,但 FusedMoE marlin 分支的条件重构(if isinstance(layer, FusedMoE) 提前返回)会影响所有使用 marlin 后端的 MoE 量化模型,需要回归验证。团队层面,Intel CPU CI(base-b-test-cpu)新增约 330 秒精测任务;文档明确了“仅 Intel AMX + 4-bit”的边界,避免用户误用。

新增平台分支逻辑 硬性 AMX 依赖 默认值静默补全 CI 依赖 Intel 托管 marlin 分支重构

关联 Issue

#27568 [Feature] Support auto-round INT4 model offline inference on CPU

完整报告

参与讨论