首页
/ vLLM 中的 LLM Compressor:FP8/INT8/INT4 量化模型的生产部署实战指南

vLLM 中的 LLM Compressor:FP8/INT8/INT4 量化模型的生产部署实战指南

2026-09-04 23:10:52作者:秋泉律Samson

本文围绕 vLLM 官方量化文档入口 LLM Compressor 指南 展开,系统讲解如何用 llm-compressor 库将大模型压缩为 FP8、INT8、INT4、W4A8 等低精度格式,并无缝加载进 vLLM 部署。读完本文,你可以独立完成「加载模型 → 准备校准数据 → 一键量化(oneshot)→ vLLM 推理验证 → lm_eval 精度评估」的完整量化流水线,并理解 vLLM 源码中 compressed-tensors 格式的加载与调度机制。

什么是 LLM Compressor,为什么需要它

现代 LLM 通常包含数十亿乃至数百亿参数,且多以 16/32 位浮点存储,对 GPU 显存要求很高,直接限制了部署选项。量化(Quantization)通过把模型权重与激活的精度降到更小的数据类型,在几乎不损失推理质量的前提下降低显存占用。LLM Compressor 正是 vLLM 量化生态的「制作端」:它负责量化算法、校准数据与格式转换的全部复杂度,最终产出可直接被 vLLM 加载的模型(compressed-tensors 格式)。

按官方文档,使用 LLM Compressor 的三大收益是:

  • 更小的内存占用:在更小的 GPU 上运行更大的模型;
  • 更低的推理成本:单卡可服务更多并发用户,直接降低生产环境单次查询成本;
  • 更快的推理速度:更小的数据类型意味着更低的内存带宽消耗,对内存带宽受限(memory-bound)的工作负载往往能转化为更高吞吐。

其关键特性包括:

  • 多种量化算法:支持 AWQ、GPTQ、AutoRound、Round-to-Nearest(RTN),并支持 QuIP、SpinQuant 风格的变换(transform),以及 KV cache 与注意力量化;
  • 多种量化方法:支持 FP8、INT8、INT4、NVFP4、MXFP4 以及混合精度量化;
  • One-Shot 量化:只需极少的校准数据即可快速完成量化;
  • vLLM 深度集成:量化产物使用 compressed-tensors 格式,vLLM 可直接加载部署;
  • Hugging Face 兼容:与 Hugging Face Hub 上的模型无缝配合,量化后通过 save_pretrained 即可保存为标准 HF 模型目录。

在 vLLM 的量化文档体系中,LLM Compressor 是推荐的量化入门路径:量化总览 在开头就提示读者从这里入手,并给出了四个细分格式的完整实操文档:

硬件支持矩阵:先确认你的平台能跑哪种方案

不同量化格式对硬件计算能力(compute capability)的要求不同,选型前必须先核对平台。量化总览 中的兼容矩阵中,与 LLM Compressor 产物直接相关的行如下:

实现 Volta Turing Ampere Ada Hopper AMD GPU x86 CPU Arm CPU
llm-compressor INT8 (W8A8)
llm-compressor INT8 (W4A8)
llm-compressor FP8 (W8A8)

(Volta 指 SM 7.0,Turing 指 SM 7.5,Ampere 指 SM 8.0/8.6,Ada 指 SM 8.9,Hopper 指 SM 9.0。)

结合各细分文档的说明,可以归纳出几条硬约束:

  • FP8 W8A8:NVIDIA GPU 需要 compute capability >= 8.9(Ada/Hopper/Blackwell)才能做 FP8 计算;Turing/Ampere 上 FP8 模型会以权重-only 的 W8A16 模式(Marlin 内核)运行;AMD MI300x 也受支持。FP8 可将模型显存需求降低约 2 倍,吞吐提升最高约 1.6 倍且精度损失极小;
  • INT8 W8A8:NVIDIA GPU 需要 compute capability > 7.5(Turing 起),Blackwell(SM >= 10.0)不支持 INT8,应改用 FP8;x86/Arm CPU 也受支持;
  • INT4 W4A16:NVIDIA GPU 需要 compute capability > 8.0(Ampere 起),适合低 QPS、低延迟场景;
  • INT8 W4A8:主要面向 Arm CPU(文档明确说明在 Arm 平台上通过 KleidiAI 加速),GPU 矩阵中无 ✅。

该矩阵会随 vLLM 演进而变化,最权威的实现清单位于 vllm/model_executor/layers/quantization 目录。

环境准备:双虚拟环境的安装方式

所有量化流程的准备工作一致:量化端和推理端要分开安装、使用不同虚拟环境(官方文档明确提示两者混装可能不兼容):

# 量化环境:安装 llm-compressor
(venv-llm-compressor) pip install llmcompressor
# 推理/评测环境:安装 vllm 与评估框架
(venv-vllm) pip install vllm "lm-eval[api]>=0.4.12"

量化流程总体分为三步或四步:加载模型 →(需要校准数据时)准备校准数据 → 应用量化 → 在 vLLM 中验证精度。下面按四种主流方案分别展开。

FP8 W8A8:无需校准数据的最快路径

FP8(8 位浮点)有两种硬件常见的表示形式,各自适用不同场景:

  • E4M3:1 位符号 + 4 位指数 + 3 位尾数,可存储 +/-448 和 nan
  • E5M2:1 位符号 + 5 位指数 + 2 位尾数,可存储 +/-57344、+/-infnan,动态范围更大但精度更低。

FP8 的一个显著优点是:用简单的 RTN(Round-to-Nearest)量化就能保持精度,因此完全不需要校准数据。推荐方案是 FP8_DYNAMIC,覆盖所有 Linear 层,它采用:权重侧静态 per-channel 量化,激活侧动态 per-token 量化。

1. 加载模型

使用标准 transformers AutoModel 类加载模型与 tokenizer:

from transformers import AutoTokenizer, AutoModelForCausalLM

MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    device_map="auto",
    dtype="auto",
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)

2. 应用 FP8 量化

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import QuantizationModifier

# 配置简单的 PTQ 量化
recipe = QuantizationModifier(
    targets="Linear",   # 量化所有 Linear 层
    scheme="FP8_DYNAMIC",
    ignore=["lm_head"],  # 输出头保持原精度
)

# 应用量化算法(无需 dataset 参数)
oneshot(model=model, recipe=recipe)

# 保存模型:Meta-Llama-3-8B-Instruct-FP8-Dynamic
SAVE_DIR = MODEL_ID.split("/")[1] + "-FP8-Dynamic"
model.save_pretrained(SAVE_DIR)
tokenizer.save_pretrained(SAVE_DIR)

3. 在 vLLM 中评估精度

from vllm import LLM

llm = LLM("./Meta-Llama-3-8B-Instruct-FP8-Dynamic")
result = llm.generate("Hello my name is")
print(result[0].outputs[0].text)

再用 lm_eval 做精度评估(例如 250 条 gsm8k 样本):

MODEL=$PWD/Meta-Llama-3-8B-Instruct-FP8-Dynamic
lm_eval \
  --model vllm \
  --model_args pretrained=$MODEL,add_bos_token=True \
  --tasks gsm8k --num_fewshot 5 --batch_size auto --limit 250

官方示例的输出分数:

|Tasks|Version|     Filter     |n-shot|  Metric   |   |Value|   |Stderr|
| --- |------:| -------------- |-----:| --------- | - |----:| - |-----:|
|gsm8k|      3|flexible-extract|     5|exact_match|↑  |0.768|±  |0.0268|
|     |       |strict-match    |     5|exact_match|↑  |0.768|±  |0.0268|

注意:量化模型对 bos token 的有无可能很敏感,而 lm_eval 默认不添加 bos token,评估时务必带上 add_bos_token=True

FP8 的 GEMM 内核选择与排障

vLLM 加载 FP8 模型时会自动挑选 GEMM 内核,并在启动时打印一行 Selected <kernel> for <module> 日志。从文档描述看,块量化(block-quantized)检查点在 CUDA 上的尝试顺序为:FlashInfer/DeepGEMM 混合内核(仅 Hopper)→ DeepGEMM → CUTLASS → Marlin → Triton → Humming → PyTorch 兜底;没有原生 FP8 支持的 GPU(如 Turing/Ampere)会落到权重-only 的 W8A16 Marlin 内核。

排障要点(来自 FP8 指南):

  • 如果推理挂起但无报错,可尝试 VLLM_USE_DEEP_GEMM=0--linear-backend cutlass
  • --linear-backend 只影响量化后的 Linear 层,MoE 专家走独立的 --moe-backend
  • 显式指定的后端若不被硬件支持会直接报错,而不会静默回退;
  • 完整取值列表可在 CLI 参考的 KernelConfig 章节查阅,或用 vllm serve --help=KernelConfig 查看。

此外,vLLM 还支持在线动态 FP8 量化作为替代路径:不产出量化检查点,直接在加载时把 BF16/FP16 模型量化为 FP8(见 在线量化文档):

from vllm import LLM

llm = LLM("facebook/opt-125m", quantization="fp8")
result = llm.generate("Hello, my name is")

该模式下所有 Linear 模块(除 lm_head)的权重量化为 FP8_E4M3(per-tensor scale),激活则每次前向实时统计 min/max 得到动态 per-tensor scale。由于每次前向都要统计激活范围,该模式的延迟改善有限——对性能敏感的场景,更推荐上面的离线 FP8_DYNAMIC 方案。

INT8 W8A8:SmoothQuant + GPTQ 的校准式量化

INT8 W8A8 需要校准数据来估计激活的量化 scale,因此流程多一步「准备校准数据」。

1. 加载模型

与 FP8 流程相同,使用 AutoModelForCausalLM.from_pretrained(MODEL_ID, device_map="auto", dtype="auto") 加载。

2. 准备校准数据

量化激活到 INT8 需要样本数据估计激活 scale。校准数据应尽量贴近真实部署数据;通用指令模型可用 ultrachat 这类数据集:

from datasets import load_dataset

NUM_CALIBRATION_SAMPLES = 512
MAX_SEQUENCE_LENGTH = 2048

# 加载并预处理数据集
ds = load_dataset("HuggingFaceH4/ultrachat_200k", split="train_sft")
ds = ds.shuffle(seed=42).select(range(NUM_CALIBRATION_SAMPLES))

def preprocess(example):
    return {"text": tokenizer.apply_chat_template(example["messages"], tokenize=False)}
ds = ds.map(preprocess)

def tokenize(sample):
    return tokenizer(sample["text"], padding=False, max_length=MAX_SEQUENCE_LENGTH, truncation=True, add_special_tokens=False)
ds = ds.map(tokenize, remove_columns=ds.column_names)

注意这里使用模型训练时的 chat template 渲染文本——这是官方最佳实践之一。

3. 应用量化(SmoothQuant + GPTQ)

W8A8 的典型 recipe 是先做 SmoothQuant 平滑迁移,再用 GPTQ 量化:

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import GPTQModifier
from llmcompressor.modifiers.smoothquant import SmoothQuantModifier

# 配置量化算法
recipe = [
    SmoothQuantModifier(smoothing_strength=0.8),
    GPTQModifier(targets="Linear", scheme="W8A8", ignore=["lm_head"]),
]

# 应用量化
oneshot(
    model=model,
    dataset=ds,
    recipe=recipe,
    max_seq_length=MAX_SEQUENCE_LENGTH,
    num_calibration_samples=NUM_CALIBRATION_SAMPLES,
)

# 保存压缩模型:Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token
SAVE_DIR = MODEL_ID.split("/")[1] + "-W8A8-Dynamic-Per-Token"
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)

保存时带上 save_compressed=True,产物为权重/激活均为 8 位整数的 W8A8 模型。

4. 在 vLLM 中加载与评估

from vllm import LLM

llm = LLM("./Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token")
lm_eval --model vllm \
  --model_args pretrained="./Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token",add_bos_token=true \
  --tasks gsm8k \
  --num_fewshot 5 \
  --limit 250 \
  --batch_size 'auto'

校准最佳实践

官方文档给出的 W8A8 最佳实践:

  • 校准样本从 512 条起步,精度下降再增加;
  • 序列长度以 2048 作为起点;
  • 使用模型训练时的 chat/instruction template;
  • 若模型经过微调,建议用训练数据的一部分做校准。

INT4 W4A16:GPTQ 权重-only 量化与超参数调优

INT4 将权重压到 4 位整数,显著减小模型体积,适合低 QPS、低延迟负载。流程与 W8A8 类似(加载 → 校准数据 → 量化 → 评估),核心区别在 recipe:

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import GPTQModifier

recipe = GPTQModifier(targets="Linear", scheme="W4A16", ignore=["lm_head"])

oneshot(
    model=model,
    dataset=ds,
    recipe=recipe,
    max_seq_length=MAX_SEQUENCE_LENGTH,
    num_calibration_samples=NUM_CALIBRATION_SAMPLES,
)

# 保存压缩模型:Meta-Llama-3-8B-Instruct-W4A16-G128
SAVE_DIR = MODEL_ID.split("/")[1] + "-W4A16-G128"
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)

产物为默认 group_size=128 分组的 W4A16 模型(G128),vLLM 端可通过 LLM("./Meta-Llama-3-8B-Instruct-W4A16-G128") 直接加载,并用与 W8A8 相同的 lm_eval 命令评估。

INT4 指南 还提供了两个关键 GPTQ 超参数的调优说明:

  • dampening_frac:控制 GPTQ 算法的强度。调低可能提升精度,但过低会引发数值不稳定导致算法失败;
  • actorder:量化权重通道时的排序策略,设为 actorder="weight" 可在不增加延迟的情况下提升精度。

一个完全展开、可自定义的 recipe 示例(基于 compressed_tensors.quantization 的显式配置组):

from compressed_tensors.quantization import (
    QuantizationArgs,
    QuantizationScheme,
    QuantizationStrategy,
    QuantizationType,
)
recipe = GPTQModifier(
    targets="Linear",
    config_groups={
        "config_group": QuantizationScheme(
            targets=["Linear"],
            weights=QuantizationArgs(
                num_bits=4,
                type=QuantizationType.INT,
                strategy=QuantizationStrategy.GROUP,
                group_size=128,
                symmetric=True,
                dynamic=False,
                actorder="weight",
            ),
        ),
    },
    ignore=["lm_head"],
    update_size=NUM_CALIBRATION_SAMPLES,
    dampening_frac=0.01,
)

INT4 的校准最佳实践比 W8A8 多一条:确保校准数据样本多样性充足,防止向特定用例过拟合

INT8 W4A8:面向 Arm CPU 的极致压缩

W4A8 把权重压到 INT4、激活保持 INT8,是模型体积与推理性能的折中方案,W4A8 指南 明确其目标平台是 Arm CPU(通过 KleidiAI 加速)。该格式提供两种权重粒度:

  • Groupwise(分组量化):精度更好;
  • Channelwise(按通道):推理性能更好。

Groupwise 版本的 recipe:

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import GPTQModifier

recipe = [
    GPTQModifier(
        targets="Linear",
        scheme="W4A8",
        ignore=["lm_head"],
        dampening_frac=0.01,
    ),
]

oneshot(
    model=model,
    dataset=ds,
    recipe=recipe,
    max_seq_length=MAX_SEQUENCE_LENGTH,
    num_calibration_samples=NUM_CALIBRATION_SAMPLES,
)

# 保存压缩模型:Meta-Llama-3-8B-Instruct-W4A8-G128-Dynamic-Per-Token
SAVE_DIR = MODEL_ID.split("/")[1] + "-W4A8-G128-Dynamic-Per-Token"
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)

Channelwise 版本则通过显式 config_groups 指定权重 strategy=CHANNELgroup_size=None,激活 strategy=TOKENdynamic=True

from llmcompressor.modifiers.quantization import GPTQModifier
from compressed_tensors.quantization import QuantizationStrategy, QuantizationType

scheme = {
    "targets": ["Linear"],
    "weights": {
        "num_bits": 4,
        "type": QuantizationType.INT,
        "strategy": QuantizationStrategy.CHANNEL,
        "symmetric": True,
        "dynamic": False,
        "group_size": None,
    },
    "input_activations": {
        "num_bits": 8,
        "type": QuantizationType.INT,
        "strategy": QuantizationStrategy.TOKEN,
        "dynamic": True,
        "symmetric": False,
        "observer": None,
    },
    "output_activations": None,
}

recipe = [
    GPTQModifier(
        targets="Linear",
        config_groups={"group_0": scheme},
        ignore=["lm_head"],
        dampening_frac=0.01,
    ),
]

量化后的模型同样用 LLM("./Meta-Llama-3-8B-Instruct-W4A8-...") 加载并跑 lm_eval(记得 add_bos_token=true)。

vLLM 源码视角:compressed-tensors 模型是如何被加载的

上述所有流程的产物都是同一套东西:save_pretrained 写出的 HF 模型目录中带有 quantization config(compressed-tensors 格式)。vLLM 加载时按 quantization_config 分发到对应的 QuantizationConfig 实现。从源码结构看,LLM Compressor 产物的消费端集中在 vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors.py

  • CompressedTensorsConfig 继承自 QuantizationConfigget_name() 返回 "compressed-tensors",这正是 vLLM 识别 LLM Compressor 检查点的标志;
  • 构造函数接收 target_scheme_map(目标层 → 量化方案的映射)、ignore 列表、quant_format、可选的 kv_cache_schemetransform_config(对应文档中提到的 QuIP/SpinQuant 风格变换);
  • get_supported_act_dtypes() 声明支持 float32/float16/bfloat16 激活,get_min_capability() 返回 70(Volta 起可用,具体内核由各 scheme 进一步限制);
  • 同一模块导入了完整的 scheme 家族:CompressedTensorsW8A8Fp8CompressedTensorsW8A8Int8CompressedTensorsW8A16Fp8(即低算力 GPU 上 FP8 模型的 weight-only 回退)、CompressedTensorsW4A8IntCompressedTensorsWNA16CompressedTensorsWNA4Int/CompressedTensorsWNA8Int(GPTQ/AWQ 风格的分组 INT 权重)以及 CompressedTensorsW4A4Fp4/CompressedTensorsW4A4Mxfp4 等 4 位浮点方案——与文档宣称的 FP8/INT8/INT4/NVFP4/MXFP4/混合精度能力一一对应;
  • Linear 层统一走 CompressedTensorsLinearMethod,MoE 模型由 compressed_tensors_moe 下的 CompressedTensorsMoEMethod 处理。

这套结构也解释了硬件矩阵为何能成立:vLLM 不需要用户指定后端,而是依据检查点中的量化 scheme 与 GPU 能力自动选择内核(如 FP8 的 DeepGEMM/CUTLASS/Marlin/Triton 链条、W8A8 的 int8 scaled_mm 路径等),这也是为什么「量化一次、多处部署」在 vLLM 生态里是可行的。更完整的量化方法注册机制(@register_quantization_config 自定义插件)可参考 量化总览 的 Out-of-Tree Quantization Plugins 一节。

常见问题与支持

  • 量化产物对 bos token 敏感:lm_eval 评估务必加 add_bos_token=True
  • FP8 推理挂起:尝试 VLLM_USE_DEEP_GEMM=0--linear-backend cutlass
  • Blackwell GPU 不支持 INT8:改用 FP8 或换 Hopper/Ada/Ampere 平台;
  • vLLM 与 llm-compressor 不要装在同一虚拟环境;
  • 遇到其他问题或有功能需求,到 llm-compressor 上游仓库(vllm-project/llm-compressor)提交 issue;更丰富的量化示例(如完整的 quantization_w4a16/llama3_example.py)可在 llm-compressor 仓库的 examples 目录中查阅。

小结

LLM Compressor 在 vLLM 量化体系中扮演「模型压缩工厂」的角色:oneshot 一行调用即可把 HF 模型转为 compressed-tensors 格式的 FP8 W8A8(免校准)、INT8 W8A8(SmoothQuant+GPTQ)、INT4 W4A16(GPTQ 分组)或 INT8 W4A8(Arm CPU)检查点,随后由 vLLM 的 CompressedTensorsConfig 与 scheme 分发机制自动完成内核选择与部署。选型时的决策链是:平台算力(FP8 需 Ada/Hopper/AMD,W4A8 面向 Arm CPU,Blackwell 避开 INT8)→ 是否需要校准(FP8_DYNAMIC 免校准)→ 精度/吞吐权衡(W8A8 均衡、W4A16 省显存、W4A8 极致压缩)。掌握本文的完整流水线与源码映射关系后,你就可以把任意 HF 模型以正确的低精度格式稳定部署到 vLLM 上了。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384