大语言模型支撑着广泛应用,但大规模服务需要精细优化。标准自回归解码作为主流基线,严格按从左到右顺序逐 token 生成并提交,简单可靠却每次只推进一个 token。推测解码(speculative decoding)在此基础上引入草稿-验证机制:由轻量草稿组件先提议候选未来 token,再由目标模型在提交前一次性验证,从而在单次前向传播中确认多个草稿 token,以提升输出吞吐。
正文
简而言之:投机解码让 vLLM 可以在一次目标模型验证中校验多个草稿 token。在我们的实验中,它对输出 token 吞吐的影响会因草稿方法和提议长度而变化,同时还取决于模型家族、草稿 checkpoint、工作负载与接受行为。
大型语言模型支持广泛的应用,但规模化服务需要仔细优化。标准自回归解码是大多数服务系统使用的基线:模型每生成一个 token,就把它追加到序列,再用更新后的序列生成下一个 token。这个过程简单可靠,但由于输出必须严格从左到右产生,每次只能推进一个已提交 token。
投机解码在基线之上引入“草稿-验证”机制:轻量级草稿组件提出候选的未来 token,目标模型在提交前验证这些候选。如果有多个草稿 token 被接受,系统就能从一次目标模型验证中提交多个输出 token,同时保持目标模型的输出行为不变。
下面先回顾自回归解码基线,再考察五种投机草稿方法:native MTP、Gemma 4 MTP、EAGLE-3、DFlash 和 DSpark。这些方法的差异主要在于草稿组件从目标模型接收什么信息,以及候选 token 是按顺序、自回归、并行还是混合方式生成。最后会说明如何在测试环境中启用这些方法,并讨论调优与可观测性问题。
自回归解码基线
标准自回归解码每步生成并提交一个新 token。例如要生成四个输出 token,需要四步:
- context → model → T1
- context + T1 → model → T2
- context + T1 T2 → model → T3
- context + T1 T2 T3 → model → T4
每步生成的 token 都会被追加到序列中,成为下一步输入的一部分。这让解码循环很简单,但每个输出 token 都需要一次模型解码。长生成过程中,这种逐 token 循环会主导延迟并限制服务吞吐。
因此投机解码要回答的关键问题是:能否在保持原模型输出行为的同时,减少“一次只前进一个 token”的次数?
投机解码的做法是把提议和验证分开。草稿组件先提出若干候选 token,原模型作为目标模型再验证这些候选,然后才提交。
投机解码的核心思想
投机解码并不替换原模型。原模型仍然是目标模型,负责最终输出,只是在其前面加了一个更快的提议阶段。
过程分两部分:
- 草稿:提出多个候选的未来 token。
- 验证:用目标模型检查这些候选。
每一轮投机解码中,轻量草稿组件会提出一个或多个未来 token。这些 token 只是候选,不会立刻被提交。目标模型随后在一次验证 pass 中评估整个候选序列。
验证从左到右进行。每个草稿 token 都会对照目标模型在对应位置的结果进行检查。被接受的 token 提交到输出序列;当一个草稿 token 被拒绝,同一提议中更靠后的候选不再被接受。
如果草稿 token 被拒绝,目标模型会给出下一个 token。剩余草稿 token 被丢弃,生成从更新后的序列继续。
概念上,标准自回归解码是这样前进的:
目标模型 → T1 → T2 → T3 → T4
投机解码则把多个候选位置放在一起评估:
草稿提议 T1 T2 T3 T4;模型验证 ✓ ✓ ✗ 停止;提交 T1、T2、替换 token。
当草稿组件产生的 token 被目标模型接受时,一次目标验证就能提交多个输出 token。如果提议被拒绝,则由目标侧的结果决定生成如何继续。
一个接受/拒绝示例
假设当前提示是:
The weather today is
草稿组件提出几个未来 token:
sunny and warm outside
目标模型从左到右验证:
sunny ✓、and ✓;第三个位置草稿提出 warm,但目标模型选择 clear;由于 outside 位于第一个被拒绝位置之后,会被丢弃。
下一轮生成从以下序列继续:
The weather today is sunny and clear
其中 clear 是目标模型给出的 token,不是来自草稿提议。
草稿方法的运作方式
虽然所有投机解码方法都遵循相同的草稿-验证总流程,但草稿组件的设计以及它与目标模型的协作方式不同。主要区别包括:
- 从目标模型接收什么类型的信息。
- 这些信息如何被纳入草稿过程。
- 候选 token 是按顺序生成还是并行生成。
据此,本文讨论的五种草稿方法可以归为三大类:
- Native MTP 模块:直接构建在目标模型架构中,使用模型自带的辅助预测路径,顺序生成候选 token。
- 独立的 MTP 草稿器:使用与特定目标模型配对的独立 checkpoint,推理时使用目标模型 activation 和共享 KV cache,顺序生成候选 token。
- 专用的目标条件草稿网络:使用针对特定目标模型训练的独立投机器,包括 EAGLE-3、DFlash 和 DSpark。EAGLE-3 从目标模型隐藏状态自回归草稿;DFlash 从目标隐藏状态并行草稿;DSpark 在并行草稿上加入轻量级因果修正和基于置信度的前缀选择。
这些分类描述的是草稿组件架构,不是目标模型家族。一个目标模型可以同时支持 native MTP,也可以有另外训练的 EAGLE-3、DFlash 或 DSpark 草稿模型。
草稿组件并不是完全独立运作。根据方法不同,它可能接收:
- 目标模型的隐表示。
- 目标模型若干选定层的隐藏状态。
- 目标模型的 KV cache。
- 由多个目标模型表示组合而成的特征。
原生 MTP(Native MTP)
多 Token 预测(Multi-Token Prediction,MTP)是一类模型内置的预测机制,用于预测紧接下一个 token 之后的更多 token。在 vLLM 中,native MTP 要求目标模型包含兼容的辅助预测组件。不同模型家族的 MTP 架构并不相同,但都会提供一个辅助路径来提议未来 token。
在第一个投机步骤中,MTP 组件把目标模型的隐表示与当前 token 的信息结合,预测第一个草稿 token。之后每一步用新草稿的 token 和上一步 MTP 产生的隐状态预测下一个候选。达到配置的候选数量后,目标模型在一次验证 pass 中统一评估。
常见的 native MTP 路径可以概括为:
目标模型或上一 MTP 的隐表示 + 平移输入 token 或最新草稿 token 的嵌入 → 模型特定的融合/投影 → 辅助预测层 → 草稿 logits。
两个输入各有用途:隐表示携带前文序列的信息;token 嵌入标识草稿继续的最新 token。常见实现沿隐藏维度将两者合并,再变换后送入辅助预测层。
物理 MTP 层数和配置的投机长度是两回事。当 numspeculativetokens 超过 checkpoint 直接提供的预测深度时,vLLM 可以复用 MTP 路径做额外前向。更大的值会在验证前提出更多候选,但也会引入更多顺序草稿工作。
Native MTP 与目标模型架构紧密相关。很多实现中 MTP 路径会与目标模型共享组件,额外内存开销通常相对较小。不过,生成多个投机 token 仍需要在验证前顺序草稿。
Gemma 4 MTP
Gemma 4 使用单独打包的 MTP 草稿组件,与特定目标模型配对。草稿组件虽然有自己的 checkpoint,但在推理时与目标模型联系紧密。
Gemma 4 目标模型 → 目标模型 activation、共享目标 KV cache → Gemma 4 MTP 草稿组件 → 候选 token。
草稿组件使用目标模型产生的 activation,并共享目标模型的 KV cache。这样它可以复用目标已经算好的上下文信息,而不是独立处理已接受前缀。
和 native MTP 一样,草稿组件层数与配置的投机长度相互独立。需要多个候选时,草稿组件顺序生成它们,之后目标模型统一验证。
EAGLE-3
EAGLE-3 使用针对特定目标模型训练的专用草稿网络。草稿组件有自己的执行路径,但仍紧密依赖目标模型产生的信息。
目标模型前向过程中,EAGLE-3 会记录 Transformer 三个位置的隐藏状态:靠近开始处、大约中间、靠近末尾。这些是同一已接受序列在目标模型处理不同阶段的上下文表示。三个隐藏状态经过拼接和投影,形成一个融合目标特征。该融合特征再与采样 token 的嵌入一起进入 EAGLE-3 草稿解码器,产生草稿 token。
两个输入用途如下:
- 融合目标特征概括已接受序列,信息来自目标模型前向的多个阶段。
- 采样 token 嵌入标识草稿要继续的 token。
EAGLE-3 以自回归方式生成草稿 token。第一个草稿 token 使用由已接受序列算出的融合目标特征和采样 token 嵌入。生成一个草稿 token 后,它的嵌入会送入下一个草稿阶段。
由于目标模型尚未处理后来的投机位置,EAGLE-3 无法取得这些位置的目标隐藏状态,因此继续草稿序列时使用前一个草稿组件的输出。
这种顺序反馈让后续草稿 token 直接依赖同一提议序列中前面的草稿 token。但需要生成更多投机 token 时,验证前的顺序草稿工作量也会增加。
DFlash
DFlash 也是为特定目标模型训练的专用草稿网络。与 MTP 和 EAGLE-3 顺序生成候选不同,DFlash 在一次前向中并行预测整块未来位置。
DFlash 每个草稿块以一个 anchor token 开头。anchor 是目标模型已经产生或确认的已知 token,因此 DFlash 不需要预测它,只需为后面被掩码的位置提供已知起点。在后续解码轮次中,anchor 通常是上一轮验证额外返回的目标 token。
anchor 占据块中第一个位置,其余位置被掩码并在同一前向中并行预测:
输入位置 0 为 anchor,位置 1 到 6 为 mask;一次 DFlash 前向输出 anchor、draft1 到 draft6。
和 EAGLE-3 一样,DFlash 先把多个目标层隐藏状态融合成表示。关键区别在于使用方式:EAGLE-3 把融合特征放在自回归草稿网络输入端;DFlash 则把融合后的目标上下文转换为额外的 Key 和 Value 表示,这些 K/V 在草稿网络的每一层都可用。被掩码草稿位置的 query 因此可以同时注意两类信息:
- 由目标模型派生出的 K/V。
- 由草稿块自身产生的 K/V。
目标上下文在整个草稿网络中始终可用,而不是只在输入端给一次。
草稿块生成后,目标模型在一次验证 pass 中评估所有提议 token。接受决策仍从左到右应用:接受已提交 token,直到第一个拒绝;剩余候选被丢弃。第一个被拒绝位置由目标模型 token 替换。
DFlash 的一个定义性特征是:所有掩码位置都在一次草稿网络前向中一起预测,而不是逐 token 反馈。因为同一 pass 中后面的位置不会以更前位置的采样输出为条件,取消了自回归草稿的逐 token 依赖。较后位置的效果因此更多取决于训练好的 checkpoint 和工作负载,尤其是在草稿块较长时。
DSpark
DSpark 在并行草稿之上增加了两个机制:
- 一个轻量级顺序头,引入草稿块内 token 之间的依赖。
- 基于置信度选择提交给目标模型验证的前缀。
DSpark 使用经过修改的 DFlash 模型作为并行骨干。骨干在一次前向中完成所有位置的草稿计算,为每个草稿位置生成一个隐藏状态和一组基础 logits,因此继承了 DFlash 的目标上下文条件方式。
完全并行的草稿组件在预测每个位置时,不会先看到同一块中更前位置已经选出的 token。当多个续写都合理时,这会产生不一致的组合。例如,可能“of course”和“no problem”都是合理续写,但独立的逐位置预测可能生成“of problem”。
DSpark 在并行骨干之后应用轻量级顺序头。骨干仍然同时计算所有位置的基础 logits;顺序头再从左到右选择 token,并利用先前已选草稿 token 的信息调整每个位置。具体来说,DSpark 使用一个轻量级 Markov 头,让已选草稿 token 之间产生依赖。对每个位置 k,Markov 头用紧邻的前一个已选草稿 token 生成一个小偏置,加到并行骨干产生的基础 logits 上,得到调整后的分布。
主草稿网络一次前向处理所有候选位置,之后只运行从左到右的轻量 Markov 头。这样后面的草稿 token 可以依赖同一块内已经选出的 token,又不必为每个位置重跑整个草稿网络。
DSpark 设计还包括一个置信度头,可以选择较短的草稿前缀提交给目标验证。但在本次实验所用的 vLLM 路径中,这个功能没有启用,因此基准结果只反映并行草稿网络加轻量 Markov 修正。
目标模型随后在一次验证 pass 中评估提议序列,草稿 token 从第一个拒绝位置起不再提交。
草稿方法小结
下表对比五种方法:草稿组件、使用的目标模型信息,以及候选 token 是顺序生成还是并行生成。所有方法中,目标模型都在一次验证 pass 中评估提议序列,接受决策从左到右应用到第一个被拒绝的草稿 token。
| 方法 | 草稿组件 | 使用的目标模型信息 | 草稿生成方式 | | --- | --- | --- | --- | | Native MTP | 模型内置的辅助 MTP 路径 | 目标模型或上一 MTP 的隐表示,与当前草稿 token 信息结合 | 重复使用 MTP 路径顺序生成 | | Gemma 4 MTP | 与目标模型配对的独立 MTP 草稿组件 | 目标模型 activation 和共享目标 KV cache | 通过配对的 MTP 组件顺序生成 | | EAGLE-3 | 专用自回归草稿网络 | 目标模型前向开始、中间、末尾附近的隐藏状态融合为一个表示 | 顺序生成,每个草稿 token 影响下一个 | | DFlash | 专用并行草稿网络 | 融合的目标模型隐藏状态,作为每层额外的 Key 和 Value | 所有候选位置在一次并行前向中一起预测 | | DSpark | DFlash 风格并行草稿网络加轻量 Markov 头 | 与并行草稿网络使用的目标条件信息相同 | 一次并行前向,再轻量顺序调整 token 选择 |
在 vLLM 中启用投机解码
vLLM 通过 --speculative-config 配置投机解码。主要区别在于方法名、是否需要单独的草稿 checkpoint,以及请求的候选 token 数。当前 vLLM 支持 mtp、eagle3、dflash 和 dspark 作为方法值。
对 native MTP,草稿组件随目标模型提供,因此不需要 model 字段:
vllm serve
对 Gemma 4 MTP、EAGLE-3、DFlash 和 DSpark,model 字段通常指向为该目标模型训练的 checkpoint:
vllm serve
Gemma 4 assistant checkpoint 虽然通过 model 字段提供,但仍使用 MTP 路径。vLLM 会把 assistant 组件与目标模型连接起来,并让它共享目标 KV cache。
启用某方法前,应确认:
- 安装的 vLLM 版本支持该方法和模型架构。
- 草稿 checkpoint 与目标模型、方法兼容。
- numspeculativetokens 与 checkpoint 兼容。
- 模型卡支持预期的硬件和推理后端。
内存考量
Native MTP 不加载单独的草稿 checkpoint,并且可能与目标模型共享 embedding 表或输出头。Gemma 4 MTP、EAGLE-3、DFlash 和 DSpark 会加载额外草稿权重,因此需要预留足够的 GPU 内存。实际开销取决于草稿组件大小、数值精度、张量并行配置和运行时缓冲区。
预训练草稿模型来源
目前已有多家机构在 Hugging Face 上发布预训练草稿模型:
- Google:提供 Gemma 4 MTP assistant checkpoint,目标包括 Gemma 4 E2B、E4B、12B、26B-A4B 和 31B。
- LightSeek Foundation:提供 EAGLE-3 和 EAGLE-3.1 草稿模型,面向 Kimi-K2.5、Kimi-K2.6 和 Kimi-K2.7-Coder,包括标准与 MLA 变体。
- Red Hat AI:提供 EAGLE-3、DFlash 和 DSpark,覆盖 Llama、Qwen、Gemma、GPT-OSS、GLM、Nemotron、Mistral 等目标系列。
- Z-Lab:提供 DFlash checkpoint,目标包括 Qwen3、Qwen3.5、Qwen3.6、Gemma 4、Kimi、MiniMax、GPT-OSS 和 Llama。
- DeepSeek AI:DeepSpec 系列为 Qwen3-4B、Qwen3-8B、Qwen3-14B 以及 Gemma 4 12B 提供三种方法的版本。
- Inferact:发布 MiniMax-M3-EAGLE3 及其 GQA 变体、Kimi-K3-DSpark 等草稿模型。
实验设置与测量
启用投机解码后,实际问题在于额外草稿工作是否能改善端到端服务性能。草稿 token 不必在每个位置都正确,因为它们在提交前会经过目标模型验证。性能取决于多少提议 token 被接受,以及省下的目标模型解码工作是否大于草稿和验证的开销。
实验使用任务级基准,而不是随机 token 序列。接受行为取决于实际模型输出的结构和可预测性,因此基于任务的提示能更真实地反映实践性能。
主要性能指标:
- 输出 token 吞吐量,以及相对非投机基线的加速比。
- 平均接受长度和草稿 token 接受率(可用时)。
- 相对非投机基线的模型质量。
模型与实验覆盖
实验覆盖五个目标模型系列和多种投机方法。具体到组合:
- Native MTP 只在 Qwen3.5-27B、Qwen3.5-122B-A10B、Qwen3.6-27B 和 Qwen3.6-35B-A3B 的内置实现上测试。
- Gemma 4 MTP 在 google/gemma-4-26B-A4B-it 和 google/gemma-4-31B-it 上测试。
- EAGLE-3 在 gemma-4-26B-A4B-it、gemma-4-31B-it、Qwen/Qwen3-8B、moonshotai/Kimi-K2.5 和 MiniMaxAI/MiniMax-M3-MXFP8 上测试。
- DFlash 在 gemma-4-26B-A4B-it、gemma-4-31B-it、Qwen3-8B、Qwen3.5-27B、Qwen3.5-122B-A10B、Qwen3.6-27B、Qwen3.6-35B-A3B 和 Kimi-K2.5 上测试。
- DSpark 在 gemma-4-31B-it 和 Qwen3-8B 上测试。
各结果都应放在具体测试配置中理解,因为模型架构、激活参数数、草稿组件大小、工作负载和服务条件都会影响表现。
吞吐测量
吞吐测量计算每秒生成 token 数,并与标准自回归基线对比;同时扫描投机 token 数量,研究投机深度如何影响端到端服务吞吐。
主要观察
测量结果随目标模型、草稿方法、工作负载和提议长度而变化。
对 gemma-4-26B-A4B-it,测试扫描内最大的吞吐比为:Gemma 4 MTP 在 GSM8K 和 MBPP 上分别为 2.74× 和 2.62×;DFlash 在 MATH500 和 HumanEval 上分别为 2.87× 和 2.79×;EAGLE-3 在四个数据集中介于 2.11× 到 2.27×。
对 gemma-4-31B-it,Gemma 4 MTP 在 GSM8K 上达到 2.00×,在 MBPP 上达到 1.99×;DFlash 在 MATH500 上达到 2.34×,在 HumanEval 上达到 2.05×。EAGLE-3 和 DSpark 在四个数据集中也都高于基线。最大吞吐对应的提议长度随工作负载不同而变化。
对 Qwen3-8B,DSpark 测量值在 MATH500 的 1.15× 到 GSM8K 的 1.63× 之间;DFlash 在 1.08× 到 1.27× 之间;EAGLE-3 在 GSM8K、HumanEval 和 MBPP 上高于基线,但在 MATH500 上的最大测量值仍低于基线。
对 Qwen3.5-27B、Qwen3.5-122B-A10B 和 Qwen3.6-27B,测试扫描内 native MTP 的最大吞吐值高于对应的 DFlash 最大值。该组中最大比值为 Qwen3.5-122B-A10B 在 MATH500 上的 2.20×。Native MTP 取得最大吞吐的提议长度约为 N=4 到 N=7,具体取决于模型和数据集。
对 Qwen3.6-35B-A3B,DFlash 在 1.77× 到 2.06× 之间,四个数据集中最大值都出现在 N=7;native MTP 在 1.28× 到 1.49× 之间,最大值出现在 N=6。这与 Qwen3.6-27B 的差异说明,同一家族内不同模型的结果也可能明显不同。
对 MiniMax-M3-MXFP8,EAGLE-3 在 HumanEval N=4 时达到 2.09×。对 Kimi-K2.5,EAGLE-3 最高达 2.33×,DFlash 最高达 2.68×。在测试扫描内,EAGLE-3 的最大值通常出现在 N=4,DFlash 的最大值出现在 N=7。
整体来看,取得最大吞吐的提议长度并不固定。顺序方法通常在前几个 N 值上吞吐上升,之后进入平台期。DFlash 和 DSpark 的 N=7 经常是较高吞吐的设置,但更大的值并不总能继续提高吞吐。
以上观察仅反映本研究使用的硬件、软件、目标模型、草稿 checkpoint、工作负载和扫描设置。
调优考虑
投机解码应视为运行时优化,而不是对每个工作负载都同样有效的固定配置。最高吞吐对应的 numspeculativetokens 取决于有多少提议 token 被接受,以及避免的目标模型解码工作是否超过草稿和验证的开销。
因此,可观测性非常重要。模型卡建议或示例配置可以作为起点,但最终设置应基于代表性工作负载和端到端测量来选择。有用的信号包括吞吐、平均接受长度、总体接受率以及逐位置接受率。
更大的提议窗口给系统更多机会在一次验证中提交多个 token。然而靠后草稿位置的接受率可能下降。此时额外候选贡献很小,却仍增加草稿和验证工作,导致吞吐趋平或回落。
从受支持的配置开始
对 native MTP,N=1 是保守起点,因为它引入的额外顺序草稿工作最少:
{"method": "mtp", "numspeculativetokens": 1}
确认正确性和稳定性后,再扫描更大的值,例如 2、3、4、5、6、7。
测量中,native MTP 取得最大吞吐的 N 随目标模型和工作负载变化。对 Qwen3.5-27B,GSM8K 和 MATH500 在 N=5,HumanEval 和 MBPP 在 N=4,MT-Bench 在 N=3。对 Qwen3.5-122B-A10B,四个 reasoning 和 code 数据集上的最大吞吐都出现在 N=7。
Qwen3.6 的测量也显示同一家族不同模型的最佳设置会变。Qwen3.6-27B 的最大值出现在 N=4 或 N=5;Qwen3.6-35B-A3B 的吞吐则增加到 N=6。
对 Gemma 4 MTP 和 EAGLE-3,增大 N 也会增加顺序草稿工作。因此即使 checkpoint 提供了推荐配置,仍值得做一次短扫描。在我们的 Gemma 4 和 EAGLE-3 实验中,吞吐通常在前几个 N 值上上升,然后进入平台。
对 DFlash,先使用草稿 checkpoint 推荐或支持的提议长度。许多 DFlash checkpoint 以固定 block size 训练。例如,blocksize = 16 时,最大提议长度通常是 numspeculative_tokens = 15,因为第一个位置是已确认的 anchor,其余 15 个位置是草稿候选。
这是受支持的最大提议长度,不一定是吞吐最高的设置。实践中值得测试较小的值,例如 N=3、7、11、15。在我们各 DFlash 实验中,N=7 经常属于较高吞吐设置;某些工作负载下最大吞吐出现在 N=11。
对 DSpark,numspeculativetokens 设置每一投机轮生成的候选 token 数。在我们的 vLLM 实验中,完整配置的提议都会提交给目标验证,因此应通过端到端吞吐比较 N=3、N=7 等不同值。
监控接受行为
相关监控信号包括:
- 吞吐:端到端服务性能相对非投机基线的变化。
- 平均接受长度:每轮投机平均提交多少草稿 token。
- 总体接受率:多少提议的草稿 token 被接受。
- 逐位置接受率:提议中靠后位置是否仍然有用。
逐位置接受率对调整提议长度尤其有帮助。如果前几个位置经常被接受,而后面位置贡献很小,减小 numspeculativetokens 可能通过省掉无用草稿工作来提高吞吐。
接受指标应与吞吐一起看。如果草稿生成很便宜,即使接受率较低,方法仍可能相对基线获得更高吞吐。反过来,高接受率不一定对应高吞吐,因为草稿组件可能带来额外开销。
让扫描与工作负载匹配
不同工作负载会产生不同的接受模式。
在我们 GSM8K 和 MATH500 测量中,中等或较深提议长度往往对应较高吞吐。例如 Qwen3.5-122B-A10B 的 native MTP 吞吐一直增加到 N=7;DFlash 的较高值常出现在 N=7 或 N=11。
HumanEval 和 MBPP 的较高吞吐设置则常是中等提议长度。代码具有可预测的局部结构,但格式、标识符和实现选择会让原本合理的续写偏离。
示例调优流程
- 从 checkpoint 支持或推荐的配置开始。
- 使用代表性提示和生成设置做基准测试。
- 记录吞吐、平均接受长度和接受率。
- 扫描几个更小和更大的提议长度。
- 根据与目标工作负载最相关的指标选择设置。本实验中,端到端服务吞吐是主要选择指标。
选出的配置不一定是提议最长、接受率最高或平均接受长度最大的那个。选择时需要考虑草稿成本、验证成本、被接受 token 数,以及与目标工作负载最相关的指标之间的权衡。
为新目标模型训练投机器
本指南不深入讨论投机器训练。下面是从相关资料整理出的典型流程。
准备代表性提示词
从能反映预期工作负载的提示开始,例如聊天、数学、代码生成、工具使用或多语言任务。另留一组独立提示用于评估。
训练响应必须由投机器将要支持的那个确切目标模型生成。tokenizer、聊天模板、思考模式和生成配置也应与部署保持一致。仅把已有响应套上目标模型的 tokenizer 或聊天模板,并不会让数据变成目标模型特有;响应本身必须来自目标模型。
选择隐藏状态的获取方式
投机器在训练中会从目标模型接收内部隐藏状态。有三种方式:
- 在线:隐藏状态由运行中的 vLLM 服务器按需生成,随后丢弃。避免大型磁盘缓存,但需要同时调度目标推理和训练资源。
- 离线:训练前生成并存储隐藏状态。训练时可释放所有 GPU,但需要大量存储。
- 混合:第一个 epoch 生成并缓存隐藏状态,之后复用。只需承担一次生成成本,又无需独立预处理阶段。
收集目标模型信息
vLLM 服务器可以运行目标模型,并从所选草稿方法需要的层中暴露隐藏状态。如果选择自定义目标层,投机器训练配置中必须使用相同的层选择。
收集的信息取决于方法:
- EAGLE-3 使用选定目标层的隐藏状态进行自回归草稿。
- DFlash 使用目标模型特征训练并行预测未来位置块的网络。
- DSpark 在 DFlash 风格草稿网络上增加轻量顺序头和置信度头。
- MTP 训练微调目标模型自身的 MTP 组件,因此需要目标模型已包含兼容 MTP 层。
训练并测试投机器
投机器配置必须匹配目标模型的隐藏大小、词表、tokenizer 和选定目标层。方法特定设置,如草稿网络深度、块大小、序列长度和学习率,也需要相应选择。
训练后,检查 checkpoint,并把它与目标模型一起在 vLLM 中服务。仅看训练损失不足以判断质量;关键是接受长度、接受率、草稿延迟、GPU 内存使用和端到端服务吞吐。
如果某个工作负载的接受效果较弱,可以调整提示混合或训练配置,然后重复过程。核心原则是使用投机器预期要支持的目标模型、生成模式和代表性工作负载。
总结
这次分享围绕 vLLM 中的投机解码这一草稿-验证式 LLM 服务方法展开。草稿组件提出候选未来 token,目标模型在提交前评估提议。
我们考察了五种草稿方法:native MTP、Gemma 4 MTP、EAGLE-3、DFlash 和 DSpark。它们主要区别在于使用目标模型信息的方式,以及候选 token 是按顺序、并行,还是并行预测加轻量顺序修正的方式生成。
实验覆盖了若干 Gemma、Qwen、MiniMax 和 Kimi 模型,运行在基于 ROCm 软件平台的 AMD Instinct MI300X 和 MI355X GPU 上。测量吞吐随目标模型、草稿 checkpoint、工作负载、提议长度和服务配置变化。
在测试配置中,一些设置只带来小幅变化,或吞吐低于非投机基线;但不少模型-工作负载组合的吞吐比超过 2×。观测范围上端的例子包括 DFlash 在 gemma-4-26B-A4B-it 上的 2.87×、Gemma 4 MTP 在同一目标上的 2.83×,以及 DFlash 在 Kimi-K2.5 上的 2.68×。
提议长度也是重要的实验变量。增大 numspeculativetokens 有时会在前几个设置内提高吞吐,但更大的值可能导致平台期或吞吐下降。Checkpoint 建议可以作为起点,但选择部署配置时,仍需要用代表性工作负载的测量和接受指标来判断。
未来工作
未来的基准测试可以加入非学习方法,例如 n-gram 投机和 suffix decoding,尤其针对代码编辑和 agent 循环这类包含重复 token 模式的工作负载。
在并发度、提示和输出长度、批大小、采样设置等维度上做更广评估,也有助于了解投机解码在不同服务条件下的行为。
另一个方向是研究投机器训练数据如何影响代码、数学、聊天、多语言提示、工具使用和结构化输出等任务上的接受率,为特定工作负载选择或训练草稿 checkpoint 提供更清晰指导。
最后,更细致地剖析草稿生成、目标验证、KV cache 行为、图执行和调度,有助于解释不同目标模型和工作负载之间出现的性能差异。
评论
这些高赞评论把焦点放在 vLLM 对 AMD 的支持质量上:有人庆贺 AMD 终于拿到“一等支持”,也有人认为官方优化太偏心,工作站级 R9700 始终被冷落。投机解码中的验证机制和接受率同样引发追问;另有几条热评借题发挥,分别谈到 LLM 的数学潜力与 DRY 原则的代价。
vLLM 对 AMD 的优化明显偏科,R9700 被遗忘
- 同一块 R9700 在官方 vLLM 下只有 20–30 token/s,Radiance 这类分支却可以跑到 150–200 token/s;因此问题不在硬件,而在开发资源几乎都投向了数据中心卡和 Ryzen AI Halo。工作站用户想用上官方加速,只能继续等。(认可度:高赞) > AMD/vLLM 的大多数工作都围绕数据中心卡或 AI Halo/Ryzen,R9700 AI Pro 被无视,真希望这种情况会变。(@intothemild)
获得 vLLM 的“一等支持”让人高兴,但更想知道接受率能不能比 NVIDIA
- 这条评论承认 AMD 被 vLLM 认真支持是好事,却紧接着把标准拉到实测竞争:同一批模型上,候选 token 的接受率要怎么和 NVIDIA 对齐。社区想看的已经不是“能跑”,而是“跑得有没有可比性”。(认可度:高赞) > 很高兴 AMD 总算在 vLLM 中拿到一等支持;但同样模型上的接受率与 NVIDIA 相比如何?(@quietraster)
目标模型验证候选 token,不等于再把自回归解码跑一遍
- 有读者凭直觉提出关键质疑:如果目标模型要照常做一次自回归解码才能拿到“正确答案”来比对,那么验证阶段就会把投机解码省下的时间全部抵消。这个疑问也顺带扯出对 token 接受率的关注;作者还吐槽了文中 target model 与 target-model 连字符混用的现象。(认可度:高赞) > 要是目标模型必须正常自回归一遍才能验证候选 token,投机解码就白做了;肯定有别的机制。(@flufluflufluuffy)
LLM 的潜力在数学,不在 hype
- 有人想把讨论从工程拉回更根本的层面:剥掉营销性的炒作,只看模型背后的数学,才能清楚看见 LLM 的变革潜力。可惜这类研究目前多发生在产业界,评论者更希望学术界能成为主要推动者。(认可度:高赞) > 丢掉 hype 只看数学,LLM 的潜力其实很明显;但愿这类研究更多来自学术界。(@jeanmichelselli)
DRY 让代码变短,却不等于让系统更简单
- 一条工程经验式的高赞评论指出,同事应用 DRY 时忽略了它可能带来的复杂度:代码确实变小了,但调用关系和认知负担常常上升。这提醒人们,优化指标好看不代表设计更健康。(认可度:高赞) > 同事不假思索地套 DRY,代码是变短了,却往往带来复杂度代价。(@myuzio)
有价值的补充
- 投机解码里的验证不需要目标模型把草稿重新生成一遍。常见的做法是让目标模型对草稿给出的整段候选 token 做一次并行前向,得到每个位置的分布,再与草稿模型比较,用拒绝采样决定接受或丢弃。被保留下来的 token 比例就是“接受率”,也是衡量 AMD 在 vLLM 中能否追平 NVIDIA 的关键指标。