对应 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 学习目标
学完这一讲,你应该能够:
- 说清楚一种压缩方法省的是字节、FLOPs 还是时间,并用 roofline 判断它在解码、预填充时各能快多少。
- 实现 RTN,并解释量化粒度(整个张量 / 每个通道 / 分组)和每个权重平均比特数之间的取舍。
- 解释 LLM 里的离群激活为什么让 W8A8 失效,以及 LLM.int8()、SmoothQuant 各怎样绕过它。
- 写出 GPTQ 的目标和更新式,说明它的三个技巧(固定顺序、懒批量更新、Cholesky)各解决什么问题。
- 解释 AWQ 为什么按激活而不是按权重判断哪些权重重要,以及它为什么用缩放而不是混合精度。
- 比较非结构化稀疏、2:4 稀疏、低秩的质量和实际加速,说明为什么”FLOPs 减少 ≠ 时延减少”。
0.2 先修与本讲范围
| 默认你已经会 | 本讲只给直觉,后面再展开 |
|---|---|
| 第 1 讲:roofline、算术强度 | 量化感知训练(QAT) |
| 第 18 讲:absmax、分块量化、NF4、QLoRA | KV 缓存量化、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/4 | 1.000 字节 | 6.5 GB |
组量化要多存尺度:4 比特、组 128 时每个权重平均 4.156 比特。非结构化稀疏用 CSR 存储反而更大——每个非零值要带一个 4 字节的列下标;2:4 每个保留的值只带 2 比特的位置信息。
1.2 时间:看瓶颈
H100 上一个 Llama-2-7B 层(7 个线性层)的 roofline 加速比:
| 格式 | 解码,批 1 | 批 256 | 预填充 2048 |
|---|---|---|---|
| W8A16 | 1.67× | 1.21× | 1.00× |
| W4A16 | 2.48× | 1.21× | 1.00× |
| W8A8 | 1.68× | 1.60× | 1.94× |
| 2:4 稀疏 | 1.55× | 1.49× | 1.94× |
| 50% 非结构化 | 0.71× | 0.68× | 0.51× |
| 低秩 r = d/4 | 1.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.0062 | 100% |
| 4 | 每个输出通道 | 4.25 | +0.0026 | 100% |
| 3 | 整个矩阵 | 3.00 | +0.0314 | 94.8% |
| 3 | 每个输出通道 | 3.25 | +0.0044 | 100% |
| 2 | 整个矩阵 | 2.00 | +0.8472 | 48.3% |
| 2 | 每个输出通道 | 2.25 | +0.1148 | 79.7% |
| 2 | 每 16 个输入一组 | 3.00 | +0.0197 | 99.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 来补偿:
再从 H⁻¹ 里去掉第 q 行和列。每次挑”量化后损失增加最少”的权重(贪心顺序)。代价是每行 O(d³),对 LLM 太慢。
3.2 三个技巧(代码 03 演示 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);
- 论文的结论是”贪心比任意顺序好不了多少,尤其在大层上”,一个解释是贪心把难量化的权重留到最后,那时已经没有多少权重可以帮它补偿。
- 懒批量更新:第 i 列的取整只受它自己这一列的更新影响,所以可以每 B = 128 列一块,块内先互相更新,块外的列等块做完再一次性更新。结果完全相同(块 1 / 32 / 128 的最大差 0),只改变访存模式:逐列更新每个元素只做一两次运算,是访存受限的。论文说 GPU 上快一个数量级;我们的 512 × 512 小矩阵在 CPU 上快 1.2–1.9 倍(有计时噪声)。
- Cholesky:只需要 H_F⁻¹ 每一行从对角线往后的部分,正好是 H⁻¹ 的 Cholesky 上三角——预先算好,数值稳定。大模型上反复做式 (5) 的更新会累积误差、让 H⁻¹ 变成不定,论文说超过几十亿参数时几乎一定会出现。另外在 H 的对角线上加 1% 的均值做阻尼。
3.3 结果(代码 03 演示 2–4)
逐层:3 比特、每行一个尺度时,每一层 GPTQ 的输出误差 ||ŴX − WX|| / ||WX|| 都只有 RTN 的 40%–62%。GPTQ 优化的是”层输出”,误差被推到了输入很少激活的方向上。
整个模型:
| 比特 | 分组 | RTN | GPTQ | GPTQ(不逐层) |
|---|---|---|---|---|
| 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_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 把量化难度从激活”挪”到权重上:
| α | 有离群:损失增加 / 准确率 |
|---|---|
| 不平滑 | +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.0001 | 100% | 1.000 | 1.67× |
| W4 RTN | +0.0026 | 100% | 0.500 | 2.48× |
| W4 GPTQ | +0.0002 | 100% | 0.500 | 2.48× |
| W3 GPTQ | +0.0008 | 100% | 0.375 | 没有常用内核 |
| 2:4 SparseGPT | +0.0599 | 91.2% | 1.125 | 1.55× |
| 50% SparseGPT(CSR) | +0.0059 | 99.0% | 3.000 | 0.71× |
| 低秩 50%(按激活加权) | +0.0061 | 98.8% | 1.000 | 1.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
- 压缩要按瓶颈算:解码访存受限看字节,预填充计算受限看硬件能不能用上低精度 / 稀疏的算力。FLOPs 少了不等于快了——非结构化稀疏在稠密硬件上更慢。
- RTN 的质量取决于粒度:尺度越细越准,代价是多存尺度。大模型上 4 比特 RTN 已经明显掉点。
- 离群激活(6.7B 以上的模型、约 0.1% 的维度)让 W8A8 失效;LLM.int8() 运行时拆出离群维度,SmoothQuant 离线把难度挪到权重上。
- GPTQ 最小化层输出的误差,每量化一列就用 H⁻¹ 补偿其余列;固定顺序、懒批量、Cholesky 让它在几小时内量化 175B 的模型,4 比特几乎无损。
- AWQ 按激活判断重要的权重,用通道缩放(而不是混合精度)保护它们;和 GPTQ 正交、可以叠加。
- SparseGPT 把 GPTQ 的框架用到剪枝;2:4 是硬件支持的稀疏;低秩对训练好的权重效果差,因为权重本身不低秩,更新才低秩。
- 今天 LLM 推理最常用的是只量化权重的 4 比特(GPTQ / AWQ);W8A8、2:4 用在计算受限的场景。
9. 常见误区 · Common misconceptions
- “4 比特量化就是快 4 倍。” 只在访存受限、内核足够好时接近;预填充(计算受限)时只量化权重没有加速,大批量解码时好处也会消失。
- “稀疏 50% 就能快 2 倍。” 非结构化稀疏在稠密硬件上往往更慢(下标更大、内核效率低);只有 2:4 这类硬件支持的结构才有加速,而且一般达不到 2 倍。
- “量化误差小的方法就是权重误差小。” GPTQ 的权重误差可能比 RTN 还大,但层输出误差小得多——重要的是输出。
- “重要的权重就是绝对值大的权重。” AWQ 的实验说明要看激活:和大激活相乘的权重最重要(按权重大小挑接近随机)。
- “按通道量化激活就能解决离群。” 矩阵乘法沿通道求和,激活的每个通道一个尺度没法用整数矩阵乘法一次算完;只能按 token 分尺度,或者像 SmoothQuant 那样把通道的差异挪到权重上。
- “训练好的权重是低秩的,所以 LoRA 有效。” 反了:权重本身并不低秩(低秩近似掉点严重),是微调的更新接近低秩。
- “小模型上没问题,大模型上也没问题。” 离群特征在 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 微基准、消融、方差与置信区间——配套代码会在本机上实测”计时为什么会骗人”。