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

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 动态适配