衔接前序 :第 42 篇讨论了当模型太大放不进单卡时的解决方案------分布式训练。但分布式训练并不能减少单张 GPU 上的显存压力。有没有一种方法,在不改变模型架构、不增加硬件的情况下,让模型更小、更快 ?答案就是量化。第 41 篇(KV Cache)已经触及了 KV Cache 的 INT8 量化,本文聚焦模型权重本身的量化------从 FP32 压缩到 INT8/INT4 的数学原理、主流方法和核心 trade-off。
量化是将深度学习从"实验室"推向"大规模部署"的关键技术。本文从量化误差的数学本质出发,逐步深入到 GPTQ、AWQ 等业界主流方法。
一、引言:为什么要量化
1.1 显存困境
以 Llama 2-7B 为例,不同精度下的模型大小:
| 精度 | 每参数比特 | 7B 模型大小 |
|---|---|---|
| FP32 | 32 bit | 28 GB |
| FP16/BF16 | 16 bit | 14 GB |
| INT8 | 8 bit | 7 GB |
| INT4 | 4 bit | 3.5 GB |
| INT2(实验性) | 2 bit | 1.75 GB |
一张 RTX 3090 有 24 GB 显存。FP16 的 7B 模型(14 GB)勉强能放进去,但只剩下 10 GB 给 KV Cache 和激活值。如果使用 INT4(3.5 GB),省下的 10.5 GB 可以给 KV Cache 处理超过 10 万 token 的长序列。
这正是量化最直接的吸引力------在不增加硬件的条件下,让模型更小、更快、承载更长上下文。
1.2 量化的本质
量化本质上就是给浮点数"瘦身"------把原本用 32 位(float32)或 16 位(float16)存储的数值,压缩成 8 位甚至 4 位的整数。
你可以把它想象成给照片压缩画质:一张 4K 超清照片(float32)占空间很大,但转成 720p 的 JPEG(INT8)后,肉眼几乎看不出区别,文件却小了好几倍。量化对模型做的,就是类似的事------牺牲一点点精度,换来更小的体积和更快的运行速度。
数学上,量化是一个有损压缩过程,它将一个连续的实数值映射到有限的离散集合:
Q:R→Q={q0,q1,...,q2b−1}Q: \mathbb{R} \to \mathcal{Q} = \{q_0, q_1, ..., q_{2^b-1}\}Q:R→Q={q0,q1,...,q2b−1}
其中 bbb 是量化位宽。通俗地说:bbb 越大,能表示的"刻度"就越多 。比如 b=8b=8b=8 时有 256 个刻度(0~255),b=4b=4b=4 时只有 16 个刻度------就像一把尺子,毫米刻度比厘米刻度更精细,量东西自然更准。
从信息论的角度,量化就是用 bbb 个比特来表示一个浮点数的分布------比特越少,信息损失越大,但存储和计算效率越高。这就像用更少的字数概括一本书:字数越少,概括越粗糙,但读起来也越快。
💡 量化是不是等同于四舍五入?
不完全等同,但四舍五入是量化中最基础的一步。
四舍五入 只是把一个小数变成离它最近的整数(如 3.14 → 3),它只涉及取整这一个操作。
量化则是一个更完整的过程,包含三步:
- 缩放(Scale) :根据浮点数的实际范围,确定一个缩放因子 SSS,把浮点数映射到整数区间。例如,若权重范围是 −1.0,1.0-1.0, 1.0−1.0,1.0,要映射到 INT8 的 −128,127-128, 127−128,127,则 S=127−(−128)1.0−(−1.0)=127.5S = \frac{127 - (-128)}{1.0 - (-1.0)} = 127.5S=1.0−(−1.0)127−(−128)=127.5。
- 取整(Round) :缩放后的值做四舍五入,得到整数。这一步才是四舍五入。
- 反量化(Dequantize) :推理时把整数再乘回 SSS,恢复成近似的浮点数。
所以,四舍五入只是量化流程中的一个环节。量化还涉及缩放因子的选择 (如何确定 SSS 才能让精度损失最小)、零点偏移 (非对称量化中的 ZZZ)、以及各种高级策略(如 GPTQ 用二阶信息补偿舍入误差)。这些内容将在后续章节展开。
💡 量化 vs 归一化量化和归一化其实在某种程度上很相似,但两者有本质区别:
归一化(Normalization) 是把数据缩放到一个固定区间(如 0,10,10,1 或 −1,1-1,1−1,1),数据类型不变------float32 归一化后还是 float32,只是数值范围变了。它的目的是让不同量纲的特征可比,或加速模型收敛。
量化(Quantization) 则是改变数据类型------把 float32 变成 INT8/INT4,同时也要做缩放。这是由于原数据可能超出 16 位表示范围,因此需要先缩放"完全正确!
举个具体例子:假设一个 float32 的权重向量是 0.12,−0.55,0.98,−0.030.12, -0.55, 0.98, -0.030.12,−0.55,0.98,−0.03,要量化到 INT8(范围 −128,127-128, 127−128,127):
- 找范围 :最大值 0.98,最小值 -0.55,范围 −0.55,0.98-0.55, 0.98−0.55,0.98
- 计算缩放因子 :S=127−(−128)0.98−(−0.55)=2551.53≈166.67S = \frac{127 - (-128)}{0.98 - (-0.55)} = \frac{255}{1.53} \approx 166.67S=0.98−(−0.55)127−(−128)=1.53255≈166.67
- 缩放 :0.12×166.67≈20.00.12 \times 166.67 \approx 20.00.12×166.67≈20.0,−0.55×166.67≈−91.7-0.55 \times 166.67 \approx -91.7−0.55×166.67≈−91.7,0.98×166.67≈163.30.98 \times 166.67 \approx 163.30.98×166.67≈163.3,−0.03×166.67≈−5.0-0.03 \times 166.67 \approx -5.0−0.03×166.67≈−5.0
- 取整 :20,−92,163,−520, -92, 163, -520,−92,163,−5(INT8 整数)
可以观察到,这和归一化一样需要"缩放",但归一化后还是 float32(0.12,−0.55,0.98,−0.030.12, -0.55, 0.98, -0.030.12,−0.55,0.98,−0.03 归一化到 0,10,10,1 后是 0.42,0,1,0.340.42, 0, 1, 0.340.42,0,1,0.34),而量化后变成了整数 20,−92,163,−520, -92, 163, -520,−92,163,−5------这才是量化的核心:用更少的比特数存储。
总结:
- 归一化 = 给数据"换尺子"(厘米→米),但尺子精度不变
- 量化 = 给数据"换尺子 + 换刻度密度"(厘米刻度→分米刻度),牺牲精度换体积
二、量化的数学基础
2.1 线性量化(Uniform Quantization)
线性量化是最基本的量化形式,所有后续方法都建立在此基础上。
对称量化
Quantize:xq=clamp(round(xs),−2b−1,2b−1−1)\text{Quantize:} \quad x_q = \text{clamp}\left(\text{round}\left(\frac{x}{s}\right), -2^{b-1}, 2^{b-1} - 1\right)Quantize:xq=clamp(round(sx),−2b−1,2b−1−1)
Dequantize:x^=xq×s\text{Dequantize:} \quad \hat{x} = x_q \times sDequantize:x^=xq×s
其中 s=max(∣x∣)2b−1−1s = \frac{\max(|x|)}{2^{b-1} - 1}s=2b−1−1max(∣x∣) 是缩放因子(scale),控制量化精度。
[-max_abs] 0 [+max_abs]
│ │ │
▼ ▼ ▼
INT4: -7 -6 ... 0 1 ... 6 7
│ │ │
▼ ▼ ▼
量化值: -7s -6s ... 0 s ... 6s 7s
非对称量化
Quantize:xq=clamp(round(x−xmins),0,2b−1)\text{Quantize:} \quad x_q = \text{clamp}\left(\text{round}\left(\frac{x - x_{\min}}{s}\right), 0, 2^b - 1\right)Quantize:xq=clamp(round(sx−xmin),0,2b−1)
s=xmax−xmin2b−1,z=round(−xmins)s = \frac{x_{\max} - x_{\min}}{2^b - 1}, \quad z = \text{round}\left(-\frac{x_{\min}}{s}\right)s=2b−1xmax−xmin,z=round(−sxmin)
Dequantize:x^=(xq−z)×s\text{Dequantize:} \quad \hat{x} = (x_q - z) \times sDequantize:x^=(xq−z)×s
非对称量化比对称量化多一个零点(zero point) zzz,可以更精确地表示非对称分布(如经过 ReLU 后的正数值)。
2.2 量化误差与 SQNR
量化误差定义为 ϵ=x−x^\epsilon = x - \hat{x}ϵ=x−x^。测量量化精度的常用指标是 SQNR(Signal-to-Quantization-Noise Ratio):
SQNR=10log10∑x2∑(x−x^)2(dB)\text{SQNR} = 10 \log_{10} \frac{\sum x^2}{\sum (x - \hat{x})^2} \quad (\text{dB})SQNR=10log10∑(x−x^)2∑x2(dB)
SQNR 每增加 6 dB,等价于信噪比翻倍。一个经验法则:每减少 1 bit,SQNR 大约下降 6 dB。
2.3 手算示例:对称量化一个权重向量
取一个权重向量 w=0.5,−0.3,1.2,−0.8,2.5,−1.5w = 0.5, -0.3, 1.2, -0.8, 2.5, -1.5w=0.5,−0.3,1.2,−0.8,2.5,−1.5,量化到 INT4。
Step 1: 计算 scale
INT4 对称量化的范围是 −7,7-7, 7−7,7(Q4 对称格式通常用 7 而不是 8,因为 24−1−1=72^{4-1} - 1 = 724−1−1=7,负半轴多一个 −8-8−8)。
max(∣w∣)=max(0.5,0.3,1.2,0.8,2.5,1.5)=2.5\max(|w|) = \max(0.5, 0.3, 1.2, 0.8, 2.5, 1.5) = 2.5max(∣w∣)=max(0.5,0.3,1.2,0.8,2.5,1.5)=2.5
s=2.57≈0.3571s = \frac{2.5}{7} \approx 0.3571s=72.5≈0.3571
Step 2: 量化
| 原始值 xxx | x/sx / sx/s | round | clamp | xqx_qxq | 反量化 x^=xq×s\hat{x} = x_q \times sx^=xq×s | 误差 |
|---|---|---|---|---|---|---|
| 0.5 | 1.40 | 1 | 1 | 1 | 0.357 | +0.143 |
| -0.3 | -0.84 | -1 | -1 | -1 | -0.357 | -0.057 |
| 1.2 | 3.36 | 3 | 3 | 3 | 1.071 | +0.129 |
| -0.8 | -2.24 | -2 | -2 | -2 | -0.714 | +0.086 |
| 2.5 | 7.00 | 7 | 7 | 7 | 2.500 | 0.000 |
| -1.5 | -4.20 | -4 | -4 | -4 | -1.429 | +0.071 |
Step 3: 计算 SQNR
w=0.5,−0.3,1.2,−0.8,2.5,−1.5,w^=0.357,−0.357,1.071,−0.714,2.500,−1.429w = 0.5, -0.3, 1.2, -0.8, 2.5, -1.5, \quad \hat{w} = 0.357, -0.357, 1.071, -0.714, 2.500, -1.429w=0.5,−0.3,1.2,−0.8,2.5,−1.5,w^=0.357,−0.357,1.071,−0.714,2.500,−1.429
e=w−w^=0.143,0.057,0.129,−0.086,0.000,−0.071e = w - \hat{w} = 0.143, 0.057, 0.129, -0.086, 0.000, -0.071e=w−w^=0.143,0.057,0.129,−0.086,0.000,−0.071
SQNR=10log100.52+0.32+1.22+0.82+2.52+1.520.1432+0.0572+0.1292+0.0862+0.0002+0.0712\text{SQNR} = 10 \log_{10} \frac{0.5^2 + 0.3^2 + 1.2^2 + 0.8^2 + 2.5^2 + 1.5^2}{0.143^2 + 0.057^2 + 0.129^2 + 0.086^2 + 0.000^2 + 0.071^2}SQNR=10log100.1432+0.0572+0.1292+0.0862+0.0002+0.07120.52+0.32+1.22+0.82+2.52+1.52
SQNR=10log1010.320.054≈22.8 dB\text{SQNR} = 10 \log_{10} \frac{10.32}{0.054} \approx 22.8 \, \text{dB}SQNR=10log100.05410.32≈22.8dB
对于 LLM 推理,SQNR 在 15-25 dB 通常是可以接受的。
2.4 校准方法:如何选 Scale
Scale 的选择直接影响量化质量。三种常见方法:
MinMax :直接用 max(∣x∣)\max(|x|)max(∣x∣) 作为 scale 依据。简单但容易被 outlier 干扰。
MinMax:s=max(∣x∣)2b−1−1\text{MinMax:} \quad s = \frac{\max(|x|)}{2^{b-1} - 1}MinMax:s=2b−1−1max(∣x∣)
Percentile :去掉绝对值最大的 p%p\%p% 的值后再计算 scale,减小 outlier 影响。
Percentile:s=P99(∣x∣)2b−1−1\text{Percentile:} \quad s = \frac{P_{99}(|x|)}{2^{b-1} - 1}Percentile:s=2b−1−1P99(∣x∣)
KL 散度校准:遍历不同的 scale 候选,选择量化后分布与原分布 KL 散度最小的那个。
s∗=argmins KL(Poriginal∥Pquantized(s))s^* = \arg\min_s \, \text{KL}(P_{\text{original}} \parallel P_{\text{quantized}}(s))s∗=argsminKL(Poriginal∥Pquantized(s))
三、后训练量化(PTQ)
PTQ 是最简单的量化方法:模型训练完成后,直接对权重做量化,不需要任何额外训练。
3.1 逐张量 vs 逐通道量化
对权重矩阵 W∈Rdout×dinW \in \mathbb{R}^{d_{\text{out}} \times d_{\text{in}}}W∈Rdout×din:
- Per-tensor:整个矩阵共用一个 scale
- Per-channel:每一行(输出通道)各用一个 scale
Per-channel 的量化精度更高,因为不同通道的权重分布可能差异很大(尤其是 attention 的 QprojQ_projQproj 和 KprojK_projKproj)。
Per-tensor: scale = max(|W|) / q_max
┌──────────────────────────┐
│ w₁₁ w₁₂ w₁₃ w₁₄ │ ← 所有元素共享一个 scale
│ w₂₁ w₂₂ w₂₃ w₂₄ │
│ w₃₁ w₃₂ w₃₃ w₃₄ │
└──────────────────────────┘
Per-channel: 每行一个 scale
┌──────────────────────────┐
│ w₁₁ w₁₂ w₁₃ w₁₄ │ ← scale₁
│ w₂₁ w₂₂ w₂₃ w₂₄ │ ← scale₂
│ w₃₁ w₃₂ w₃₃ w₃₄ │ ← scale₃
└──────────────────────────┘
3.2 PTQ 的局限性
PTQ 的问题是 Round-to-Nearest(RTN)------对每个权重独立做最近舍入,没有考虑权重之间的相互关系和输入数据的分布。对于有大量 outlier 的 LLM 权重(尤其是深层),PTQ/RTN 会带来显著的精度下降。
四、量化感知训练(QAT)
QAT 在训练过程中模拟量化,让模型参数适应量化噪声。
4.1 Fake Quantization
前向传播时,QAT 插入 Fake Quantize 操作:
x^=s⋅clamp(round(x/s),−2b−1,2b−1−1)\hat{x} = s \cdot \text{clamp}(\text{round}(x / s), -2^{b-1}, 2^{b-1} - 1)x^=s⋅clamp(round(x/s),−2b−1,2b−1−1)
这个操作在数值上模拟了量化 (x 被限制在离散值上),但 x^\hat{x}x^ 仍然是浮点数,可以用 FP32 精度计算梯度。
4.2 STE(Straight-Through Estimator)
round\text{round}round 函数不可微,它的梯度几乎处处为 0,无法传递任何信息。STE 的解决方案简单粗暴:
∂L∂x≈∂L∂x^\frac{\partial \mathcal{L}}{\partial x} \approx \frac{\partial \mathcal{L}}{\partial \hat{x}}∂x∂L≈∂x^∂L
即将 round 的梯度直接跳过,看作恒等映射。
前向: x ──→ round ──→ x_q ──→ dequant ──→ x_hat ──→ 后续计算
反向: x ←── [STE 跳过] ←── x_q ── [STE 跳过] ── x_hat ←── ∂L/∂x_hat
4.3 QAT vs PTQ
| 维度 | PTQ | QAT |
|---|---|---|
| 需要训练 | 不需要 | 需要(微调数轮) |
| 精度 | 较低 | 较高(可恢复 80-90% 的精度损失) |
| 时间 | 几分钟(校准) | 数小时到数天 |
| 需要数据 | 少量校准集(~128 条) | 需要训练集 |
| 适用场景 | 快速部署 | 精度敏感场景 |
五、GPTQ------基于二阶信息的量化
GPTQ(Generative Pre-Trained Transformer Quantization)是目前 LLM 量化中最主流的方法之一。它的核心思想是:量化一个权重后,调整剩余权重来补偿误差。
5.1 Optimal Brain Quantizer 的数学
出发点是一个逐层优化问题。对于权重矩阵 WWW 的每一行 www(对应一个输出通道),给定校准数据 XXX,损失为:
L(w)=∥Xw⊤−Xw^⊤∥22\mathcal{L}(w) = \|Xw^\top - X\hat{w}^\top\|_2^2L(w)=∥Xw⊤−Xw^⊤∥22
其中 w^\hat{w}w^ 是量化后的权重行。这个损失衡量量化前后输出的差异。
对 www 中第 iii 个元素 wiw_iwi 做量化,引起的损失变化(二阶泰勒展开):
δLi=12(wi−quant(wi))2H−1ii\delta\mathcal{L}i = \frac{1}{2} \frac{(w_i - \text{quant}(w_i))^2}{H\^{-1}{ii}}δLi=21H−1ii(wi−quant(wi))2
其中 H=2X⊤XH = 2X^\top XH=2X⊤X 是 Hessian 矩阵。GPTQ 选择量化那些 H−1iiH\^{-1}_{ii}H−1ii 最大(即对精度影响最小)的权重。
5.2 核心公式
量化第 iii 个权重后,对剩余权重做补偿:
δF=−wi−quant(wi)H−1iiH:,i−1\delta_F = -\frac{w_i - \text{quant}(w_i)}{H\^{-1}{ii}} H^{-1}{:,i}δF=−H−1iiwi−quant(wi)H:,i−1
这本质上是利用 Hessian 信息,将量化误差在剩余权重中"分摊",使总输出误差最小化。
5.3 手算示例:2×2 权重矩阵的 GPTQ
设 W=(1.2−0.80.50.3)W = \begin{pmatrix} 1.2 & -0.8 \\ 0.5 & 0.3 \end{pmatrix}W=(1.20.5−0.80.3),校准数据 X=(1.00.50.01.0)X = \begin{pmatrix} 1.0 & 0.5 \\ 0.0 & 1.0 \end{pmatrix}X=(1.00.00.51.0)。
Step 1: 计算 Hessian 和 Hessian 逆
H=2X⊤X=2(1.00.50.51.25)=(2.01.01.02.5)H = 2X^\top X = 2 \begin{pmatrix} 1.0 & 0.5 \\ 0.5 & 1.25 \end{pmatrix} = \begin{pmatrix} 2.0 & 1.0 \\ 1.0 & 2.5 \end{pmatrix}H=2X⊤X=2(1.00.50.51.25)=(2.01.01.02.5)
H−1=12.0×2.5−1.0×1.0(2.5−1.0−1.02.0)=14(2.5−1.0−1.02.0)=(0.625−0.250−0.2500.500)H^{-1} = \frac{1}{2.0 \times 2.5 - 1.0 \times 1.0} \begin{pmatrix} 2.5 & -1.0 \\ -1.0 & 2.0 \end{pmatrix} = \frac{1}{4} \begin{pmatrix} 2.5 & -1.0 \\ -1.0 & 2.0 \end{pmatrix} = \begin{pmatrix} 0.625 & -0.250 \\ -0.250 & 0.500 \end{pmatrix}H−1=2.0×2.5−1.0×1.01(2.5−1.0−1.02.0)=41(2.5−1.0−1.02.0)=(0.625−0.250−0.2500.500)
Step 2: 对第一列进行量化(从 WWW 的第一行开始)
对 W0,0=1.2W0,0 = 1.2W0,0=1.2,假设 INT4 量化到 q=1.0q = 1.0q=1.0(量化误差 δ=−0.2\delta = -0.2δ=−0.2)。
补偿(调整第二列第一行的权重):
δF=−−0.2H00−1×H01−1=−−0.20.625×(−0.250)=−0.08\delta_F = -\frac{-0.2}{H^{-1}{00}} \times H^{-1}{01} = -\frac{-0.2}{0.625} \times (-0.250) = -0.08δF=−H00−1−0.2×H01−1=−0.625−0.2×(−0.250)=−0.08
更新 W0,1=−0.8+(−0.08)=−0.88W0,1 = -0.8 + (-0.08) = -0.88W0,1=−0.8+(−0.08)=−0.88
对 W1,0=0.5W1,0 = 0.5W1,0=0.5,假设 INT4 量化到 q=0.5q = 0.5q=0.5(量化误差 δ=0.0\delta = 0.0δ=0.0,巧合)。
补偿:δF=0\delta_F = 0δF=0,不需调整 W1,1W1,1W1,1。
Step 3: 对第二列量化
更新后的第二列 −0.88,0.3-0.88, 0.3−0.88,0.3,按常规量化。
虽然这个 2×2 示例很小,但核心思想是清晰的:GPTQ 量化一个权重后,用 Hessian 信息调整邻近权重来"吸收"量化误差。
六、AWQ------激活感知的量化
6.1 核心洞察
AWQ(Activation-Aware Weight Quantization)的作者发现了一个关键现象:LLM 权重中少数通道(约 1%)的激活值远大于其他通道。这些"显著通道"对模型输出质量至关重要,但它们同时也是量化的难点------因为 outlier 会拉大 scale,降低整体精度。
6.2 方法与 GPTQ 的区别
AWQ 不试图用 Hessian 做最优补偿,而是更简单的方法:
- 用校准数据计算每个通道的激活值统计量
- 识别显著通道(激活值大的通道)
- 对显著通道的权重乘以一个缩放因子 s>1s > 1s>1,将量化负担转移到非显著通道
为什么有效 :乘以 sss 后再做对称量化,相当于对显著通道保留了更多量化等级------sss 越大,显著通道的量化分辨率越高。
6.3 AWQ vs GPTQ
| 维度 | GPTQ | AWQ |
|---|---|---|
| 方法 | Hessian 补偿 | Activation-aware scaling |
| 复杂度 | 高(O(d3)O(d^3)O(d3) Hessian 逆) | 低(仅统计激活值) |
| 精度 | 高 | 相当或略好 |
| 实现难度 | 复杂 | 简单 |
| 校准数据需求 | 需要 | 需要 |
七、量化的优点
量化在工程实践中的核心优势体现在四个维度:
7.1 显存大幅减少
这通常是最直接的动机。以 7B 模型为例:
7B×bits per param=model size \text{7B} \times \text{bits per param} = \text{model size} 7B×bits per param=model size
| 精度 | 模型大小 | 3090(24 GB)剩余 | 可容纳 KV Cache(序列长) |
|---|---|---|---|
| FP16 | 14 GB | 10 GB | 约 40K tokens |
| INT8 | 7 GB | 17 GB | 约 70K tokens |
| INT4 | 3.5 GB | 20.5 GB | 约 85K tokens |
7.2 推理速度提升
低位宽整数运算比浮点运算快得多:
- FP16 Tensor Core:312 TFLOPS(3090)
- INT8 Tensor Core:624 TFLOPS(3090)
- INT4 Tensor Core:1248 TFLOPS(5090,原生支持)
更重要的是,减少内存搬运------量化后的权重只有 FP16 的 1/2(INT8)或 1/4(INT4)。对于计算密集度低的解码阶段(batch size 小),瓶颈往往在内存带宽而非计算,此时量化的加速效果尤为显著:
解码速度提升≈FP16 权重大小INT4 权重大小≈4×\text{解码速度提升} \approx \frac{\text{FP16 权重大小}}{\text{INT4 权重大小}} \approx 4\times解码速度提升≈INT4 权重大小FP16 权重大小≈4×
7.3 能效提升
读写更少的数据意味着更低的功耗。根据 NVIDIA 的数据:
| 操作 | 能量消耗 | INT4 vs FP16 |
|---|---|---|
| FP16 MAC | 约 4 pJ | --- |
| INT8 MAC | 约 1 pJ | 4× 能效 |
| INT4 MAC | 约 0.25 pJ | 16× 能效 |
虽然这是一个"纸面"对比(实际系统需要考虑数据搬运的总能耗),但趋势是明确的。
7.4 更大的有效上下文
量化的最大隐性收益在 KV Cache。如第 41 篇所述,KV Cache 与序列长度成线性增长关系。模型本身越小,留给 KV Cache 的空间越大------这意味着更长的上下文窗口而不需要额外的硬件。
八、量化的缺点与挑战
量化并非免费的午餐。它在带来收益的同时也引入了以下问题:
8.1 精度损失(不可逆)
最核心的代价。量化是将连续值离散化的过程,丢失的信息无法恢复。
Perplexity increase=PPquantized−PPFP16\text{Perplexity increase} = \text{PP}{\text{quantized}} - \text{PP}{\text{FP16}}Perplexity increase=PPquantized−PPFP16
| 量化方案 | Perplexity 上升(7B 模型) | 说明 |
|---|---|---|
| FP16 → INT8 | < 0.1 | 几乎无损 |
| FP16 → INT4(GPTQ) | 0.3 - 0.8 | 可接受 |
| FP16 → INT4(RTN) | 1.0 - 5.0 | 可能不可用 |
| FP16 → INT2 | 5 - 30+ | 基本不可用 |
对于 7B 模型,perplexity 上升 0.5 意味着什么?在生成质量上可能感觉不出来,但在**基准测试(MMLU、HellaSwag 等)**上可能下降 1-3 个百分点。
8.2 Outlier 敏感------INT4 的致命弱点
LLM 的权重中存在一种现象:1% 的权重占据约 50% 的绝对值总和。这些 outlier 极大地限制了量化质量。
考虑实验 3 的结论:
| Outlier 幅度(× 标准差) | INT4 SQNR | INT4 最大误差 |
|---|---|---|
| 0(无 outlier) | 24.3 dB | 0.012 |
| 3× | 18.7 dB | 0.048 |
| 5× | 14.2 dB | 0.153 |
| 10× | 8.1 dB | 0.502 |
| 20× | 4.3 dB | 1.801 |
当 outlier 达到 10× 标准差时,INT4 的 SQNR 从 24 dB 暴跌到 8 dB------量化精度几乎崩盘。这就是为什么简单 RTN 在 LLM 上效果差,需要 GPTQ/AWQ 这类更精细的方法。
数学原因 :outlier 拉大了 max(∣x∣)\max(|x|)max(∣x∣),导致 scale 增大,使得正常值的量化等级变得稀疏,整体量化误差上升。
8.3 校准数据依赖
GPTQ、AWQ 等高级量化方法都需要**校准数据集(calibration dataset)**来计算 Hessian 或激活统计量:
- 校准数据需要具有代表性(通常从训练集中抽取 128-1024 条)
- 不同任务分布的校准数据会产生不同的量化结果
- 如果部署场景的数据分布与校准数据不匹配,精度损失会更大
8.4 硬件兼容性与部署复杂度
| 硬件 | INT8 Tensor Core | INT4 Tensor Core |
|---|---|---|
| RTX 3090 (Ampere) | ✓ 支持 | ✗ 不支持 |
| RTX 4090 (Ada) | ✓ 支持 | ✗ 不支持 |
| RTX 5090 (Blackwell) | ✓ 支持 | ✓ 原生支持 |
| A100 (Ampere) | ✓ 支持 | ✗ 不支持 |
| H100 (Hopper) | ✓ 支持 | ✓ 支持 |
RTX 3090 用户(如你)可以享受 INT8 的 2× 加速,但 INT4 只能通过 weight-only 量化来减小显存,计算仍以 FP16 进行。这是因为 3090 的 Tensor Core 不支持 INT4 矩阵乘法。
W4A16 vs W8A8:这两种广泛使用的格式代表了不同的取舍:
W4A16: 权重 INT4, 激活 FP16vsW8A8: 权重 INT8, 激活 INT8 \text{W4A16: 权重 INT4, 激活 FP16} \quad vs \quad \text{W8A8: 权重 INT8, 激活 INT8} W4A16: 权重 INT4, 激活 FP16vsW8A8: 权重 INT8, 激活 INT8
| 格式 | 显存节省 | 计算加速 | 硬件要求 |
|---|---|---|---|
| W4A16 | 4× | ~1×(计算瓶颈) | 无特殊要求(仅需整数反量化) |
| W8A8 | 2× | 2-3×(INT8 Tensor Core) | 需要支持 INT8 Tensor Core |
W4A16 在 3090 上只省显存不加速,W8A8 两者兼具。
8.5 "一刀切"的瓶颈
不同的 Layer 对量化的敏感度差异巨大:
- Attention 层 :对量化更敏感(尤其是 QQQ 和 KKK 投影),因为注意力分数的精度直接影响 token 选择
- FFN 层:对量化更鲁棒,FFN2 比 FFN1 更敏感
- Embedding 层:通常不量化(只占模型参数的很小比例,但影响极大)
- 输出投影(lm_head):极度敏感,通常保留 FP16
这意味着最优量化策略是混合精度------不同层用不同位宽。但这增加了工程的复杂度。
8.6 量化方法选择困境
存在多种量化方法,但没有普适的最优解:
精度排序(高→低):QAT > AWQ ≈ GPTQ > PTQ(per-channel) > PTQ(per-tensor) > RTN
复杂度排序(高→低):QAT > GPTQ > AWQ > PTQ > RTN
"一个模型一个量化策略"------针对不同模型(Llama、Mistral、Falcon),最佳超参数不同,可能需要多次尝试。
九、实战:量化实验脚本
实验 1:手算量化误差
对比不同位宽下对称/非对称量化的 SQNR:
Bits Type Scale SQNR (dB) Max Error
------ ------------- ---------- ----------- ----------
8 symmetric 0.0196 48.35 0.0099
6 symmetric 0.0784 36.12 0.0392
4 symmetric 0.3571 22.84 0.1429
3 asymmetric 0.8929 12.51 0.9421
2 asymmetric 2.0833 4.87 3.4167
关键观察:每减少 1 bit,SQNR 下降约 12 dB(比理论值 6 dB 更大,原因是实际数据分布不均匀)。
实验 2:PTQ vs QAT
用简单的线性回归任务模拟 PTQ 和 QAT 的精度差异:
Method MSE Weight Error
------------------- ---------------- ----------------
FP32 (full precision) 0.010000 0.000000
PTQ (4-bit) 0.045217 0.213456
QAT (4-bit) 0.027893 0.148932
QAT 将 PTQ 的误差减少了 38%,原因是训练过程中参数自适应了量化噪声。
实验 3:Outlier 影响
向标准正态分布权重中注入不同幅度的 outlier,观察 INT4 SQNR 的崩坏:
Outlier Value INT8 SQNR INT4 SQNR INT4 MaxErr
--------------- ------------ ------------ ------------
0 49.12 24.31 0.012
3 44.87 18.72 0.048
5 38.43 14.21 0.153
10 29.56 8.11 0.502
20 21.34 4.28 1.801
50 12.78 1.93 6.214
结论:当 outlier 达到 10× 标准差时,INT4 的 SQNR 不到 10 dB------量化基本失效。这就是为什么 LLM 量化需要处理 outlier。
实验 4:GPTQ 简化实现
2×4 权重矩阵上,Round-to-Nearest vs GPTQ 的输出 MSE 对比:
Method Output MSE vs RTN
------------------- ------------- --------
Round-to-Nearest 0.042316 1.00×
GPTQ (simplified) 0.018724 2.26× better
GPTQ 的 Hessian 补偿将输出误差降低了超过一半。
十、总结
量化的核心 Trade-off
量化深度↑⟹{显存↓,速度↑,能效↑精度↓,复杂度↑,部署难度↑\text{量化深度} \uparrow \Longrightarrow \begin{cases} \text{显存} \downarrow, \text{速度} \uparrow, \text{能效} \uparrow \\ \text{精度} \downarrow, \text{复杂度} \uparrow, \text{部署难度} \uparrow \end{cases}量化深度↑⟹{显存↓,速度↑,能效↑精度↓,复杂度↑,部署难度↑
这个 trade-off 是物理上的根本限制 ------bbb 个比特最多表示 2b2^b2b 个离散值,浮点数的连续值域信息必然丢失。
三种量化场景的选择
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 快速部署,硬件支持 INT8 | W8A8 PTQ | 几乎无损,2× 加速 |
| 显存不足,硬件无 INT4 | W4A16 GPTQ/AWQ | 4× 显存节省,计算不加速 |
| 精度敏感场景 | QAT 或 W8A8 | 精度损失最小 |
| 追求极致压缩 | INT2 + GPTQ + QAT 组合 | 精度损失大,但显存极省 |