vLLM INT8 W8A8 量化:Blackwell 卡不支持,环境还得和推理分开
vLLM 支持把权重和激活都量化为 INT8(W8A8),节省内存、加快推理,模型体积减小的同时性能基本保持。下面是完整的离线量化流程、评估方式和几个常踩的坑。
硬件与依赖边界
注意:INT8 计算只在计算能力大于 7.5 的 NVIDIA GPU 上得到支持,覆盖 Turing、Ampere、Ada Lovelace、Hopper。
Blackwell 限制:计算能力 >= 10.0 的 GPU(如 RTX 6000 Blackwell)不支持 INT8,需要改用 FP8 量化,或者换到 Hopper / Ada / Ampere 架构上运行。
依赖:
pip install llmcompressor==0.1.0
另外需要安装 vllm 和 lm-evaluation-harness 用于评估。
环境要分开:请为 vLLM 和 llm-compressor 使用独立的 Python 环境,两者混装在同一环境里可能无法协同工作(依赖版本冲突)。量化是一次性离线流程,推理是线上常驻流程,两者解耦之后各自升级互不影响。
量化过程分 4 个主要步骤:加载模型 → 准备校准数据 → 应用量化 → 评估 vLLM 的准确性。
1)加载模型
使用包装了 AutoModelForCausalLM 的 SparseAutoModelForCausalLM:
from llmcompressor.transformers import SparseAutoModelForCausalLM
from transformers import AutoTokenizer
MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
model = SparseAutoModelForCausalLM.from_pretrained(
MODEL_ID, device_map="auto", torch_dtype="auto"
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
2)准备校准数据
把激活值量化为 INT8 时,需要样本数据来估计激活尺度,最好使用与部署数据高度匹配的校准数据。通用指令微调模型可以用 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)
3)应用量化
from llmcompressor.transformers 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,
)
SAVE_DIR = MODEL_ID.split("/")[1] + "-W8A8-Dynamic-Per-Token"
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)
这个过程创建一个 W8A8 模型,权重和激活都量化为 8 位整数。
4)评估准确性
量化后可以直接在 vLLM 加载运行:
from vllm import LLM
model = LLM("./Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token")
使用 lm_eval 评估:
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'
注意:量化模型可能对 bos token 的存在很敏感,评估时务必包含 add_bos_token=True,否则分数可能异常波动。
最佳实践
- 以 512 个样本作为校准数据开始(如果准确性下降则增加样本数量)
- 使用序列长度 2048 作为起点
- 采用模型所训练的聊天模板或指令模板
- 如果已经微调过模型,考虑使用一部分训练数据进行校准
- 量化时固定忽略
lm_head输出层
故障排除
- Blackwell 上无法使用 INT8:CC >= 10.0 的 GPU 不支持该方案,改用 FP8 量化或换用 Hopper / Ada / Ampere。
- 加载时报
No compressed-tensors compatible scheme:用报错中打印的weight_quant/input_quant字段检查位宽、策略与动态性是否落在受支持组合内。 - 量化环境与推理环境冲突:确认 llm-compressor 与 vLLM 分环境部署。
- 更复杂的量化问题:到 vllm-project/llm-compressor 仓库 issue 区提交。
vLLM 加载时如何分派量化方案
vLLM 加载时会读取 checkpoint 的 quantization_config(compressed-tensors 格式),按权重尺度策略(channel / tensor)× 激活动态/静态 × 激活是否对称自动分派到 CompressedTensorsW8A8Int8 方案,无需手动指定量化参数。compressed-tensors 格式的消费端在 vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors.py。
相关链接
- llm-compressor 仓库:
- Hugging Face 上可直接用于 vLLM 的 INT8 量化 checkpoint 集合
- vLLM 量化文档
Tags: int8量化, vLLM, llm-compressor, GPTQ, SmoothQuant, W8A8