【学习笔记】投机解码:让大模型推理快 2-3 倍的「免费午餐」(14/35)

前面三篇推理优化我们讲了:
-
第 11 篇 三板斧(KV Cache / PagedAttention / Continuous Batching)—— 优化吞吐
-
第 12 篇 量化压缩 —— 优化显存
-
第 13 篇 Flash Attention —— 优化 prefill 延迟
这一篇我们讲一个专门优化 decode 延迟的奇思妙想——
投机解码(Speculative Decoding)。
它的神奇之处在于:用一种「先猜后验」的方式,让单个序列的生成速度提升 2-3 倍,而且数学上完全无损。
更神奇的是:它的核心思想极其简单——让小模型先草拟,大模型再验证——但这个简单想法背后是对 GPU 计算特性的深刻理解。
如果你做过相关工作,下面这些问题应该不陌生:
-
单用户场景下,70B 模型怎么跑得更快?
-
为什么投机解码在 batch 大时反而没用?
-
Medusa、EAGLE、Lookup Decoding 都是什么?
-
投机解码不会改变输出吗?真的"无损"?
-
vLLM 怎么开启投机解码?
读完本文你将能:
-
理解投机解码的核心洞察——为什么验证比生成便宜
-
区分 4 种主流变种的差异、适用场景
-
用 vLLM 实际启用投机解码并实测
-
知道何时该用、何时不该用
我们开始。
一、为什么 Decode 是个独特的瓶颈
1.1 Decode 阶段的特殊性
回顾第 11 篇我们讲的 Prefill vs Decode:
|
阶段 |
计算特性 |
主要瓶颈 |
| Prefill |
一次处理 n 个 token,并行度高 |
算力(compute-bound) |
| Decode |
一次处理 1 个 token,串行生成 |
显存带宽(memory-bound) |
Decode 阶段的核心痛苦:
每生成 1 个 token,都要把整个模型权重从 HBM 读到 SRAM 一遍。
对 Llama-3-70B(INT8,70 GB 权重):
-
单次 decode 必须读 70 GB
-
H100 显存带宽 3.35 TB/s
-
理论最大速度:3350 / 70 ≈ 48 tokens/s(单序列)
这是个硬上限——再怎么优化,单序列也快不过这个数。
1.2 batch 救不了单用户
第 11 篇我们讲过 Continuous Batching 能把吞吐量提到几千 tokens/s。但那是 batch 整体——单用户感知的速度仍然是 ~50 tokens/s。
单用户视角:48 tokens/s 在生成
batch 整体:4800 tokens/s(100 用户并发)
对单用户来说,batch 不能加速我的请求。这就是为什么 ChatGPT 那种聊天场景,"打字速度"是个绕不开的体验痛点。
1.3 GPU 的「隐藏算力」
但其实 GPU 在 decode 阶段算力是闲置的。
H100 的算力 vs 带宽:
-
FP16 算力:989 TFLOPS
-
带宽:3.35 TB/s
-
算力/带宽比:295
意思是:读 1 byte 数据,GPU 能算 295 次。但 decode 阶段每个 token 只算约 2 次(前向乘加)。算力利用率约 0.7%。
GPU 99% 的算力在 decode 时被浪费了。
如果能把这浪费的算力利用起来——让 GPU 一次性算 N 个 token 而不是 1 个——速度就能提升 N 倍。这正是投机解码的切入点。
二、投机解码的核心原理
2.1 一个反直觉的洞察
验证 N 个 token 和生成 1 个 token,对 GPU 来说时间几乎一样。
为什么?因为 attention 在 prefill 模式下可以并行。
具体说:
-
生成 1 个 token:处理 1 个 query,往 KV Cache 找历史 → forward 一次
-
验证 N 个 token:把 N 个 query 一起送入 → 同样 forward 一次
两者计算量差异很大(N 倍),但都是 1 次 forward——而 forward 时间主要由「读完模型权重一遍」决定,与计算量关系不大(memory-bound)。
所以:在 decode 时,一次性验证 N 个 token 几乎是免费的。
2.2 投机解码的核心算法
那怎么搞到 N 个候选 token 来验证?用一个小模型先草拟。
完整流程:
1. Draft 模型生成 K 个候选 token (快)
2. Target 模型一次性 forward 验证 K 个 token
3. 比较两个模型的概率分布,决定接受哪些
4. 至少接受 1 个,最多 K 个
5. 重复
关键问题:怎么"决定接受"?这里有数学保证——通过 rejection sampling,最终输出的概率分布与"Target 模型直接生成"完全一致。
2.3 接受策略:Rejection Sampling
设 Draft 模型在某位置给出 token x 的概率是 q(x),Target 模型给出 x 的概率是 p(x):
情况 A:p(x) >= q(x)
→ 直接接受这个 token
情况 B:p(x) < q(x)
→ 以 p(x)/q(x) 的概率接受
→ 否则,从 normalized(max(0, p - q)) 重新采样一个 token
这种策略让最终采样概率严格等于 p(x)——也就是 Target 模型自己直接采样的分布。
关键认知:投机解码数学上完全无损——和 Target 模型直接生成的结果分布完全一致(在贪心解码下甚至 token 序列也完全相同)。
2.4 加速效果取决于什么
加速比 ≈ (1 + 接受长度) / (1 + Draft 模型相对耗时)
两个关键因素:
-
接受长度 (Accept Length):Draft 模型预测准的连续 token 数
-
Draft 模型耗时占比:Draft 模型相对 Target 模型的速度
举例(Target = Llama-3-70B,Draft = Llama-3-1B):
-
Draft 耗时 ≈ Target 的 1/30
-
平均接受长度 ≈ 4
-
实际加速:约 2.5-3×
2.5 一个完整的例子
Prompt: "今天天气真"
Draft 模型生成 4 个候选: "好啊,我想"
Target 模型一次性 forward 这 4 个 token:
位置 0 (好): Target 概率高 → 接受
位置 1 (啊): Target 概率高 → 接受
位置 2 (,): Target 概率高 → 接受
位置 3 (我): Target 觉得应该是"出" → 拒绝,重新采样得到"出"
最终输出: "好啊,出"
下一轮 Draft 从"出"继续生成 4 个候选 ...
一次 Target forward 输出了 4 个 token——而平时只能输出 1 个。4× 加速。
三、投机解码的四大变种
3.1 经典投机解码(2022, Google + DeepMind 同时提出)
最朴素的形式:用一个独立的小模型做 Draft。
典型组合:
|
Target |
Draft |
加速 |
|
Llama-3-70B |
Llama-3-1B / 8B |
2-3× |
|
GPT-4 |
GPT-3.5 |
2× |
|
Claude Opus |
Claude Haiku |
2.5× |
条件:
-
Draft 和 Target 必须 tokenizer 完全一致(不一致就没法验证)
-
Draft 越准越好(但不一定是越大越好——平衡点)
-
Draft 越快越好
优点:
-
概念简单
-
不需要改 Target 模型
-
通用
缺点:
-
需要部署 2 个模型
-
显存占用增加
-
通常 Draft 比 Target 小 7-10 倍才能有收益
3.2 Medusa(2024):自己「分裂」出多个头
不想部署两个模型?那就让 Target 模型自己同时预测多个未来 token。
Medusa 的做法:
在 Target 模型最后一层 加 K 个额外的 head:
head_0: 预测下一个 token (原本就有)
head_1: 预测下下个 token
head_2: 预测下下下个 token
...
每个 head 是个轻量分类器,只需训练几小时。
工作流程:
-
一次 forward,每个 head 给出多个候选
-
形成"候选 token 树"
-
Target 模型再 forward 一次验证整个树
-
接受最长的有效路径
优点:
-
只用一个模型
-
训练成本低(额外的 head 几小时训完)
-
加速 2-3×
缺点:
-
需要轻微改造模型
-
接受长度通常比双模型方案短
3.3 EAGLE 系列(2024-2025):当下 SOTA
EAGLE 把投机解码推到了新高度。核心思想:
用 Target 模型的 hidden state 而不是最终 logits 做 draft。
原因:hidden state 信息量远高于 logits(logits 是 hidden state 投影到 vocab 后的结果),用它做 draft 更准。
EAGLE 的结构:
Target 模型
├─ 最后一层 hidden state ──→ EAGLE 小模型 (1-2 层 Transformer)
└─ logits 输出 ↓
生成候选 token 树
↓
Target 验证
EAGLE-2 (2024 中) 引入了动态树结构——根据置信度自适应调整候选树形状。
EAGLE-3 (2025) 进一步把多层 hidden state 一起利用,加速达到 3-5×。
实测(Llama-3-70B):
|
方法 |
加速 |
|
无投机 |
1× |
|
经典(Llama-1B Draft) |
2.3× |
|
Medusa |
2.5× |
|
EAGLE-2 |
3.5× |
| EAGLE-3 | 4.5× |
EAGLE 系列已经成为 vLLM、SGLang 等推理框架的默认推荐。
3.4 N-gram / Lookup Decoding(不需要模型!)
最极端的方案——根本不用模型来做 draft。
核心洞察:很多场景下,输出和输入有大量重复:
-
RAG:模型经常引用 prompt 中的原文
-
Code 助手:modify 代码时大量复用原代码
-
多轮对话:经常重复之前提到的实体
-
翻译:人名地名直接保留
Lookup Decoding 做法:
1. 从 prompt + 已生成 token 中提取所有 n-gram
2. 当当前生成位置匹配某个 n-gram 的前缀
3. 直接用 n-gram 的后续作为候选
4. Target 验证
优点:
-
零额外模型
-
零额外显存
-
实现极简
缺点:
-
只对"重复输入"场景有效
-
通用对话场景效果差
适用场景:
-
RAG → 2-3× 加速
-
Code 完成 → 3-5× 加速(代码重复多)
-
长文档总结 → 2× 加速
-
闲聊 → 几乎无收益
3.5 四种方法对比
|
方法 |
需要额外模型 |
加速比 |
适用场景 |
实现复杂度 |
|
经典 |
是(小模型) |
2-3× |
通用 |
中 |
|
Medusa |
否(加 head) |
2-3× |
通用 |
中 |
| EAGLE-3 |
否(轻量小网络) |
3-5× |
通用 |
中 |
|
Lookup |
否 |
2-5×(视场景) |
RAG / Code |
极低 |
四、工程实战:怎么启用投机解码
4.1 vLLM 启用 EAGLE 投机解码
# vLLM 0.6+ 支持 EAGLE
vllm serve meta-llama/Llama-3-70B-Instruct \
--speculative-model "yuhuili/EAGLE-LLaMA3-Instruct-70B" \
--num-speculative-tokens 5 \
--use-v2-block-manager \
--speculative-draft-tensor-parallel-size 1 \
--tensor-parallel-size 4
关键参数:
-
--speculative-model:Draft 模型(EAGLE 权重) -
--num-speculative-tokens:每次猜测的 token 数(典型 4-8) -
--use-v2-block-manager:必须开 -
--speculative-draft-tensor-parallel-size:Draft 的并行配置
4.2 vLLM 启用经典投机解码
如果你的 Target 是 70B,用 8B 做 Draft:
vllm serve meta-llama/Llama-3-70B-Instruct \
--speculative-model meta-llama/Llama-3-8B-Instruct \
--num-speculative-tokens 5 \
--tensor-parallel-size 4
⚠️ Tokenizer 必须完全一致——同家族模型才行(Llama → Llama,不能 Llama → Qwen)。
4.3 vLLM 启用 Lookup(n-gram)投机
vllm serve meta-llama/Llama-3-70B-Instruct \
--speculative-model "[ngram]" \
--num-speculative-tokens 5 \
--ngram-prompt-lookup-max 4
特别适合 RAG 场景——零成本启用。
4.4 实测性能数据
我们用 Llama-3-70B INT8 在 H100 × 2 上做对比(单序列、prompt 1K、生成 500):
|
配置 |
TPOT (ms) |
tokens/s |
相对 |
|
无投机 |
22 |
45 |
1× |
|
Lookup (n-gram) |
12 |
83 |
1.8× |
|
经典 (8B Draft) |
9 |
110 |
2.5× |
|
Medusa |
8 |
125 |
2.8× |
| EAGLE-3 | 5 | 200 | 4.4× |
输出完全一致(贪心解码下逐 token 比对,无差异)。
4.5 选 Draft 模型的经验
如果用经典投机解码,Draft 模型选择规则:
|
Target 规模 |
推荐 Draft |
加速 |
|
7B-14B |
0.5B-1B |
2× |
|
32B |
1B-3B |
2.5× |
|
70B |
7B-8B |
2.5-3× |
|
405B / 671B |
13B-32B |
3× |
规则:Draft 约为 Target 的 1/10-1/30 大小。太小不准(接受率低),太大太慢(draft 自己耗时多)。
五、避坑 + 决策建议
5.1 投机解码的「死敌」:大 batch
投机解码最大的反直觉事实:
batch 越大,投机解码的收益越小。
原因:
-
投机解码利用的是"闲置算力"
-
大 batch 已经把算力打满
-
没有"免费午餐"可吃了
实测:
|
Batch Size |
投机解码加速 |
|
1 |
3× |
|
4 |
2× |
|
16 |
1.3× |
|
64 |
1.05× |
|
256 |
~1× |
结论:
-
单用户场景(batch=1):投机解码 = 必开
-
高并发场景(batch > 32):投机解码意义不大
-
中等并发:根据 SLA 决定
5.2 适用场景判断
|
场景 |
投机解码推荐度 |
| 单用户对话
(ChatGPT 风格) |
⭐⭐⭐⭐⭐ 必开 |
| Code 助手
(IDE 集成) |
⭐⭐⭐⭐⭐ 必开 |
| RAG
(重复内容多) |
⭐⭐⭐⭐ 推荐 Lookup |
| Agent
(单次复杂任务) |
⭐⭐⭐⭐ 推荐 |
| 大流量 API
(batch 大) |
⭐⭐ 边际收益 |
| 批量任务
(离线处理) |
⭐ 不必开 |
5.3 5 大常见坑
坑 1:Tokenizer 不匹配
症状:启用后直接 crash 或输出乱码。
对策:Target 和 Draft 必须完全相同 tokenizer 和 vocab。同家族模型一般 OK,跨家族不行。
坑 2:Draft 太大
症状:开了投机解码反而变慢。
对策:Draft 模型本身耗时不能超过 Target 的 1/3,否则得不偿失。
坑 3:num_speculative_tokens 设置不当
症状:加速不明显。
对策:
-
设小(如 2):接受率高但收益少
-
设大(如 16):接受率下降快
-
经验值:4-8
坑 4:长尾恶化
症状:99% 延迟反而比平均延迟差。
原因:投机解码对某些"难预测"内容(数字、代码、专有名词)效果差。
对策:
-
监控 p99 延迟
-
必要时为不同请求路径选不同策略
坑 5:在采样温度高时失效
症状:温度 = 1.0 时加速比明显下降。
原因:高温度让 Target 分布平坦,rejection sampling 拒绝率高。
对策:
-
温度低的场景(< 0.5):投机解码效果最好
-
温度高的场景:考虑改用 Lookup 等不依赖 Draft 分布的方法
5.4 进阶:投机解码 + 其他优化的组合
投机解码可以和大部分推理优化叠加:
|
组合 |
协同效果 |
|
投机解码 + Flash Attention |
✓ 完美兼容 |
|
投机解码 + PagedAttention |
✓ vLLM 默认支持 |
|
投机解码 + INT4 量化 |
✓ Target/Draft 都可量化 |
|
投机解码 + Continuous Batching |
△ batch 大时效益降 |
实际生产部署的"豪华套餐":
Llama-3-70B AWQ INT4 + Flash Attention v3 + PagedAttention
+ EAGLE-3 投机解码 + FP8 KV Cache
单 H100 上的吞吐:从无优化的 30 tokens/s 提升到 3500+ tokens/s——综合 100×+ 加速。
六、扩展话题与下一篇预告
6.1 投机解码的未来方向
研究热点:
-
自适应 N:根据当前 token 难度动态调整候选数量
-
多 Draft 集成:多个小模型同时草拟,取共识
-
Draft 在线学习:Draft 模型根据 Target 反馈持续微调
-
Tree-based decoding 进化:更复杂的候选树结构
6.2 大公司怎么用
OpenAI、Anthropic 用没用投机解码?
虽然没公开承认,但 ChatGPT / Claude 的延迟特性强烈暗示用了:
-
单用户感知速度远超理论极限(70B 单卡 48 tokens/s vs Claude 实测 100+)
-
速度对内容类型敏感(代码块更快)
OpenAI o1 系列的"thinking"阶段特别适合投机解码——因为它会生成大量"草稿思考",重复模式很多。
6.3 投机解码 vs 多 Token 预测(MTP)
DeepSeek V3 引入了 MTP(Multi-Token Prediction)——这是训练时就让模型一次预测多个 token。和投机解码思想相似但是训练阶段的优化。
两者可以叠加:
-
MTP 让模型本身就能多 token 预测
-
投机解码进一步加速 decode
DeepSeek V3 的 R1 模型据说就用了这种组合。
6.4 不适用场景
虽然投机解码很强,但这些场景不要用:
-
极致追求吞吐的批处理(用 batch 更划算)
-
极小模型(< 7B)单用户 —— 已经够快,没必要
-
嵌入式 / 端侧场景 —— 显存吃紧不容许多模型
七、结语:从「省时间」到「省 GPU」
读完本文你应该明白:
-
Decode 阶段 GPU 99% 算力闲置——这是投机解码的物理基础
-
验证 N 个 token 几乎等于生成 1 个 token 的时间——这是核心洞察
-
数学上无损——rejection sampling 保证输出分布与原模型一致
-
EAGLE-3 是当下 SOTA——3-5× 加速,已在主流推理框架可用
-
Lookup Decoding 在 RAG / Code 场景免费 2-3×
-
大 batch 下意义不大——这是投机解码最重要的边界条件
参考文献:
更多推荐

所有评论(0)