深度学习的数学原理(四十三)—— 模型量化

衔接前序 :第 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),它只涉及取整这一个操作。

量化则是一个更完整的过程,包含三步:

  1. 缩放(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。
  2. 取整(Round) :缩放后的值做四舍五入,得到整数。这一步才是四舍五入
  3. 反量化(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):

  1. 找范围 :最大值 0.98,最小值 -0.55,范围 −0.55,0.98-0.55, 0.98−0.55,0.98
  2. 计算缩放因子 :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
  3. 缩放 :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
  4. 取整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−xmin⁡s),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⁡−xmin⁡2b−1,z=round(−xmin⁡s)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=10log⁡10∑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=10log⁡100.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=10log⁡1010.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∗=arg⁡min⁡s 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 做最优补偿,而是更简单的方法:

  1. 用校准数据计算每个通道的激活值统计量
  2. 识别显著通道(激活值大的通道)
  3. 对显著通道的权重乘以一个缩放因子 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
18.7 dB 0.048
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 ~1×(计算瓶颈) 无特殊要求(仅需整数反量化)
W8A8 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 组合 精度损失大,但显存极省
相关推荐
Kobebryant-Manba2 小时前
记录暂退法
人工智能·深度学习
如此这般英俊2 小时前
手搓Claude Code-第二章 tool_use
人工智能·python·ai·语言模型
阿聪谈架构3 小时前
第14章:多模态AI实战 —— 让AI"看懂"图片和文档
人工智能·后端
心.c3 小时前
AI Agent 的新战场:从会动手,到被允许动手
人工智能·ai
救救孩子把3 小时前
89-机器学习与大模型开发数学教程-8-7 本书总结与展望
人工智能·机器学习
X54先生(人文科技)3 小时前
ELR-SELLM 碳硅光阴协同演进系统架构文档
人工智能·深度学习·系统架构·开源协议
云烟成雨TD3 小时前
Spring AI 1.x 系列【39】MCP Java SDK 与 Spring AI 集成
java·人工智能·spring
继续商行3 小时前
性能优化的工程美学与极致追求
人工智能
超梦dasgg3 小时前
详细讲解 AI 上下文(Context)
人工智能·状态模式