?它如何让 LLM 变快?)
投机解码Speculative Decoding是一种让 LLM 在不损失质量的前提下更快生成文本的技术。一个小草稿模型快速猜出接下来几个 token大模型则用一次运行把这一批猜测全部校验完。本文我们来讲清楚投机解码它是什么、没有它时 LLM 生成为什么慢、小草稿模型与大目标模型如何配合更快地产出 token、保证质量不降的拒绝采样数学、2 到 3 倍加速的真实数字、它在生产环境中的用武之地以及需要留意的取舍。我们将覆盖以下内容投机解码解决了什么问题全局图景为什么 LLM 生成很慢投机解码的核心思路逐步走一遍校验步骤真实数字与加速比用在哪些地方权衡快速总结投机解码解决了什么问题当我们使用 ChatGPT、Claude 或 Gemini 这类内部跑着 LLM 的工具时会注意到模型是一个词一个词地写。它不是一口气写完整段而是写一个 token再写下一个再下一个。这很慢。模型越大越慢。每一个 token 都逼着整个模型做一次完整的前向传播。为了产出一个 token几十亿参数要从显存里读一遍。然后下一个 token同样的事再来一遍。一遍又一遍。投机解码就是一项让这个过程变快、同时既不改模型、也不损失任何质量的技术。全局图景在深入细节之前先看全局。投机解码用两个模型。一个又小又快但不那么聪明一个又大又慢但非常聪明。小模型快速写出一份接下来几个 token 的草稿大模型则一口气把这些 token 全部校验完。用一句话说投机解码 小模型起草 token 大模型并行校验这些 token。如果草稿写得好我们用一个「大模型步」的代价换来好几个 token。如果草稿写坏了我们扔掉错的 token从草稿正确的地方接着往下走。输出结果和只用大模型时完全一致。没有任何质量损失。为什么 LLM 生成很慢要理解投机解码必须先理解普通的 LLM 生成为什么慢。LLM 以自回归autoregressive方式生成文本。也就是说模型产出一个 token再把这个 token 作为输入产出下一个。假设我们让模型写出「I love teaching AI and Machine Learning.」它会这样做步骤 1输入 I - 输出 love 步骤 2输入 I love - 输出 teaching 步骤 3输入 I love teaching - 输出 AI ... 以此类推每一步整个大模型都要完整跑一次。如果模型有 700 亿参数那么每产出一个 token这 700 亿参数都要从显存里读一遍。有意思的地方在这里。GPU 算得并不慢它慢在搬数据。生成单个 token 的时间里绝大部分花在把模型权重搬进计算单元上而不是真正做数学运算。这意味着 GPU 有大量空闲的算力在等内存的时候闲置着。看到问题了吗我们为每一个 token 都付出了完整的内存代价却几乎没用上 GPU 的算力。投机解码填的就是这道缝隙。投机解码的核心思路投机解码背后的想法非常简单。假设我们有两个模型一个叫草稿模型draft model的小模型。它很快比如说 10 亿参数。一个叫目标模型target model的大模型。它是聪明的那一个比如说 700 亿参数。计划是这样的草稿模型快速写出接下来 4 到 5 个 token一个接一个。目标模型把这些草稿 token 一起接过来用单次前向传播全部检查完。对每个草稿 token目标模型给出判断「是我也会这么写」或者「不这是错的」。我们保留目标模型接受的全部 token扔掉其余的然后从那里继续。整体流程大致如下[ 草稿模型 ] | 依次写出 4 个 token | v -------------------------- | 草稿t1 t2 t3 t4 | -------------------------- | v [ 目标模型 ] | 对全部 4 个 token 做一次前向 | v ---------------------------------- | 接受 t1接受 t2拒绝 t3 | | 修正 t3丢弃 t4 | ---------------------------------- | v 从已接受的前缀继续神奇之处在第二步。目标模型用一次前向同时检查了 4 到 5 个 token用的是原本闲置的那部分 GPU 算力。加载模型权重的内存代价只付了一次却拿回了多个 token。打个现实中的比方。假设有一位资深工程师非常严谨也非常慢还有一位初级工程师手快但会犯些错。没有投机解码时资深工程师自己一行一行写完所有代码。有了投机解码初级工程师先快速写出 5 行草稿资深工程师把这 5 行一起读完批准正确的那些改掉第一个错的然后从那里继续。大多数情况下初级工程师至少前几行是对的。于是资深工程师只需要审一批稿而不用每行都从零开始写。整个思路就是这些。逐步走一遍现在我们把一轮投机解码完整走一遍看看实践中是什么样子。假设目前已有的 prompt 是「I love teaching AI」草稿长度为 4。步骤 1草稿模型拿着「I love teaching AI」一个一个生成 4 个 token草稿 tokenand、Machine、Learning、today草稿模型很小所以这一步非常快。步骤 2目标模型把 prompt 加上这 4 个草稿 token 一起接过来。在一次前向传播中目标模型给出每个位置的概率分布位置 1AI 之后 目标预测 and - 与草稿一致 位置 2AI and 之后 目标预测 Machine - 与草稿一致 位置 3AI and Machine 之后目标预测 Learning - 与草稿一致 位置 4AI and Machine Learning 之后目标预测 everyday - 与草稿的 today 不一致步骤 3我们从前往后接受 token直到遇到第一个被拒绝的。“and” 被接受“Machine” 被接受“Learning” 被接受“today” 被拒绝。目标模型想要的是 “everyday”。步骤 4我们把被拒绝的 token 换成目标模型的选择。于是我们接受「and Machine Learning everyday」。步骤 5我们带着「I love teaching AI and Machine Learning everyday」这个新 prompt开始下一轮。在看校验过程的可视化之前先看看进入目标模型的全部输入——它们在一次并行前向中同时处理全部 8 个输入并行进入目标模型一次前向 位置 1 2 3 4 5 6 7 8 输入 I love teaching AI and Machine Learning today \____________ prompt ____________/ \______ 4 个草稿 token ______/ | | v v ---------------------------------------------------- | 目标模型一次并行前向传播 | ---------------------------------------------------- | v v v v v v v v 预测 预测 预测 预测 预测 预测 预测 预测 每个位置各预测下一个 token8 个位置同时被处理。在每个位置上目标模型都预测「下一个 token 应该是什么」。于是一次并行前向我们就拿到了 8 个「下一 token」预测。我们把这些预测与 4 个草稿 token 比对决定接受哪些——4 个全部同时校验而不是一个一个来。同一轮的可视化如下位置1234草稿“and”“Machine”“Learning”“today”目标“and”“Machine”“Learning”“everyday”判定通过通过通过修正发生了什么位置 1、2、3接受草稿与目标一致位置 4拒绝替换成目标的 “everyday”本轮新增的 token“and” “Machine” “Learning” “everyday” 4 个 token目标模型前向次数1 次一轮下来我们拿到的是 4 个 token 而不是 1 个而目标模型只跑了一次。这就是加速的来源。如果 4 个草稿 token 全部被接受一轮就能拿到 5 个 token——4 个草稿 token 加上 1 个奖励 token。这个奖励是白送的目标模型的单次前向在每个位置都产出概率分布包括最后一个草稿 token 之后的那一个位置。所以不额外花代价又多得一个 token。完美运转。校验步骤现在自然会有个问题目标模型凭什么决定接受还是拒绝一个草稿 token最简单的版本是如果草稿 token 和目标模型本来会选的一样就接受。但这不是全部故事。LLM 是概率性的它并不总是挑那个最可能的 token而是从概率分布里采样。所以我们不能只检查「草稿 token 是否等于目标的最高选择」。我们需要一条更聪明的规则。于是拒绝采样rejection sampling登场了。规则是这样的对每个草稿 token我们有两个概率q(x) 草稿模型会选这个 token 的概率p(x) 目标模型会选这个 token 的概率接受规则如下如果 p(x) q(x) 总是接受 否则 以 p(x) / q(x) 的概率接受用具体数字看一看。例 1草稿 token “Machine”——目标强烈同意。q(Machine) 0.6草稿模型有 60% 的概率选 “Machine”p(Machine) 0.8目标模型有 80% 的概率选 “Machine”因为p q0.8 0.6我们总是接受 “Machine”。目标模型同意而且比草稿模型更坚定没有任何理由拒绝。例 2草稿 token “today”——目标不同意。q(today) 0.5草稿模型有 50% 的概率选 “today”p(today) 0.1目标模型只有 10% 的概率选 “today”因为p q0.1 0.5我们不能总是接受而是以如下概率接受p / q 0.1 / 0.5 0.2也就是说20% 的情况我们接受 “today”80% 的情况拒绝它。我们靠抛一枚有偏的硬币来定。直觉很简单。当目标模型同意草稿p q时草稿没问题——接受。当目标模型不同意p q时草稿有风险——我们只在部分情况下接受比例取决于目标模型还相信它多少。如果 token 被拒了我们不会就此作罢。我们会从一个「修正分布」里重新采样一个 token这个分布填补了两个模型之间的差距。差距按max(0, p(x) - q(x))计算然后归一化。用数字看看。假设在那个被拒绝的位置上有三个 token 可选“everyday”、“today”、“tomorrow”。草稿与目标的概率如下tokenq(x)p(x)“everyday”0.20.5“today”0.50.1“tomorrow”0.30.4草稿选的是 “today”但它被接受规则拒了。现在我们对每个 token 计算修正分布max(0, p - q)再归一化everydaymax(0, 0.5 - 0.2) 0.3 today max(0, 0.1 - 0.5) 0 tomorrowmax(0, 0.4 - 0.3) 0.1 合计 0.4 修正后(everyday) 0.3 / 0.4 0.75 修正后(today) 0 修正后(tomorrow) 0.1 / 0.4 0.25所以替代 token 从这个修正分布中采样75% 概率是 “everyday”25% 概率是 “tomorrow”。概率质量向那些「目标模型比草稿模型更想要」的 token 转移。“today” 一点都分不到因为目标根本不想要它——草稿把它抬得太高了。这里的数学是精心设计的使得投机解码的最终输出与「只用目标模型逐 token 生成」具有完全相同的概率分布。这不是近似而是数学上精确相等的取值。注意为了保证本文简短易读我们有意跳过了完整的数学证明。该证明表明无论算法走哪条路径最终得到某个 tokenx的概率都恰好等于p(x)。如果好奇细节可以在投机解码的原始论文里找到完整证明。这是投机解码最重要的性质。我们拿到了速度上的好处质量上一无所失。这就是投机解码的精妙之处。不过并非所有技术都会让这个分布原封不动。有些技术会有意做极小的偏移——LLM 水印watermarking就是靠这种微小的偏移把一个信号藏进生成的文本里。真实数字与加速比把这些放进真实数字里就能清楚看到我们省了多少时间。假设目标模型每个 token 花 50 毫秒草稿模型每个 token 花 5 毫秒草稿长度取 4 个 token没有投机解码时基线每个 token 都要逼着目标模型完整跑一次。1 个 token 1 次目标前向 50 ms所以生成任意 N 个 token 需要N × 50 ms。有投机解码时一轮的代价是4 个草稿 token × 5 ms 20 ms起草 1 次目标校验 50 ms并行校验全部 4 个 每轮合计 70 ms每轮代价固定为 70 毫秒。变化的是这一轮产出多少 token取决于有多少草稿 token 被接受。来看三种情况。最好情况——4 个草稿 token 全部被接受。我们得到 4 个被接受的 token外加目标模型白送 1 个奖励 token也就是 70 毫秒拿到 5 个 token。无投机解码5 个 token × 50 ms 250 ms 有投机解码 70 ms 加速比 250 / 70 3.57 倍平均情况——4 个草稿 token 中有 3 个被接受。前 3 个草稿 token 被接受第 4 个被拒并被目标模型的选择替换。于是我们得到 3 个接受 1 个修正 向前推进 4 个 token正是上面逐步演示的那一轮。无投机解码4 个 token × 50 ms 200 ms 有投机解码 70 ms 加速比 200 / 70 2.86 倍最差情况——0 个草稿 token 被接受。即便如此我们仍然向前推进 1 个 token。当第一个草稿 token 就被拒时我们在该位置从修正分布max(0, p - q)中采样一个替代 token所以进展有保证绝不会是零。但起草的时间白花了。无投机解码1 个 token × 50 ms 50 ms 有投机解码 70 ms 加速比 50 / 70 0.71 倍更慢了所以投机解码是一场下注。草稿模型与目标模型意见一致时我们大赚不一致时我们付一点小罚金。只要平均接受率足够高这笔下注就划算——对同一模型家族来说通常足够高。再看看对完整回答的用户可见影响。假设用户问了一个问题目标模型需要生成 200 个 token 的回答。沿用上面平均情况——每轮向前推进 4 个 token3 个草稿被接受加上 1 个在被拒位置被修正的 token。在真实负载中这个数字可能在 1 到 5 之间这里取 4 仅作示例。算式如下无投机解码 200 个 token × 50 ms 10,000 ms 10.0 秒 有投机解码 每轮代价4 个草稿 token × 5 ms 1 次目标前向 × 50 ms 70 ms 每轮 token 数平均4 所需轮数200 / 4 50 轮 总时间50 轮 × 70 ms 3,500 ms 3.5 秒 节省时间6.5 秒2.86 倍加速同样的对比放到时间轴上时间秒0 1 2 3 4 5 6 7 8 9 10 无投机解码|| 10.0s 200 次目标前向每 token 一次 有投机解码|| 3.5s 50 轮每轮 4 个 token -------- 节省 6.5 秒 --------同样的回答用户等 3.5 秒而不是 10 秒。这就是用户能感知到的收益。实践中大多数负载的加速比在 2 到 3 倍之间。具体数字取决于小草稿模型与大目标模型有多契合草稿有多长输入风格与草稿模型见过的内容有多相似草稿模型的行为越接近目标模型接受率越高加速比越大。用在哪些地方今天很多生产级 LLM 系统都在用投机解码。一些真实例子vLLM、SGLang 和 TensorRT-LLM 都开箱支持投机解码用于大模型服务。Llama 系列常常把一个小 Llama 和一个大 Llama 配对以加速推理。DeepSeek-V3 在训练时加入了名为多头预测Multi-Token PredictionMTP的辅助目标即额外加一个轻量模块一层 transformer 加一个输出头让它学会预测未来位置上的下一个 token。推理时这个模块就成了内置的起草器因此不需要单独的草稿模型。DeepSeek 报告由此获得约 1.8 倍的生成加速。这个想法之所以有这么多变体是因为它是在不重新训练大模型的前提下让 LLM 服务提速 2 到 3 倍的最便宜手段之一。权衡投机解码让生成变快但也要付出代价我们需要第二个模型。草稿模型要占用额外的显存。对超大规模部署来说这不是小事。两个模型必须共享同一个 tokenizer分词器。如果它们的词表不同草稿 token 就无法与目标模型期望的输入对齐。接受率取决于草稿与目标有多一致。如果草稿模型与目标差得太远大部分草稿 token 都会被拒时间就白费了。对非常短的输出跑两个模型的开销可能把加速比抵消掉。草稿长度必须调。太短拿不到多少加速太长会浪费在被拒的 token 上。在低批量batch size下加速效果最明显——那时解码受显存带宽约束GPU 算力闲置。批量很大时GPU 已被连续批处理Continuous Batching占满投机解码赖以取用的空闲算力不复存在收益缩小某些情况下甚至会反转。对大多数大模型服务负载来说这笔取舍是值得的。用户看到更快的响应GPU 也用得更有效率。后来的方法干脆不再需要第二个模型而是让大模型自己给自己起草。比如 Medusa 用额外的解码头decoding heads来完成起草。EAGLE 更进一步起草的不是模型带噪声的输出 token而是它平滑的内部特征。N-gram 投机N-gram Speculation走了另一条路完全不需要模型来起草它在 prompt 里查找最后几个 token把它们之后出现过的内容复制过来当草稿再交给大模型校验——当回答重复了输入内容时这招非常好用。快速总结回顾一下我们学到的问题LLM 一次只生成一个 token每个 token 都逼着整个大模型跑一次。在加载内存的同时GPU 的算力闲着。这很慢。核心思路用一个小而快的草稿模型写出几个 token用大而慢的目标模型在一次并行前向里把它们全部校验完。接受规则每个草稿 token 依据一条叫拒绝采样的数学规则被接受或拒绝。这套数学保证最终输出与大模型独自生成的结果一致。收益2 到 3 倍加速且无质量损失。代价草稿模型要额外占显存且加速幅度取决于两个模型有多一致。用一句话说投机解码 小模型起草 token 大模型并行校验这些 token。投机解码是那种罕见的、能给我们速度却不牺牲质量的技术。它不改模型不改输出只是把 GPU 用得更聪明。这就是为什么今天几乎每一个现代 LLM 服务系统都支持它。常见问题投机解码会降低输出质量吗不会。拒绝采样规则的设计目标就是让最终输出与「只用目标模型逐 token 生成」具有完全相同的概率分布。这不是近似。我们拿到速度上的好处质量上一无所失。投机解码会让生成变慢吗在某些情况下会。如果一个草稿 token 都没被接受一轮的代价就比单次目标前向更高比如 70 毫秒而不是 50 毫秒。在批量非常大时收益也可能缩小甚至反转而非常短的输出因为两个模型的开销可能根本享受不到好处。草稿模型和目标模型需要同一个 tokenizer 吗需要。两个模型必须共享同一个 tokenizer。如果它们的词表不同草稿 token 就无法与目标模型期望的输入对齐。我们总是需要单独的草稿模型吗不需要。DeepSeek-V3 用多头预测MTP由一个轻量的额外模块充当内置起草器。Medusa 让大模型用额外的解码头给自己起草。N-gram 投机则完全不需要起草模型它直接从 prompt 里复制草稿。