附加讲 A2 · 模型压缩:量化、稀疏与低秩 · Quantization, Sparsity & Low-Rank

Advanced Topics in Machine Learning Systems (LLM Edition) · 附录 A2。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:原课没有单独排这一讲。原课第 1 讲的”课程地图”在”算法优化”一层列出了剪枝(量化、稀疏、低秩近似);第 18 讲的 QLoRA 用到了分块量化和 NF4,第 20 讲的 Sheared-LLaMA 做了结构化剪枝。本讲把训练后压缩(post-training compression)整理成一讲。 精读论文:E. Frantar, S. Ashkboos, T. Hoefler, D. Alistarh, GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers(ICLR 2023);J. Lin et al., AWQ: Activation-aware Weight Quantization for On-Device LLM Compression and Acceleration(MLSys 2024)。补充:T. Dettmers et al., LLM.int8()(NeurIPS 2022);G. Xiao et al., SmoothQuant(ICML 2023);E. Frantar, D. Alistarh, SparseGPT(ICML 2023);A. Mishra et al., Accelerating Sparse Deep Neural Networks(NVIDIA,2021)。 依据说明:本讲没有对应的 slides,内容依据上面几篇论文的公开内容。论文数字只做转述。

compress.py(RTN、GPTQ、AWQ、SmoothQuant、LLM.int8() 的模拟、按大小 / N:M / SparseGPT 剪枝、低秩、H100 上的速度账)和全部实验都是本讲义自己设计的;被压缩的是第 5 讲的 LikesWorld 小模型,它比真实的 LLM 好压缩得多;离群激活是人为构造的(不改变模型的输出);速度是 roofline 估算。 配套代码 Code:code/appendixA2/(5 个脚本 + 一个共享模块;需要 numpy 和 torch;每个都在 15 秒左右) 预计阅读时间:2.5 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标

学完这一讲,你应该能够:

  1. 说清楚一种压缩方法省的是字节、FLOPs 还是时间,并用 roofline 判断它在解码、预填充时各能快多少。
  2. 实现 RTN,并解释量化粒度(整个张量 / 每个通道 / 分组)和每个权重平均比特数之间的取舍。
  3. 解释 LLM 里的离群激活为什么让 W8A8 失效,以及 LLM.int8()、SmoothQuant 各怎样绕过它。
  4. 写出 GPTQ 的目标和更新式,说明它的三个技巧(固定顺序、懒批量更新、Cholesky)各解决什么问题。
  5. 解释 AWQ 为什么按激活而不是按权重判断哪些权重重要,以及它为什么用缩放而不是混合精度。
  6. 比较非结构化稀疏、2:4 稀疏、低秩的质量和实际加速,说明为什么”FLOPs 减少 ≠ 时延减少”。

0.2 先修与本讲范围

默认你已经会本讲只给直觉,后面再展开
第 1 讲:roofline、算术强度量化感知训练(QAT)
第 18 讲:absmax、分块量化、NF4、QLoRAKV 缓存量化、FP8 / FP4 训练
第 16 讲:预填充与解码低比特矩阵乘法内核的实现
第 20 讲:结构化剪枝知识蒸馏

0.3 本讲地图

§1  压缩省的是什么:字节、FLOPs、时间            ← 代码 01
§2  最简单的量化:RTN;离群激活与 W8A8           ← 代码 02
§3  GPTQ:用二阶信息补偿量化误差                 ← 代码 03
§4  按激活缩放:AWQ 与 SmoothQuant               ← 代码 04
§5  稀疏与低秩                                   ← 代码 05 演示 1–2
§6  把它们放在一起                               ← 代码 05 演示 3
§7  论文精读:GPTQ;AWQ(附 SmoothQuant、SparseGPT)
§8 小结  §9 常见误区  §10 练习  §11 延伸阅读  §12 下一讲预告

0.4 关于实验

  • 被压缩的模型:第 5 讲的 LikesWorld 模型(2 层,d = 64),只压缩 10 个线性层(词嵌入和输出层不动,和多数论文一样)。它很”皮实”:4 比特、甚至 3 比特的 RTN 准确率都还是 100%,所以多数比较看测试损失的增加,2 比特和稀疏才看得出准确率的差别。
  • 离群激活:小模型的激活很平(每个通道的最大值 / 中位数 ≈ 2)。我们在每个 RMSNorm 后挑 2 个通道把 γ 放大 100 倍、后面线性层的对应列缩小 100 倍——数学上不改变模型,但激活出现了和 LLM 一样形状的离群通道。
  • 校准数据:128 篇测试分布的文档(约 3300 个 token);测试 300 篇。

1. 压缩省的是什么 · Bytes, FLOPs, or time?(代码 01)

1.1 字节

Llama-2-7B 的线性层(6.5B 个权重)存成不同格式:

格式每个权重线性层总共
BF16 稠密2.000 字节13.0 GB
W8A16(只量化权重)1.016 字节6.6 GB
W4A16(组 128)0.531 字节3.4 GB
2:4 稀疏(BF16)1.125 字节7.3 GB
50% 非结构化(CSR)3.000 字节19.4 GB
低秩 r = d/41.000 字节6.5 GB

组量化要多存尺度:4 比特、组 128 时每个权重平均 4.156 比特。非结构化稀疏用 CSR 存储反而更大——每个非零值要带一个 4 字节的列下标;2:4 每个保留的值只带 2 比特的位置信息。

1.2 时间:看瓶颈

H100 上一个 Llama-2-7B 层(7 个线性层)的 roofline 加速比:

格式解码,批 1批 256预填充 2048
W8A161.67×1.21×1.00×
W4A162.48×1.21×1.00×
W8A81.68×1.60×1.94×
2:4 稀疏1.55×1.49×1.94×
50% 非结构化0.71×0.68×0.51×
低秩 r = d/41.28×1.25×1.82×
  • 解码是访存受限的:读的字节少多少就快多少。W4A16 的字节只有 27%,理想是 3.8 倍;这里 2.5 倍是因为 7 个内核每个 4 µs 的启动开销占了不少。GPTQ 论文报告 A100 上端到端 3.25 倍、A6000 上 4.5 倍。
  • 预填充是计算受限的:只量化权重不减少 FLOPs(反量化成 BF16 再算),没有加速;要让矩阵乘法本身用低精度(W8A8 用 INT8 Tensor Core)或者跳过乘加(2:4 稀疏)。
  • 批变大到接近 H100 的拐点(约 295 个 token)时,只量化权重的好处迅速消失。

1.3 FLOPs 少了 ≠ 快了

  • 非结构化稀疏:FLOPs 减半,但在为稠密计算设计的硬件上解码时字节变多、预填充时稀疏内核效率低——两头都变慢。这就是 NVIDIA 只在硬件里支持 2:4 这种规整稀疏的原因,也是第 20 讲 Sheared-LLaMA 坚持剪出规整形状的原因。
  • 低秩:大矩阵上快;但 256 × 256、批 1 时,多出来的一次内核启动(8 µs vs 4 µs)比计算本身还贵。

压缩的收益要按瓶颈算:访存受限时看字节,计算受限时看硬件能不能用上低精度 / 稀疏的算力,启动受限时看内核个数。


2. 最简单的量化:RTN · Round-to-nearest(代码 02)

2.1 量化粒度

RTN:把每个权重四舍五入到最近的格点。非对称量化把 [min, max] 均匀分成 2^bits − 1 份:q = clamp(round(w / Δ) + z, 0, 2^bits − 1),还原 ŵ = (q − z)·Δ。一个尺度 Δ 管多少权重,就是粒度:

比特粒度每个权重损失增加准确率
4整个矩阵4.00+0.0062100%
4每个输出通道4.25+0.0026100%
3整个矩阵3.00+0.031494.8%
3每个输出通道3.25+0.0044100%
2整个矩阵2.00+0.847248.3%
2每个输出通道2.25+0.114879.7%
2每 16 个输入一组3.00+0.019799.7%

(“每个权重”把 16 位的尺度摊进去;小模型的 d_in 只有 64,真实模型是几千,摊得更薄。)

尺度越细,每组的范围越窄、格点越密,代价是要多存尺度。真实 LLM 比这个小模型难量化得多:GPTQ 论文表 3 里,OPT-175B 用 RTN 4 比特困惑度从 8.34 涨到 10.54,3 比特直接崩到 7.3×10³。

2.2 离群激活(LLM.int8())

Dettmers et al. 发现,6.7B 参数以上的 Transformer 里会出现”系统性的离群特征”:

  • 大约 0.1% 的隐藏维度;
  • 数值比其他维度大到约 20 倍;
  • 出现在所有层、几乎所有 token 上;
  • 把它们清零会让准确率大幅下降——它们很重要。

我们在小模型上人为制造同样的形状:注入后第 1 块注意力输入的”最大通道 / 中位数通道”从 2 变成 129,测试损失不变(0.789575 → 0.789575)。我们放大了 100 倍、占 3% 的通道,比论文报告的更夸张:小模型本身的激活很平,放大 30 倍时 W8A8 几乎不受影响。

2.3 W8A8:激活也量化

权重 8 比特(每个输出通道一个尺度),激活三种做法:

做法原模型有离群
激活整个张量一个尺度+0.0012 / 100%+0.2561 / 77.4%
激活每个 token 一个尺度+0.0002 / 100%+0.0055 / 99.7%
LLM.int8():|x| > 6 的维度留浮点+0.0001 / 100%+0.0000 / 100%
  • 整个张量一个尺度时,尺度被几个大通道撑大,其余通道只分到很少几个量化级——少数离群值毁掉所有通道的精度。
  • 每个 token 一个尺度好一些,但离群值是按通道出现的,救不了同一个 token 里的其他通道。
  • 按通道给激活分尺度又不行:矩阵乘法 Σ_j x_j·w_j 是沿通道求和的,每个通道的尺度不同就没法用整数矩阵乘法一次算完(SmoothQuant §3)。
  • LLM.int8() 的混合精度分解:运行时找出离群的维度(这里平均 5.6% 的输入维度),这些列用 FP16 算,其余用 INT8。几乎无损,但要在运行时检测、拆成两个矩阵乘法——论文报告 6.7B 以下的模型反而比 FP16 慢,大的矩阵乘法才快约 2 倍。

3. GPTQ:用二阶信息补偿量化误差 · GPTQ(代码 03)

3.1 目标与 OBQ

GPTQ 一层一层地量化,目标是层输出的误差最小:min_Ŵ ||ŴX − WX||²。它对每一行是一个二次函数,Hessian H = 2XXᵀ(我们的记号里 X 的每一行是一个 token,H = 2XᵀX,d_in × d_in),只和输入有关。

OBQ(Optimal Brain Quantization)的做法:一次量化一个权重 q,然后更新其余还没量化的权重 F 来补偿:

δF=−wq−quant(wq)[HF−1]qq⋅(HF−1):,q,\delta_F = -\frac{w_q - \mathrm{quant}(w_q)}{[H_F^{-1}]_{qq}} \cdot (H_F^{-1})_{:,q},

再从 H⁻¹ 里去掉第 q 行和列。每次挑”量化后损失增加最少”的权重(贪心顺序)。代价是每行 O(d³),对 LLM 太慢。

3.2 三个技巧(代码 03 演示 1)

  1. 任意顺序:所有行按同一个固定的列顺序量化,H_F⁻¹ 对所有行一样,只要更新 d_in 次而不是 d_row·d_in 次。复杂度从 O(d_row·d_col³) 降到 O(max{d_row·d_col², d_col³})。
    • 贪心顺序是不是一定更好?在第 1 块前馈的 w2 上(前 8 行,Σ(q − w)ᵀH(q − w)):3 比特 RTN 643、贪心 310、固定顺序 133;2 比特 3280 / 2023 / 713——这一层上贪心反而更差;
    • 随机的 6 维小问题上,贪心 20 次里 15 次不差于固定顺序,平均误差却略大(33.5 vs 30.3);
    • 论文的结论是”贪心比任意顺序好不了多少,尤其在大层上”,一个解释是贪心把难量化的权重留到最后,那时已经没有多少权重可以帮它补偿。
  2. 懒批量更新:第 i 列的取整只受它自己这一列的更新影响,所以可以每 B = 128 列一块,块内先互相更新,块外的列等块做完再一次性更新。结果完全相同(块 1 / 32 / 128 的最大差 0),只改变访存模式:逐列更新每个元素只做一两次运算,是访存受限的。论文说 GPU 上快一个数量级;我们的 512 × 512 小矩阵在 CPU 上快 1.2–1.9 倍(有计时噪声)。
  3. Cholesky:只需要 H_F⁻¹ 每一行从对角线往后的部分,正好是 H⁻¹ 的 Cholesky 上三角——预先算好,数值稳定。大模型上反复做式 (5) 的更新会累积误差、让 H⁻¹ 变成不定,论文说超过几十亿参数时几乎一定会出现。另外在 H 的对角线上加 1% 的均值做阻尼。

3.3 结果(代码 03 演示 2–4)

逐层:3 比特、每行一个尺度时,每一层 GPTQ 的输出误差 ||ŴX − WX|| / ||WX|| 都只有 RTN 的 40%–62%。GPTQ 优化的是”层输出”,误差被推到了输入很少激活的方向上。

整个模型:

比特分组RTNGPTQGPTQ(不逐层)
4每行+0.0026 / 100%+0.0002 / 100%+0.0002 / 100%
3每行+0.0044 / 100%+0.0008 / 100%+0.0010 / 100%
2每行+0.1148 / 79.7%+0.0114 / 100%+0.0236 / 98.7%
2每 16+0.0197 / 99.7%+0.0133 / 99.7%+0.0106 / 99.8%
  • 分组对 RTN 帮助很大,对 GPTQ 在这里没有帮助:GPTQ 已经通过补偿解决了大部分问题,小模型的 d_in 只有 64 / 128。大模型上 GPTQ + 组 128 是最常见的部署配置。
  • 逐层顺序(后面的层用已经量化过的前面层的输出做校准):每行一个尺度、2 比特时明显更好,后面的层顺便补偿了前面层的误差;分组时差别在噪声量级。
  • 校准数据:2 / 8 / 32 / 128 篇文档时损失增加 +0.041 / +0.019 / +0.018 / +0.010,准确率 94.8% / 99.5% / 96.2% / 99.7%——只用 2 篇也比 RTN 好不少,但 2 比特这么低时结果对校准集很敏感。论文用 128 段 2048 个 token 的随机文本。

4. 按激活缩放:AWQ 与 SmoothQuant · Activation-aware scaling(代码 04)

4.1 哪些权重重要(AWQ 的观察)

输出误差 = 权重误差 × 激活。激活大的输入通道上,同样的权重误差被放大得最多。在注入离群的模型上,3 比特 RTN,保留 3% 的输入通道(整列)为浮点:

做法损失增加
全部 RTN+0.0120
按激活大小挑出 3% 留浮点+0.0036
按权重大小挑出 3% 留浮点+0.0067

AWQ 论文表 1(OPT-6.7B、INT3、组 128)更极端:RTN 困惑度 23.54,保留 1% 按激活挑的通道 11.39,按权重挑 22.37、随机挑 24.23。

4.2 不用混合精度,用缩放

混合精度(同一个矩阵里一部分列是浮点)对硬件不友好。AWQ 的做法:把输入通道 j 的权重乘 s_j、对应的激活除以 s_j,数学上不变;激活大的通道的权重被放大后,在量化网格上分到更多的格点,相对误差变小。s 怎么定?

s=sXα,α∗=arg⁡min⁡α∥Q(W⋅diag(s))(diag(s)−1X)−WX∥,s = s_X^{\alpha},\qquad \alpha^* = \arg\min_\alpha \|Q(W\cdot \mathrm{diag}(s))(\mathrm{diag}(s)^{-1}X) - WX\|,

s_X 是每个通道激活的平均绝对值,α 在 [0, 1] 上网格搜索(论文用 20 个点)。

在一层上(第 1 块的 wqkv,3 比特):误差曲线不单调,α = 0.95 时最好,是 RTN 的 55%;两个离群通道的 s 约是中位数的 114 倍——几乎正好把我们注入的 100 倍还原了回去。s 最后合并进前一个算子(LayerNorm 的 γ),推理时没有额外的计算,量化后的权重仍是整齐的 INT3 / INT4。

4.3 只量化权重:五种做法(代码 04 演示 3)

做法4 比特3 比特
RTN+0.0045+0.0120
保留 3% 浮点(按激活)+0.0023+0.0036
AWQ+0.0009+0.0072
GPTQ+0.0014+0.0041
AWQ + GPTQ+0.0008+0.0022

(注入离群的模型,每行一个尺度,准确率都在 99.8% 以上。)

  • 4 比特时 AWQ 比 GPTQ 好,3 比特时 GPTQ 比 AWQ 好。两者对付不同的误差来源:AWQ 让离群通道的权重不被舍入掉,GPTQ 把舍入误差补偿到别的列上。
  • 两者可以叠加(先缩放、再 GPTQ),3 比特时最好。
  • 没有离群的原模型上,3 比特 AWQ(+0.0101)反而比 RTN(+0.0044)差:每层的缩放只让这一层在校准数据上的输出误差最小,整体损失不一定跟着变小。AWQ 的收益来自激活分布明显不均匀——真实 LLM 正是这样。

4.4 SmoothQuant:让 W8A8 也能用(代码 04 演示 4)

W8A8 的难点在激活(§2.3),权重本身很好量化。SmoothQuant 把量化难度从激活”挪”到权重上:

sj=max⁡∣Xj∣αmax⁡∣Wj∣1−α,X′=X diag(s)−1,W′=diag(s) W.s_j = \frac{\max|X_j|^{\alpha}}{\max|W_j|^{1-\alpha}},\qquad X' = X\,\mathrm{diag}(s)^{-1},\quad W' = \mathrm{diag}(s)\,W.

α有离群:损失增加 / 准确率
不平滑+0.2561 / 77.4%
0.25+0.0013 / 100%
0.5+0.0005 / 100%
0.75+0.0002 / 100%
1.0−0.0001 / 100%

α = 0.5 时第 1 块 wqkv 的激活”max / 中位数”从 129 降到 1.4,权重的仍是 1.4;α = 1 时激活完全拉平、权重的变成 2.0。论文对多数模型用 α = 0.5,激活特别难的(GLM-130B)用 0.75。缩放离线算好、合并进前一层,推理时就是一个普通的 INT8 矩阵乘法——所以真的能加速(论文:最多 1.56 倍、显存省一半)。


5. 稀疏与低秩 · Sparsity and low-rank(代码 05)

5.1 剪枝:按大小 vs SparseGPT

SparseGPT 和 GPTQ 是同一个框架:剪掉一个权重相当于把它”量化”到 0,误差 e = (w − 0)/[H⁻¹]_jj 补偿到后面的列上;每 128 列一块,按 OBS 的显著性 w² / [H⁻¹]_jj² 选掩码。

稀疏方式按绝对值SparseGPT
非结构化 50%+0.0181 / 96.8%+0.0059 / 99.0%
非结构化 60%+0.0602 / 87.9%+0.0476 / 86.7%
非结构化 70%+0.1951 / 60.4%+0.1999 / 63.7%
2:4+0.0657 / 85.0%+0.0599 / 91.2%
4:8+0.0347 / 96.3%+0.0255 / 94.7%
  • 50% 时 SparseGPT 好得多;但它不是处处更好(60%、4:8 时损失更低、准确率却略低),70% 时两者都崩了——一次性剪枝有上限,小模型的冗余比大模型少得多。
  • SparseGPT 论文:OPT-175B 一次性剪到 50%–60% 困惑度几乎不变,2:4 时 8.34 → 8.74,4:8 时 8.45;175B 的模型 4.5 小时内剪完。

5.2 2:4 结构化稀疏与硬件

NVIDIA Ampere 起的稀疏 Tensor Core:每 4 个连续的权重里最多 2 个非零,硬件只做非零的那一半乘加,标称 2 倍的有效算力;每个保留的值带 2 比特的位置信息。论文的做法是”训练 → 按 2:4 剪 → 重新训练”,准确率能回到稠密模型的水平;大的 GEMM 接近 2 倍加速,小的达不到。2:4 比非结构化难:重要的权重扎堆时,每 4 个里也只能留 2 个(小模型上 85% / 91% vs 非结构化 50% 的 97% / 99%)。

5.3 低秩

W ≈ U·V,秩 r 满足 r·(d_out + d_in) = 比例 × d_out·d_in:

参数比例截断 SVD按激活加权的 SVD
75%+0.0080 / 100%+0.0002 / 100%
50%+0.0425 / 92.7%+0.0061 / 98.8%
25%+1.1383 / 18.0%+1.2738 / 9.0%
  • 按激活加权的 SVD(最小化 ||(W − Ŵ)Xᵀ||,对 W·(XᵀX)^{1/2} 做 SVD)比截断 SVD 好得多——和 GPTQ、SparseGPT 用 H = XᵀX 是同一个思想。
  • 但低秩比量化和剪枝都难:参数留 50% 就开始掉,而 4 比特量化只占 1/4 还几乎无损。
  • 为什么:前 25% 的奇异值占 ||W||² 的比例,训练后的 W 平均 69%,随机初始化 52%,训练中的变化量 ΔW = W − W₀ 81%。训练好的权重并不低秩,更新才接近低秩——这正是第 18 讲 LoRA 的前提。

6. 把它们放在一起 · Putting it together(代码 05 演示 3)

做法损失增加准确率字节 / 权重Llama-2-7B 解码加速(估算)
W8 RTN+0.0001100%1.0001.67×
W4 RTN+0.0026100%0.5002.48×
W4 GPTQ+0.0002100%0.5002.48×
W3 GPTQ+0.0008100%0.375没有常用内核
2:4 SparseGPT+0.059991.2%1.1251.55×
50% SparseGPT(CSR)+0.005999.0%3.0000.71×
低秩 50%(按激活加权)+0.006198.8%1.0001.28×
  • 对”解码访存受限”的 LLM 推理,**只量化权重(W4 + GPTQ / AWQ)**性价比最高:字节最少、质量几乎不掉、内核成熟——今天开源模型最常见的部署格式。
  • **W8A8(SmoothQuant)**在预填充和大批量(计算受限)时有用。
  • 2:4 稀疏在计算受限时有用,但一次性剪枝的质量损失明显,往往要再微调。
  • 非结构化稀疏和低秩在”质量 / 字节”上都不占优,非结构化稀疏在稠密硬件上甚至更慢。

选压缩方法要同时看三件事:质量、硬件有没有对应的内核、瓶颈在字节还是 FLOPs。


7. 论文精读 · Paper reading

7.1 GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers(Frantar, Ashkboos, Hoefler, Alistarh, ICLR 2023)

1. 问题:GPT-3 175B 的 FP16 参数就要 326 GB,推理要好几块 GPU。能不能在不重新训练的前提下把权重量化到 3–4 比特、几乎不损失精度,而且量化过程本身在几个 GPU 小时内完成?

2. 为什么重要又困难:

  • 推理的显存和带宽决定了成本:解码是访存受限的,字节少了就快;
  • 量化感知训练(QAT)对 1000 亿参数的模型太贵;
  • 精确的训练后量化方法(AdaRound、BRECQ、OBQ)能处理 ResNet 级别(2500 万参数)的模型,扩展到 1000 亿参数要几百倍的时间。

3. 以前的工作为什么不行:

  • RTN(ZeroQuant、LLM.int8() 的权重部分)在 8 比特时还行,4 比特时大模型上明显掉点,3 比特崩溃;
  • OBQ 很准,但每行 O(d³),对 d = 12288 的层不可行。

4. 新意:

  • 基于 OBQ 的层级量化,目标是层输出的平方误差;
  • 任意顺序:所有行按同一个列顺序量化,H⁻¹ 只更新 d_col 次;
  • 懒批量更新:128 列一块,解决访存瓶颈,GPU 上快一个数量级;
  • Cholesky 重写:预先求 H⁻¹ 的 Cholesky 上三角 + 1% 阻尼,解决大模型上的数值不稳定;
  • 支持分组量化,和定制的 3 / 4 比特 × FP16 矩阵向量乘法内核配合实现加速。

5. 结果:

  • 175B 的模型在一块 A100 上约 4 小时量化完;
  • OPT-175B WikiText2:FP16 8.34,GPTQ 4 比特 8.37、3 比特 8.68;RTN 4 比特 10.54、3 比特 7.3×10³;
  • 越大的模型越好量化;
  • 3 比特的 OPT-175B 能放进一块 80 GB 的 A100;端到端生成比 FP16 快约 3.25 倍(A100)、4.5 倍(A6000);
  • 2 比特、三值量化在大模型上仍有一定的准确率。

本讲的验证:在小模型上实现了完整的算法 1(含分组和阻尼),每一层的输出误差是 RTN 的 40%–62%;2 比特时准确率 79.7% → 100%;懒批量更新的结果和逐列更新完全相同;逐层顺序有帮助。和论文不同的观察:在这个小模型上,贪心顺序(OBQ)并不比固定顺序好,分组对 GPTQ 没有帮助。

局限:

  • 只量化权重,激活仍是 FP16;预填充(计算受限)时没有加速;
  • 依赖校准数据,低比特时对校准集敏感;
  • 加速依赖定制的内核;act-order(按 H 的对角线重排列)等改进是后来的实现里加的。

7.2 AWQ: Activation-aware Weight Quantization for On-Device LLM Compression and Acceleration(Lin et al., MLSys 2024)

1. 问题:在边缘设备(笔记本、手机、Jetson)上跑 LLM,需要 4 比特的只量化权重,而且要真的变快、不依赖反向传播或大量校准数据、能推广到指令微调模型和多模态模型。

2. 为什么重要又困难:

  • 边缘设备的显存和带宽小,解码是访存受限的;
  • GPTQ 用重建(二阶补偿)去拟合校准集,可能过拟合到校准集的分布;
  • 混合精度(像 LLM.int8())对硬件不友好。

3. 以前的工作为什么不行:

  • RTN 在低比特时掉点;
  • GPTQ 的重建依赖校准集,换领域(代码、数学、多模态)时可能变差;某些模型上需要 act-order 等技巧;
  • 混合精度的方法难以高效实现。

4. 新意:

  • 观察:只有 0.1%–1% 的权重是”重要的”,保留它们为 FP16 就能大幅改善量化质量;而判断重要性要看激活的分布,不是看权重;
  • 分析:把权重乘 s、激活除以 s 时,量化误差对这个权重的相对影响缩小约 s 倍(尺度 Δ 基本不变),但 s 太大会放大其他通道的误差;
  • 方法:按通道缩放 s = s_X^α,在 α ∈ [0, 1] 上网格搜索,不需要反向传播或重建,对校准集不敏感;再加权重裁剪的搜索;
  • TinyChat:配套的 4 比特推理框架,内核融合 + 按平台打包权重。

5. 结果:

  • OPT-6.7B INT3(组 128):RTN 23.54,保留 1% 浮点 11.39,s = 2 的缩放 11.92;AWQ 在多个模型族上优于 RTN 和 GPTQ;
  • 在指令微调模型、代码 / 数学、多模态模型上也有效;
  • TinyChat 比 Hugging Face 的 FP16 实现快 3 倍以上,能在移动 GPU 上部署 70B 的 Llama-2。

本讲的验证:在注入离群的模型上复现了”按激活挑重要权重”的观察(+0.0036 vs 按权重挑 +0.0067);α 的搜索曲线、最优 α 接近 1,离群通道的 s 正好接近注入的倍数;AWQ 和 GPTQ 可以叠加。也看到它的边界:激活分布平的原模型上,AWQ 反而略差于 RTN。

局限:

  • 只量化权重,预填充不加速;
  • 依赖激活分布不均匀;缩放只是一维的(每个输入通道一个数),表达能力有限;
  • 低于 3 比特时仍然困难。

7.3 (补充)SmoothQuant、SparseGPT 与 LLM.int8()

  • LLM.int8()(Dettmers et al., NeurIPS 2022):发现 6.7B 以上模型的系统性离群特征;向量级量化 + 混合精度分解让 175B 模型的 INT8 推理不掉点;但运行时的分解开销让小模型变慢。
  • SmoothQuant(Xiao et al., ICML 2023):激活难量化、权重好量化,就用一个数学等价的缩放把难度挪过去;W8A8 全部用 INT8 矩阵乘法,OPT-175B、BLOOM-176B、GLM-130B 上几乎不掉点,能在一个 8 卡节点里服务 530B 的模型,最多 1.56 倍加速、显存减半。
  • SparseGPT(Frantar & Alistarh, ICML 2023):把 GPTQ 的框架用到剪枝上,一次性剪到 50%–60%,支持 2:4 / 4:8,可以和量化叠加。

7.4 论文之间的联系

  • 同一个目标函数:GPTQ、SparseGPT、按激活加权的 SVD 都最小化 ||ŴX − WX||²,Hessian 都是 H = XᵀX——“只在乎层输出”让它们能把误差推到输入很少激活的方向上。
  • 同一个观察的两种用法:AWQ 和 SmoothQuant 都看到”激活的通道大小极不均匀”。AWQ 用它保护权重(只量化权重时,放大重要通道的权重);SmoothQuant 用它平滑激活(W8A8 时,缩小激活的离群通道、把难度挪给权重)。缩放方向相反,因为要保护的对象不同。
  • 都要配合硬件:GPTQ / AWQ 要低比特的矩阵向量乘法内核才能加速,SmoothQuant 要 INT8 Tensor Core,2:4 要稀疏 Tensor Core;LLM.int8() 和非结构化稀疏质量好,但没有高效内核,就快不起来。
  • 一个可以写成评审的新想法:把 AWQ 的缩放搜索和 GPTQ 的补偿放进同一个目标里联合优化(我们的实验里两者叠加已经是最好的);或者把 SmoothQuant 的”难度迁移”推广到 4 比特的 W4A4,配合新硬件的 FP4 Tensor Core。

8. 小结 · Summary

  1. 压缩要按瓶颈算:解码访存受限看字节,预填充计算受限看硬件能不能用上低精度 / 稀疏的算力。FLOPs 少了不等于快了——非结构化稀疏在稠密硬件上更慢。
  2. RTN 的质量取决于粒度:尺度越细越准,代价是多存尺度。大模型上 4 比特 RTN 已经明显掉点。
  3. 离群激活(6.7B 以上的模型、约 0.1% 的维度)让 W8A8 失效;LLM.int8() 运行时拆出离群维度,SmoothQuant 离线把难度挪到权重上。
  4. GPTQ 最小化层输出的误差,每量化一列就用 H⁻¹ 补偿其余列;固定顺序、懒批量、Cholesky 让它在几小时内量化 175B 的模型,4 比特几乎无损。
  5. AWQ 按激活判断重要的权重,用通道缩放(而不是混合精度)保护它们;和 GPTQ 正交、可以叠加。
  6. SparseGPT 把 GPTQ 的框架用到剪枝;2:4 是硬件支持的稀疏;低秩对训练好的权重效果差,因为权重本身不低秩,更新才低秩。
  7. 今天 LLM 推理最常用的是只量化权重的 4 比特(GPTQ / AWQ);W8A8、2:4 用在计算受限的场景。

9. 常见误区 · Common misconceptions

  1. “4 比特量化就是快 4 倍。” 只在访存受限、内核足够好时接近;预填充(计算受限)时只量化权重没有加速,大批量解码时好处也会消失。
  2. “稀疏 50% 就能快 2 倍。” 非结构化稀疏在稠密硬件上往往更慢(下标更大、内核效率低);只有 2:4 这类硬件支持的结构才有加速,而且一般达不到 2 倍。
  3. “量化误差小的方法就是权重误差小。” GPTQ 的权重误差可能比 RTN 还大,但层输出误差小得多——重要的是输出。
  4. “重要的权重就是绝对值大的权重。” AWQ 的实验说明要看激活:和大激活相乘的权重最重要(按权重大小挑接近随机)。
  5. “按通道量化激活就能解决离群。” 矩阵乘法沿通道求和,激活的每个通道一个尺度没法用整数矩阵乘法一次算完;只能按 token 分尺度,或者像 SmoothQuant 那样把通道的差异挪到权重上。
  6. “训练好的权重是低秩的,所以 LoRA 有效。” 反了:权重本身并不低秩(低秩近似掉点严重),是微调的更新接近低秩。
  7. “小模型上没问题,大模型上也没问题。” 离群特征在 6.7B 以上才系统性出现;反过来,大模型往往更容易量化和剪枝(冗余更多)。小模型上的结论都要打折扣。

10. 练习 · Exercises(含参考答案)

练习 1(比特账):3 比特、组 64、每组一个 16 位尺度和一个 3 比特零点,每个权重平均多少比特?70B 模型的线性层大约多大?

答案:3 + (16 + 3)/64 ≈ 3.30 比特;70B × 3.30 / 8 ≈ 28.9 GB。

练习 2(粒度):一个 4096 × 4096 的矩阵,有一列的权重都是其他列的 50 倍。整个矩阵一个尺度和每个输出通道一个尺度,哪个受它影响更大?每 128 个输入一组呢?

答案:整个矩阵一个尺度时这一列决定了全局的尺度,其余所有权重都只分到很少的格点;每个输出通道一个尺度时,每一行的尺度仍被这一列的元素撑大(每一行都包含这一列),影响一样大;按输入分组(每 128 个一组)时只有这一列所在的组受影响,其余组正常——分组的方向很重要。

练习 3(GPTQ 更新):一行 w = (1.4, 0.5),H⁻¹ = [[2, 1], [1, 2]],格点是整数。先量化第一个权重,第二个权重怎样更新?更新后量化成多少?和 RTN 比呢?

答案:quant(1.4) = 1,e = (1.4 − 1) / [H⁻¹]₁₁ = 0.2;w₂ ← 0.5 − e × [H⁻¹]₁₂ = 0.5 − 0.2 = 0.3 → 量化成 0。RTN 会把 0.5 量化成 0 或 1(正好在中间);GPTQ 根据”第一个权重往下舍入了 0.4”和”两个输入负相关”(H⁻¹ 的非对角元为正,对应 H = 2XᵀX 的非对角元为负)这两件事,把第二个权重也往下推:x₂ 大致和 −x₁ 同向,w₂ 变小正好抵消一部分 w₁ 变小带来的输出误差。

练习 4(离群与 W8A8):激活有 4096 个通道,其中 4 个通道的最大值是 60,其余最大 1。INT8 对称量化、整个张量一个尺度时,普通通道能用到多少个量化级?SmoothQuant α = 0.5(假设权重每个通道的最大值都是 1)后呢?

答案:尺度 = 60 / 127 ≈ 0.47,普通通道(|x| ≤ 1)只能用到 ±2 个量化级,共 5 个。α = 0.5 时离群通道 s = √60 ≈ 7.7,激活变成 60 / 7.7 ≈ 7.7,普通通道 s = 1,新的尺度 = 7.7 / 127 ≈ 0.061,普通通道能用 ±16 个量化级;代价是离群通道的权重放大了 7.7 倍。

练习 5(AWQ 的取舍):为什么 AWQ 里 s 太大会变差?用”量化尺度 Δ 由一组里绝对值最大的权重决定”来解释。

答案:把重要通道的权重放大 s 倍后,如果它变成了这一组里最大的权重,组的尺度 Δ 就被它撑大,组里其他通道的相对误差都变大(论文表 2:s = 4 时 21.2% 的组尺度变了,困惑度回升)。所以 s 要在”保护重要通道”和”不撑大尺度”之间折中,用 α 搜索。

练习 6(2:4 的字节):BF16 的 2:4 稀疏,每 4 个权重存 2 个值和 2 个 2 比特的下标。每个权重平均多少字节?和 INT4 稠密比呢?

答案:(2 × 2 字节 + 4 比特) / 4 = (4 + 0.5) / 4 = 1.125 字节;INT4(组 128)约 0.53 字节。解码时 2:4 稀疏比 INT4 读的字节多一倍,所以解码更慢;但预填充时 2:4 能用稀疏 Tensor Core,INT4 只量化权重则没有加速。

练习 7(低秩的参数):一个 4096 × 11008 的矩阵,秩 r 的低秩分解要多少参数?r 多大时参数是原来的一半?为什么正方形 64 × 64 的矩阵取 r = 32 一点都不省?

答案:r × (4096 + 11008) = 15104 r;一半是 4096 × 11008 / 2 = 22.5M,r ≈ 1493。64 × 64 时 r = 32 要 32 × 128 = 4096 个参数,正好等于原矩阵——低秩要 r < d_out·d_in / (d_out + d_in) 才省。

练习 8(选型):你要在一块 24 GB 的消费级显卡上部署一个 13B 的对话模型,主要是单用户交互(批 1)。你会选什么压缩方法?如果是数据中心里的批量离线推理(批很大、长提示词)呢?

答案:单用户交互是解码、访存受限:选 4 比特只量化权重(AWQ 或 GPTQ,组 128),13B × 0.53 字节 ≈ 7 GB,放得下还留了 KV 缓存的空间,速度约快 3 倍。批量离线推理以预填充和大批量为主,计算受限:选 W8A8(SmoothQuant)或 FP8,用低精度 Tensor Core 提高吞吐;硬件支持时可以考虑 2:4 稀疏(要评估质量、可能要微调)。


11. 延伸阅读 · Further reading

  • M. Nagel et al., Up or Down? Adaptive Rounding for Post-Training Quantization(ICML 2020)——AdaRound。
  • E. Frantar, D. Alistarh, Optimal Brain Compression(NeurIPS 2022)——OBQ。
  • B. Hassibi, D. Stork, Optimal Brain Surgeon(NeurIPS 1992)——用 Hessian 的逆剪枝,GPTQ / SparseGPT 的源头。
  • Z. Yao et al., ZeroQuant(NeurIPS 2022)——细粒度量化 + 层级蒸馏。
  • S. Ashkboos et al., QuaRot(2024);Z. Liu et al., SpinQuant(2024)——用旋转消除离群值,走向 W4A4。
  • M. Sun et al., Wanda: A Simple and Effective Pruning Approach for LLMs(ICLR 2024)——按”权重 × 激活”剪枝,不需要权重更新。
  • NVIDIA, FP8 Formats for Deep Learning(2022)。
  • 本仓库 CMU11-868 第 19 讲、第 20 讲(量化与 GPTQ 的实现)、第 18 讲(NF4、QLoRA)、第 20 讲(结构化剪枝)。

12. 下一讲预告 · Next lecture

附加讲 A3 · 课程项目指南:从想法到 MLSys 论文——原课一半的成绩来自课程项目(一页开题、录播展示、最多 8 页的 MLSys 格式报告)。A3 把这门课反复用到的方法论整理成一份指南:

  • 怎样找题目(瓶颈 + 被忽视的工作负载 + 新硬件特性)、怎样在动手前先做”信封背面估算”判断值不值得做;
  • 开题的四个部分;MLSys 论文的结构和评审常问的问题;
  • 实验方法学:基线、端到端 vs 微基准、消融、方差与置信区间——配套代码会在本机上实测”计时为什么会骗人”。
← 返回课程目录