1. 自回归解码的瓶颈
1.1 逐 token 生成的低效
LLM 自回归生成:每次前向传播只产生 1 个 token。对于 $N$ 个 token 的输出,需要 $N$ 次串行前向传播。
每次前向传播的计算量:
$$\text{FLOPS}{\text{per step}} \approx 2 \times \text{Params}{\text{active}}$$
但实际 GPU 利用率很低——单 token 推理是内存带宽受限的:
$$\text{Compute intensity} = \frac{\text{FLOPS}}{\text{Bytes loaded}} = \frac{2N}{N \times \text{dtype_size}} = \frac{2}{\text{dtype_size}}$$
FP16 下计算强度仅为 1 FLOP/byte,远低于 GPU 的算力/带宽比(A100: 312 TFLOPS / 2TB/s = 156 FLOP/byte)。GPU 大部分时间在等待内存加载权重。
1.2 理想情况
如果能一次前向传播生成 $k$ 个 token,就能将权重复用 $k$ 次,计算强度提升 $k$ 倍。这就是推测解码的核心动机。
2. 推测解码原理
2.1 基本思想
推测解码(Speculative Decoding)使用两个模型:
- Target Model:大而精确的目标模型(如 LLaMA-2 70B)
- Draft Model:小而快速的草稿模型(如 LLaMA-2 7B 或 1B)
流程:
1. Draft Model 自回归快速生成 k 个 token: [t1, t2, ..., tk]
2. Target Model 一次性对 [prompt + t1, ..., tk] 做前向传播
3. Target Model 的输出可以验证 t1..tk 的正确性
4. 接受匹配的 token,拒绝不匹配的,从第一个拒绝点重新生成
2.2 为什么能加速
关键洞察:Target Model 一次处理 $k+1$ 个 token 的成本远小于 $k+1$ 次单 token 推理。
| 方式 | 前向传播次数 | 每次处理 token 数 | 总权重加载次数 |
|---|---|---|---|
| 标准自回归 | $k$ | 1 | $k$ |
| 推测解码 | 1 | $k+1$ | 1 |
即使 Target Model 要处理更多 token,但权重只加载一次,内存带宽利用大幅提升。
2.3 接受/拒绝机制
Draft Model 生成 token 时也输出了概率分布 $q(x)$,Target Model 输出概率分布 $p(x)$。接受规则:
$$\text{accept}(x) \text{ with probability } \min\left(1, \frac{p(x)}{q(x)}\right)$$
如果拒绝,从残差分布中重新采样:
$$p_{\text{residual}}(x) = \frac{\max(0, p(x) - q(x))}{\sum_{x’} \max(0, p(x’) - q(x’))}$$
重要性质:这种接受/拒绝机制保证推测解码的输出分布与纯 Target Model 完全一致——零质量损失。
import torch
import torch.nn.functional as F
def speculative_sampling(draft_logits, target_logits, draft_token):
"""推测解码的接受/拒绝采样"""
draft_prob = F.softmax(draft_logits, dim=-1)
target_prob = F.softmax(target_logits, dim=-1)
# 接受概率
accept_prob = torch.minimum(
torch.ones_like(draft_prob),
target_prob / (draft_prob + 1e-10)
)
# 随机决定是否接受
rand = torch.rand_like(draft_prob)
accepted = rand < accept_prob
if accepted[draft_token]:
return draft_token # 接受 Draft Model 的 token
else:
# 从残差分布重新采样
residual = torch.clamp(target_prob - draft_prob, min=0)
residual = residual / residual.sum()
return torch.multinomial(residual, 1).squeeze()
3. 加速效果分析
3.1 接受率
设 Draft Model 的每个 token 被接受的概率为 $\alpha$(接受率),则 $k$ 个推测 token 中平均接受的数量为:
$$E[\text{accepted}] = \frac{1 - \alpha^{k+1}}{1 - \alpha} - 1$$
当 $\alpha = 0.8$, $k = 4$ 时:平均接受 2.95 个 token,每次 Target Model 前向传播产出 3.95 个 token。
3.2 加速比
设 Draft Model 速度是 Target Model 的 $1/\beta$ 倍($\beta \ll 1$ 表示 Draft Model 快得多),则理论加速比:
$$\text{Speedup} \approx \frac{E[\text{accepted}] + 1}{1 + \beta \cdot k}$$
| 接受率 $\alpha$ | 推测长度 $k$ | $\beta$ (Draft/Target 速度比) | 理论加速比 |
|---|---|---|---|
| 0.5 | 4 | 0.1 | 1.6× |
| 0.7 | 4 | 0.1 | 2.3× |
| 0.8 | 4 | 0.1 | 2.8× |
| 0.9 | 4 | 0.1 | 3.2× |
| 0.8 | 8 | 0.1 | 3.5× |
| 0.8 | 4 | 0.2 | 2.0× |
关键结论:接受率是决定性因素。接受率 >0.7 时加速效果显著。
3.3 影响接受率的因素
| 因素 | 影响 | 原因 |
|---|---|---|
| Draft 模型与 Target 模型相似度 | 高 | 分布更接近 |
| 任务类型 | 贪婪生成 > 创意生成 | 贪婪更可预测 |
| 上下文复杂度 | 简单上下文 > 复杂上下文 | 简单上下文两个模型更易一致 |
| 推测长度 | 短 > 长 | 越长累积偏差越大 |
| 温度 | 低温 > 高温 | 低温接近贪婪,更可预测 |
4. Medusa:多头并行推测
4.1 问题
传统推测解码需要两个模型,增加系统复杂度和内存占用。Draft Model 的选择也需要工程考量。
4.2 Medusa 方案
Medusa 在 Target Model 之上增加 $k$ 个预测头,每个头预测未来第 $i$ 个 token:
Target Model 隐状态
├── LM Head → t0 (当前 token)
├── Medusa Head 1 → t1 (下一个 token)
├── Medusa Head 2 → t2
└── Medusa Head 3 → t3
一次前向传播直接得到多个候选未来 token,无需独立 Draft Model。
class MedusaHead(nn.Module):
"""Medusa 预测头"""
def __init__(self, d_model, vocab_size):
super().__init__()
self.linear = nn.Linear(d_model, d_model)
self.lm_head = nn.Linear(d_model, vocab_size)
def forward(self, hidden_state):
x = F.silu(self.linear(hidden_state))
return self.lm_head(x)
class MedusaModel(nn.Module):
def __init__(self, base_model, num_heads=4):
super().__init__()
self.base_model = base_model
self.medusa_heads = nn.ModuleList([
MedusaHead(base_model.config.hidden_size, base_model.config.vocab_size)
for _ in range(num_heads)
])
def forward(self, input_ids):
# 基座模型前向传播
outputs = self.base_model(input_ids, output_hidden_states=True)
hidden = outputs.hidden_states[-1]
# 基座预测当前 token
base_logits = self.base_model.lm_head(hidden)
# Medusa 头预测未来 token
medusa_logits = [head(hidden) for head in self.medusa_heads]
return base_logits, medusa_logits
4.3 Medusa 的优势
| 特性 | 传统推测解码 | Medusa |
|---|---|---|
| 额外模型 | 需要 Draft Model | 无需 |
| 内存开销 | 额外 Draft Model 参数 | 仅几个额外线性层 |
| 推测方式 | 串行自回归 | 并行一次出多个 |
| 训练需求 | Draft Model 预训练 | Medusa 头需微调 |
| 加速比 | 2-3× | 2-3× (相当) |
5. EAGLE
5.1 改进
EAGLE 在 Medusa 基础上进一步改进:
- 使用隐状态而非 token:Draft 头基于 Target Model 的隐状态预测,信息更丰富
- 自回归推测:Medusa 头的输出反馈给自身,支持更长的推测链
- 树状推测:生成多个候选路径,Target Model 一次验证整棵树
5.2 树状推测
t0
/ | \
t1a t1b t1c
/ \ | |
t2a t2b t2c t2d
Target Model 一次性验证所有路径上的 token,选择最长的接受路径。
5.3 性能对比
| 方法 | 推测长度 | 加速比 | 额外参数 |
|---|---|---|---|
| 标准推测解码 | 4-8 | 2-3× | Draft Model (~1B) |
| Medusa | 4 | 2-3× | ~200M |
| EAGLE | 8+ | 3-4× | ~200M |
6. vLLM 中的推测解码实现
6.1 使用方式
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, # 推测长度
use_v2_block_manager=True,
)
sampling_params = SamplingParams(
temperature=0.0, # 贪婪解码接受率最高
max_tokens=1000,
)
outputs = llm.generate(["请解释量子计算的基本原理"], sampling_params)
6.2 vLLM 推测解码架构
┌─────────────────────────────────────────┐
│ vLLM 推理引擎 │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Draft │───→│ Target Model │ │
│ │ Model │ │ (单次前向验证) │ │
│ │ (快速生成) │ │ │ │
│ └──────────┘ └──────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ [t1,t2,t3,t4,t5] ← 验证 & 接受/拒绝 │
│ │ │
│ ▼ │
│ [accepted tokens] │
│ │ │
│ ▼ │
│ 更新 KV Cache │
└─────────────────────────────────────────┘
6.3 性能数据
vLLM 官方基准测试(LLaMA-2 70B + 7B Draft):
| 场景 | 标准解码 | 推测解码 | 加速比 |
|---|---|---|---|
| 代码生成 (temp=0) | 28 tok/s | 85 tok/s | 3.0× |
| 文本摘要 (temp=0) | 28 tok/s | 72 tok/s | 2.6× |
| 创意写作 (temp=0.7) | 28 tok/s | 45 tok/s | 1.6× |
| 对话 (temp=0.7) | 28 tok/s | 52 tok/s | 1.9× |
代码/摘要等可预测性高的任务加速最明显,创意生成因接受率低而加速有限。
7. 推测解码的适用场景
7.1 高收益场景
| 场景 | 原因 | 加速预期 |
|---|---|---|
| 代码生成 | 语法规则强,Draft 可预测 | 2.5-3.5× |
| 文本摘要 | 结构化输出 | 2-3× |
| 问答系统 | 答案可预测性较高 | 2-2.5× |
| 翻译 | 逐句对应 | 2-3× |
| 贪婪解码 | 接受率最高 | 2.5-3.5× |
7.2 低收益场景
| 场景 | 原因 | 加速预期 |
|---|---|---|
| 创意写作 | 高温度,不可预测 | 1.2-1.6× |
| 多样化采样 | 高温度采样 | 1.1-1.4× |
| 极短输出 | 开销占比大 | <1.2× |
| Draft Model 太大 | 推测成本高 | <1.5× |
8. 总结
推测解码是当前最实用的 LLM 推理加速技术之一,它通过"快速推测 + 批量验证"的范式,在不损失输出质量的前提下实现 2-3× 加速。核心限制是接受率——Draft Model 与 Target Model 的分布一致性决定加速上限。Medusa 和 EAGLE 通过消除独立 Draft Model、引入树状推测,进一步简化了部署并提升了效率。对于代码生成、摘要等可预测性高的任务,推测解码几乎是"免费的加速"。在 vLLM 等推理框架中,推测解码已开箱即用,是生产部署的标准加速选项。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
