【词汇专栏】Speculative Decoding:投机解码的智慧
·
W20_Speculative_Decoding_投机解码的智慧.md
Speculative Decoding:投机解码的智慧
一句话理解
Speculative Decoding(投机解码) 是"用小聪明换大效率"的艺术——用一个又快又小的大模型猜测接下来几个词,再用大模型一次性验证,快速又省力。
传统方式:大模型一个字一个字憋 → 慢,但准确
投机解码:小模型猜3个词 → 大模型一次验证3个 → 快,且准确
为什么需要投机解码?
大模型推理的瓶颈:
| 方案 | 每步计算 | 每步时间 | 生成100字总耗时 |
|---|---|---|---|
| 大模型(70B) | 1次完整Transformer | 100ms | 100 × 100ms = 10秒 |
| 小模型(7B) | 1次完整Transformer | 10ms | 100 × 10ms = 1秒(但质量差) |
| 投机解码 | 小模型猜5个 → 大模型验证 | 120ms | 100 ÷ 5 × 120ms = 2.4秒 |
核心洞察:验证比生成快很多!小模型猜5个,大模型一次验证,只需额外20ms。
核心洞察:验证比生成快很多!
工作原理
三步走
第一步:小模型"投机"猜测
- 输入:“今天天气”
- 小模型生成:
["很", "不错", "啊", ",", "适合"](5个Token,一次性并行生成)第二步:大模型"验证"
- 输入:“今天天气很不错啊,适合”
- 大模型验证每个位置:
- 位置1 “很”:✓ 接受
- 位置2 “不错”:✓ 接受
- 位置3 “啊”:✗ 拒绝 → 改为 “很”
- 位置4 “,”:需要重新采样
- 位置5 “适合”:需要重新采样
第三步:输出结果
- 最终接受:
["很", "不错", "很", ",", "出"]- 最终文本:“今天天气很不错很,出…”
数学原理
# 投机解码的核心逻辑
def speculative_decoding(draft_tokens, large_model, small_model):
"""
draft_tokens: 小模型猜测的Token序列
"""
# 大模型验证所有位置
large_probs = large_model.predict_proba(
input_ids + draft_tokens # 一次输入
)
accepted = []
for i, draft_token in enumerate(draft_tokens):
# 大模型在第i个位置的概率分布
q = large_probs[i] # 大模型认为每个词的概率
# 小模型在第i个位置的概率分布
p = small_model.probs[i] # 小模型猜测的概率
# 比较概率
if q[draft_token] >= p[draft_token]: # 小模型猜对了
accepted.append(draft_token)
else: # 小模型猜错了
# 从修正后的分布采样一个新Token
corrected_prob = softmax(q - p) # 重新归一化
new_token = sample(corrected_prob)
accepted.append(new_token)
break # 后续全部重新生成
return accepted
为什么有效?
关键假设
| 验证结果 | 概率 |
|---|---|
| 0个对 | 5% |
| 1个对 | 30% |
| 2个对 | 40% |
| 3个对 | 20% |
| 4个对 | 4% |
| 5个对 | 1% |
平均接受:约3个 ✓
加速比:3倍!(假设小模型"猜对"的概率 ≈ 70%)
理论加速比
公式:加速比 ≈ N / (1 + (N - E))
其中:
- N = 小模型一次猜的Token数(Draft数)
- E = 期望接受的Token数
例如:
- N=5, E=3 → 加速比 = 5 / (1 + 2) ≈ 1.67倍
- N=10, E=7 → 加速比 = 10 / (1 + 3) ≈ 2.5倍
- N=20, E=15 → 加速比 = 20 / (1 + 5) ≈ 3.3倍
2026年最新演进
1. EAGLE系列(自回归投机解码)
| 版本 | 时间 | 特点 |
|---|---|---|
| EAGLE-1 | 早期 | 首个实用的自回归投机解码 |
| EAGLE-2 | 2025年 | 接受率提升至85%+ |
| EAGLE-3 | 2026年 | 稳定版发布 |
EAGLE的核心改进:
- 顶层Token预测:只猜最后一个位置
- 层次化验证:从粗到细
- 多假设分支:同时保留多个可能的路径
- 自适应Draft长度:动态调整猜测数量
2. LongSpec(长上下文投机解码)
2026年针对长上下文场景的新方案:
问题:长上下文下,大模型验证也变慢
传统投机解码:
- 小模型猜5个 → 大模型验证
- 但大模型在长上下文下也很慢
LongSpec方案:
- 分段验证:只验证最近N个Token的注意力
- 历史用近似:远处的Token用压缩表示
- 结果:长上下文也能保持2-3倍加速
3. Medusa(多Token同时预测)
| 对比 | 方式 |
|---|---|
| 传统 | 1个 → 1个 → 1个 → 1个 → 1个 |
| Medusa | 1个 → [3个并行预测] → [接受/拒绝] |
每个"头"预测一个位置:
- Head 1:预测第1个Token
- Head 2:预测第2个Token
- Head 3:预测第3个Token
优势:不需要额外的小模型!
代码实战
基础实现(概念版)
import torch
import torch.nn.functional as F
def speculative_decode(
large_model,
small_model,
input_ids,
max_draft=5,
temperature=0.0
):
"""
投机解码的简化实现
"""
outputs = []
draft_cache = [] # 小模型猜测的Token
while len(outputs) < max_new_tokens:
# 第一步:小模型"投机"
with torch.no_grad():
small_logits = small_model(input_ids + draft_cache)
small_probs = F.softmax(small_logits / temperature, dim=-1)
# 采样N个Token
draft_tokens = torch.multinomial(
small_probs[-1], max_draft
).tolist()
# 第二步:大模型验证
large_logits = large_model(input_ids + outputs + draft_tokens)
large_probs = F.softmax(large_logits / temperature, dim=-1)
# 第三步:接受/拒绝
accepted = []
for i, draft_tok in enumerate(draft_tokens):
q = large_probs[i] # 大模型概率
p = small_probs[i] if i < len(small_probs) else None
if p is not None:
# 使用概率阈值或接受准则
accept_prob = min(1.0, q[draft_tok].item() / max(p[draft_tok].item(), 1e-10))
else:
accept_prob = q[draft_tok].item()
if torch.rand(1).item() < accept_prob:
accepted.append(draft_tok)
else:
# 拒绝:从大模型分布采样
new_tok = torch.multinomial(q, 1).item()
accepted.append(new_tok)
break # 后续全部重新生成
outputs.extend(accepted)
if len(accepted) < len(draft_tokens):
# 被拒绝,需要更新draft_cache
draft_cache = []
else:
draft_cache = []
return outputs
vLLM中使用投机解码
from vllm import LLM, SamplingParams
# vLLM自动启用投机解码
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
speculative_model="meta-llama/Llama-3.1-8B-Instruct", # 用同模型
num_speculative_tokens=5, # 一次猜5个
tensor_parallel_size=1,
)
sampling_params = SamplingParams(temperature=0)
outputs = llm.generate(prompts, sampling_params)
适用场景与限制
适用场景
| 场景 | 效果 | 说明 |
|---|---|---|
| 流式输出 | ⭐⭐⭐⭐⭐ | 接受即输出,响应更快 |
| 长文本生成 | ⭐⭐⭐⭐ | 加速比稳定 |
| 批量推理 | ⭐⭐⭐ | 有额外开销 |
| 短对话 | ⭐⭐ | 开销可能大于收益 |
不适用场景
| 场景 | 原因 |
|---|---|
| 小Batch | 小模型推理有固定开销 |
| 低延迟要求极高 | 投机解码有额外延迟波动 |
| 资源受限 | 需要同时加载两个模型 |
常见问题
Q1:投机解码会影响输出质量吗?
不会。投机解码的输出与大模型自回归生成完全一致:
数学保证:
P(接受第i个Token) = min(1, P_大模型(该Token))
→ 被接受的Token分布 = 大模型原始分布
→ 输出质量 = 大模型质量
Q2:小模型和大模型差距多大合适?
不是越大越好,也不是越小越好:
| 差距 | 问题 |
|---|---|
| 太大(小模型太弱) | 猜对率低,加速比下降 |
| 太小(小模型太强) | 两个模型太接近,浪费资源 |
最佳差距:
- 参数量比:10:1(7B猜,70B验)
- 能力差距:BLEU差5-15%最合适
Q3:GPU资源不够怎么办?
# 方案1:使用量化小模型
small_model = AutoModelForCausalLM.from_pretrained(
"tinyllama-1b",
load_in_4bit=True # 4位量化,大幅降低显存
)
# 方案2:使用CPU小模型
small_model = AutoModelForCausalLM.from_pretrained("gpt2") # 124M
# 方案3:使用投机解码的蒸馏版本(Medusa)
# 不需要额外小模型
性能对比
测试环境:LLaMA-2-70B on A100 80GB
| 方案 | 吞吐量 | 提升 |
|---|---|---|
| 自回归解码 | 45 tokens/s | 基准 |
| 投机解码(EAGLE-2) | 120 tokens/s | ↑ 167% |
| vLLM + 投机解码 | 150 tokens/s | ↑ 233% |
| 延迟对比(生成1000 tokens) | 时间 |
|---|---|
| 自回归解码 | 22秒 |
| 投机解码 | 8秒 |
总结
Speculative Decoding的核心智慧:
- 核心洞察:验证比生成快(10倍差距)
- 方法:小模型猜,大模型验
- 效果:2-3倍加速,保证输出质量
- 本质:用并行换串行,用小错换大效
2026年进化:
- EAGLE系列:接受率突破85%+
- LongSpec:长上下文支持
- Medusa:无需额外小模型
- 自适应:动态调整猜测策略
"投机"不丢人,省力才是真聪明!
延伸阅读
| 相关文章 | 说明 |
|---|---|
| W19 KV Cache | 另一个加速推理的技术 |
| W13 Transformer | 了解自注意力机制 |
| W07 上下文窗口 | 与长上下文的关系 |
本文收录于「AI词汇专栏」
相关阅读:W19 KV Cache · W07 上下文窗口
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)