Transformer 解剖:从 Attention 到推理系统
第 16 章 量化实战:INT8 / INT4 / GPTQ / AWQ / FP8
第 16 章 量化实战:INT8 / INT4 / GPTQ / AWQ / FP8
第 14 章我们看到 Decode 阶段的瓶颈是 HBM 带宽——每生成一个 token 都要把整个模型权重从 HBM 拉一遍。Llama-3 70B 在 FP16 下是 140 GB,单卡 H100 的 HBM 带宽 3.35 TB/s——一次 forward 至少 42 ms。这是「物理学的下限」。
但等一下——如果我们能把模型权重压缩成 35 GB(INT4)呢?读取时间立刻降到 10 ms——同样硬件下 token 速度翻 4 倍。
这就是 量化(Quantization) 的核心价值:不改变模型结构,只是把每个参数用更少的位表示。FP16 → INT8 显存减半、INT4 减 4 倍、INT2 减 8 倍——直接对应 HBM 带宽节省,对应 Decode 加速。
但量化不是免费的。位数越少,每个参数的精度越低,模型质量必然损失一些。这一章我们要讲清楚:用多少位最划算?怎么把质量损失压到最小?哪些方案在哪些硬件上能跑?
读完这章你能:
- 推导 FP16 → INT8 / INT4 的量化数学(缩放因子、零点、对称 vs 非对称);
- 解释 GPTQ 和 AWQ 两大主流后训练量化(PTQ)方法的差异;
- 理解 FP8(E4M3 / E5M2)和 INT8 的工程取舍;
- 看懂 outlier 问题——为什么 SmoothQuant 必须存在;
- 在面对一个新模型时知道选哪种量化(INT8 / INT4 / FP8)。
16.1 一个最朴素的问题:FP16 太精确了?
LLM 训练时用的标准格式是 FP16 或 BF16——每参数 16 位 = 2 字节。这是个相对精确的浮点表示——FP16 能精确表示约 1024 个不同的小数值(在某个区间内)。
问题:神经网络真的需要这么高精度吗?
研究表明,不需要。一个训练好的 LLM 的参数分布大致是高斯型——绝大多数参数集中在 -1 到 1 之间,少数极端值散落在外面。如果你看具体值的精度,只需要分辨大约 16-256 个不同等级就足够保留模型能力。FP16 的 1024 级精度在大多数地方是「浪费」。
flowchart TB
subgraph "FP16 (16 bit)"
F1[每参数 2 byte<br/>~65536 个等级<br/>但大多数参数<br/>用不了这么多]
end
subgraph "INT8 (8 bit)"
I1[每参数 1 byte<br/>256 个等级<br/>对大多数权重已足够]
end
subgraph "INT4 (4 bit)"
I2[每参数 0.5 byte<br/>16 个等级<br/>边缘但仍可用]
end
subgraph "INT2 (2 bit)"
I3[每参数 0.25 byte<br/>4 个等级<br/>质量明显损失]
end
量化的核心数学:把一个 FP16 浮点数映射到一个低位整数,加上一个缩放因子(scale),让两者大致等价:
其中 是 FP16 的标量。 占的位数(4、8、16 位)就是「量化精度」。
举个例子:一组权重值 [0.5, -0.8, 0.3, -1.2, 0.9]:
- 找最大绝对值:1.2
- 设 INT8 范围 -127 到 127(对称量化),缩放因子
- 量化:每个值除以 s 取整 →
[53, -85, 32, -127, 95] - 反量化:每个值乘以 s →
[0.501, -0.803, 0.302, -1.200, 0.898]
可以看到反量化后的值和原值非常接近,但每个权重只用了 1 byte(INT8)而不是 2 byte(FP16)。
16.2 量化的几个维度
「量化」这个词在不同上下文下指的是不同事。要分清下面几个维度:
维度 1:量化什么?
- 权重量化(Weight Quantization):模型参数(W)量化
- 激活量化(Activation Quantization):每层输入 / 输出(A)量化
- KV Cache 量化(第 15 章已讲)
通常用 WnAm 表示「权重 n 位 + 激活 m 位」:
W16A16:FP16 训练 / 推理基线W8A8:权重和激活都 INT8W4A16:权重 INT4,激活保持 FP16W4A4:权重和激活都 INT4
维度 2:什么时候量化?
- PTQ(Post-Training Quantization):训练完之后再量化。优点是简单(不重训);缺点是质量损失稍大。
- QAT(Quantization-Aware Training):训练时就模拟量化,模型适应量化。优点是质量好;缺点是要重训,成本高。
主流大模型几乎全部用 PTQ——重训 70B 模型成本几百万美元,PTQ 几小时跑完。
维度 3:粒度多细?
- per-tensor:整个权重张量用一个缩放因子。最粗,质量最差。
- per-channel / per-group:每行(或每 N 个相邻元素)用一个独立的缩放因子。中等。
- per-token:每个 token 的激活独立量化。最细,质量最好。
更细的粒度让缩放因子更贴合数据分布,但额外存储缩放因子也是开销。主流方案:W4 用 group_size=128 的 group-wise 量化,A8 用 per-token 量化。
维度 4:对称还是非对称?
- 对称量化(symmetric):缩放范围对称(-127 到 127)
- 非对称量化(asymmetric):加一个零点(zero point)偏移,能更精确表示有偏分布
非对称对激活更友好(激活分布常常偏正),对称对权重够用。
把这几个维度组合起来,就有几十种「量化方案」——但工程上真正主流的就 4-5 种,下面分别讲。
16.3 INT8 量化:W8A8 与 SmoothQuant
最简单的量化是 W8A8——权重和激活都 INT8。每参数从 2 byte 降到 1 byte,显存和 HBM 读取直接减半。
朴素 W8A8 的算法:
# 权重量化(per-channel)
scale_w = w.abs().max(dim=-1, keepdim=True) / 127
w_int8 = (w / scale_w).round().clamp(-127, 127).to(torch.int8)
# 激活量化(per-token)
scale_a = x.abs().max(dim=-1, keepdim=True) / 127
x_int8 = (x / scale_a).round().clamp(-127, 127).to(torch.int8)
# INT8 矩阵乘法(GPU Tensor Core 支持)
y_int32 = torch.matmul(x_int8, w_int8.T).to(torch.int32)
# 反量化
y_fp16 = y_int32.to(torch.float16) * scale_a * scale_w
但朴素 W8A8 在 LLM 上有个致命问题:激活 outlier。
激活 outlier 现象
研究发现(Dettmers et al., LLM.int8(),NeurIPS 2022),LLM 的激活分布不是正常的钟形分布——少数(约 0.1%)激活值是远超其他值的「outlier」(绝对值大几十倍)。
flowchart LR
subgraph "激活 outlier"
NORMAL[99.9% 的激活<br/>绝对值 < 6]
OUT[0.1% 的激活<br/>绝对值 > 60]
end
NORMAL --> Q[量化时缩放因子<br/>被 outlier 撑大]
OUT --> Q
Q --> BAD[绝大多数值精度被牺牲<br/>质量大幅下降]
朴素量化的逻辑「按最大值算缩放」会让缩放因子被 outlier 撑大——绝大多数小值被量化到几乎全是 0,精度全部损失。
LLM.int8() 的解决方案
Dettmers 等人提出 混合精度:把 outlier 通道单独保留 FP16,其他通道用 INT8。具体地:
- 检测每层激活中的 outlier 列(通过统计阈值)
- outlier 列:FP16 计算
- 非 outlier 列:INT8 计算
- 最后合并
这种方案在 OPT、BLOOM 等早期开源大模型上几乎无损。但代价是计算路径分裂、kernel 实现复杂。
SmoothQuant 的精妙
更聪明的方案是 SmoothQuant(Xiao et al., 2023):通过数学等价变换,把激活的 outlier「转移」到权重上。
直觉:在 这个矩阵乘里,如果激活 有大 outlier、权重 比较平整,那么对每个通道引入一个缩放 :
选合适的 ,让 (缩放后的激活)outlier 减小,(缩放后的权重)outlier 略增——但权重的分布原本就比较平整,能容忍一些放大;激活的 outlier 反而被压平。
flowchart LR
subgraph "原始"
X1["X<br/>有大 outlier"]
W1["W<br/>分布平整"]
end
subgraph "SmoothQuant 之后"
X2["X'<br/>outlier 被分摊给 W"]
W2["W'<br/>略有 outlier 但仍可量化"]
end
X1 -.等价变换.-> X2
W1 -.等价变换.-> W2
X2 --> QQ["X' 和 W' 都能 INT8 量化<br/>质量几乎无损"]
W2 --> QQ
SmoothQuant 让 W8A8 在主流大模型上能做到「与 FP16 精度几乎无差」(< 0.5% 困惑度损失),同时显存减半、HBM 带宽减半。这是今天 W8A8 部署的事实标准。
16.4 INT4 量化:GPTQ 与 AWQ
INT4 把每参数压到 0.5 byte——比 FP16 减 4 倍。但 INT4 只有 16 个等级,朴素量化质量损失严重。要让 INT4 可用,需要更聪明的算法。
GPTQ:基于 Hessian 的逐列量化
GPTQ(Frantar et al., ICLR 2023)的核心想法:量化是有顺序的,每量化一列,根据它的误差去补偿后面未量化的列。
具体地,对一个权重矩阵 ,逐列量化 。每量化一列 ,会引入一个误差 (量化前减量化后)。GPTQ 用这个误差去修正后面未量化的列:
其中 是从 Hessian 矩阵推出来的修正系数——它告诉我们「未量化的列 应该如何调整,以最小化整体重建误差」。
直观感受:每量化一列就「调整下一列以弥补这一列的损失」——把误差像击鼓传花一样传下去,最终所有列的整体误差最小。
GPTQ 的代价:
- 需要一份校准数据(calibration data)——一小批文本(通常几十到几百条),用来估算 Hessian
- 量化耗时:70B 模型大概几个小时(在单 GPU 上)
GPTQ 是早期 INT4 量化的代表,至今仍是最常用的方案之一。bitsandbytes、auto-gptq 等库都实现了它。
AWQ:基于权重显著性
AWQ(Lin et al., MLSys 2024)走了一条不同的路:不是所有权重一样重要,量化时应该保护那 1% 最重要的权重。
AWQ 的关键 insight:
- 少数显著(salient)通道对模型质量贡献最大——这些通道对应的激活值大、影响输出多
- 如果只对显著通道做轻度缩放、对其他通道做激进 INT4 量化,整体质量损失可以非常小
具体算法:
- 用校准数据测量每个通道的「激活显著性」(activation magnitude)
- 给每个通道选一个缩放因子 ——显著通道大 (让权重更精细),不显著通道小
- 然后做标准 INT4 量化
AWQ 的优势:
- 不需要 Hessian 估计,比 GPTQ 简单 10 倍
- 推理时不需要任何额外计算(缩放被吸收到 LayerNorm 里)
- 在主流 LLM 上质量略优于 GPTQ
AWQ 在 2024 年成为 INT4 量化的事实标准——Llama / Mistral / Qwen 的 INT4 版本几乎都用 AWQ。
GPTQ vs AWQ 实测对比
在 Llama-2 7B 上的典型困惑度(PPL)对比(数值越低越好):
| 方案 | WikiText-2 PPL | C4 PPL | 量化时间 |
|---|---|---|---|
| FP16(基线) | 5.47 | 7.26 | – |
| GPTQ INT4 | 5.59 | 7.41 | 2 小时 |
| AWQ INT4 | 5.55 | 7.36 | 10 分钟 |
| 朴素 INT4 | 7.21 | 9.50 | 几秒 |
可以看到:
- 朴素 INT4 损失明显(PPL 上升 30%+)—— 不可用
- GPTQ 和 AWQ 都把损失压到 < 2%,几乎无感
- AWQ 速度快得多
16.5 FP8:Hopper 时代的新格式
NVIDIA H100 / B200 GPU 引入了新的硬件支持:FP8 浮点——8 位浮点格式,比 INT8 表达能力更强。
FP8 有两种格式:
- E4M3(4 位 exponent + 3 位 mantissa + 1 位 sign):精度高但范围小,适合权重和激活
- E5M2(5 位 exponent + 2 位 mantissa + 1 位 sign):范围大但精度低,适合梯度
flowchart LR FP16["FP16: 1+5+10<br/>范围 ±65k<br/>精度 1024 等级"] E4M3["FP8 E4M3: 1+4+3<br/>范围 ±448<br/>精度 8 等级 / 区间"] E5M2["FP8 E5M2: 1+5+2<br/>范围 ±57k<br/>精度 4 等级 / 区间"] INT8["INT8: 1+7<br/>范围 ±127<br/>线性 256 等级"]
FP8 vs INT8:
- FP8 是浮点——动态范围大,对 outlier 友好(不需要 SmoothQuant 之类的预处理)
- INT8 是定点——精度均匀,需要 outlier 处理才能用好
实际上 FP8 用起来比 INT8 简单——直接 cast,质量损失很小。但 FP8 需要硬件支持(H100、H200、B200、A100 不支持)。
DeepSeek-V3 是首个全 FP8 训练的 frontier 模型——权重、激活、梯度的大部分计算都用 FP8。这把训练成本打到了同等模型的一半。
未来趋势:FP8 取代 INT8 + FP4 取代 INT4。NVIDIA Blackwell(B200)已经原生支持 FP4,H100 / H200 通过算法模拟也能用——硬件演化方向已经确定。
16.6 不同方案的对照表
把所有量化方案放一张表里对比:
| 方案 | 权重 | 激活 | KV | 显存压缩 | 硬件支持 | 质量损失 | 适用 |
|---|---|---|---|---|---|---|---|
| FP16(基线) | FP16 | FP16 | FP16 | 1× | 通用 | 0 | 训练 / 推理基线 |
| BF16 | BF16 | BF16 | BF16 | 1× | A100+ | 几乎无 | 取代 FP16 训练 |
| W8A8 (SmoothQuant) | INT8 | INT8 | FP16/INT8 | 2× | T4+ | < 0.5% PPL | 中等并发推理 |
| W8A16 | INT8 | FP16 | FP16 | 2× | 通用 | < 0.3% PPL | 简单部署 |
| W4A16 (GPTQ) | INT4 | FP16 | FP16 | 4× | T4+ | 1-2% PPL | 高密度部署 |
| W4A16 (AWQ) | INT4 | FP16 | FP16 | 4× | T4+ | < 1.5% PPL | 替代 GPTQ |
| W4A8 | INT4 | INT8 | INT8 | 4× | A100+ | 2-3% PPL | 极致部署 |
| FP8 (E4M3) | FP8 | FP8 | FP8 | 2× | H100+ | < 0.3% PPL | 新硬件最优 |
| FP4 | FP4 | FP4 | FP4 | 4× | B200 | 1-2% PPL | 下一代主流 |
实际工业部署的常见组合:
- 训练:BF16(标准)或 FP8(前沿,DeepSeek、最新 OpenAI)
- 推理(性能优先):W4A16 + AWQ + INT8 KV
- 推理(精度优先):W8A8 + SmoothQuant
- 推理(H100+):FP8 全栈
16.7 量化的硬件依赖
不是所有 GPU 都支持所有量化:
Tensor Core 算力对照(H100 SXM 单卡,TFLOPS):
| 精度 | TFLOPS | 相对 FP16 |
|---|---|---|
| FP32 | 67 | 0.07× |
| TF32 | 989 | 1× |
| BF16 / FP16 | 989 | 1× |
| FP8 (E4M3) | 1979 | 2× |
| INT8 | 1979 | 2× |
| INT4 | 3958 | 4× |
| FP4 (B200) | 7916 | 8× |
理论上 INT4 矩阵乘比 FP16 快 4 倍——但实际推理瓶颈在 HBM 带宽(memory-bound),加速因子接近「权重大小压缩比」(也就是 4 倍)。
消费卡支持:
- RTX 4090 / 4070 等 Ada Lovelace 架构:支持 FP8、INT8、INT4
- RTX 3090 / 3060 等 Ampere:支持 INT8、INT4,不支持 FP8
- 老的 V100 / T4:只支持 INT8
结论:选量化方案要考虑目标硬件。给 4090 用户部署:FP8 是最优选;给 3090 用户:INT4 + AWQ;给 V100:W8A8 + SmoothQuant。
16.8 一个端到端例子
最后给一个完整流程:用 vLLM 部署一个 INT4 量化的 Llama-3-70B。
Step 1:选量化方案
70B 模型 FP16 是 140 GB——单卡 H100 80GB 装不下。要么 4 卡 TP,要么量化到单卡。
INT4 后只有 35 GB——单卡 H100 80GB 能装!剩 45 GB 给 KV Cache,能撑大量并发。
Step 2:用 AWQ 做量化
pip install autoawq
# 用 autoawq 库量化
python -c "
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = 'meta-llama/Meta-Llama-3-70B-Instruct'
quant_path = 'llama-3-70b-awq'
quant_config = {'zero_point': True, 'q_group_size': 128, 'w_bit': 4, 'version': 'GEMM'}
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoAWQForCausalLM.from_pretrained(model_path)
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized(quant_path)
"
70B 模型量化大约 1-2 小时(A100 / H100 单卡)。
Step 3:用 vLLM 部署
pip install vllm
vllm serve llama-3-70b-awq \
--quantization awq \
--kv-cache-dtype fp8 \
--max-model-len 32768 \
--gpu-memory-utilization 0.95
注意配合 --kv-cache-dtype fp8 使用 FP8 KV Cache(H100 上原生支持,进一步省 KV 显存)。
Step 4:质量验证
跑一组评测(如 MMLU、HellaSwag)对比原始 FP16 vs INT4-AWQ 的分数差距。一般在 1% 以内。
Step 5:性能 benchmark
测试 throughput(tokens/s)和 latency(ms/token)。INT4 相比 FP16 通常:
- Decode 速度:3-4× 提升
- 单卡并发能力:4-8× 提升
16.9 量化的工程经验
经验 1:不是所有模型都量化友好
- 训练充分(数据 / 步数足)的模型量化损失更小
- 小模型(< 7B)量化损失大于大模型——参数少,每个都很关键
- MoE 模型对量化更敏感——专家间的差异更小,错误一个就影响多个 token
经验 2:长上下文 + 量化要小心
INT4 量化在 128K 长上下文上会显示出累积误差。建议:
- KV Cache 不量化或只 INT8(不 INT4)
- 重要应用做完 NIAH 评估再上线
经验 3:W8A8 和 W4A16 的比较
- W8A8:质量更好,但激活量化复杂(per-token、SmoothQuant)
- W4A16:质量略差,但激活保 FP16,实现简单
工业上 W4A16 + AWQ 更主流,因为它在「质量损失/工程复杂度」上甜点更高。
经验 4:FP8 是未来
如果你的硬件是 H100+ / B200+,优先选 FP8——不需要 outlier 处理、量化时间短、质量损失最小。
经验 5:量化对训练动态没用
量化主要服务于推理。训练时即使是 FP8 训练(DeepSeek-V3)也是「以全 FP16 为参照、动态调整」——不是简单地把所有计算 cast 到 FP8。
16.10 把量化纳入完整推理 pipeline
量化是推理优化金字塔的一层,不是独立技术。完整的最优推理 pipeline 是:
flowchart TB ARCH["架构层<br/>GQA / MLA / SwiGLU"] --> Q Q["量化层<br/>FP8 / INT4 + AWQ"] --> K K["KV Cache 层<br/>PagedAttention + Prefix Caching + INT8 KV"] --> A A["Attention 内核<br/>Flash Attention 2/3"] --> S S["调度层<br/>Continuous Batching + Chunked Prefill"] --> D D["分布式层<br/>TP / PP / EP / PD 分离"]
每一层都贡献 2-4× 优化,叠加起来从一个朴素 PyTorch 实现到 vLLM 集群是 100-1000× 的吞吐提升。量化是其中关键一环——它把 HBM 带宽这个最硬的瓶颈直接压下去 2-4 倍。
本章小结
- 量化的价值在 Decode 时显现——HBM 带宽 = 推理瓶颈,量化 = HBM 读取压缩。
- 量化的几个维度:权重 vs 激活 vs KV、PTQ vs QAT、对称 vs 非对称、粒度(per-tensor / channel / group / token)。
- W8A8 + SmoothQuant 是 INT8 部署的标配——通过等价变换把激活 outlier 转移到权重。
- W4A16 + GPTQ / AWQ 是 INT4 部署的标配——AWQ 比 GPTQ 简单 10 倍且质量略好。
- FP8 是 H100+ 时代的新主流——比 INT8 表达能力强、不需要 outlier 处理。DeepSeek-V3 用 FP8 完成训练。
- 不同硬件支持不同量化——选方案要看目标硬件能跑什么。
- 量化是推理优化的一层,不是全部——和 GQA / Flash Attention / KV Cache 等技术叠加才能发挥威力。
- 质量损失通常 < 2%——主流 INT4 方案对大模型几乎无损。
下一章我们看推理优化的另一个独立维度——投机解码(Speculative Decoding)。它用一个小模型「赌」出多个候选 token,让大模型一次验证多个、绕开 Decode 的逐 token 限制——又一种 4-5× 的加速。
延伸阅读
- Dettmers et al., LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale, NeurIPS 2022——LLM.int8() 论文。
- Xiao et al., SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models, ICML 2023——SmoothQuant。
- Frantar et al., GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers, ICLR 2023——GPTQ。
- Lin et al., AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration, MLSys 2024——AWQ。
- Micikevicius et al., FP8 Formats for Deep Learning, 2022——FP8 标准。
- bitsandbytes 库: https://github.com/TimDettmers/bitsandbytes
- AutoAWQ: https://github.com/casper-hansen/AutoAWQ
- DeepSeek-V3 Technical Report——FP8 训练实战。
- vLLM 量化文档: https://docs.vllm.ai/en/latest/quantization/