LESSON 38 · 卷IV 大语言模型

推理提速:量化与投机解码

同样一个模型,有人的笔记本能跑,有人的服务器却排长队。秘密在于:大模型生成文字慢,慢的不是「算」,而是「搬」。看懂了这一点,两个最有效的提速技巧就水到渠成。

第 1 站

先搞清楚:生成慢,慢在哪?

提速之前得先找到病根。回想第 32 课:FlashAttention 的关键洞察是「搬数据比算数据贵」。生成文字时,这个矛盾达到了极致——

模型每生成一个 token,都要把全部权重从显存读进计算单元跑一遍。可这一趟里每个权重只被用了很少几次(一个用户、一个 token),读进来就用一下,然后又得读下一个。

70B 模型(FP16):权重 = 70B × 2 字节 = 140 GB
显存带宽(H100 级)≈ 3.35 TB/s → 读一遍 ≈ 140 ÷ 3350 ≈ 42 ms → 上限约 24 token/秒
计算量:2 × 70B = 140 GFLOP;算力约 1000 TFLOP/s → 只需 ≈ 0.14 ms
搬运 ÷ 计算 ≈ 300 倍 —— 这叫「内存带宽受限(memory-bound)」
生成 1 个 token 的时间账(70B 模型,FP16,H100 级显卡,一个用户)计算140 GFLOP≈ 0.14 ms搬运权重140 GB ÷ 3.35 TB/s≈ 42 ms搬运是计算的约 300 倍:GPU 的算力绝大部分时间在「等数据」
图 38-1生成 1 个 token 的时间账(只有一个用户时)。计算只占一条细线,绝大部分时间花在把权重从显存搬进来。GPU 的算力,多数时候闲着。
既然算力闲着、时间都花在搬运权重上,有哪两条思路能让生成变快?
第 2 站

第一条路:量化——用更少的位数存权重

模型训练时通常用 16 位浮点数(FP16 / BF16)存每个权重。可推理时,这么高的精度往往是浪费。量化就是把权重换成更少的位数,比如 8 位、4 位整数,需要计算时再乘回一个缩放因子还原。做法出奇朴素:

取一组权重 [0.12, −0.53, 0.31, 0.90],用 4 位整数(−7 ~ 7 共 15 个格点)
缩放因子 s = 最大绝对值 ÷ 7 = 0.90 ÷ 7 ≈ 0.129
量化:q = round(w ÷ s) → [1, −4, 2, 7]  还原:q × s → [0.129, −0.514, 0.257, 0.900]
误差:[0.009, 0.016, 0.053, 0] ——每个权重只偏一点点,但存储从 16 位降到了 4 位

这笔账很划算:70B 模型从 140 GB 缩到 35 GB,要搬的东西少了 4 倍,带宽受限的生成速度也能提高近 4 倍,而且一张消费级显卡也许就装得下了。拖动滑块,亲眼看看「格点变粗」是怎么回事:

LAB · 32-A
量化网格:24 个权重被「挤」到少数几个格点上,看误差与体积
每个权重占的位数–
平均相对误差
–
被挤成 0 的权重
–
70B 模型体积
–
单用户速度上限
–
上排灰点是原始权重,下排橙点是量化后的值,连线显示每个权重被「搬」到了哪里。速度上限 = 3.35 TB/s ÷ 模型体积(只算搬运权重的理想上界,忽略缩放因子等开销)。
第 3 站

量化的坑:离群值,以及怎么绕开它

你可能已经在上面的实验里踩到了坑:点一下「加入离群值」——一个特别大的权重,就能让整张表的缩放因子变大,把其余正常的小权重全部「挤」成 0。

这不是杞人忧天:研究发现大模型里确实存在少数「离群」的权重和激活通道,它们数值巨大,却对结果举足轻重。实用的量化方案都要想办法对付它,最常见的招数是:

GROUP
分组 / 分块量化
每 32 或 128 个数共用一个缩放因子,离群值只连累它自己那一小组(你刚才在实验里试过了)。
PTQ
训练后量化
GPTQ、AWQ 等:拿一小批校准数据,逐层调整取整方式、保护「重要通道」,不用重新训练就能压到 4 位。
QAT
量化感知训练
训练时就模拟量化带来的误差,让模型提前适应「粗网格」,质量更稳,但要花训练成本。
FP8 / FP4
低精度浮点
不用整数,而用位数很少的浮点数,并按「微缩放」小块共享缩放因子。FP8 已用于大规模训练,4 位微缩放格式也开始被直接用来发布权重。
⚠️ 遇到的拦路虎 · 量化不是免费午餐

位数压得越低,误差越大:8 位通常几乎无损,4 位多数任务还可以接受,2~3 位往往明显退化。而且长链条的推理更「怕」误差累积——第 36 课那种写几千字思考的模型,一路上小误差可能被放大。还有一个好消息:上一课的 KV 缓存也可以量化,让「显存账单」两头一起瘦。

第 4 站

第二条路:投机解码——小模型打草稿,大模型来验收

再看思路二。既然权重反正要整个读一遍,那一次前向传播里,让大模型同时处理很多个 token,几乎不比只处理 1 个花更多时间(算力本来就闲着)。可生成是自回归的——下一个 token 要等上一个出来才知道,怎么并行?

投机解码(Speculative Decoding)的妙招是「先猜、后验」:找一个又小又快的草稿模型,让它先连猜 k 个 token;然后把这 k 个 token 一起交给大模型,一次前向同时检查每个位置——草稿和大模型自己的选择一致就收下,第一个不一致的地方,换成大模型自己的答案,后面的草稿作废。

普通解码:大模型一个 token 一步大模型 → t1大模型 → t2大模型 → t3大模型 → t4大模型 → t5投机解码:小模型先打草稿,大模型一次并行验收t1t2t3t4小模型:4 步很快大模型:一次前向,同时验 t1~t4t1t2t3t4t5验收通过的全部收下,还能白送一个「大模型自己的」token —— 一次大模型的时间,产出多个 token
图 38-2上:大模型一步一个 token。下:小模型先打草稿,大模型一次并行验收,通过的全收下。只要草稿命中率够高,一次大模型的时间里就能产出多个 token。
设草稿命中率 α = 0.8,每轮打 k = 4 个草稿
每轮期望产出 E = (1 − αk+1) / (1 − α) = (1 − 0.8⁵) / 0.2 ≈ 3.36 个 token
每轮成本 = 1 次大模型 + 4 次小模型(每次约 5%)= 1 + 4 × 0.05 = 1.2
加速比 = 3.36 ÷ 1.2 ≈ 2.8 倍
LAB · 32-B
投机解码模拟器:小模型每轮打 k 个草稿,大模型一次验收
草稿被接受的概率 α–
每轮草稿个数 k–
小模型相对成本 c–
点「跑一轮」,看草稿被接受(橙底)、被拒绝(划线)、以及大模型补上的 token(粗框)
每轮期望产出 token
–
理论加速比
–
模拟:累计轮数 / token
–
模拟:平均每轮产出
–
公式:每轮期望产出 E = (1 − αk+1) / (1 − α);每轮成本 = 1 次大模型 + k 次小模型 = 1 + c·k(以一次大模型为 1);加速比 = E / (1 + c·k)。试试把 α 拉低或把 k 拉高,你会发现草稿并不是打得越多越好。
先澄清一个常见误会

有人担心「让小模型参与」会拉低质量。其实不会:投机解码用一种「拒绝采样」的验收规则,保证最终输出的概率分布与只用大模型完全一致——小模型只是帮忙「猜」,最终拍板的永远是大模型。所以它是一种无损的加速。

第 5 站

还有几招:工程师的工具箱

量化和投机解码是这一课的主角,但推理引擎里还有不少同样重要的招数,思路都是同一个:别让昂贵的显存和算力空转。

MTP
多 token 预测
训练时让模型一次预测后面好几个 token(DeepSeek-V3 采用)。推理时这些额外的预测头可以直接当「草稿」用——不用另配一个小模型,自己给自己打草稿。
BATCH
连续批处理
把很多用户的请求拼到一起算,共享同一次权重搬运。而且某个请求一结束,立刻把新请求补进来,GPU 永远不空转。
PAGED
PagedAttention
把 KV 缓存像操作系统管理内存那样「分页」存放,不用给每个请求预留一整块连续空间,大幅减少碎片浪费(vLLM 的核心思想)。
PREFIX
前缀缓存
很多请求开头的长提示词是一样的(系统提示、文档),把这部分的 KV 缓存起来复用,第二次直接跳过重算。
第 6 站

总结

💡 章节速记 · 本课核心

大模型生成慢,是因为搬权重比算数更贵(内存带宽受限)。量化让权重更瘦、搬得更少;投机解码让大模型一次验收多个草稿、搬一次干多份活。此外还有 MTP、连续批处理、PagedAttention、前缀缓存等工程手段,目标都是「别让显存和算力空转」。

💡 用大白话梳理:这一课的核心直觉

  • 内存墙:70B 模型搬一遍权重约 42 ms,计算只要 0.14 ms,慢在「搬」而不在「算」。
  • 量化:w ≈ q × s,位数减半、体积减半、理想速度翻倍;4 位是常见的甜点区。
  • 离群值:一个大权重会撑大缩放因子、挤没小权重;用分组量化、PTQ(GPTQ/AWQ)、QAT、微缩放浮点来对付。
  • 投机解码:小模型猜 k 个,大模型一次并行验收;E = (1 − α^(k+1))/(1 − α),加速比 = E/(1 + c·k),无损。
  • 工具箱:MTP、连续批处理、PagedAttention、前缀缓存——都在想办法让每次搬运多干点活。
小测验

学习小测验

动动脑筋:核心直觉小测验(选出你的答案后点击「提交」,即可查看生动通俗的详细解析)

Q1为什么在只有一个用户时,大模型逐个生成 token 的速度,主要受限于「显存带宽」而不是「算力」?
Q2关于投机解码,下面哪种说法是正确的?