详解模型量化技术的分类

描述

模型量化技术:INT8、INT4 与 AWQ/GPTQ 压缩方案

适用读者:要把开源大模型部署到生产环境的工程师。本文不讲量化数学推导,只讲:每种量化方案在 7B/13B/70B 模型上显存能省多少、推理速度提多少、精度掉多少、坑在哪里。读完你应该能自己拍板"我的卡该用 INT8 还是 INT4、用 GPTQ 还是 AWQ"。

引子:那个 70B 模型 4 张卡也塞不下的下午

去年我们想给客户部署一个 Qwen-72B 的服务做离线文档分析。客户的预算只够买 4 张 A100 40G(消费级),按理说 4×40G=160G 装个 72B BF16 模型(140GB)应该够了。

实际试下来:

加载模型直接 OOM:模型权重 140GB + KV cache + 激活值,实际峰值 180GB+;

切到 load_in_8bit 后能加载,但单卡推理只有 3 tokens/s(不实用);

切到 AWQ 4bit 量化后,72B 模型只用 36GB 显存,4 张卡 40G 富余,推理速度飙到 28 tokens/s;

PPL(困惑度)只从 4.2 涨到 4.5,业务指标基本不掉。

这就是量化的价值——它不创造新能力,它把"塞不下/跑不动"的模型变得"塞得下/跑得动"。这一篇把生产里能用的量化方案(INT8/INT4、bitsandbytes、GPTQ、AWQ)讲透。

一、量化到底解决什么问题

量化的核心是用更少的 bit 表示权重和激活值,从而降低显存占用、提升推理速度。本质上是一次"用精度换性能"的交易。

大模型推理面临三个硬约束:

硬约束 现象 数字举例
显存墙 模型太大塞不进单卡 72B BF16 = 140GB,单卡 80G 装不下
带宽墙 推理慢,瓶颈在权重搬运 72B 一次推理要搬 140GB 权重,A100 HBM 带宽 2TB/s → 70ms 起步
成本墙 多卡推理贵 8 卡 A100 月成本 ~10w,多租户分摊不下来
功耗墙 单卡功率到顶 A100 400W,单机柜 8 卡就是 3.2kW

量化能缓解这四个,但不是免费的——量化会引入误差,可能掉点。

 一句话记忆:量化是用"精度小幅损失"换"显存/速度/成本大幅下降",关键是把精度损失控制在业务可接受范围内。

二、适合什么场景 / 不适合什么场景

场景 推荐方案 理由
显存够用,只想省成本 INT8 (bitsandbytes) 精度几乎不掉,推理速度 1.5-2x
单卡要跑 13B-70B AWQ 4bit / GPTQ 4bit 显存 4x 压缩,速度 3-4x
CPU / 边缘设备 GGUF (llama.cpp) Q4_K_M 纯 CPU 推理,4bit 量化最成熟
极致低显存 (6G-8G) AWQ 4bit + vLLM 13B 模型 4bit 量化后 7-8GB 显存
服务端高吞吐 AWQ 4bit + vLLM/TGI 4bit + 连续批处理 + PagedAttention
科研/离线分析 不量化 精度优先

不适合量化的场景:

任务对小数点后第 3-4 位敏感(如精算、量化交易)——量化的累计误差不能接受

训练时量化(QAT 不在本篇范围,生产里 99% 是 PTQ)

极小模型(< 1B)——量化收益小,反而引入部署复杂度

三、整体架构:量化方案分类

按"压缩比"和"量化时机"两个维度划分:

 

                  量化精度
              ┌──────┼──────┐
              │      │      │
            FP16   INT8   INT4
           (基线)   (2x)   (4x)
              │      │      │
   量化时机 ──┼──────┼──────┼──
              │      │      │
   训练后量化 PTQ     ✓      ✓    ← 生产主流
   训练中量化 QAT     ✓      -    ← 科研

 

按"量化对象"再细分:

方案 量化对象 精度损失 工程成熟度 推荐场景
bitsandbytes 8bit 权重 几乎不掉 ★★★★★ HuggingFace 生态首选
bitsandbytes 4bit (NF4) 权重 -1 到 -2 PPL ★★★★★ 训练 + 推理
GPTQ 权重 (per-group) 1-3 PPL ★★★★ 通用推理
AWQ 权重 (保护关键通道) 0.5-1.5 PPL ★★★★★ 生产推荐
SmoothQuant 权重 + 激活 1-2 PPL ★★★ INT8 部署
ZeroQuant 权重 + 激活 1-2 PPL ★★ 学术
GGUF (llama.cpp) 权重 (k-quant) 1-3 PPL ★★★★ CPU 推理

按"量化粒度"分(这一维度对精度影响最大):

粒度 含义 精度 推理速度
Per-tensor 整层一个缩放因子 最差 最快
Per-channel 每个 channel 一个缩放因子 较好 较快
Per-group 每 32/64/128 个权重一个缩放因子 较快(GPTQ/AWQ 都用这个)

四、核心流程拆解

4.1 量化的基本数学(一段话讲清)

把 FP16 的浮点权重 w 映射到 INT8 整数 q,公式是:

 

q = round((w - zero_point) / scale)
w ≈ q * scale + zero_point

 

scale 和 zero_point 是两个量化参数,按"粒度"决定它们的存在范围:

Per-tensor:整层共用一对 scale/zero_point

Per-channel:每个卷积核/线性层一组

Per-group:每 32/128 个权重一组

对称 vs 非对称:

对称:zero_point = 0,只存 scale

非对称:存 scale + zero_point,精度略好但计算稍慢

4.2 PTQ(Post-Training Quantization)流程

输入:训练好的 FP16 模型 + 校准数据集 动作:

校准:用 100-500 条样本跑一遍前向,统计激活值分布

计算 scale/zero_point:基于统计结果

量化权重:从 FP16 转到 INT8/INT4

保存量化模型

异常处理:

校准数据选错:量化后 PPL 爆炸(典型:用了不同领域的数据)

离群值(outlier):少数大权重导致 scale 偏大,其余量化精度差——AWQ 用保护通道解决

激活值范围不稳定:用 SmoothQuant 迁移到权重端

4.3 加载 + 推理流程

输入:量化模型 + 推理请求 动作:

加载:把 INT8/INT4 权重反量化(或保留量化,按框架而定)

推理:前向计算

输出:反量化结果(或保持 INT,框架负责)

关键差异:

vLLM:保留 INT4 权重,推理时按需反量化(省带宽)

Transformers:每次前向反量化(省显存但费算力)

llama.cpp:自定义 GGUF 格式,纯 CPU 推理

五、关键代码:四种主流方案的最小可跑示例

5.1 bitsandbytes 8bit/4bit(最简单)

 

# inference_8bit.py
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

# ===== 8bit 量化 =====
bnb_8bit = BitsAndBytesConfig(load_in_8bit=True)

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2-7B-Instruct",
    quantization_config=bnb_8bit,
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-7B-Instruct")

# 推理
inputs = tokenizer("北京是中国的", return_tensors="pt").to("cuda")
out = model.generate(**inputs, max_new_tokens=50, do_sample=False)
print(tokenizer.decode(out[0], skip_special_tokens=True))
# inference_4bit.py
bnb_4bit = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",            # 关键:NF4 比 FP4 精度高
    bnb_4bit_compute_dtype=torch.bfloat16,  # 计算用 BF16
    bnb_4bit_use_double_quant=True,       # 双重量化
    bnb_4bit_quant_storage=torch.uint8,   # 存储格式
)

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2-7B-Instruct",
    quantization_config=bnb_4bit,
    device_map="auto",
)

 

bitsandbytes 关键参数:

load_in_4bit=True:启用 4bit 加载

bnb_4bit_quant_type="nf4":NF4 是 NormalFloat 4bit,专为正态分布权重设计,比 FP4 精度高 11%

bnb_4bit_compute_dtype=torch.bfloat16:反量化后用什么 dtype 计算,BF16 比 FP16 范围大,不易溢出

bnb_4bit_use_double_quant=True:对 scale/zero_point 也做 4bit 量化,7B 模型多省 0.4GB

实测性能(Qwen2-7B,单张 A100 80G):

方案 显存 推理速度 (tokens/s) PPL (wiki text)
BF16 (基线) 14.2GB 38 4.2
INT8 (bnb) 7.8GB 28 4.21
NF4 (bnb) 4.5GB 22 4.35

5.2 GPTQ 4bit(生产高吞吐场景)

GPTQ 用二阶信息(Hessian)做逐层量化误差最小化,精度优于普通 INT4。

 

# 安装
pip install auto-gptq optimum
# quantize_gptq.py
from datasets import load_dataset
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig

# ===== 1. 准备校准数据 =====
# 关键:校准数据要和实际任务同领域
calib_dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="train[:512]")
calib_data = [d["text"] for d in calib_dataset if len(d["text"]) > 200]

# ===== 2. 量化配置 =====
quantize_config = BaseQuantizeConfig(
    bits=4,                  # 4bit
    group_size=128,          # 每 128 个权重一组(越小越准,越大越快)
    desc_act=False,          # 是否按激活值排序(True 更准但慢)
    damp_percent=0.01,
)

# ===== 3. 加载 + 量化 =====
model = AutoGPTQForCausalLM.from_pretrained(
    "Qwen/Qwen2-7B-Instruct",
    quantize_config,
    trust_remote_code=True,
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-7B-Instruct", trust_remote_code=True)

model.quantize(calib_data, use_triton=True, batch_size=4)
model.save_quantized("./qwen2-7b-gptq-4bit")
tokenizer.save_pretrained("./qwen2-7b-gptq-4bit")

 

GPTQ 关键参数:

bits=4:量化位数,3/4/8 都支持

group_size=128:per-group 量化的组大小。64 精度更好但慢,256 更快但掉点

desc_act=False:是否按激活值重要性排序。True 在某些任务上更准,但推理时 group 不连续会变慢 20%

damp_percent=0.01:Hessian 阻尼,防止数值不稳定

校准数据的选择(最容易被忽略的坑):

任务:中文对话 → 校准数据用中文 Wikipedia + 中文对话数据,不要用英文

任务:法律合同 → 用法律文书

数据量:128-512 条就够,再多收益递减

绝对不要用测试集当校准集(数据泄露)

5.3 AWQ 4bit(生产部署首选)

AWQ(Activation-aware Weight Quantization)的核心洞察是:模型 1% 的权重决定了 99% 的输出。它通过"保护关键通道"实现比 GPTQ 更高的精度。

 

pip install autoawq
# quantize_awq.py
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

# ===== 1. 加载模型 =====
model_path = "Qwen/Qwen2-7B-Instruct"
quant_path = "./qwen2-7b-awq-4bit"

model = AutoAWQForCausalLM.from_pretrained(
    model_path,
    safetensors=True,    # 必须 safetensors 格式
)
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)

# ===== 2. 量化配置 =====
quant_config = {
    "zero_point": True,       # 非对称量化(精度更好)
    "q_group_size": 128,      # group size
    "w_bit": 4,               # 权重量化位数
    "version": "GEMM",        # GEMM 比 GEMV 快(vLLM 兼容 GEMM)
}

# ===== 3. 准备校准数据 =====
# AWQ 的 calibration 只需要 32-128 条样本就够了
calib_data = []
for d in load_dataset("wikitext", "wikitext-2-raw-v1", split="train[:128]"):
    text = d["text"].strip()
    if len(text) > 200:
        calib_data.append(text.strip())

# ===== 4. 量化 =====
model.quantize(tokenizer, quant_config=quant_config, calib_data=calib_data)
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)

 

AWQ 关键参数:

zero_point=True:非对称量化

q_group_size=128:经验最优值

w_bit=4:4bit 是甜点

version="GEMM":必须用 GEMM 才能被 vLLM 高效支持

AWQ vs GPTQ 实测(Qwen2-7B,wikitext PPL):

方案 PPL 推理速度 (vLLM, tokens/s) 显存
BF16 4.20 38 14.2GB
GPTQ-4bit (g128) 4.42 56 4.8GB
AWQ-4bit (g128) 4.32 62 4.7GB

AWQ 全面胜出:精度高、速度快、显存省。

 一句话记忆:生产部署 4bit,AWQ 几乎总是比 GPTQ 好——除非你只能加载 safetensors 受限的旧模型。

5.4 vLLM 加载量化模型(生产部署)

 

# serve_awq.py
from vllm import LLM, SamplingParams

llm = LLM(
    model="./qwen2-7b-awq-4bit",
    quantization="awq",          # 显式指定
    dtype="float16",
    gpu_memory_utilization=0.9,
    max_model_len=4096,
    tensor_parallel_size=1,
)

prompts = ["北京是中国的"]
sampling_params = SamplingParams(temperature=0.7, max_tokens=50)
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
    print(output.outputs[0].text)

 

vLLM 启动命令:

 

python -m vllm.entrypoints.openai.api_server 
  --model ./qwen2-7b-awq-4bit 
  --quantization awq 
  --tensor-parallel-size 1 
  --gpu-memory-utilization 0.9 
  --port 8000

 

vLLM 对量化格式的支持:

格式 支持 备注
AWQ 性能最好
GPTQ desc_act=False 时最优
bitsandbytes 4bit 仅支持推理,不支持训练
FP8 (E4M3) H100 上效果拔群
SmoothQuant INT8 场景
GGUF 必须用 llama.cpp

5.5 llama.cpp / GGUF(CPU 推理)

适合:边缘设备、CPU 推理、苹果 M1/M2/M3、嵌入式。

 

# 转换 HuggingFace 模型到 GGUF
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
python convert_hf_to_gguf.py ../Qwen2-7B-Instruct 
  --outfile qwen2-7b.gguf 
  --outtype q8_0          # Q8_0 量化

 

常用 GGUF 量化方案:

方案 大小(7B) 质量 用途
Q2_K 2.7GB 极致压缩,不推荐
Q4_0 3.8GB 还行 老方案,已被 Q4_K_M 取代
Q4_K_M 4.1GB CPU 推理推荐
Q5_K_M 4.8GB 很好 质量优先
Q6_K 5.5GB 接近 FP16 高质量
Q8_0 6.7GB 几乎无损 显存宽裕时

 雷点:Q4_0 和 Q4_K_M 不是一回事。Q4_K_M 是新版 k-quant 方法,精度比 Q4_0 高 30%。永远选 Q4_K_M 而非 Q4_0。

六、上线后如何评估效果

量化不是"加载能跑就完事",必须做精度评估。生产里翻车最多的就是"量化后 benchmark 没掉,业务指标掉了"。

6.1 精度指标

指标 工具 掉点容忍度
PPL(困惑度) transformers  评估 < 0.3 (5% 内)
MMLU EleutherAI/lm-eval-harness < 2%
C-Eval (中文) 同上 < 2%
HumanEval (代码) 同上 < 3%
业务指标 内部评估 < 1% (必须)

PPL 评估代码:

 

from datasets import load_dataset
from torch import nn

test = load_dataset("wikitext", "wikitext-2-raw-v1", split="test")
text = "

".join(test["text"][:1000])

encodings = tokenizer(text, return_tensors="pt").to("cuda")
with torch.no_grad():
    outputs = model(**encodings, labels=encodings["input_ids"])
ppl = torch.exp(outputs.loss).item()
print(f"PPL: {ppl:.2f}")

 

6.2 推理性能

指标 测法 优化空间
P50/P99 延迟 压测 1000+ 请求 vLLM/TGI 后通常 100-300ms
吞吐量 (tokens/s) 批量 32+ 并发测 量化后通常 2-4x 提升
首 token 延迟 (TTFT) 流式响应测 量化能降 30%+
显存峰值 nvidia-smi 量化后 4x 下降

6.3 业务指标

最关键的一环——用真实业务 query 跑 A/B:

指标 健康值
业务采纳率 不掉 > 1%
错误率 不增加
人工抽检通过率 不掉 > 3%

 一句话记忆:PPL 涨 0.5 通常无害,但业务采纳率掉 1% 就要回滚。量化评估永远是业务指标说了算。

七、常见坑与避雷(12 条实战总结)

坑 1:AWQ 在 chat 模型上效果差

现象:base model 上 AWQ 4bit 完美,但 Instruct/Chat 模型 PPL 涨 1.0+。 根因:chat 模型对 outlier 权重更敏感,AWQ 的保护通道算法在 SFT 后的模型上识别不准。 解决:

校准数据必须用 chat 模板格式化过的对话(不是裸文本)

试 group_size=64(更细粒度)

备选:切 GPTQ-4bit

坑 2:group_size 选错

现象:group_size=32 时 PPL 完美但推理慢 50%;group_size=-1(per-channel)时 PPL 爆炸。 根因:粒度和速度的 trade-off。 解决:

128 是黄金值(速度/精度平衡)

显存紧张 → 256

精度要求高 → 64

坑 3:校准数据选错

现象:用英文校准数据量化中文模型,PPL 涨 1.5+。 根因:量化算法假设权重分布和校准数据分布匹配。 解决:

中文模型 → 中文校准数据(wiki-zh、CLUECorpus)

领域模型(法律/医疗)→ 领域校准数据

不要用测试集做校准

坑 4:AWQ 不支持某些模型

现象:Qwen2.5、Llama 3.1、DeepSeek 等新模型 AWQ 量化时报 "module not supported"。 根因:AWQ 实现依赖特定 layer 类型,新模型用了 MoE 或新结构。 解决:

查 AWQ 官方 GitHub 的 supported models 列表

不支持时退到 GPTQ

或等 autoawq 更新

坑 5:bitsandbytes 4bit + 多卡训练崩溃

现象:QLoRA 训练时 NCCL 报错 CUDA Error 700。 根因:4bit 量化 + 多卡 + 某些版本的 accelerate 不兼容。 解决:

 

pip install bitsandbytes==0.43.0 accelerate==0.30.0 transformers==4.41.0
# 锁定版本

 

坑 6:vLLM 加载 AWQ 模型报 "Unrecognized quantization"

现象:ValueError: Unrecognized quantization method 'awq_marlin'。 根因:AWQ 量化时 version="GEMM" 用了老格式,vLLM 期望 Marlin kernel。 解决:

 

# 量化时
quant_config = {
    "version": "GEMM",  # 不要用 "Marlin" 旧选项
    "w_bit": 4,
    "q_group_size": 128,
}
# vLLM 0.4+ 自动用 Marlin 内核加速

 

坑 7:量化模型 PPL 看起来 OK 但业务掉点

现象:PPL 4.2 → 4.4(涨 5%),业务指标掉 8%。 根因:PPL 是全局平均,掩盖了长尾 case的恶化(如数字推理、代码生成)。 解决:

准备 50-100 条业务高频 query 的人工评估集

重点测边界 case(数字、单位、专有名词)

量化前后对比,业务指标不掉才算通过

坑 8:温度/采样参数对量化敏感

现象:base model 上 temperature=0.7 完美,量化模型上同样参数出现重复。 根因:量化引入的噪声让采样分布变化。 解决:

量化后重做采样参数 grid search

考虑 temperature=0.3-0.5(降低随机性)

或用 repetition_penalty=1.05-1.1

坑 9:vLLM 推理输出 NaN

现象:vLLM 启动后第一个 batch 输出 NaN。 根因:AWQ/GPTQ 模型和 vLLM 的 CUDA kernel 版本不匹配。 解决:

 

# 升级 vLLM
pip install -U vllm
# 或指定 kernel
vllm serve ./model --quantization awq --enforce-eager  # 关闭 cuda graph

 

坑 10:量化 + 推理框架不匹配

现象:模型用 GPTQ 量化,llama.cpp 加载失败。 根因:GPTQ、AWQ、bitsandbytes、GGUF 是不同格式,互不通用。 解决:

生产部署(GPU):AWQ/GPTQ → vLLM

CPU 推理:HF 模型 → GGUF → llama.cpp

训练:bitsandbytes 4bit + QLoRA

不要混用

坑 11:量化后多轮对话效果下降

现象:单轮对话 OK,多轮对话质量明显下降。 根因:KV cache 没用量化,错误累积;多轮上下文里 attention 模式变了。 解决:

试 vllm 的 enable_prefix_caching=True

多轮对话场景考虑用 8bit 量化(精度更高)

检查 chat template 是否在量化后被破坏

坑 12:INT4 在小模型上反而掉点

现象:1.5B/3B 模型用 INT4 后效果下降 5%+,但 7B+ 模型没事。 根因:小模型表达能力本就有限,量化误差占比大。 解决:

小模型(< 3B)→ 优先 INT8 或不量化

中型(7B-13B)→ INT4 安全

大型(70B+)→ INT4 收益最大

 最后一条:量化是"已知收益、未知风险"的工程动作。永远保留 FP16 备份,回滚只要 5 分钟;强行上线后回滚可能要一周。

八、优化方向(从"能用"到"好用")

按性价比从高到低:

AWQ 4bit + vLLM:90% 场景的最优解,直接上

INT8 + 连续批处理:如果精度敏感,先上 INT8 再考虑 INT4

FP8 (E4M3) + H100:H100 用户优先用 FP8,比 INT4 精度高 1 倍,速度相当

KV cache 量化 (KV Quant):vLLM 0.4+ 支持,长上下文场景显存再省 50%

Speculative Decoding:用小模型 draft、大模型 verify,推理速度再提 2x

Tensor Parallel:单卡装不下时用 TP,比模型并行简单

Continuous Batching:vLLM/TGI 默认开,吞吐提 10x+

Flash Attention + PagedAttention:标配,不开就亏

算子融合 (Operator Fusion):vLLM 内置,对量化模型特别有效

预编译 (Ahead-of-Time Compilation):TensorRT-LLM、MLC-LLM,极致优化但难部署

何时该考虑别的方案:

量化后精度掉得受不了 → 不要硬撑,换 INT8 或不量化

单卡装不下 + 量化没用 → 上 Tensor Parallel(下篇讲)

训练成本太高 → 用云上 spot 实例 + DeepSpeed + QLoRA(前面两篇讲过)

九、收尾

量化的核心是"用精度换性能",99% 的生产部署都应该至少做 INT8 量化。决策树:

显存富裕(> 模型大小 × 2) → 先不量化,验证基线

显存 = 模型大小 → 上 INT8,几乎无损失

显存 < 模型大小 → 上 AWQ 4bit,首选

显存 = 模型大小 / 4 → 上 AWQ 4bit + Tensor Parallel

CPU 推理 → GGUF + Q4_K_M

H100 → 试 FP8 (E4M3)

三种主流方案的选择:

bitsandbytes:最简单,HuggingFace 生态原生,但推理性能不如 AWQ

GPTQ:成熟稳定,生态好,但精度略逊于 AWQ

AWQ:当前生产部署的最佳选择——精度、速度、显存都占优

什么时候该考虑别的方案?当量化后精度损失超过业务容忍度——这不是量化的问题,是模型能力或任务复杂度的问题。这时候该看的是模型蒸馏(用大模型教小模型)或LoRA 微调(前面讲过)。真正的工程难点从来不是单点技术,而是怎么把"量化 + 微调 + 推理框架 + 业务评估"串成一条流水线——下一篇要讲的分布式训练策略,就是这条流水线的"上游"环节。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分