投机解码
投机解码(Speculative Decoding,也称 Speculative Sampling)是一种在不损失生成质量的前提下显著加速大语言模型推理的技术,其核心思想是”先用小模型猜、再用大模型一次性验证”,从而把多次串行前向传播(forward pass,即把输入送入神经网络跑一次计算得到输出)压缩成一次并行计算。该技术由 DeepMind 和 Google 在 2022-2023 年间分别独立提出,目前已成长为业界最主流的推理加速方案之一,被 vLLM、TensorRT-LLM、TGI 等主流框架广泛集成。
可以把投机解码想象成”主管 + 实习生”的工作模式。实习生(Draft Model,小而快)先快速起草一份包含若干个词的方案;主管(Target Model,大而准)不需要逐字审阅,而是把整份草稿一次性铺在桌上看一遍,找出第一个不满意的词,从那里截断并改写。只要实习生的猜测够准,主管一次审查就能”批量通过”好几个词,整个产出速度因此大幅提升——而且最终文件和主管亲自一字一字写出来的一模一样。
关键点在于:大模型的推理瓶颈往往不是算力,而是内存带宽——每生成一个 token 都要把全部权重从显存(GPU 上的高速存储器,即 HBM,High Bandwidth Memory)搬到计算单元,大量的计算能力其实是闲置的。这就像一个厨师(计算单元)每做一道菜都要从仓库(显存)取一趟食材,取食材的时间远超烹饪时间——厨师大部分时间在等搬运。投机解码正是利用这部分闲置算力,让大模型在”一次搬权重”的过程中顺便验证多个候选位置,相当于让厨师一次取回多道菜的食材、一锅端地炒完。
串行推理为什么慢
Section titled “串行推理为什么慢”自回归(autoregressive,即每一步的输出都依赖前面所有步的输出)生成时,大模型逐 token 输出:每一步都要做一次完整的 forward,把数百亿参数从 HBM 读到计算核(GPU 上的运算单元)。这个过程的耗时主要由权重的读取(内存带宽)决定,而不是乘加运算本身。
我们可以用一个简单的 roofline 模型来量化。假设一个 70B 参数的模型使用 FP16(每个参数 2 字节),则权重总量约 140 GB。若 GPU 的 HBM 带宽为 2 TB/s(即每秒可传输 2 × 10¹² 字节),那么仅搬运权重就需要:
也就是说,仅搬运权重这一步,每个 token 就要花 70 毫秒——而实际执行一次前向传播的浮点运算本身可能只需几毫秒。GPU 的计算单元(如 Tensor Core)在大部分时间里都在空转,算力利用率(GPU 利用率)通常很低,往往不到 5%。
直观类比:这就像你每次只搬一块砖上楼,但每次都要等电梯从一楼上来。真正花时间的不是搬砖(计算),而是等电梯(内存带宽)。投机解码的思路是:既然电梯都上来了,不如一次多搬几块砖。
投机解码的三步流程
Section titled “投机解码的三步流程”-
草稿生成(Drafting):用一个参数量小得多的 Draft Model(例如 1B 或 7B),在已有上下文基础上自回归地连续生成 k 个草稿 token,记为 。小模型单步很快——因为它的参数少、权重小、搬运快——所以串行生成 k 个 token 的总开销远小于大模型单步。
-
并行验证(Parallel Verification):把这段草稿连同原始上下文拼成长度为 的序列(n 为已有上下文长度),喂给 Target Model(例如 70B 大模型)做一次 forward。由于 Transformer 的注意力机制可以并行处理序列中所有位置,大模型这一次 forward 就能同时给出位置 到 每个位置的条件概率分布 。关键在于:这”一次 forward”计算量的增量主要来自多出来的 个位置的 attention 计算(与 成线性关系),而权重大搬运只发生一次。
-
接受/拒绝采样(Accept/Reject Sampling):从位置 开始,对每个草稿 token ,比较大模型分布 与小模型分布 ,按下面规则决定是否接受:
- 如果 ,则按概率 接受 ;
- 若接受,继续验证下一个位置;
- 若拒绝,则从修正分布 中重新采样一个 token 替换 ,并丢弃其后所有草稿;
- 若全部 个都被接受,则额外从大模型在位置 的分布中采样一个 token 作为”奖励”(bonus token)。
这里的 表示将 重新归一化为一个合法的概率分布(使各项之和为 1)。
无损保证:数学推导
Section titled “无损保证:数学推导”上述接受/拒绝采样规则并非随意设计,而是经过严格推导的。我们可以证明:最终输出的每个 token 的边缘分布严格等于大模型单独生成时的分布 。下面是证明过程。
对于某个位置,草稿 token 由小模型按分布 采样产生。大模型在相同位置的分布为 。最终输出该 token 的概率由两部分组成:
第一项(接受路径):
第二项(拒绝路径):当草稿被拒绝时(拒绝概率为 ),从修正分布 中重采样,其中归一化常数 。注意一个恒等式:
(因为 ,所以正负偏差之和相等。)这正是拒绝概率,因此:
合并两项:
证毕。无论小模型分布 是什么,最终输出分布都严格等于 。这就是”无损”的数学含义——输出与”让大模型从头逐字生成”在统计意义上完全一致,只是采样路径不同。
通俗理解:当 时(小模型过于”自信”地选了这个 token),接受概率 ,可能被拒绝,拒绝后从 的”差额”中补回来;当 时(大模型比小模型更喜欢这个 token),接受概率为 1(必定接受),但还要通过修正分布把 的差额补上。两种情况互补,最终恰好恢复 。
加速并不来自”少算”,而来自”一次算更多”。大模型本来每次只处理一个位置,现在一次处理 个位置,额外增加的浮点运算量很小(因为权重只搬了一次),但如果草稿被接受多个,就等于一次输出了多个 token。
设每轮平均接受 个 token( 称为平均接受长度),则大模型每轮输出 个 token(加 1 是因为拒绝位置或奖励位置贡献的额外 token)。相比逐 token 生成的 次串行 forward,理论加速比约为:
其中 略大于单步串行时间(因为多了 个位置的 attention 计算,但权重只搬一次)。接受率(accepted length) 越高,加速比越大。典型情况下可获得 1.5x 到 3x 的端到端加速;当 Draft Model 与 Target Model 非常接近时(如同一模型系列的不同尺寸),加速比可进一步突破。
下面以 transformers 库为例,演示如何用一个小模型作为 Draft Model、一个大模型作为 Target Model 进行投机解码。这是一个完整可运行的教学版实现,严格遵循接受/拒绝采样规则,保证输出无损。实际生产环境推荐使用 vLLM 等推理引擎以获得更高吞吐。
import torchimport torch.nn.functional as Ffrom transformers import AutoTokenizer, AutoModelForCausalLM
# ========== 模型加载 ==========# 草稿模型(小而快)与目标模型(大而准)# 注意:两者必须使用相同的 tokenizer(即词表完全一致),否则 token id 无法对齐MODEL_DRAFT = "meta-llama/Llama-3.2-1B"MODEL_TARGET = "meta-llama/Llama-3.1-8B"
tokenizer = AutoTokenizer.from_pretrained(MODEL_DRAFT)draft_model = AutoModelForCausalLM.from_pretrained( MODEL_DRAFT, torch_dtype=torch.float16, device_map="auto")target_model = AutoModelForCausalLM.from_pretrained( MODEL_TARGET, torch_dtype=torch.float16, device_map="auto")draft_model.eval()target_model.eval()
@torch.no_grad()def get_logits(model, input_ids): """一次前向传播,返回每个位置预测下一个 token 的 logits。 logits 是模型最后一层输出的未归一化分数,经 softmax 后即为概率分布。""" out = model(input_ids) # out.logits 形状: [batch, seq_len, vocab_size] # 位置 i 的 logits 预测的是位置 i+1 的 token return out.logits
def speculative_decode(input_ids, draft_model, target_model, max_new_tokens=128, draft_k=4, temperature=0.0): """投机解码主循环。
参数: input_ids —— 已有上下文的 token id(shape: [1, n]) draft_k —— 每轮草稿长度 temperature —— 0 表示贪心解码(greedy,取概率最高的 token); >0 表示按概率采样 返回: 生成的完整 token id 序列 """ generated = input_ids.clone() tokens_produced = 0
while tokens_produced < max_new_tokens: # ---- 第 1 步:小模型自回归生成 k 个草稿 token ---- draft_ids = generated.clone() draft_probs_list = [] # 记录小模型在每个位置的采样概率分布
for _ in range(draft_k): logits = get_logits(draft_model, draft_ids)[:, -1, :] if temperature == 0.0: next_token = logits.argmax(dim=-1, keepdim=True) q = F.softmax(logits, dim=-1) # 小模型分布 q(x) else: q = F.softmax(logits / temperature, dim=-1) next_token = torch.multinomial(q, num_samples=1) draft_probs_list.append(q) draft_ids = torch.cat([draft_ids, next_token], dim=-1)
# ---- 第 2 步:大模型一次 forward 并行验证 ---- # 取出大模型在 k 个草稿位置上的 logits target_logits = get_logits(target_model, draft_ids) # 位置 generated_len-1 到 generated_len+k-2 的 logits 预测草稿位置 0 到 k-1 start = generated.shape[-1] - 1 target_logits_k = target_logits[:, start : start + draft_k, :] # [1, k, vocab]
# ---- 第 3 步:逐位置接受/拒绝采样 ---- accepted_count = 0 for i in range(draft_k): draft_token = draft_ids[0, generated.shape[-1] + i].item() p = F.softmax(target_logits_k[:, i, :].squeeze(0), dim=-1) # 大模型分布 q = draft_probs_list[i].squeeze(0) # 小模型分布
if temperature == 0.0: # 贪心模式:大模型 argmax 与草稿一致则接受 target_top = p.argmax().item() if target_top == draft_token: accepted_count += 1 continue else: # 拒绝,用大模型的 argmax 替换(贪心无损) replacement = torch.tensor([[target_top]], device=generated.device) break else: # 采样模式:严格按接受/拒绝规则 p_x = p[draft_token].item() q_x = q[draft_token].item() accept_prob = min(1.0, p_x / max(q_x, 1e-10))
if torch.rand(1).item() < accept_prob: accepted_count += 1 # 接受这个草稿 token continue else: # 拒绝:从修正分布 max(0, p - q) 归一化后重采样 corrected = torch.clamp(p - q, min=0.0) corrected = corrected / corrected.sum() # 归一化 replacement = torch.multinomial(corrected, num_samples=1) break else: # 全部 k 个都被接受,额外从大模型最后一个位置采样一个 bonus token bonus_logits = target_logits[:, start + draft_k, :] if temperature == 0.0: replacement = bonus_logits.argmax(dim=-1, keepdim=True) else: bonus_p = F.softmax(bonus_logits / temperature, dim=-1) replacement = torch.multinomial(bonus_p, num_samples=1) accepted_count = draft_k got_bonus = True
# ---- 拼接本轮结果 ---- if accepted_count > 0: accepted_tokens = draft_ids[0, generated.shape[-1]: generated.shape[-1] + accepted_count] generated = torch.cat([generated, accepted_tokens.unsqueeze(0)], dim=-1) tokens_produced += accepted_count
# 拒绝位置或 bonus 的替换 token if 'replacement' in dir() or 'replacement' in locals(): generated = torch.cat([generated, replacement], dim=-1) tokens_produced += 1
# 遇到 EOS(end of sequence,序列结束标记)则停止 if tokenizer.eos_token_id in generated[0, input_ids.shape[-1]:].tolist(): break
return generated
# ========== 运行示例 ==========prompt = "用一段话解释什么是投机解码(Speculative Decoding):"input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to(draft_model.device)
result = speculative_decode( input_ids, draft_model, target_model, max_new_tokens=100, draft_k=4, temperature=0.0)print(tokenizer.decode(result[0], skip_special_tokens=True))代码要点提示:
- 贪心模式(
temperature=0)下,接受条件简化为”大模型 argmax 是否等于草稿 token”,天然无损。- 采样模式(
temperature>0)必须严格按 接受、从 重采样,否则会偏离原始分布。- 实际生产中不会用
transformers逐 token 跑(太慢),而是用 vLLM 等引擎做 KV-cache(键值缓存,即把每层的中间结果缓存复用)管理和批处理。
- 接受率是关键:Draft Model 与 Target Model 越接近,接受率越高、加速越明显;模型差异大时加速比可能低于 1.5x。2025 年的研究表明,同系列模型(如 Llama-3.2-1B → Llama-3.1-8B)的接受率显著高于跨系列组合。
- 草稿长度 k 要折中:k 太小验证开销占比大,k 太大则小模型误差累积导致拒绝率高;实践中 k 常取 4 到 8。一些自适应方法会根据近期接受率动态调整 k。
- 通信与调度成本:在多卡张量并行(tensor parallelism,即把一个模型的权重切分到多张 GPU 上并行计算)场景下,小模型调用引入额外同步开销,需要把 draft 推理与 target 推理合理调度到不同流(stream)或设备上。
- 量化与投机兼容:Draft Model 可以做激进的量化(quantization,即降低参数精度以减少显存占用,如 4-bit),进一步降低其开销,而不影响最终输出质量。Target Model 同样可以量化,但需注意精度对接受率的影响。
- 批量推理需小心:投机解码在 batch(批处理,即同时处理多个请求)推理中实现更复杂——不同请求接受长度不同,需要 tree attention(树注意力,即对树状排列的候选序列并行计算注意力)或修改 KV-cache 管理来实现不规则的批处理。
- 无损前提依赖正确实现:贪心解码下投机解码天然无损(取 argmax 等价);带采样的实现必须严格按接受/拒绝分布来采样,否则会偏离原始分布。部分框架(如早期版本的 vLLM)在采样模式下做了一定近似,需查阅文档确认。
- vLLM:开源 LLM 推理引擎,内置
speculative配置项支持多种投机解码后端(n-gram、draft model、EAGLE/Medusa 等)。截至 2025 年,vLLM 已将 speculative decoding 作为一等公民支持,提供开箱即用的配置。生产环境广泛使用。 - TensorRT-LLM:NVIDIA 推理框架,原生支持 Medusa、EAGLE、Lookahead 等多种投机解码策略,部署在 Triton Inference Server 上服务大规模 API。2025 年的版本进一步优化了 MoE(Mixture of Experts,混合专家模型)场景下的投机解码效率。
- DeepSpeed-FastGen:微软推出的高吞吐推理框架,集成 speculative decoding 支持。
- TGI(Text Generation Inference):HuggingFace 的推理服务,支持通过
--speculative-model指定草稿模型。 - Llama.cpp / llama-server:在消费级硬件上支持基于 n-gram 的投机解码(不需要单独 draft model),适合本地部署。2025 年进一步支持了 EAGLE 风格的草稿方案。
- SGLang:新兴的高性能推理引擎,2025 年迅速崛起,原生支持 EAGLE 和 Medusa 后端,在处理结构化输出(如 JSON 生成)时配合投机解码效果尤为突出。
典型类库与工具
Section titled “典型类库与工具”| 类库 | 语言 | 说明 |
|---|---|---|
| vLLM | Python | 生产级 LLM 推理引擎,内置多种投机解码后端 |
| TensorRT-LLM | C++/Python | NVIDIA 高性能推理框架,支持 Medusa/EAGLE |
| SGLang | Python | 新兴高性能引擎,原生支持 EAGLE/Medusa |
| DeepSpeed-MII / FastGen | Python | 微软高吞吐推理,集成投机解码 |
| HuggingFace TGI | Rust/Python | HF 官方推理服务,支持草稿模型配置 |
| llama.cpp | C++ | 消费级本地推理,支持 n-gram / EAGLE 投机解码 |
| Medusa | Python | 多头投机解码的原始实现与训练工具 |
| EAGLE | Python | 基于隐藏层特征的高精度草稿方案实现 |
| SpecBench | Python | 投机解码基准测试套件,统一评估各方案加速效果 |
Medusa:多头投机
Section titled “Medusa:多头投机”Medusa 摒弃了单独的 Draft Model,转而在 Target Model 的最后一个隐藏层(hidden layer,即 Transformer 各注意力块输出后、最终投影层之前的中间表示层)之上并联多个额外的预测头(Medusa heads)。每个头直接预测未来第 个位置的 token,这样一次 forward 就能得到多个候选未来词,组成一棵候选树。
配合 tree attention(树注意力)对树状候选并行打分验证,省去了维护第二个模型的麻烦,部署更简洁。Medusa-2 进一步引入了典型的接受长度估计和更精细的训练策略,将端到端加速比提升至 2x-3x 以上。Medusa 的代价是需要对预测头做轻量训练(通常数千步即可收敛),但主模型权重完全不变。
EAGLE:特征级草稿
Section titled “EAGLE:特征级草稿”EAGLE(Extrapolation Algorithm for Greater Language-model Efficiency)进一步提升草稿精度。它的核心观察是:草稿模型的输入不应只是 token id 序列,而应包含 Target Model 的隐藏层特征(即 Transformer 中间层的语义表示向量)。EAGLE 用一个轻量网络(通常一两层 Transformer decoder),以上一位置的隐藏特征和当前 token embedding 作为输入,自回归地生成草稿。由于携带了丰富的语义信息,草稿接受率显著高于纯 token 级别的方法,加速比也随之提升。
EAGLE-2(2024)引入了动态草稿树(dynamic draft tree)机制:不再固定生成一条线性草稿链,而是根据每一步的上下文置信度自适应地调整候选树的结构和大小,在高置信度分支上分配更多候选、在低置信度处提前剪枝,进一步提高了有效接受长度。
EAGLE-3(2025)的改进方向包括:训练一个更轻量但更精准的草稿网络、改进训练数据策略以覆盖更广泛的任务分布、以及优化与长上下文(long-context)模型的兼容性。EAGLE-3 在多个公开 benchmark(基准测试)上将加速比推至 3x-4x 区间,成为 2025 年最受关注的投机解码方案之一。
Lookahead 与 n-gram 投机
Section titled “Lookahead 与 n-gram 投机”Lookahead Decoding 和基于 n-gram 的投机(如 prompt lookup decoding)不需要任何额外模型:它们直接从已生成或 prompt(提示词,即用户输入给模型的文本)中按 n-gram(n 元语法,即连续 n 个 token 的序列)匹配抽取候选 token 作为草稿。对于代码补全、文档摘要、RAG(Retrieval-Augmented Generation,检索增强生成)等存在大量重复片段的场景,这种零成本投机往往能带来可观的加速。
2025 年,REST(Retrieval-based Speculative Translation) 等方法将这一思路扩展到从外部语料库中检索候选片段,在翻译和摘要任务上取得了不错的效果。
MoE 模型的投机解码
Section titled “MoE 模型的投机解码”Mixture of Experts(MoE,混合专家模型,即每次前向传播只激活部分专家子网络的稀疏架构)是 2024-2025 年大模型的主流趋势(如 Mixtral、DeepSeek-V3、Qwen-MoE 等)。MoE 模型天然具有更高的算力冗余(每个 token 只激活少数专家),因此投机解码在 MoE 上能获得更高的加速比——因为验证更多候选位置时,激活的专家数量增加有限。2025 年的研究表明,在 DeepSeek-V3 等模型上,EAGLE 配合投机解码可获得 3x-5x 加速。
性能评估:SpecBench
Section titled “性能评估:SpecBench”2025 年,学术界和工业界共同推动了 SpecBench——一个标准化的投机解码基准测试套件,用于公平比较不同方法在不同模型、不同硬件上的加速效果。SpecBench 覆盖多种任务类型(对话、代码、推理、翻译等),报告指标包括:
- 端到端加速比(End-to-end Speedup):相比无投机解码基线的实际吞吐提升。
- 平均接受长度(Accepted Length):每轮平均被接受的草稿 token 数。
- 草稿开销(Draft Overhead):草稿生成和验证带来的额外计算占比。
- 显存开销(Memory Overhead):草稿模型或预测头占用的额外显存。
根据 SpecBench 的公开结果,2025 年主流方案在单卡 A100/H100 上的端到端加速比大致为:
| 方案 | 典型加速比 | 额外显存 | 适用场景 |
|---|---|---|---|
| n-gram / Prompt Lookup | 1.2x-2x | 几乎为零 | 代码补全、RAG |
| Draft Model(同系列小模型) | 1.5x-2.5x | 草稿模型大小 | 通用 |
| Medusa-2 | 2x-3x | 数百 MB(预测头) | 需训练预测头 |
| EAGLE-2 / EAGLE-3 | 2.5x-4x | 1-2 GB(草稿网络) | 当前最高加速 |
| 术语 | 英文 | 解释 |
|---|---|---|
| 投机解码 | Speculative Decoding | 用小模型猜、大模型并行验证的无损加速方法 |
| 草稿模型 | Draft Model | 参数量小、生成快,负责快速产生候选 token |
| 目标模型 | Target Model | 参数量大、精度高,负责一次并行验证草稿 |
| 接受率 | Acceptance Rate / Accepted Length | 每轮被大模型接受的草稿 token 平均数量 |
| 接受/拒绝采样 | Accept/Reject Sampling | 保证输出分布无损的采样规则 |
| 多头投机 | Medusa | 在目标模型上并联多个预测头预测未来位置 |
| 特征级草稿 | EAGLE | 利用目标模型隐藏特征生成高精度草稿 |
| 树注意力 | Tree Attention | 对树状候选并行打分的注意力计算方式 |
| 内存带宽瓶颈 | Memory-Bandwidth Bound | 推理速度受限于权重搬运而非计算 |
| 无损加速 | Lossless Acceleration | 输出分布与原模型严格一致的加速 |
| KV-cache | KV-cache | 键值缓存,缓存每层注意力中间结果以避免重复计算 |
| 前向传播 | Forward Pass | 将输入送入神经网络跑一次计算得到输出的过程 |
| 贪心解码 | Greedy Decoding | 每步取概率最高的 token 的解码方式 |
| SpecBench | SpecBench | 投机解码标准化基准测试套件 |
奠基论文(2022-2023):
- Chen et al., Accelerating Large Language Model Decoding with Speculative Sampling(DeepMind, 2023)——首次提出投机采样框架
- Leviathan et al., Fast Inference from Transformers via Speculative Decoding(Google, 2023)——与 DeepMind 并行的独立工作
核心方法论文(2024):
- Cai et al., Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads(2024)——多头投机
- Li et al., EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty(ICML 2024)——特征级草稿
- Cai et al., EAGLE-2: Faster Inference of Language Models with Dynamic Draft Trees(2024)——动态草稿树
2025 年最新进展:
- EAGLE-3(2025):轻量草稿网络 + 长上下文兼容,加速比突破 4x
- SpecBench(2025):投机解码标准化评估基准
- REST / RESTA:基于检索的投机解码,适用于翻译与摘要
- SGLang 投机解码集成(2025):高性能引擎中的原生 EAGLE 支持
- MoE + Speculative Decoding 研究系列:DeepSeek-V3、Qwen-MoE 等稀疏模型上的高加速比实践
相关文档:
- 推理优化、LLM 推理、流式输出、解码策略
- vLLM 官方文档关于 speculative decoding 的配置说明:https://docs.vllm.ai
- EAGLE 项目主页:https://sites.google.com/view/eagle-llm
- SpecBench 仓库:https://github.com/hemingkx/Spec-Bench