vLLM 高性能推理部署指南 / 06 - 模型量化
06 - 模型量化
通过量化技术在更少的 GPU 上运行更大的模型,同时保持可接受的精度。
6.1 量化概述
6.1.1 什么是模型量化?
模型量化(Quantization)是将模型权重从高精度浮点数(如 FP16/BF16)转换为低精度表示(如 INT8/INT4)的技术,以减小模型体积和加速推理。
原始权重(FP16): 0.00732 → 量化后(INT8): 127
精度: 16 bits → 8 bits
体积: 减半
速度: 快 1.5-2x
6.1.2 量化类型
| 类型 | 说明 | vLLM 支持 |
|---|---|---|
| 权重量化 | 仅量化模型权重 | ✅ |
| 权重量化 + 激活量化 | 权重和激活都量化 | ✅(FP8) |
| KV Cache 量化 | 量化 KV Cache | ✅(FP8) |
| GPTQ/AWQ | 训练后量化(PTQ) | ✅ |
| BitsAndBytes | 动态量化 | ✅ |
6.1.3 量化方案对比
| 方案 | 量化比特 | 精度损失 | 推理加速 | 模型大小缩减 | GPU 兼容性 | 推荐度 |
|---|---|---|---|---|---|---|
| AWQ | 4-bit | 低 | 1.5-2x | ~75% | 通用 | ⭐⭐⭐⭐⭐ |
| GPTQ | 4-bit | 低 | 1.3-1.8x | ~75% | 通用 | ⭐⭐⭐⭐ |
| FP8 | 8-bit | 极低 | 1.5x | 50% | H100+ | ⭐⭐⭐⭐⭐ |
| INT8 (LLM.int8()) | 8-bit | 中 | 1.2x | 50% | 通用 | ⭐⭐⭐ |
| BitsAndBytes | 4/8-bit | 中-高 | 有限 | 50-75% | 通用 | ⭐⭐ |
| SqueezeLLM | 4-bit | 中 | 1.3x | ~75% | 通用 | ⭐⭐ |
6.2 AWQ 量化
6.2.1 原理
AWQ(Activation-aware Weight Quantization)的核心思想是:不是所有权重同等重要,应该根据激活值的分布来决定量化的精度。
传统量化:对所有权重同等对待
AWQ 量化:保护重要权重(激活值大的通道)的精度
关键观察:
- 少数权重通道的激活值远大于其他通道
- 这些"显著通道"(salient channels)对模型质量影响巨大
- 保护这些通道可以大幅减少量化误差
6.2.2 使用预量化 AWQ 模型
# awq_inference.py
"""使用 AWQ 量化模型推理"""
from vllm import LLM, SamplingParams
# 直接加载 AWQ 量化模型(推荐从 HuggingFace 下载已量化的模型)
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct-AWQ", # 已量化的 AWQ 模型
quantization="awq",
max_model_len=4096,
gpu_memory_utilization=0.9,
)
sampling_params = SamplingParams(
temperature=0.7,
max_tokens=256,
)
outputs = llm.generate(["什么是量子计算?"], sampling_params)
print(outputs[0].outputs[0].text)
# 命令行启动 AWQ 模型
vllm serve Qwen/Qwen2.5-7B-Instruct-AWQ \
--quantization awq \
--served-model-name qwen-7b-awq \
--max-model-len 4096
6.2.3 自行量化模型
# awq_quantize.py
"""自行对模型进行 AWQ 量化"""
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = "Qwen/Qwen2.5-7B-Instruct"
quant_path = "Qwen2.5-7B-Instruct-AWQ"
# 1. 加载模型
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)
# 2. 准备校准数据(用于确定量化参数)
# 使用一小批代表性数据
calibration_data = [
"人工智能是计算机科学的一个分支。",
"机器学习使计算机能够从数据中学习。",
"深度学习使用多层神经网络。",
# ... 更多校准样本
]
# 3. 执行量化
model.quantize(
tokenizer,
quant_config={
"zero_point": True,
"q_group_size": 128,
"w_bit": 4,
"version": "GEMM",
},
)
# 4. 保存量化模型
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
print(f"量化模型已保存到 {quant_path}")
6.2.4 AWQ 配置参数
| 参数 | 值 | 说明 |
|---|---|---|
w_bit | 4 | 量化位宽(通常为 4) |
q_group_size | 128 | 量化分组大小 |
zero_point | True | 是否使用零点 |
version | “GEMM” | 计算内核版本 |
6.3 GPTQ 量化
6.3.1 原理
GPTQ 基于近似二阶信息的逐层量化方法:
核心思想:
1. 逐层处理:一次量化一层的权重
2. 最优脑损伤(Optimal Brain Quantization):最小化量化误差
3. Hessian 信息:利用二阶导数指导量化顺序
4. 分组量化:将权重分组独立量化
6.3.2 使用 GPTQ 模型
# gptq_inference.py
"""使用 GPTQ 量化模型推理"""
from vllm import LLM, SamplingParams
llm = LLM(
model="TheBloke/Llama-2-7B-Chat-GPTQ",
quantization="gptq",
max_model_len=4096,
dtype="half", # GPTQ 模型通常使用 FP16
)
sampling_params = SamplingParams(temperature=0.7, max_tokens=256)
outputs = llm.generate(["Hello, how are you?"], sampling_params)
print(outputs[0].outputs[0].text)
# 命令行启动
vllm serve TheBloke/Llama-2-7B-Chat-GPTQ \
--quantization gptq \
--dtype half \
--served-model-name llama-7b-gptq
6.3.3 自行量化 GPTQ 模型
# gptq_quantize.py
"""GPTQ 量化示例"""
from transformers import AutoTokenizer
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
model_path = "Qwen/Qwen2.5-7B-Instruct"
quant_path = "Qwen2.5-7B-Instruct-GPTQ"
# 量化配置
quantize_config = BaseQuantizeConfig(
bits=4, # 量化位数:2, 3, 4, 8
group_size=128, # 分组大小
damp_percent=0.01, # 阻尼系数
desc_act=True, # 按激活值降序排列
sym=False, # 非对称量化
)
# 加载模型
model = AutoGPTQForCausalLM.from_pretrained(model_path, quantize_config)
tokenizer = AutoTokenizer.from_pretrained(model_path)
# 准备校准数据
examples = [
tokenizer("Hello world", return_tensors="pt"),
# ... 更多校准样本
]
# 执行量化
model.quantize(examples)
# 保存
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
6.3.4 AWQ vs GPTQ 对比
| 维度 | AWQ | GPTQ |
|---|---|---|
| 量化速度 | 快 | 较慢 |
| 推理速度 | 更快 | 较快 |
| 精度 | 略优 | 好 |
| 显存占用 | 相同 | 相同 |
| 校准数据需求 | 少量 | 少量 |
| 生态支持 | 增长中 | 成熟 |
| vLLM 推荐度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
建议:优先选择 AWQ,它是 vLLM 推荐的量化方案。
6.4 FP8 量化
6.4.1 FP8 格式说明
FP8(8-bit Floating Point)是 Hopper 架构(H100/H200)原生支持的格式:
FP8 有两种变体:
E4M3(推理推荐):
1 位符号 + 4 位指数 + 3 位尾数
动态范围: ±448
精度: 相对较高
E5M2(训练推荐):
1 位符号 + 5 位指数 + 2 位尾数
动态范围: ±57344
精度: 相对较低
6.4.2 使用 FP8 模型
# fp8_inference.py
"""FP8 量化推理"""
from vllm import LLM, SamplingParams
# 方式一:加载已量化的 FP8 模型
llm = LLM(
model="meta-llama/Llama-3.1-70B-Instruct",
quantization="fp8",
max_model_len=4096,
tensor_parallel_size=2,
)
# 方式二:在线 FP8 量化(vLLM 自动量化 FP16 模型)
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
quantization="fp8",
# vLLM 会自动将 FP16 权重转换为 FP8
)
sampling_params = SamplingParams(temperature=0.7, max_tokens=256)
outputs = llm.generate(["What is FP8?"], sampling_params)
print(outputs[0].outputs[0].text)
# 命令行
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--quantization fp8 \
--tensor-parallel-size 4
6.4.3 FP8 硬件要求
重要:FP8 量化仅支持以下 GPU:
- NVIDIA H100(Hopper 架构)
- NVIDIA H200
- NVIDIA L40S(Ada Lovelace 架构)
- NVIDIA RTX 4090(部分支持)
6.4.4 FP8 KV Cache
除了权重量化,vLLM 还支持将 KV Cache 存储为 FP8 格式:
# fp8_kv_cache.py
"""FP8 KV Cache 减少 KV Cache 内存占用"""
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
kv_cache_dtype="fp8", # 使用 FP8 存储 KV Cache
# 每个 block 可节省约 50% 的 KV Cache 内存
)
# 命令行启用 FP8 KV Cache
vllm serve model --kv-cache-dtype fp8
6.5 INT8 量化
6.5.1 LLM.int8() 方法
# int8_inference.py
"""INT8 量化推理"""
from vllm import LLM, SamplingParams
# vLLM 支持通过 BitsAndBytes 加载 INT8 模型
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
load_format="bitsandbytes", # 使用 BitsAndBytes 加载
quantization="bitsandbytes", # INT8 量化
max_model_len=4096,
)
6.5.2 量化的显存节省
| 模型 | FP16 大小 | INT8 大小 | AWQ/GPTQ 4bit | 节省比例 |
|---|---|---|---|---|
| 7B | 14 GB | 7 GB | 3.5 GB | 50-75% |
| 13B | 26 GB | 13 GB | 6.5 GB | 50-75% |
| 34B | 68 GB | 34 GB | 17 GB | 50-75% |
| 70B | 140 GB | 70 GB | 35 GB | 50-75% |
6.6 量化模型的选择指南
6.6.1 决策流程
需要量化?
│
├── GPU 是 H100/H200/L40S?
│ └── 是 → FP8(最佳质量/性能比)
│
├── 需要最大压缩(4-bit)?
│ ├── 是 → AWQ(推荐)或 GPTQ
│ └── 否 → INT8 / FP8
│
└── 快速实验/不确定?
└── BitsAndBytes(简单但性能有限)
6.6.2 场景推荐
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 生产环境(H100) | FP8 | 硬件原生支持,精度损失极小 |
| 生产环境(A100) | AWQ 4-bit | 最佳性能和精度平衡 |
| 大模型挤进单卡 | AWQ 4-bit | 最大压缩比 |
| 多模型部署 | AWQ 4-bit | 减少每模型的显存占用 |
| 快速实验 | BitsAndBytes | 最简单的使用方式 |
| 精度敏感场景 | FP8 或 INT8 | 更高的数值精度 |
6.7 量化对性能的影响
6.7.1 基准测试
# benchmark_quantization.py
"""量化性能基准测试"""
import time
from vllm import LLM, SamplingParams
def benchmark_model(model_name, quantization=None, prompts=None):
"""测试模型性能"""
kwargs = {"model": model_name, "max_model_len": 2048}
if quantization:
kwargs["quantization"] = quantization
llm = LLM(**kwargs)
sampling_params = SamplingParams(temperature=0, max_tokens=128)
# Warmup
llm.generate(prompts[:2], sampling_params)
# Benchmark
start = time.time()
outputs = llm.generate(prompts, sampling_params)
elapsed = time.time() - start
total_tokens = sum(len(o.outputs[0].token_ids) for o in outputs)
return {
"model": model_name,
"quant": quantization or "none",
"time": elapsed,
"tokens": total_tokens,
"throughput": total_tokens / elapsed,
}
prompts = [f"Explain concept {i} in detail." for i in range(20)]
# 对比测试
results = [
benchmark_model("Qwen/Qwen2.5-7B-Instruct", None, prompts),
benchmark_model("Qwen/Qwen2.5-7B-Instruct-AWQ", "awq", prompts),
benchmark_model("Qwen/Qwen2.5-7B-Instruct-GPTQ", "gptq", prompts),
]
for r in results:
print(f"{r['quant']:8s} | {r['time']:.2f}s | {r['throughput']:.1f} tok/s")
6.7.2 预期性能数据
| 配置 | 吞吐量 (tokens/s) | 显存占用 | 质量评估 |
|---|---|---|---|
| FP16 (基线) | 100% | 100% | 最高 |
| FP8 | 95-100% | 50% | 极高 |
| INT8 | 85-95% | 50% | 高 |
| AWQ 4-bit | 90-110% | 25% | 较高 |
| GPTQ 4-bit | 80-100% | 25% | 较高 |
6.8 注意事项
量化质量:4-bit 量化可能导致模型在某些复杂推理任务上质量下降。建议在目标任务上进行评估。
模型兼容性:不是所有模型都有现成的量化版本。如果找不到,可自行量化。
KV Cache 量化:FP8 KV Cache 可独立于权重量化使用,即使权重是 FP16。
组大小(Group Size):量化分组越小精度越高,但推理速度可能略慢。128 是常用的平衡点。
量化校准:校准数据的质量和代表性直接影响量化后的模型质量。建议使用领域相关的校准数据。
6.9 扩展阅读
上一章:05 - 核心架构解析 | 下一章:07 - LoRA 动态适配