跳到主要内容

推理效率与压缩 — 为什么慢在「搬数」,怎么把模型塞进小显存

这一章讲三件事: 推理为什么慢——用算术强度这笔账证明,慢不在「算不动」而在「搬不动」(内存墙); 加速的两路办法:系统级省搬运、算法级省次数(投机解码:小模型起草、大模型验收); 压缩的三套刀法:量化(16 位压 4 位)、蒸馏(大模型教小模型)、剪枝(剪掉不重要的参数)。 主走查: 书里的量化算例——一组真数从浮点压成整数再还原,看误差到底有多大。

1. KV cache 与两阶段:每步只算一个 token

第 05 章讲过,自回归生成是一个词一个词挨着来。如果每生成一个词都把整段历史重算一遍, 成本会爆炸。原书把推理拆成两个阶段,拆解的依据是一个朴素的观察: 生成第 t+1 个词时,前 t 个词算过的 key/value 一个字都没变—— 把它们缓存起来,每步只需为新来的那个词做计算。这就是键值缓存(KV cache)1:

预填充(prefill)阶段:整段输入一次性算完,顺便把每个位置的 key/value 存进缓存
│ —— 并行度高,像训练时的前向

解码(decoding)阶段:每步只输入最新那个词,从缓存里取历史的 key/value 算注意力,
│ 生成一个词,再把它的 key/value 追加进缓存
└──── 循环直到结束 —— 每一步都绕不开「读整个缓存+读全部参数」
图说:两阶段的瓶颈完全不同,这就是下一节要算的账。

2. 内存墙:慢的不是算力,是带宽

「哪个阶段慢」可以算出来。先给显卡定个性:它有两个指标—— 算力(每秒多少次浮点运算,A100 是 312 TFLOP/s,即每秒 3.12×10¹⁴ 次) 显存带宽:每秒能从显存搬多少字节——A100 是 2039GB/s。 两者相除得到一个临界值:A100 的最大算术强度 ≈ 142.51 次/字节—— 平均每搬一个字节,最多供得起 142 次运算2

再给模型的每个操作算同样的账:计算量 ÷ 访问量 = 这个操作的算术强度。 强度高于 142.51,瓶颈在算力(compute-bound);低于它,瓶颈在搬数(memory-bound)。 代进 LLaMA-7B 的真实形状(批量 8、序列 1024)——原书表 9.1/9.2 的完整推导,结论两行3:

阶段线性变换强度注意力强度判定
预填充≈ 2731≈ 115算力受限(高于或接近 142.51)
解码全部 ≤ 8≤ 8搬数受限——「内存墙」

「带宽」这个词就是上一段的「显存带宽」:每秒能从显存搬多少字节;解码阶段的强度远低于 142.51, 意味着瓶颈不在算而在搬。

直觉解释:解码阶段每步只处理一个词,计算量小得可怜,但每一步都要把 整个模型的参数(7B 模型是十几 GB)和整个 KV 缓存从显存搬一遍—— 活儿不多,路太远。所以推理低效的根源在解码阶段的搬数,不在算力。 这个判据直接决定了下面所有优化方法的方向:要么少搬,要么少跑。

3. 系统级三招:各省一种搬运

FlashAttention:标准注意力要把 QKᵀ 这个 T×T 的中间矩阵写出来再读回去, T 一大这就是最大的搬运户。它把计算分块(切成一小块一小块)、把中间结果一直留在高速缓存里, 最终输出才写显存——LLaMA-2 7B(序列 2048、批量 8)的注意力时间降到原来的十分之一4

PagedAttention:KV 缓存的传统管理是每追加一个词就重新申请一块显存、整体拷贝, 又慢又碎。它借鉴操作系统的分页(把内存预先切成固定大小的块来管理)思想:显存预先分成固定块,缓存按块分配、按需取用, 免去反复申请拷贝;查询还能与多个缓存块并行计算5

连续批处理(continuous batching):传统批处理要等整批最长的那条生成完才放下一批, 短请求全在陪等。它把调度粒度改到单条请求:谁生成完谁先走、新请求随时插入, 等效批量始终拉满——批量越大,每次搬参数摊到的计算越多,算术强度越高。 DeepSpeed-MII 的 SplitFuse 更进一步,把预填充切块,让两个阶段在同一批里混跑6

4. 算法级:投机解码主走查

系统级省搬运,算法级省大模型出场的次数。代表是投机解码,洞察是: 生成步骤的难度不均——「微软的创始人是」难猜,但「微软的创始人是比尔」后面接「盖茨」, 小模型也会。那就让小模型先起草,大模型只做验收7:

输入:「Give me six hours to chop down a tree」
大模型完整回答共 9 个词:「I will spend the first four sharpening the axe」
—— 原书例(数字全是真的):

第 ① 轮:小模型起草 3 词「I will use」
→ 大模型一次前向验收 3 个位置:前两个对,第三个错
→ 改成「I will spend」(对的全留,错的由大模型当场改)
第 ② 轮:小模型起草「four hours to」→ 验收:第一个就错 → 改成「the」
第 ③ 轮:小模型起草「first three sharpening」→ 第二个错 → 改成「first four」
第 ④ 轮:小模型起草「sharpening the axe」→ 全对,收下

账本:小模型共起草 12 个词(便宜),大模型只跑了 4 次前向(每次验收一整段,还是并行);
不用投机解码,大模型要跑 9 次。约 2 倍加速,**且输出与大模型逐词生成完全一致**——
因为每个词最终都过了大模型的验收。
图说:省的不是计算,是大模型「逐词出场」的次数;质量不变是验收机制保证的。

同族还有三招,各换一个角度8:级联解码(FrugalGPT)让模型从快到慢排队, 每一级先跑二分类(只答「是/否」两选一)的判断——「当前答案够不够好」,够好就不再麻烦更大的模型;

非自回归/半自回归(Medusa)给模型加额外的预测头,一次出 2~3 个词, 但单独用质量差,通常给投机解码当「起草员」;提前退出给每层配一个预测头, 如果某一层的输出分布已经很有把握(用(分布的「不确定程度」,越集中在少数词上熵越低)衡量), 就不再往更深的层走。

5. 量化:把 16 位压成 4 位,账怎么算

量化是把浮点数映射成整数的压缩技术:参数从 16 位浮点压成 8 位或 4 位整数, 显存直接减半再减半。映射公式有两个旋钮:缩放因子 S(一格整数代表多大区间) 和零点 Z(整数的 0 对应浮点的哪个值);压整数用,还原时反乘回去9

主走查:书里那组数,压一遍再还原

原书给了一组可以全程手算的例子(所有数字来自原书,按示例代码跑出)10:

输入:X = [[1.2, 2.4, 3.6], [11.2, 12.4, 13.6]],要压进 8 位整数 [-128, 127]

第 ① 步 定参数(非对称量化:范围 [1.2, 13.6] 正好对准 [-128, 127]):
解方程组 S·(127+Z) = 13.6,S·(−128+Z) = 1.2
→ S = (13.6−1.2)/(127−(−128)) = 12.4/255 ≈ 0.0486,Z = 152
(原书口径:12.4/255 ≈ 0.04863,四舍五入到 4 位小数写成 0.0486;
Z 的联立精确解约 152.7,原书取整成 152——第②③步全部按这对取整后的参数算)
第 ② 步 压缩:Xq = [[−127, −103, −78], [78, 103, 127]]
—— 每个数只占 1 字节,是 16 位浮点的一半
第 ③ 步 还原:X̃ ≈ [[1.2157, 2.3827, 3.5984], [11.1843, 12.4000, 13.5671]]
—— 误差出现在小数点后第二位
对照:若用对称量化(零点必须是 0),范围得扩到 [−13.6, 13.6],
S 变大到 0.1067,[−13.6, 1.2) 整段没有数据,白浪费一半刻度,误差更大。
图说:量化的全部学问,就是「刻度怎么对准数据的实际分布」。

大模型量化的三个特殊问题与对策

把上面的方法搬到大模型,多出三件事11:

  • 只压权重还不够,激活里有大个的:参数超过约 6.7B 后,激活值里出现 离群值——少数通道的数值比其他的大几个数量级,一对齐刻度,普通值全被压成 0。 LLM.int8() 的对策:离群通道单独用 16 位算,其余用 8 位;
  • 激活比权重难压:SmoothQuant 把激活的「难」匀一部分给权重—— 给激活除一个缩放、给权重乘同一个缩放(数学上等价),两边都变得好压;
  • 压坏了能补:GPTQ 按列分组压权重,每压一列,用还没压的列补偿误差,可压到 3/4 位; AWQ 发现只有 0.1%~1% 的「显著权重」对性能影响大,对它们单独保护; 压到 2 位这种极限,再用 LoRA(第 07 章)微调补偿——65B 压到 2 位 + 微调, 能追平 13B 模型的 16 位水平

经验结论与自做实验

原书汇总的学术界共识,外加作者自己的对照实验(表 9.3,LLaMA-7B/13B,三种数据集)—— 显存与分数一起读12:

模型FP16 显存INT8INT4分数变化(ShareGPT,AlpacaFarm 胜率)
LLaMA-7B12.58GB6.65GB3.94GB72.05 → 72.86 → 70.31
LLaMA-13B24.40GB12.53GB7.34GB75.59 → 73.79 → 71.99

三条结论:INT8 权重量化几乎无损;INT4 只掉一两分,显存却省 69%(7B:12.58→3.94); 同样显存预算,宁要更大的模型压到 4 位,不要更小的模型保持 8 位—— 60B 的 4bit 通常强过 30B 的 8bit13。作者的实用建议:显存紧张优先 4 位12

6. 蒸馏与剪枝:从结构上下刀

量化压的是「每个参数占几位」,另外两招直接改「有多少参数」。

蒸馏让大模型(教师)教小模型(学生)。教什么分两派14: 反馈式只教最终答案的分布——教师 softmax 输出的软标签 (不只有「正确答案是谁」,还有「各个答案分别有多像」,信息量远比标准答案大);

特征(中间层那些数字里携带的信息)式,则教中间层的激活;信息更富,但要处理结构对不齐的问题。

大模型时代的主流是黑盒蒸馏:拿不到教师的参数,就让它把「推理过程」也写出来—— 比如思维链蒸馏:教师生成「因为只有球杆用于高尔夫,所以选 A」, 学生同时学答案和这段理由(例 9.2)。

剪枝把不重要的参数直接删掉15非结构化(不按整块结构、零敲碎打地删)剪枝给权重矩阵盖一张 0/1 掩码 (被盖住的变 0):压缩比高,但零也参与运算,不真加速,需要专门软硬件; 结构化剪枝整组删(删维度、删头、删层),真减矩阵乘。 代表战绩:SparseGPT 非结构化剪掉 60% 参数,困惑度基本不动。

Sheared LLaMA 把 LLaMA-2 7B 结构化剪到 2.7B,再续训 50B token, 保住原模型 87.8% 的评测成绩——它的做法是按目标规模学一组「删/留」掩码变量(变量:这里指待求解的未知数, 每个结构一个,取 0 删取 1 留), 同时动态调整数据配比(哪类数据学得差就多喂)。

判断(我们的,不是书里的): 这一章真正可迁移的不是某个具体数字,是算术强度这把诊断尺: 142.51 换一张卡就要重算,但「算一下计算量÷访问量,看它落在临界值哪一侧」的判法不变—— 先诊断瓶颈在算还是在搬,再决定动系统(省搬运)还是动算法(省次数),顺序不能反。 压缩那半章同理:同显存宁大勿小不是压缩技术,是预算分配原则。 如果错,会错在: 如果新硬件把带宽提到远超算力增长(或 MoE 让每步激活的参数大幅变少), 「解码阶段是内存墙」的前提会松动,「宁大勿小」也要按新的显存单价重算—— 但「先算强度、再动手」的诊断流程本身不依赖任何一张具体的卡。

7. 作者的判断与证据

  • 可复算的账: 算术强度的全部推导(表 9.1/9.2 与 LLaMA-7B 的代入)原书给了公式与中间值3; 量化算例的 S/Z 与还原误差来自原书示例代码的运行结果10;
  • 自做实验: 表 9.3 的显存与三基准分数是作者团队用 bitsandbytes 实跑12;
  • 文献共识: 「同显存宁大勿小」「激活比权重难压」「2 位+LoRA 补偿」 是原书对多篇论文的归纳,各自标了出处13;
  • 工程口径: FlashAttention 的十分之一、投机解码的约 2 倍, 是原书引用的论文报告值,实际收益随任务与批量而变47

8. 边界与局限

  • 算术强度分析基于稠密模型与 A100;MoE 模型(每步只激活部分参数)的搬数账不同, 新硬件(H100、专用推理卡)的临界值也不是 142.51——判据方法通用,数字要重代。
  • 投机解码的加速比依赖「小模型起草的命中率」,长难句上草稿频繁被拒,收益缩水。
  • 表 9.3 只测了权重量化;激活量化的低比特化在原书成书时仍是开放问题。
  • 剪枝后的模型需要续训才能回分(Sheared LLaMA 续训 50B token), 这笔训练成本要计入「省了多少」的账。

9. 可带走的

  1. 推理两阶段:预填充(并行、算力受限)+ 逐词解码(串行、带宽受限=内存墙);
  2. 临界值判据:算术强度 = 计算量 ÷ 访问量;A100 的临界值 ≈ 142.51;解码阶段全部 ≤ 8;
  3. KV cache 让每步只算一个词,但每步仍要搬全部参数与整个缓存;
  4. 系统三招:FlashAttention(中间结果不写显存,注意力时间降至 1/10)、 PagedAttention(显存分页)、连续批处理(谁完谁走);
  5. 投机解码:小模型起草、大模型并行验收——9 个词 4 次前向,约 2 倍加速,质量不变;
  6. 量化两旋钮:缩放 S 与零点 Z;非对称量化贴合数据分布,对称量化浪费刻度;
  7. 大模型量化三坎:激活离群值(6.7B 以上出现)、激活比权重难压、极限位数用 LoRA 补;
  8. INT8 几乎无损;INT4 掉一两分省约七成显存;同显存宁要 60B@4bit 不要 30B@8bit;
  9. 蒸馏教分布(软标签)或教过程(思维链);剪枝分结构化(真加速)与非结构化(只省存);
  10. Sheared LLaMA:7B→2.7B 保 87.8%——剪枝+续训是「定制小模型」的完整配方。

10. 原文地图

主题原书章原文位置
KV cache 与两阶段9.2.1text/49-ch09-02-9-2-efficiency-optimization.txt:17(搜「key-value caching」) · text/49-ch09-02-9-2-efficiency-optimization.txt:19(搜「prefill stage」)
算术强度与 A100 142.519.2.1text/49-ch09-02-9-2-efficiency-optimization.txt:25(搜「142.51」)
瓶颈分析(LLaMA-7B 代入)9.2.1text/49-ch09-02-9-2-efficiency-optimization.txt:219(搜「2730.67」) · text/49-ch09-02-9-2-efficiency-optimization.txt:221(搜「memory wall」)
FlashAttention9.2.2text/49-ch09-02-9-2-efficiency-optimization.txt:229(搜「matrix blocking」) · text/49-ch09-02-9-2-efficiency-optimization.txt:231(搜「one tenth」)
PagedAttention9.2.2text/49-ch09-02-9-2-efficiency-optimization.txt:235(搜「paging strategy」)
连续批处理与 SplitFuse9.2.2text/49-ch09-02-9-2-efficiency-optimization.txt:243(搜「continuous batching」)
投机解码 9 词走查9.2.3text/49-ch09-02-9-2-efficiency-optimization.txt:253(搜「two times」) · text/49-ch09-02-9-2-efficiency-optimization.txt:255(搜「sharpening the axe」)
级联/非自回归/提前退出9.2.3text/49-ch09-02-9-2-efficiency-optimization.txt:259(搜「FrugalGPT」) · text/49-ch09-02-9-2-efficiency-optimization.txt:263(搜「Medusa」) · text/49-ch09-02-9-2-efficiency-optimization.txt:269(搜「entropy」)
推理库9.2.4text/49-ch09-02-9-2-efficiency-optimization.txt:281(搜「llama.cpp」) · text/49-ch09-02-9-2-efficiency-optimization.txt:283(搜「vLLM」)
量化公式与旋钮9.3.1text/50-ch09-03-9-3-model-quantization.txt:15(搜「scaling factor」)
对称/非对称与粒度9.3.1text/50-ch09-03-9-3-model-quantization.txt:31(搜「symmetric quantization」) · text/50-ch09-03-9-3-model-quantization.txt:35(搜「128 columns」)
量化算例 S=0.04869.3.1text/50-ch09-03-9-3-model-quantization.txt:49(搜「0.0486」) · text/50-ch09-03-9-3-model-quantization.txt:51(搜「0.1067」)
GPTQ/AWQ9.3.2text/50-ch09-03-9-3-model-quantization.txt:63(搜「GPTQ」) · text/50-ch09-03-9-3-model-quantization.txt:65(搜「salient weights」)
离群值与混合精度分解9.3.2text/50-ch09-03-9-3-model-quantization.txt:73(搜「6.7B」)
SmoothQuant9.3.2text/50-ch09-03-9-3-model-quantization.txt:97(搜「SmoothQuant」)
经验共识(宁大勿小等)9.3.3text/50-ch09-03-9-3-model-quantization.txt:127(搜「60B」) · text/50-ch09-03-9-3-model-quantization.txt:131(搜「2 bits」)
表 9.3 自做实验9.3.3text/50-ch09-03-9-3-model-quantization.txt:137(搜「Table 9.3」) · text/50-ch09-03-9-3-model-quantization.txt:165(搜「12.58」)
蒸馏两派与思维链蒸馏9.4.1text/51-ch09-04-9-4-other-model-compression-methods.txt:15(搜「soft labels」) · text/51-ch09-04-9-4-other-model-compression-methods.txt:41(搜「ℒlabel」)
剪枝两类与战绩9.4.2text/51-ch09-04-9-4-other-model-compression-methods.txt:55(搜「ln_structured」) · text/51-ch09-04-9-4-other-model-compression-methods.txt:63(搜「87.8%」)
Sheared LLaMA 方法9.4.2text/51-ch09-04-9-4-other-model-compression-methods.txt:65(搜「targeted structured pruning」) · text/51-ch09-04-9-4-other-model-compression-methods.txt:81(搜「50B tokens」)

Footnotes

  1. 出处:「9.2.1 Prefill and Decoding」第 17-19 段(text/49-ch09-02-9-2-efficiency-optimization.txt:17,搜「key-value caching」;text/49-ch09-02-9-2-efficiency-optimization.txt:19,搜「prefill stage」)。

  2. 出处:「9.2.1 Quantitative Metrics for Inference Efficiency」第 25 段(text/49-ch09-02-9-2-efficiency-optimization.txt:25,搜「142.51」)。A100-80G:312 TFLOP/s、带宽 2039 GB/s。

  3. 出处:「9.2.1 Memory Wall and Bottleneck Analysis」第 219-221 段(text/49-ch09-02-9-2-efficiency-optimization.txt:219,搜「2730.67」;text/49-ch09-02-9-2-efficiency-optimization.txt:221,搜「memory wall」)。LLaMA-7B:N=32、D=128、H=4096,B=8、T=1024。 2

  4. 出处:「9.2.2 FlashAttention」第 229-231 段(text/49-ch09-02-9-2-efficiency-optimization.txt:229,搜「matrix blocking」;text/49-ch09-02-9-2-efficiency-optimization.txt:231,搜「one tenth」)。 2

  5. 出处:「9.2.2 PagedAttention」第 235-237 段(text/49-ch09-02-9-2-efficiency-optimization.txt:235,搜「paging strategy」)。

  6. 出处:「9.2.2 Batch Management Optimization」第 243 段(text/49-ch09-02-9-2-efficiency-optimization.txt:243,搜「continuous batching」)。

  7. 出处:「9.2.3 Speculative Decoding」第 253-255 段(text/49-ch09-02-9-2-efficiency-optimization.txt:253,搜「two times」;text/49-ch09-02-9-2-efficiency-optimization.txt:255,搜「sharpening the axe」)。 2

  8. 出处:「9.2.3」第 259-271 段(text/49-ch09-02-9-2-efficiency-optimization.txt:259,搜「FrugalGPT」;text/49-ch09-02-9-2-efficiency-optimization.txt:263,搜「Medusa」;text/49-ch09-02-9-2-efficiency-optimization.txt:269,搜「entropy」)。

  9. 出处:「9.3.1 Mathematical Representation」第 15 段(text/50-ch09-03-9-3-model-quantization.txt:15,搜「scaling factor」)。

  10. 出处:「9.3.1 Examples and Practices of Quantization」第 49-51 段(text/50-ch09-03-9-3-model-quantization.txt:49,搜「0.0486」;text/50-ch09-03-9-3-model-quantization.txt:51,搜「0.1067」)。 2

  11. 出处:「9.3.2 Post-Training Quantization Methods」第 63-97 段(text/50-ch09-03-9-3-model-quantization.txt:63,搜「GPTQ」;text/50-ch09-03-9-3-model-quantization.txt:65,搜「salient weights」;text/50-ch09-03-9-3-model-quantization.txt:73,搜「6.7B」;text/50-ch09-03-9-3-model-quantization.txt:97,搜「SmoothQuant」)。

  12. 出处:「9.3.3 Experimental Analysis」第 137 段(text/50-ch09-03-9-3-model-quantization.txt:137,搜「Table 9.3」)与表中数值(text/50-ch09-03-9-3-model-quantization.txt:165,搜「12.58」)。「显存紧张优先 4bit」见第 137 段。 2 3

  13. 出处:「9.3.3 Existing Research Findings」第 127-131 段(text/50-ch09-03-9-3-model-quantization.txt:127,搜「60B」;text/50-ch09-03-9-3-model-quantization.txt:131,搜「2 bits」)。 2

  14. 出处:「9.4.1 Model Distillation」第 15-25 段(text/51-ch09-04-9-4-other-model-compression-methods.txt:15,搜「soft labels」)与思维链蒸馏第 41 段(text/51-ch09-04-9-4-other-model-compression-methods.txt:41,搜「ℒlabel」)。

  15. 出处:「9.4.2 Model Pruning」第 55-81 段(text/51-ch09-04-9-4-other-model-compression-methods.txt:55,搜「ln_structured」;text/51-ch09-04-9-4-other-model-compression-methods.txt:63,搜「87.8%」;text/51-ch09-04-9-4-other-model-compression-methods.txt:81,搜「50B tokens」)。