强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 兼容性推荐度
AWQ4-bit1.5-2x~75%通用⭐⭐⭐⭐⭐
GPTQ4-bit1.3-1.8x~75%通用⭐⭐⭐⭐
FP88-bit极低1.5x50%H100+⭐⭐⭐⭐⭐
INT8 (LLM.int8())8-bit1.2x50%通用⭐⭐⭐
BitsAndBytes4/8-bit中-高有限50-75%通用⭐⭐
SqueezeLLM4-bit1.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_bit4量化位宽(通常为 4)
q_group_size128量化分组大小
zero_pointTrue是否使用零点
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 对比

维度AWQGPTQ
量化速度较慢
推理速度更快较快
精度略优
显存占用相同相同
校准数据需求少量少量
生态支持增长中成熟
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节省比例
7B14 GB7 GB3.5 GB50-75%
13B26 GB13 GB6.5 GB50-75%
34B68 GB34 GB17 GB50-75%
70B140 GB70 GB35 GB50-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%最高
FP895-100%50%极高
INT885-95%50%
AWQ 4-bit90-110%25%较高
GPTQ 4-bit80-100%25%较高

6.8 注意事项

量化质量:4-bit 量化可能导致模型在某些复杂推理任务上质量下降。建议在目标任务上进行评估。

模型兼容性:不是所有模型都有现成的量化版本。如果找不到,可自行量化。

KV Cache 量化:FP8 KV Cache 可独立于权重量化使用,即使权重是 FP16。

组大小(Group Size):量化分组越小精度越高,但推理速度可能略慢。128 是常用的平衡点。

量化校准:校准数据的质量和代表性直接影响量化后的模型质量。建议使用领域相关的校准数据。


6.9 扩展阅读


上一章05 - 核心架构解析 | 下一章07 - LoRA 动态适配