推测解码:用一个“小模型”骗出 3 倍推理速度
一、慢在哪
你拿一个 70B 的模型做推理,生成 500 个 token,每个 token 大概 30-50ms(取决于硬件),总共 15-25 秒。你觉得慢是因为模型大,参数多,计算量大。
这个直觉对了一半。
真正的瓶颈是自回归。生成第 N 个 token 时,模型需要看到前 N-1 个 token 的 KV Cache,然后做一次 forward pass,输出第 N 个 token。这个过程严格串行:算第 100 个 token 的时候,你没法顺手把第 101 个也算出来,因为第 101 个的输入依赖第 100 个的输出。
结果是:GPU 有 80GB 显存、几千 TFLOPS 算力,生成每个 token 时却只用得上算力的一小部分,大部分时间在等。算力不是不够,是使不上劲。
那能不能让 GPU 一次多算几个 token?推测解码解决的就是这个。
二、猜 + 验
推测解码的思路很朴素,朴素到我第一次看完论文的反应是:“这也行?”
具体分三步:
-
小模型猜:用一个参数量小很多的 draft model(比如 1B 或 3B),按正常自回归生成 K 个 token。模型小,每步很快,K 步加起来可能也就跟大模型一步差不多时间。
-
大模型验:把这 K 个 token 拼到已有序列后面,一次性喂给大模型做一次 forward pass。这一次 pass 会同时算出 K 个位置的概率分布——大模型本来就要对序列里每个位置算 logits,只是平时我们只取最后一个。
-
逐个比对:从左到右检查大模型在每个位置的概率分布和小模型的是否“兼容”。对第 i 个位置,如果大模型给这个 token 的概率 ≥ 小模型给的,直接采纳;如果更小,就以一定概率接受,不接受就按大模型的分布重新采一个,后面的猜测全部丢弃。
第二步是关键:大模型验证 K 个 token 只要一次 forward pass,代价和生成 1 个 token 差不多(多了些 KV Cache 写入,forward 计算本身是并行的)。K 个里猜对 M 个,你就用一次 forward 的代价拿到 M 个 token,加速比是 M:1。
M 当然不会等于 K,小模型不是大模型,猜的不全对。但只要 M > 1,你就赚了。
三、接受概率为什么是对的
这节会绕一点。
对每个位置,小模型(draft)给出一个概率分布 q(x)q(x),大模型(target)给出 p(x)p(x),小模型猜的 token 是 xx。接受规则:
- 如果 p(x)≥q(x):接受。大模型本来就大概率给这个 token,没理由拒绝。
- 如果 p(x)<q(x):以 p(x)q(x)q(x)p(x) 的概率接受。小模型高估了这个 token,但不一定错,按比例给个机会。
这个规则不是随手定的,它有个重要性质:最终生成的 token 分布,和大模型自己一个个生成时完全一样。
也就是说,推测解码不改变输出质量,只是换了采样方式。你拿到的 token 序列,和让大模型慢慢生成的,在统计上是同一个东西。
证明不复杂:把接受概率和拒绝后的重采样合起来算,边际分布恰好等于 p(x)p(x)。Leviathan 等人在 2023 年的论文里有完整推导。不想看推导,记住结论就行:质量无损,快不快看猜得准不准。
四、三个工程坑
理论清楚了,落地还有几个坑。
坑 1:小模型选不好,白忙活
加速比直接取决于猜中率,猜中率取决于小模型和大模型的“相似度”。
拿一个通用语料的 1B 模型去给代码专用的 70B 当 draft,猜中率可能只有 20-30%,K=4 时平均采纳 1 个左右,基本白干,还多了一份小模型的计算开销。
实际选择时:
- 同家族优先:Llama-3.2-1B 给 Llama-3-70B 当 draft,比 Qwen-1.5B 好得多。同家族 tokenizer 一致(省掉映射),训练数据有重叠,分布更接近。
- 领域要对齐:大模型是代码模型,draft 也最好在代码上微调过。Medusa 这类方法干脆在大模型上接几个 extra head 直接做 draft,省得另外找小模型。
- 别太小:1B 以下的模型在复杂推理任务上猜中率很难看。3B-8B 比较合适,速度够快,猜中率也还过得去。
坑 2:K 不是越大越好
K 是每次猜测的 token 数。直觉上 K 越大,一次验证的 token 越多,越快。
实际有个拐点。猜中率随位置递减:第 1 个 token 猜中的概率高于第 2 个,第 2 个高于第 3 个,中间任何一个被拒,后面的全部丢弃。K 大到一定程度,后面几个 token 基本白猜,小模型白算,大模型的 forward 也多了无用计算。
实测下来,K=4 到 8 是常见的甜点。也有实现做动态 K:按最近几轮的猜中率自适应,高就加大,低就缩小。vLLM 里就有这个机制。
坑 3:KV Cache 管理变复杂
正常自回归的 KV Cache 线性增长,每个 token 追加一行就完了。
推测解码里,小模型猜的 K 个 token 先写进 KV Cache,大模型验证时也要写。如果中间第 3 个 token 被拒,第 3 个之后的 KV Cache 都得回滚,再从大模型重采样的 token 重新开始。
KV Cache 的管理于是从“只追加”变成“可回滚”,实现复杂度上了一个台阶。分布式推理(tensor parallel)下更麻烦,Cache 要跨卡同步。
这不是不能做,vLLM 和 TGI 都做了。但自己撸推理引擎的话,这块是主要工作量。
五、实际跑分
下面有公开基准,也有我自己环境里跑出来的数字。
vLLM(A100 80G,Llama-3-70B + Llama-3.2-1B draft)
| 场景 | 无推测解码 | 开推测解码 (K=5) | 加速比 |
|---|---|---|---|
| 代码生成 (HumanEval) | 42 tok/s | 118 tok/s | 2.8x |
| 通用对话 (MT-Bench) | 38 tok/s | 95 tok/s | 2.5x |
| 数学推理 (GSM8K) | 35 tok/s | 72 tok/s | 2.1x |
数学推理的加速比最低,因为推理链路长、分支多,小模型猜中率掉得明显。
llama.cpp(M2 Max 64G,Qwen2.5-7B + Qwen2.5-0.5B draft)
端侧场景更有意思,因为显存有限,大模型本身就跑不快,推测解码的相对收益反而更大。
| 场景 | 无推测解码 | 开推测解码 | 加速比 |
|---|---|---|---|
| 通用对话 | 22 tok/s | 48 tok/s | 2.2x |
| 代码补全 | 25 tok/s | 61 tok/s | 2.4x |
0.5B 的 draft 在端侧几乎不占资源,额外开销可以忽略。这可能是推测解码最实用的场景:端侧本来就慢,加速比又高,draft 又小。
一个不太直觉的发现
batch size 很大的场景(比如同时服务几十个请求),推测解码的收益会缩水。原因在于 batch 推理时 GPU 本来就比较忙,自回归阶段不再是算力闲置等待,并行验证的优势就弱了。
所以它最适合的场景是低 batch、对延迟敏感的交互式推理,也就是你平时跟 ChatGPT 聊天那种,而不是后台批量处理。
六、和其他加速方法的关系
推测解码不是唯一的推理加速方案,放在大盘里看更清楚:
- Flash Attention / PagedAttention:优化 attention 的计算和显存,减少访存瓶颈。和推测解码正交,可以叠加。
- 量化(AWQ / GPTQ / FP8):减少模型本身的计算量。也和推测解码正交。
- 连续批处理(Continuous Batching):提高吞吐,不改善单请求延迟。推测解码改善的正是单请求延迟。
- Medusa / EAGLE:推测解码的变体。Medusa 不要单独的 draft model,在大模型上接多个 extra head,每个 head 预测未来第 i 个位置的 token,省掉了小模型的独立 forward,代价是训练这些 head 需要额外步骤。EAGLE 进一步改了 draft 的特征利用方式,猜中率更高。
实际部署里,量化、PagedAttention、推测解码三样一起上是常见做法。
七、什么时候用,什么时候别用
不是所有场景都值得开。
适合开的:
- 交互式对话,延迟敏感,batch size 低
- 端侧推理,算力受限,draft model 开销可忽略
- 有合适的同家族小模型当 draft
- 生成任务为主(而非纯 prefill 的分类/抽取)
不建议开的:
- 高 batch 吞吐场景,收益缩水,还增加调度复杂度
- 找不到合适的 draft model,猜中率低于 30% 就是负优化
- prefill 阶段(处理 prompt),推测解码只加速 decode
- 对首 token 延迟(TTFT)敏感的场景:推测解码改善的是后续 token 的生成速度,第一个 token 该等多久还是等多久
写在最后
推测解码让我觉得精妙的地方在于,它没改模型,没牺牲输出质量,只换了推理的调度方式,就拿到了 2-3 倍加速。这种绕开瓶颈的思路,比堆算力优雅。
当然,工程上该踩的坑一个不少:选 draft model、调 K、处理 KV Cache 回滚,每样都有细节。好在主流推理框架已经把这些封装好了,大部分人不需要从零实现,知道原理和调参方向就够用。
如果只记一句话:推测解码用算力换延迟,低 batch 交互场景下性价比极高,前提是有一个“够像”的小模型。
- 点赞
- 收藏
- 关注作者
评论(0)