本文要点
自回归 LLM 推理存在一个根本问题:每个 token 必须串行生成,成为推理瓶颈。
Speculative Decoding(投机解码)通过引入小模型预测 + 大模型验证的范式,实现了理论上 3-5 倍的加速。本文将从原理到实践,深入剖析这一技术的核心机制与工程部署。
阅读本文后你将了解:
- 自回归生成的内存带宽瓶颈本质
- Speculative Decoding 的 draft-verify 两阶段流程
- 概率接受准则的数学证明:为什么输出分布保持不变
- Draft Model 的选型策略与推荐模型组合
- Medusa、EAGLE、Self-Speculative Decoding 等前沿变体
- vLLM 等框架的实际部署配置与性能数据
一、自回归生成的瓶颈
1.1 顺序生成问题
# 自回归生成的瓶颈for i in range(max_tokens): # 每个 token 依赖于前一个 next_token = model.predict_next(prev_token) tokens.append(next_token) prev_token = next_token # 串行!自回归模型的生成过程天然是串行的:第 个 token 的生成依赖于前 个 token 的完整上下文。这意味着无论 GPU 的并行计算能力有多强,生成过程都无法突破这一顺序依赖。
1.2 内存带宽墙
即使计算足够快,内存带宽成为瓶颈:
- 每个 token 需要加载整个模型权重
- 对于 70B 模型,仅加载权重就需要 ~140GB/s
- GPU 的计算利用率(MFU)在 batch_size=1 时通常低于 10%
这说明自回归推理是 memory-bandwidth bound(内存带宽受限),而非 compute bound(计算受限)。每次前向传播只产生一个 token,但需要读取全部模型参数,造成了巨大的带宽浪费。
关键洞察:如果能批量处理多个 token 的验证,就能将多次内存读取合并为一次,充分利用 GPU 的并行计算能力——这正是 Speculative Decoding 的出发点。
二、Speculative Decoding 原理
2.1 核心思想
投机解码使用两阶段方法:
2.2 Draft-Verify 流水线
完整的 Speculative Decoding 可以看作一个流水线:draft 阶段自回归地生成 个候选 token,verify 阶段用大模型一次前向传播同时验证所有候选 token。
2.3 验证过程
def speculative_decoding(draft_model, verify_model, x, k=4): # Stage 1: Draft model 并行生成 draft_tokens = [] draft_probs = [] x_draft = x for _ in range(k): logits = draft_model(x_draft) probs = softmax(logits[-1]) next_tok = sample(probs) draft_tokens.append(next_tok) draft_probs.append(probs[next_tok]) x_draft = concat(x_draft, next_tok)
# Stage 2: Verify model 批量验证 # 关键:一次前向传播获取所有位置的 logits all_logits = verify_model(x.extend(draft_tokens))
# Stage 3: 接受或拒绝 accepted = 0 for i, tok in enumerate(draft_tokens): prob_verify = softmax(all_logits[len(x) + i - 1])[tok] prob_draft = draft_probs[i]
# 基于概率比接受 if random() < min(1, prob_verify / prob_draft): accepted += 1 else: # 拒绝时从修正分布中重新采样 residual_probs = relu(softmax(all_logits[len(x) + i - 1]) - draft_probs_list[i]) tok = sample(normalize(residual_probs)) break
return draft_tokens[:accepted] + [tok] if accepted < k else draft_tokens2.4 概率接受准则
关键洞察:接受概率 = min(1, P_verify / P_draft)
- 如果大模型也预测该 token → 接受
- 如果大模型更可能 → 一定接受
- 如果大模型不太可能 → 可能拒绝
- 保证输出与原模型完全相同分布
数学证明:输出分布不变性
Speculative Decoding 最优雅的性质是输出分布严格等于目标模型的分布。下面给出证明思路。
定义:
- :draft model 在当前上下文下的 token 概率分布
- :target model 在当前上下文下的 token 概率分布
接受-拒绝机制:
对于 draft model 采样的 token :
- 以概率 接受
- 拒绝时,从修正分布 中重新采样
证明输出等于 :
当 时:
当 时:
由于 ,代入后得到:
结论:无论哪种情况,最终输出分布严格等于 。这意味着 Speculative Decoding 是一种无损加速——不会改变模型输出质量。
三、为什么有效?
3.1 条件概率接受
数学上证明了:
当 draft model 与 verify model 一致时,100% 接受。实际上,即使 draft model 远小于 target model,只要两者的输出分布”方向大致一致”(在 top-k token 上重叠),接受率就能保持在较高水平。
3.2 期望接受长度
设每个 draft token 的平均接受率为 ,draft 长度为 ,则期望接受的 token 数为:
当 时,期望接受约 4.0 个 token。即使偶尔全部拒绝,平均也能获得接近 的加速效果。
3.3 速度提升分析
| 场景 | 接受率 α | 理论加速比 | 适用场景 |
|---|---|---|---|
| 高接受率 (>90%) | 0.90+ | 3-5x | 简单续写、代码补全 |
| 中接受率 (70-90%) | 0.70-0.90 | 2-3x | 通用对话、摘要 |
| 低接受率 (<70%) | <0.70 | <1.5x | 创意写作、多语言 |
3.4 实际效果
# 实际测试 (LLaMA 70B + LLaMA 7B)# k=5, 典型序列长度 512 tokens
results = { "draft_accept_rate": 0.85, "speedup": 3.2, # 3.2x 加速 "tokens_per_second": 45, # vs 14 baseline "draft_model_overhead": "10-15% 单步时间", # draft 生成开销}四、Draft Model 选择
4.1 推荐模型组合
选择合适的 draft model 是 Speculative Decoding 成功的关键。核心原则是:draft model 要足够好以保证高接受率,又要足够快以保证整体加速。
| Target Model | Draft Model | 参数比 | 加速比 | 备注 |
|---|---|---|---|---|
| LLaMA-2 70B | LLaMA-2 7B | 10:1 | 3-5x | 经典组合,社区验证充分 |
| LLaMA-2 70B | LLaMA-2 13B | 5.4:1 | 2-3x | 更高接受率但加速有限 |
| LLaMA-3 70B | LLaMA-3 8B | 8.75:1 | 2.8-4x | 同系列架构更匹配 |
| Qwen2.5 72B | Qwen2.5 7B | 10.3:1 | 3-4x | 国产模型推荐组合 |
| Mixtral 8x7B (MoE) | Mistral 7B | ~4:1(活跃) | 1.5-2x | MoE 活跃参数少,加速有限 |
| GPT-4 级别 | GPT-3.5 级别 | ~10:1(估计) | 2-4x | API 层面可能不支持 |
4.2 Draft Model 选择标准
- 架构兼容:推荐同系列模型(如 LLaMA + LLaMA),架构差异会导致分布差异
- 大小比例:draft model 参数通常为 target model 的 5%-15%(即 7-20 倍大小差异)
- 速度要求:draft model 单步推理时间应 < target model 的 10%,否则无法获得显著加速
- 内存约束:draft model 最好能完全放入 GPU SRAM,避免额外内存访问开销
4.3 大小比例与接受率的权衡
参数比太小 (<5:1) → draft 慢,开销大,加速不明显参数比太大 (>20:1) → draft 差,接受率低,浪费验证参数比适中 (8-12:1) → 平衡速度与准确率,最优区间4.4 训练专门的 Draft Model
一些工作探索了为特定 target model 蒸馏训练专用 draft model:
- 知识蒸馏:用 target model 的输出分布训练小 model
- Top-K 对齐:重点对齐 top-k token 的排序,而非整个词表
- Medusa Heads:直接在 target model 上训练额外的预测头(见变体章节)
五、变体与改进
Speculative Decoding 自提出以来,出现了多种改进变体。以下是最具代表性的几种:
5.1 Self-Speculative Decoding
不需要单独的 draft model,用同一个模型的不同层作为 draft:
# 使用浅层作为 draft,深层作为 verifydef self_speculative(model, x, draft_layers=12, verify_layers=24): # Draft: 仅执行前 12 层 draft_logits = model(x, layers=slice(0, draft_layers)) draft_tokens = sample(draft_logits)
# Verify: 执行全部 24 层 verify_logits = model(x.extend(draft_tokens), layers=slice(0, verify_layers)) # 验证...优势:
- 无需加载额外的 draft model,节省显存
- 浅层与深层共享参数,分布天然更接近
- 实现简单,部署友好
劣势:
- 浅层输出质量有限,接受率通常低于专用 draft model
- 需要模型支持中间层输出(layer-skipping)
- 典型加速比:1.5-2.0x
代表工作:Google 的 Self-Speculative Decoding (2023) 在 PaLM 上实现了约 1.6-2.0x 的加速。
5.2 Medusa:多头并行预测
Medusa 使用多个独立的预测头,并行预测多个未来 token,避免串行 draft 生成:
# Medusa 架构class MedusaModel(nn.Module): def __init__(self, base_model, num_heads=5): self.backbone = base_model # 原始 LLM backbone 冻结 self.medusa_heads = nn.ModuleList([ nn.Sequential( nn.Linear(hidden, hidden), nn.SiLU(), nn.Linear(hidden, vocab_size) ) for _ in range(num_heads) ])
def forward(self, x): hidden = self.backbone(x).last_hidden_state # 共享表示
# 每个 head 独立预测第 i 个未来 token draft_tokens = [] for i, head in enumerate(self.medusa_heads): logits = head(hidden) # Head 0 → x_{t+1}, Head 1 → x_{t+2}, ... draft_tokens.append(sample(logits))
return draft_tokens核心区别:
| 特性 | 标准 Speculative Decoding | Medusa |
|---|---|---|
| Draft 生成 | 自回归串行生成 k 个 token | 并行预测 k 个 token |
| 额外参数 | 完整的 draft model | 仅 k 个小预测头 |
| 显存开销 | 需要加载 draft model | <1% 额外参数 |
| 训练成本 | 无需训练(复用现有模型) | 需要训练预测头 |
| 接受率 | 较高(自回归 draft) | 较低(独立预测) |
| 典型加速 | 2-5x | 2-3x |
训练方法:
Medusa head 的训练采用两阶段策略:
- 阶段一:冻结 backbone,仅训练 Medusa heads,使用 teacher-forcing loss
- 阶段二(可选):联合微调 backbone + heads,提升接受率
5.3 EAGLE:特征级草稿
EAGLE (Extrapolation Algorithm for Greater Language-model Efficiency) 的核心创新是在**特征层面(feature-level)**而非 token 层面进行 draft:
# EAGLE 的核心思路(简化版)class EAGLEDraft: def __init__(self, target_model): self.target = target_model self.draft_net = AutoRegressiveHead( input_dim=hidden * 2, # 拼接 [特征, token embedding] output_dim=hidden, )
def draft(self, x, features_history): """基于历史特征序列自回归地生成 draft 特征""" draft_features = [] feat = features_history[-1] for _ in range(k): # 利用上下文特征 + token embedding 联合预测 next_feat = self.draft_net(concat(feat, embedding(last_token))) draft_features.append(next_feat) feat = next_feat
# 将 draft 特征送入 target model 的最后几层进行验证 # 只需执行少量层即可,大幅减少计算量EAGLE 的关键优势:
- 在特征空间做 draft:利用 target model 的中间表征(而非词表分布),信息更丰富
- 自回归地生成特征:保持了序列依赖关系,接受率显著高于 Medusa
- 验证开销小:只需将 draft 特征通过 target model 的最后几层,而非完整前向传播
性能对比:
| 方法 | 接受率 (k=5) | 加速比 (70B) | 额外显存 |
|---|---|---|---|
| 标准 Speculative (7B draft) | ~85% | 3.0-3.5x | ~14GB |
| Medusa-5 heads | ~65% | 2.0-2.5x | <1GB |
| EAGLE | ~90% | 3.5-4.0x | ~2GB |
5.4 其他变体
- Staged Speculative Decoding:多级 draft(小模型 → 中模型 → 大模型),逐级验证
- SpecInfer:树形 draft 结构,同时考虑多个候选路径
- BiLD (Bi-directional Latent Draft):利用双向上下文提升 draft 质量
- Cascade Speculative Decoding:在多轮对话中复用 draft model 的 KV Cache
六、性能基准
6.1 主流方法性能对比
以下是在常见 benchmark 上的性能数据(单卡 A100-80G,batch_size=1):
| 配置 | Accept Rate | Tokens/s | 加速比 | Latency (ms/token) |
|---|---|---|---|---|
| Baseline (LLaMA-2 70B) | - | 14.2 | 1.0x | 70.4 |
| + SpecDec (LLaMA-2 7B, k=5) | 84.3% | 45.1 | 3.18x | 22.2 |
| + SpecDec (LLaMA-2 7B, k=8) | 81.7% | 48.6 | 3.42x | 20.6 |
| + Medusa-5 heads | 63.2% | 32.8 | 2.31x | 30.5 |
| + EAGLE | 89.1% | 52.3 | 3.68x | 19.1 |
| Baseline (Qwen2.5 72B) | - | 12.8 | 1.0x | 78.1 |
| + SpecDec (Qwen2.5 7B, k=5) | 82.6% | 39.7 | 3.10x | 25.2 |
6.2 不同 draft 长度 k 的影响
k=3: 加速比 2.3x | 延迟波动小,适合对尾延迟敏感的场景k=5: 加速比 3.2x | 综合最优,大多数场景推荐k=8: 加速比 3.4x | 略高于 k=5,但拒绝时浪费更多计算k=12: 加速比 3.3x | 开始下降,边际收益递减的最优值取决于接受率 ,理论最优约为 。当 时,。
6.3 不同任务的表现差异
| 任务类型 | 平均接受率 | 加速比 | 原因 |
|---|---|---|---|
| 代码补全 | ~92% | 4.0x | 代码结构规律,draft 准确率高 |
| 翻译 | ~87% | 3.5x | 翻译有对照信息,预测较容易 |
| 通用对话 | ~82% | 3.0x | 开放域生成,不确定性较高 |
| 创意写作 | ~68% | 1.8x | 风格多样,draft 与 target 差异大 |
| 数学推理 | ~75% | 2.5x | 推理链较长但模式相对固定 |
七、实际部署
7.1 vLLM 中的配置
vLLM 从 v0.3.0 开始支持 Speculative Decoding,配置非常简洁:
from vllm import LLM, SamplingParams
# 基本配置llm = LLM( model="meta-llama/Llama-2-70b-hf", speculative_model="meta-llama/Llama-2-7b-hf", # draft model num_speculative_tokens=5, # draft 长度 k speculative_draft_tensor_parallel_size=1, # draft model 用单卡 tensor_parallel_size=4, # target model 用 4 卡)
sampling_params = SamplingParams( temperature=0.0, # greedy 时加速效果最好 max_tokens=512,)
outputs = llm.generate(["解释量子计算的原理"], sampling_params)vLLM Speculative Decoding 关键参数:
| 参数 | 说明 | 推荐值 |
|---|---|---|
speculative_model | Draft model 路径 | 同系列小模型 |
num_speculative_tokens | Draft 长度 k | 5 |
speculative_draft_tensor_parallel_size | Draft model TP 大小 | 1(单卡即可) |
speculative_max_model_len | Draft model 最大上下文 | 与 target 一致 |
性能调优建议:
temperature=0(贪心解码)时加速效果最好,因为 draft 和 target 都确定性地选择 top-1- 高温度采样时接受率下降,但仍能获得 2x 左右加速
- 建议在 batch_size 较小的在线推理场景使用,batch 较大时 GPU 已充分利用,加速效果有限
7.2 HuggingFace Transformers
from transformers import AutoModelForCausalLM, AutoTokenizerimport torch
# 加载 target model(量化以节省显存)target_model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-2-70b-hf", load_in_4bit=True, device_map="auto",)
# 加载 draft modeldraft_model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-2-7b-hf", device_map="auto",)
# 从 v4.36+ 开始支持 assisted_decodingtokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-70b-hf")inputs = tokenizer("解释量子计算的原理", return_tensors="pt").to("cuda")
outputs = target_model.generate( **inputs, assistant_model=draft_model, # 指定 draft model max_new_tokens=512, do_sample=False,)7.3 TensorRT-LLM 部署
NVIDIA TensorRT-LLM 也原生支持 Speculative Decoding:
# TensorRT-LLM 配置示例# 需要分别构建 target 和 draft enginetrtllm-build \ --model_dir meta-llama/Llama-2-70b-hf \ --output_dir engines/target/ \ --tp_size 4
trtllm-build \ --model_dir meta-llama/Llama-2-7b-hf \ --output_dir engines/draft/ \ --tp_size 1
# 推理时启用 speculative decoding# 在 pyT runtime 中配置 draft_model_path 和 speculative_length7.4 部署注意事项
- 显存规划:需要同时加载 target model 和 draft model,总显存需求约增加 10-15%
- KV Cache 管理:拒绝 token 后需要回滚 KV Cache,确保框架支持此操作
- 动态 batch:在连续批处理(continuous batching)场景下,draft token 拒绝会导致序列长度变化,需要框架层面支持
- 延迟 vs 吞吐:Speculative Decoding 更适合延迟敏感的单请求场景;高吞吐场景下 batch 已经很大,加速效果有限
八、总结
| 特性 | 说明 |
|---|---|
| 核心思想 | 小模型预测 + 大模型验证 |
| 加速比 | 2-5x(取决于任务和 draft model 质量) |
| 输出保证 | 与原模型分布完全相同(数学可证) |
| 适用场景 | batch_size 较小、延迟敏感的在线推理 |
| 额外资源 | 需要加载 draft model(约 10-15% 额外显存) |
| 最佳实践 | 同系列模型、参数比 8-12:1、draft 长度 k=5 |
九、常见问题 FAQ
9.1 Q1: Speculative Decoding 会改变模型输出吗?
不会。这是 Speculative Decoding 最核心的保证。概率接受准则 + 拒绝时修正分布重采样的机制,在数学上严格证明了最终输出分布与 target model 完全一致。无论 draft model 质量如何,输出质量不会下降——只会影响速度。
9.2 Q2: draft model 和 target model 必须是同一系列吗?
不是必须,但强烈推荐。理论上任何模型组合都可以,但不同系列的模型在 token 选择偏好上差异较大,会导致接受率显著下降。例如用 GPT-2 作为 LLaMA 的 draft model,接受率可能低于 50%。同系列模型(LLaMA + LLaMA)共享相似的分布特征,接受率通常 >80%。
9.3 Q3: 什么时候不适合使用 Speculative Decoding?
以下场景不建议使用:
- 高吞吐离线推理:batch_size 较大时 GPU 已经充分利用,加速效果有限甚至可能降低吞吐
- 创意写作等高温度采样:高温度下分布更均匀,draft 和 target 的 top-k 重叠减少
- 极端资源受限:无法同时加载两个模型的场景
- MoE 模型:MoE 的活跃参数较少,target 本身推理速度已较快,加速空间有限
9.4 Q4: draft 长度 k 应该怎么设置?
推荐从 开始,然后根据实际接受率调整:
- 如果接受率 >90%,可以尝试增大到
- 如果接受率 <75%,降低到
- 理论最优值约为
9.5 Q5: Medusa 和标准 Speculative Decoding 该选哪个?
- 资源充足、追求最高加速:标准 Speculative Decoding(接受率更高)
- 显存受限、追求低延迟:Medusa(额外参数 <1%,无需加载额外模型)
- 追求最佳综合效果:EAGLE(接受率高 + 显存开销适中)
9.6 Q6: Speculative Decoding 能和量化一起用吗?
可以。draft model 和 target model 都可以独立使用量化技术(如 GPTQ、AWQ、GGUF)。一个常见配置是 target model 使用 4-bit 量化、draft model 使用 FP16,这样整体显存开销很低,同时 draft model 保持较高精度以保证接受率。
9.7 Q7: batch 推理时能用 Speculative Decoding 吗?
可以,但加速效果会随 batch_size 增大而递减。原因是 batch 较大时 GPU 计算已经接近饱和,draft 带来的额外计算开销占比更高。通常在 batch_size ≤ 4 时效果最好,batch_size > 16 时加速效果可以忽略。
9.8 Q8: 如何监控 Speculative Decoding 的效果?
关键监控指标:
- Accept Rate:每个 step 中被接受的 draft token 比例
- Effective Draft Length:平均每个 step 接受的 token 数
- Tokens/s:实际生成速度
- Draft Overhead:draft 生成时间占总推理时间的比例
如果 Accept Rate 持续低于 70%,应该检查 draft model 是否合适或降低 k 值。
常见问题 FAQ
9.1 Speculative Decoding 会改变模型输出吗?
不会。数学上证明了 Speculative Decoding 的输出分布与原始模型完全一致。接受准则保证了采样等价性。
9.2 什么场景下 Speculative Decoding 效果最好?
① 批处理大小为 1 或较小的推理;② Draft model 和 target model 风格相似(同系列模型);③ 输出确定性较高的任务(翻译、摘要);④ GPU 利用率较低的推理(内存带宽受限)。
9.3 接受率一般有多高?
使用同系列模型作为 draft(如 LLaMA 7B → LLaMA 70B),典型接受率 70-90%。Token 级别的接受率更高(>85%)。接受率越高,加速比越大。
9.4 Speculative Decoding 和 KV Cache 量化可以一起用吗?
可以。两者优化不同瓶颈:Speculative Decoding 减少 token 生成次数,KV Cache 量化减少内存带宽占用。结合使用可叠加加速效果。
小结
小结
Speculative Decoding 是 LLM 推理加速领域的一项重要突破,其核心价值在于:
- 无损加速:通过严格的数学证明保证输出分布不变,不牺牲任何输出质量
- 通用性强:适用于任何自回归模型,不需要修改模型架构
- 工程落地容易:主流推理框架(vLLM、TensorRT-LLM、HuggingFace)均已支持
- 持续演进:Medusa、EAGLE 等变体在显存效率、接受率等方面持续改进
在 LLM 服务化的工程实践中,Speculative Decoding 已经成为低延迟在线推理的标配技术。对于延迟敏感的应用场景(如实时对话、代码补全),推荐在 vLLM 中开启 Speculative Decoding,选择同系列 7-8B 模型作为 draft model,通常可以获得 2-3 倍的延迟降低。
随着 EAGLE 等新一代方案的出现,未来 Speculative Decoding 的加速比有望进一步提升,同时降低对额外 draft model 的依赖。
参考资料
- Fast Speculative Decoding with Medusa - Medusa 多头并行预测
- Speculative Decoding: Exploiting Local Draft - 原始 Speculative Decoding 论文
- Medusa: Simple LLM Inference Acceleration - Medusa 详细方案
- EAGLE: Speculative Sampling with Large Language Model - EAGLE 特征级 draft
- Self-Speculative Decoding - Google Self-Speculative 方案
- vLLM Speculative Decoding 文档 - vLLM 官方配置指南
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






