LLM 量化精读笔记 · 05 权重量化 I:RTN 与 GPTQ(含二阶误差补偿推导)
对应:GPTQ 论文(arXiv:2210.17323,ICLR 2023);OBQ(Frantar & Alistarh 2022);OBS(Hassibi et al. 1993);MIT 6.5940 Lecture 5。 学完本章你应该能:① 说明 RTN 为什么在 4-bit 会翻车;② 从"最小化逐元素误差"升级到"最小化层输出误差";③ 完整推导 OBS/OBQ 的最优补偿公式$\delta F = -(w_{q} - \hat{w}_q)/[H^{-1}]_{qq} \cdot H^{-1}_{:,q}$;④ 说清 GPTQ 的三个工程化观察(顺序无关、行并行、Cholesky)及其复杂度含义;⑤ 手算一个 2×2 的补偿例子。
目录(本章) #
- 本章目标
- 问题设定:weight-only 量化
- RTN:最朴素的基线
- 目标函数升级:从逐元素误差到输出误差
- OBS:删除一个权重的代价
- OBQ:把"删除"换成"量化"
- GPTQ:把 OBQ 规模化
- 手算例:补偿是怎么发生的
- GPTQ 的局限与后续
- 本章小结
- 习题与解答
- 延伸阅读
1. 本章目标 #
04 章我们学会了"选 scale、防 outlier"——那本质还是每个权重独立四舍五入(RTN)。本章回答一个更聪明的问题:
量化必然引入误差;能不能让"还没被量化的权重"去补偿"已经被量化权重"的误差?
这就是 OBQ/GPTQ 的核心思想:量化一个、全局补偿。理解这一章,就理解了一半的 PTQ 文献(AWQ、QuIP# 都是在"补偿策略"上做文章)。
2. 问题设定:weight-only 量化 #
2.1 场景 #
权重量化(W4A16 / W4A8)只压缩权重,激活保持高精度:
存储/带宽收益:权重位宽减半$\to$模型文件减半、decode 搬运减半(03 章模型) 计算收益:解码时仍可与低精度激活配合(W4A8),或权重复用高精度激活(W4A16)
这也是推理服务最常见的起点:先量化权重,因为权重最不敏感、收益最大、风险最小。
2.2 形式化 #
设一层权重$W \in \mathbb{R}^{d_{\text{row}} \times d_{\text{col}}}$,输入激活$X \in \mathbb{R}^{d_{\text{col}} \times N}$(N 个校准样本),量化后 $\hat{W}$。目标:
$$ \min \|WX - \hat{W}X\|^{2}_F $$约束:$\hat{W}$ 的每个元素属于量化网格(如 INT4 + group scale)
注意目标函数里没有单独惩罚逐元素误差——我们关心的是层输出(进而整个模型)的误差。
3. RTN:最朴素的基线 #
3.1 定义 #
RTN(Round-to-Nearest):对每个权重独立做"就近舍入到量化网格":
$$ \hat{W}_ij = \operatorname{clamp}(\operatorname{round}(W_{\text{ij}} / s_{\text{channel}}), q_{\min}, q_{\max}) \times s_{\text{channel}} $$scale 可以是 per-channel 或 per-group(04 章)。实现成本几乎为零。
3.2 为什么 4-bit 会翻车 #
RTN 的三个盲区:
- 忽略分布形状:只保证每个权重离自己最近的网格点,不保证整体输出误差小。
- 忽略层间作用:每层独立量化,误差沿层累积(02 章)。
- ignoring outlier 关联:RTN 不利用"哪些通道重要"(04 章的激活幅度)和"哪些权重动起来代价小"(Hessian)。
实验事实:$FP16 \to INT8 RTN$通常无损;INT4 RTN 在 7B 以上模型明显掉点;INT3 以下基本不可用。RTN 是天花板最低、地板也最低的方法。
4. 目标函数升级:从逐元素误差到输出误差 #
考虑量化单个权重$w_{q}$(W 的第 q 列)对输出的影响。设$L(W) = \frac{1}{2}\|WX - \hat{W}X\|^{2}_F$($\frac{1}{2}$便于求导)。
把 L 在最优权重处做二阶泰勒展开(最优处梯度$g = 0$):
$$ \Delta L \approx \frac{1}{2} \delta w^{T} H \delta w $$其中 H 是 Hessian。对矩阵乘的 MSE 目标:
$H = \partial ^{2}L/\partial w^{2} = X X^{T}$(对列向量形式的权重;$\frac{1}{2}$消掉 2)
GPTQ 论文记为$H = 2XX^{T}$(不写$\frac{1}{2}$),并加阻尼项:
$$ H = 2 X X^{T} + \lambda I, \lambda = 0.01 \times \operatorname{mean}(\operatorname{diag}(2XX^{T})) $$H 的直觉:H 编码了"输入各通道之间的相关性"。H 的对角线 = 每个输入通道的激活能量(谁更重要);非对角线 = 通道间的相关(误差能否被别处补偿)。
H 的维度是$d_{\text{col}} \times d_{\text{col}}$(按输入通道),不是参数个数。一层 12288 通道$\to H$约$12288^{2} \times 4 B \approx 600 MB$,可接受。
5. OBS:删除一个权重的代价 #
OBS(Optimal Brain Surgeon, Hassibi et al. 1993)问:删掉一个权重,如何调整其余权重让损失恢复最多?
设我们要把$w_{q}$设成目标值$\hat{w}_q$(删除= $\hat{w}_q = 0$),约束:
$$ e_{q}^{T} \delta w = \hat{w}_q - w_{q} $$其中$e_{q}$是第 q 个单位向量($\delta w$只在第 q 个分量上取定值)。求:
$$ \begin{aligned} min_\delta w \frac{1}{2} \delta w^{T} H \delta w s.t. e_{q}^{T} \delta w &= \hat{w}_q - w_{q} \end{aligned} $$用拉格朗日乘子法:
$$ \begin{aligned} L &= \frac{1}{2} \delta w^{T} H \delta w + \lambda (e_{q}^{T} \delta w - (\hat{w}_q - w_{q})) \partial L/\partial \delta w &= H \delta w + \lambda e_{q} = 0 \to \delta w = -\lambda H^{-1} e_{q} \end{aligned} $$代入约束:$e_{q}^{T} \delta w = -\lambda [H^{-1}]_{qq} = \hat{w}_q - w_{q}$
$$ \to \lambda = -(\hat{w}_q - w_{q}) / [H^{-1}]_{qq} $$于是最优调整:
$$ \delta w = (\hat{w}_q - w_{q}) / [H^{-1}]_{qq} \times H^{-1}_{:,q} $$写成论文里的形式(只更新未处理权重 F):
$$ \delta F = -(w_{q} - \hat{w}_q) / [H^{-1}]_{qq} \times H^{-1}_{:,q} $$对应的最小损失增量:
$$ \Delta L_{\text{min}} = \frac{1}{2} (w_{q} - \hat{w}_q)^{2} / [H^{-1}]_{qq} $$三个关键结论:
- 补偿量正比于$H^{-1}$的第 q 列:误差往"与 q 相关性强"的权重上摊。
- 分母$[H^{-1}]_{qq}$越小,量化 q 的代价越大:$H^{-1}$对角大 = 该权重"孤立且关键",动它补不回来。
- $\Delta L$公式给了敏感性度量:哪个权重量化最便宜,一目了然。
6. OBQ:把"删除"换成"量化" #
OBQ(Optimal Brain Quantizer, Frantar & Alistarh 2022)把 OBS 的"删掉"换成"量化到$\hat{w}_q$":
算法(逐权重):
- 计算$H^{-1}$(含阻尼$\lambda$)
- 对每个待量化权重 q: a. 计算量化误差$\text{err}_q = w_{q} - \operatorname{quant}(w_{q})$ b. 更新其余未量化权重:$\delta F = -\text{err}_q / [H^{-1}]_{qq} \times H^{-1}_{:,q}$ c. 记录量化后的值 d. 更新$H^{-1}$(去掉第 q 维,rank-1 修正)
- 贪心顺序:每次选$\Delta L = \frac{1}{2} \text{err}_q^{2} / [H^{-1}]_{qq}$最小的 q
贪心顺序的含义:先量化"代价最小"的权重,让后面的补偿空间最大化。
复杂度:每量化一个权重要更新整列$H^{-1}$($O(d_{\text{col}}^{2})$),共$d_{\text{row}} \times d_{\text{col}}$个权重$\to$$O(d_{\text{row}} \cdot d_{\text{col}}^{3})$。对$4096^{2}$的层都嫌慢,更别说$12288^{2}$。
7. GPTQ:把 OBQ 规模化 #
GPTQ(Frantar et al. 2023)基于三个观察把 OBQ 变成可落地的算法:
7.1 观察一:量化顺序几乎不影响结果 #
论文实验发现:对 LLM 权重,贪心选序和固定顺序的结果几乎一样。于是:
去掉贪心:按固定列顺序量化(如从左到右) 省掉:每次比较所有候选权重的$\Delta L$($O(d_{\text{col}})$的排序开销)
7.2 观察二:行之间可以并行 #
补偿公式$\delta F = -\text{err}_q/[H^{-1}]_{qq} \times H^{-1}_{:,q}$里,H 和$H^{-1}$只依赖输入 X,不依赖权重行。所以 W 的$d_{\text{row}}$行共享同一套$H^{-1}$,各行独立量化、独立补偿,可以并行。
7.3 观察三:Cholesky 一次性分解 #
$H^{-1}$只需要算一次,并且用 Cholesky 分解$H^{-1} = LL^{T}$缓存起来;量化时按 128 列一块批量更新,避免逐列更新$H^{-1}$:
复杂度:$O(d_{\text{col}}^{3})$(一次 Cholesky)$+ O(d_{\text{row}} \cdot d_{\text{col}}^{2})$(批量补偿)
$$ \approx O(d_{\text{row}} \cdot d_{\text{col}}^{2}) $$对比 OBQ:$O(d_{\text{row}} \cdot d_{\text{col}}^{3})$
7.4 GPTQ 伪代码(逐层) #
输入:权重 W(d_row × d_col),校准激活 X(d_col × N),位宽 b,分组大小 G
for layer in model:
H = 2 X Xᵀ + λI # λ = 0.01·mean(diag(2XXᵀ))
H_inv = cholesky_inverse(H) # 一次分解,H⁻¹ = LLᵀ
W_hat = W.clone()
for block in range(0, d_col, 128):
for q in block: # 固定顺序
w_q = W[:, q]
ŵ_q = quantize_group(w_q, b, G) # 分组 RTN
err = w_q − ŵ_q
W_hat[:, q] = ŵ_q
# 补偿本块内剩余列
W[:, block 中 q 之后] −= (err / H_inv[q,q])[:, None] × H_inv[q, 剩余列][None, :]
# 用 Cholesky 因子批量补偿块之后的列
W[:, block_end:] −= batch_update(W, W_hat, H_inv, block)
# 替换层权重为 W_hat(连同 scale 一起保存)
(具体实现见 GPTQ 官方仓库;这里保留核心数学结构,块内/块间的 Cholesky 批量更新细节略去。)
7.5 数值与业界地位 #
- 一次量化 OPT-175B / BLOOM-176B 约 4 GPU 小时(A100)。
- $INT4 + group=128$:perplexity 与 FP16 差异在噪声范围附近(论文 Table 2;具体值因模型而异)。
- INT3:仍可用,但已有可感知退化;INT2:明显退化(催生了 QuIP#,06 章)。
- 落地:vLLM、TensorRT-LLM、HuggingFace(AutoGPTQ、GPTQModel)均有生产级 kernel。
8. 手算例:补偿是怎么发生的 #
设一层只有一行权重$W = [2.0, 1.0]$($d_{\text{row}}=1$,$d_{\text{col}}=2$),校准激活:
$$ X = [[1.0, 0.5], $$ [0.5, 1.0]] # 两个样本、两个输入通道,正相关
计算 H(用 GPTQ 的记法,忽略$\frac{1}{2}$):
$$ \begin{aligned} H &= 2 X X^{T} = 2 \times [[1.25, 1.0], [1.0, 1.25]] &= [[2.5, 2.0], [2.0, 2.5]] H^{-1} &= (1/2.25) \times [[2.5, -2.0], [-2.0, 2.5]] &= [[1.111, -0.889], [-0.889, 1.111]] \end{aligned} $$现在量化第 0 列:$w_0 = 2.0 \to \hat{w}_0 = 1.0$(假设网格$step=1$):
$$ \begin{aligned} err_0 &= 2.0 - 1.0 = 1.0 \delta w_1 &= -err_0 / [H^{-1}]_{00} \times [H^{-1}]_{01} &= -1.0 / 1.111 \times (-0.889) = +0.80 w_1 &= 1.0 + 0.80 = 1.80 \end{aligned} $$解读:$w_0$被量化小了 1.0,但输入通道 0 和 1 正相关(x₀ 大时 x₁ 通常也大),所以把$w_1$调大 0.8 可以部分抵消输出的损失。
该步的损失增量:
$$ \Delta L = \frac{1}{2} err_{0}^{2} / [H^{-1}]_{00} = \frac{1}{2} \times 1.0 / 1.111 \approx 0.45 $$如果两个通道完全独立(H 对角),$[H^{-1}]_{01} = 0 \to \delta w_1 = 0$,补偿失效——补偿能力来自输入通道之间的相关性。
9. GPTQ 的局限与后续 #
9.1 局限 #
- 二次假设:目标函数只在最优附近近似二次;量化误差大(低 bit)时,补偿公式不再最优。
- 校准集依赖:H 来自校准数据;校准集分布偏了,H 就偏了。
- 需要重建(reconstruction):逐层前向 + 线性代数,量一次要几分钟到几小时(远慢于 RTN 的几秒)。
- 没有用激活幅度信息:H 用到了激活的二阶统计,但 AWQ 证明"一阶幅度 + 简单缩放"在很多场景同样有效且更省。
9.2 方法谱系位置 #
RTN(零补偿,最简单)
↓ 加入二阶补偿
GPTQ(量化一个、全局补偿)
├─ AWQ(换成"激活幅度缩放"补偿,更简单,06 章)
└─ QuIP#(先"洗牌"再量化,2-bit 天花板更高,06 章)
10. 本章小结 #
- RTN 是最朴素基线:逐元素独立舍入,4-bit 翻车。
- 目标函数:$\min \|WX - \hat{W}X\|^{2}——$误差要看"输出",不是"每个数"。
- 二阶工具:$H = 2XX^{T} + \lambda I$编码输入通道的相关性;$H^{-1}$决定补偿方向。
- OBS/OBQ 公式:$\delta F = -(w_{q} - \hat{w}_q)/[H^{-1}]_{qq} \cdot H^{-1}_{:,q}$;$\Delta L = \frac{1}{2}(w_{q}-\hat{w}_q)^{2}/[H^{-1}]_{qq}$。
- GPTQ 三件套:固定顺序(贪心没必要)+ 行并行(H 共享)+ Cholesky 批量更新$\to O(d_{\text{row}}\cdot d_{\text{col}}^{2})$,175B 模型 4 GPU 小时。
- 补偿的本质:利用输入通道相关性,用没量化的权重"背"已量化权重的误差。
一句话记忆:"$GPTQ =$量化一个权重,让其他权重用 Hessian 告诉它的方向,把误差补回来;RTN 就是各扫门前雪。"
11. 习题与解答 #
题 1(推导):OBS 公式 #
重新推导$\delta F = -(w_{q} - \hat{w}_q)/[H^{-1}]_{qq} \times H^{-1}_{:,q}$,写出拉格朗日函数、驻点条件和$\lambda$的求解。
题 1 解答
$L = \frac{1}{2}\delta w^{T}H\delta w + \lambda (e_{q}^{T}\delta w - (\hat{w}_q-w_{q}))$。驻点:$H\delta w + \lambda e_{q} = 0 \to \delta w = -\lambda H^{-1}e_{q}$。约束:$-\lambda [H^{-1}]_{qq} = \hat{w}_q - w_{q} \to \lambda = -(\hat{w}_q-w_{q})/[H^{-1}]_{qq}$。代回:$\delta w = (\hat{w}_q-w_{q})/[H^{-1}]_{qq}\cdot H^{-1}_{:,q}$。写成$-(w_{q}-\hat{w}_q)/[H^{-1}]_{qq}\cdot H^{-1}_{:,q}$等价。
题 2(手算):补偿方向 #
延续 8 节的例子,若激活改为负相关$X = [[1, -0.5], [-0.5, 1]]$,量化$w_0 = 2.0 \to 1.0$后,$w_1$应该怎么调?直觉上为什么?
题 2 解答
$H = 2[[1.25, -1],[-1, 1.25]]$,$H^{-1} = (1/2.25)[[2.5, 1],[1, 2.5]]$。$\delta w_1 = -1.0/1.111 \times 0.889 = -0.80 \to w_1 = 0.20$。 直觉:通道 0 与 1 负相关(x₀ 大时 x₁ 小),$w_0$被调小后,输出偏低主要发生在 x₀ 大的样本,此时 x₁ 小,所以$w_1$也要调小(而不是调大)才能匹配。
题 3(思考):H 的 λ 阻尼 #
为什么 GPTQ 要在 H 的对角加$\lambda = 0.01\cdot \operatorname{mean}(\operatorname{diag}(H))$?
题 3 解答
校准数据里某些输入方向可能能量极低(近乎零方差),导致 H 奇异、$H^{-1}$爆炸;加阻尼项让 H 正定、数值稳定(ridge 回归的思路)。$\lambda$取对角线均值的 1% 是经验值:太小不起作用,太大扭曲补偿。
题 4(编程):小规模 GPTQ 核心 #
实现 8 节的例子($W=[2,1]$,X 正相关),并验证:量化 col0 后$w_1$的更新、$\Delta L$;再实现"无补偿"版本对比层输出误差 ‖WX−ŴX‖。
题 4 解答要点
有补偿:$\hat{W} = [1, 1.8]$,输出误差$\|(W-\hat{W})X\|^{2} = \|[1, -0.8]X\|^{2}$(X 两列),应显著小于无补偿$\hat{W}=[1,1]$的误差。再验证$\Delta L \approx 0.45$与手算一致。
题 5(开放):GPTQ 与 AWQ 的取舍 #
读 AWQ 论文后回答:为什么 AWQ 声称"无需重建、几分钟量化",却能在 4-bit 追平 GPTQ?它把"二阶补偿"换成了什么?(提示:04 章 7.2 的激活幅度。)
题 5 解答要点
AWQ 观察到"通道重要性 ∝ 激活幅度",用$s=(\max|X|)^\alpha$的 per-channel 缩放直接保护重要通道,等效于把误差预算定向到不敏感处,不需要逐权重求解。代价:没有真正的"误差补偿",2-bit 以下不如 QuIP#。详见 06 章。
12. 延伸阅读 #
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers(arXiv:2210.17323):本章主文献
- OBQ:Optimal Brain Compression: A Framework for Accurate Post-Training Quantization and Pruning(Frantar & Alistarh, NeurIPS 2022)
- OBS:Hassibi, Stork & Wolff, Optimal Brain Surgeon and general network pruning(1993):二阶补偿的源头
- GPTQ 官方代码:块更新与 Cholesky 的工程实现
- 上一篇: 04 量化粒度、校准与离群值;下一篇:[06 权重量化 II:AWQ、SqueezeLLM、QuIP#]——三种"不用重建"或"2-bit 更强"的路线。