vLLM EAGLE 投机解码实战:EAGLE/EAGLE3 草稿模型配置、源码实现与接受率评测
本文以 vLLM 的 EAGLE 系列投机解码(speculative decoding)为主线,覆盖 Eagle Drafter 与 Eagle3 Drafter 两种模式的完整配置方法、speculative_config 各参数含义,并结合仓库源码深入剖析 EAGLE 草稿头的架构命名、Proposer 调用链与"embedding + 目标模型隐状态"融合的前向实现,最后讲解如何利用仓库内置离线脚本提取请求级接受率(acceptance rate)来量化加速效果。读完后,你可以直接在生产环境配置 EAGLE 投机解码,并具备验证其无损性与性能收益的完整手段。
EAGLE 投机解码在 vLLM 中的定位
vLLM 官方文档将投机解码定位为降低中低 QPS(queries per second)、内存受限(memory-bound)场景下逐 token 延迟的手段(见 docs/features/speculative_decoding/README.md)。在官方提供的方法谱系中,EAGLE 属于"基于模型的"方法:
模型基方法(EAGLE、MTP、draft model、PARD、MLP)能带来最好的延迟收益;而 n-gram、suffix 等轻量方法在高负载下不增加额外开销,只提供中等加速。
EAGLE 的核心思想(Extrapolation Algorithm for Greater Language-model Efficiency)是训练一个轻量草稿头(drafter),它以目标模型上一轮的隐藏状态(hidden states)加上 token embedding 作为输入,极低成本地"外推"出后续若干 token 的候选,再由目标模型一次性并行验证。由于草稿头只有一到两层 Transformer 层、参数量远小于目标模型,在中低负载下能显著摊薄每步解码的延迟。
vLLM 支持两个 EAGLE 变体,通过 speculative_config 中的 method 字段切换:
| method | 说明 | 草稿头架构前缀 |
|---|---|---|
eagle |
第一代 EAGLE,输入为 token embedding 与目标模型隐状态的拼接 | Eagle{原架构},如 EagleLlamaForCausalLM |
eagle3 |
EAGLE-3,额外利用多层隐状态 | Eagle3{原架构},如 Eagle3LlamaForCausalLM |
架构命名规则直接由 EAGLE 配置类在加载草稿模型时改写,见 vllm/transformers_utils/configs/eagle.py:method="eagle" 时把目标架构名加上 Eagle 前缀;method="eagle3" 时加 Eagle3 前缀(若架构名已含前缀则保持不变)。同一个 EAGLEConfig 还承载 truncated_vocab_size 等字段,用于控制草稿头输出层词表规模。
Eagle Drafter 配置示例
以下是最典型的 EAGLE 离线推理配置:目标模型为 Llama 3 8B Instruct(4 卡张量并行),草稿模型使用社区预训练好的 EAGLE 头,独立使用 1 卡张量并行,每步投机 2 个 token:
from vllm import LLM, SamplingParams
prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(
model="meta-llama/Meta-Llama-3-8B-Instruct",
tensor_parallel_size=4,
speculative_config={
"model": "yuhuili/EAGLE-LLaMA3-Instruct-8B",
"draft_tensor_parallel_size": 1,
"num_speculative_tokens": 2,
"method": "eagle",
},
)
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
参数逐项解读:
| 键 | 取值 | 含义 |
|---|---|---|
model |
yuhuili/EAGLE-LLaMA3-Instruct-8B |
草稿头(EAGLE 头)的标识符,可以是 Hugging Face 仓库名或本地路径 |
draft_tensor_parallel_size |
1 |
草稿模型自身的张量并行度,与目标模型的 tensor_parallel_size=4 相互独立 |
num_speculative_tokens |
2 |
每步由草稿头提出并等待目标模型验证的 token 数 |
method |
eagle |
显式指定投机方法;省略时 vLLM 会尝试从草稿模型信息推断 |
关于 method 的自动推断:从 vllm/config/speculative.py 的源码结构看,当未显式给出 method 时,vLLM 会检查草稿模型配置与模型名,例如草稿模型名中包含 eagle3 字样时会自动归一化为 method="eagle3"。因此显式写出 method 是最稳妥的做法,可以避免歧义。
EAGLE 方法额外支持两个开关(在 examples/features/speculative_decoding/spec_decode_offline.py 中均有对应命令行入口):
disable_padded_drafter_batch(默认false):控制草稿批次是否做 padding 对齐;parallel_drafting(默认false):并行草稿生成,官方文档注明仅与 EAGLE 及 draft_model 类方法兼容。从 vllm/config/speculative.py 的内部方法表中可以看到,eagle3对应原生 EAGLE3 不支持并行草稿,而 P-EAGLE 变体支持(草稿深度为 K-1)。
Eagle3 Drafter 配置示例
Eagle3 Drafter 与 Eagle Drafter 的配置方式一致,区别在 method 字段与草稿模型本身:
from vllm import LLM, SamplingParams
prompts = ["The future of AI is"]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(
model="meta-llama/Meta-Llama-3-8B-Instruct",
tensor_parallel_size=2,
speculative_config={
"model": "RedHatAI/Llama-3.1-8B-Instruct-speculator.eagle3",
"draft_tensor_parallel_size": 2,
"num_speculative_tokens": 2,
"method": "eagle3",
},
)
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
与上文示例相比:method 改为 eagle3,草稿模型换成 EAGLE-3 训练的 speculator 权重,且示例中草稿模型与目标模型同用 2 卡张量并行。由于 EAGLE-3 草稿头本身容量更大、对多层隐状态建模,仓库中自带的回归基线(见下文)也显示其平均接受长度高于经典 EAGLE。
源码剖析:EAGLE 草稿头如何工作
Proposer 入口
vLLM V1 引擎中 EAGLE 的提案逻辑由 vllm/v1/spec_decode/eagle.py 中的 EagleProposer 承担,它继承自通用基类 SpecDecodeBaseProposer(vllm/v1/spec_decode/llm_base_proposer.py),构造时的关键参数是 pass_hidden_states_to_model=True——这正体现了 EAGLE 的数据流:每个解码步,目标模型的前向会顺带产出隐状态,Proposer 将这些隐状态直接喂给草稿头,而不是让草稿头从零开始重算。
草稿头前向:embedding 与隐状态的融合
以 Llama 家族的 EAGLE 头实现 vllm/model_executor/models/llama_eagle.py 为例,可以看到三个标志性结构:
-
融合层
fc(L110-L118):一个ReplicatedLinear,输入维度为hidden_size * 2、输出为hidden_size,把 token embedding 与上一轮隐状态在最后一维拼接后投影到单一隐状态空间:self.fc = ReplicatedLinear( input_size=self.config.hidden_size * 2, output_size=self.config.hidden_size, bias=False, ... )前向(L123-L130)为
hidden_states = self.fc(torch.cat((input_embeds, hidden_states), dim=-1)),之后再进入一层(或少数几层)标准LlamaDecoderLayer。 -
跳过首层 input_layernorm(L43-L57):EAGLE 论文实现中第一层不做输入归一化,源码用
nn.Identity()替换并附原始实现引用注释,这是 EAGLE 草稿头与"普通小模型"的关键差异之一。 -
权重名映射(L64-L75):通过
WeightsMapper把 HuggingFace 风格的q_proj/k_proj/v_proj、gate_proj/up_proj映射到 vLLM 融合后的qkv_proj、gate_up_proj,保证社区发布的 EAGLE 权重可以直接加载。
仓库为不同目标模型家族都提供了对应的 EAGLE 头实现,例如 llama_eagle.py、llama_eagle3.py、deepseek_eagle.py、deepseek_eagle3.py、qwen3_eagle3.py、mistral_eagle.py、llama4_eagle.py、cohere_eagle.py 等。选择草稿模型时应确认其对应的目标模型家族已有对应实现。
无损性保障
官方 README(docs/features/speculative_decoding/README.md)给出了三点无损性论述:理论上投机解码采样在硬件浮点精度内无损;算法层面 vLLM 用 tests/samplers/test_rejection_sampler.py 验证拒绝采样收敛到目标分布,并用 tests/v1/spec_decode 下的端到端断言验证"greedy + 投机解码 == greedy";同时明确 logprob 不保证跨运行稳定。因此 EAGLE 配置下开启投机不会改变(greedy 场景)生成结果本身,只影响延迟。
预训练 EAGLE 草稿模型的选择
无需自己训练,Hugging Face 上已有两类主流社区维护的 EAGLE 草稿模型集合(按名称在 HF Hub 检索即可):
- RedHatAI/speculator-models 集合:覆盖 Llama、Mistral 等主流家族的 speculator/EAGLE 头;
- yuhuili 发布的 EAGLE 系列:如
yuhuili/EAGLE-LLaMA3-Instruct-8B、yuhuili/EAGLE3-LLaMA3.1-Instruct-8B。
选择草稿头的基本原则:
- 与目标模型严格对应:EAGLE 头是在特定目标模型上训练的,词表、层结构都绑定该模型家族;
- 方法匹配:EAGLE-3 训练的权重必须用
method="eagle3"加载,经典 EAGLE 权重用method="eagle"; - 版本注意:官方文档保留了历史提醒——若使用较老版本的 vLLM(0.7.0 之前),需先运行官方提供的转换脚本改造 EAGLE 权重目录,再把改造后的本地路径填入
model。当前仓库版本已内置完整加载路径,按上述配置直接加载即可。
离线评测:提取请求级接受率
仓库自带了一个功能完整的离线评测脚本 examples/features/speculative_decoding/spec_decode_offline.py,它正是 eagle.md 文档指向的"更详细的离线示例"。核心用法:
python examples/features/speculative_decoding/spec_decode_offline.py \
--method eagle \
--eagle-dir yuhuili/EAGLE-LLaMA3.1-Instruct-8B \
--num-spec-tokens 3 \
--tp 1 \
--dataset-name hf \
--dataset-path philschmid/mt-bench \
--num-prompts 80 \
--enable-chunked-prefill \
--print-output
脚本关键参数(见 L43-L76 的解析逻辑):
--method:支持ngram、eagle、eagle3、mtp、draft_model五种方法;--eagle-dir:EAGLE 头路径;不指定时默认yuhuili/EAGLE-LLaMA3.1-Instruct-8B(eagle)或yuhuili/EAGLE3-LLaMA3.1-Instruct-8B(eagle3),见 L109-L115;--num-spec-tokens:投机深度;--tp:目标模型并行度;--parallel-drafting/--disable-padded-drafter-batch:对应上文两个 EAGLE 开关;--print-output:打印逐条 prompt 的生成文本。
脚本的亮点是请求级接受率统计(L185-L221):通过 llm.get_metrics() 读取四个指标——
vllm:spec_decode_num_drafts(草稿轮次数)vllm:spec_decode_num_draft_tokens(总草稿 token 数)vllm:spec_decode_num_accepted_tokens(被目标模型接受的 token 数)vllm:spec_decode_num_accepted_tokens_per_pos(各位置接受数向量)
并据此输出平均接受长度(mean acceptance length)以及每个投机位置上的接受率,可以直观判断"加深 num_speculative_tokens 是否还划算"——越靠后的位置接受率衰减越快。
脚本还内置 --test 回归模式(L232-L259):在 1xH100、80 条 mt-bench prompt、num_spec_tokens=3、greedy 采样条件下,断言平均接受长度落在期望值 2% 误差内——eagle 方法期望约 2.296,eagle3 方法期望约 2.811。这一内置基线也佐证了 EAGLE-3 草稿头的接受长度显著优于经典 EAGLE,可作为你本地环境性能回归的参照锚点(实际数值随硬件与批次构成浮动)。
在线服务:--speculative-config JSON 配置
除 Python API 外,vllm serve 同样接受 JSON 形式的投机配置,两个键位与 Python 字典完全一致(docs/features/speculative_decoding/README.md 的 schema 说明):
vllm serve meta-llama/Meta-Llama-3-8B-Instruct \
--tensor-parallel-size 4 \
--speculative-config '{
"method": "eagle",
"model": "yuhuili/EAGLE-LLaMA3-Instruct-8B",
"draft_tensor_parallel_size": 1,
"num_speculative_tokens": 2
}'
几条容易踩坑的注意事项(同样来自官方 README 的 Notes 部分):
tensor_parallel_size不是speculative_config的合法键,草稿模型并行必须写draft_tensor_parallel_size;temperature、top_p属于采样参数,不放进投机配置;target_model_config、draft_parallel_config等内部字段由 vLLM 自动填充,用户不应设置;- 已知不兼容:截至 0.15.0 版本,流水线并行(pipeline parallelism)与投机解码不可组合使用。
小结
- vLLM 中启用 EAGLE 投机解码只需在
speculative_config中给出四个核心键:method(eagle/eagle3)、model(草稿头)、num_speculative_tokens、draft_tensor_parallel_size; - 源码层面,EAGLE 草稿头以"token embedding + 目标模型隐状态"拼接过
fc投影层进入少量 Transformer 层(vllm/model_executor/models/llama_eagle.py),由 vllm/v1/spec_decode/eagle.py 的EagleProposer在每个解码步消费目标模型透传的隐状态完成提案; - 用 examples/features/speculative_decoding/spec_decode_offline.py 提取逐位置接受率,可以定量指导
num_speculative_tokens的取值,并利用内置--test基线(eagle ≈ 2.296 / eagle3 ≈ 2.811 平均接受长度)做回归验证; - 选择草稿头时确认目标模型家族匹配、方法变体一致,并优先使用社区针对你的目标模型训练过的 EAGLE 权重。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00