EAGLE 演化史 (1 → 2 → 3) 与 DFlash 的回答
速读卡片 (TL;DR)
EAGLE-1: 把 draft 从 token 级搬到 second-to-top-layer feature 级,再用 shifted token 解 sampling 不确定性 → 70B 上 ~3× speedup。 EAGLE-2: 发现 draft confidence 是 acceptance rate 的良好近似,把静态 binary 树换成 context-aware dynamic draft tree → +20–40% over v1。 EAGLE-3: 拆掉 feature regression loss + 多层特征融合 + training-time test 模拟自身误差 → 在 ShareGPT/UltraChat 上首次出现 draft data scaling law,~6.5× speedup。 DFlash: 直接换掉 draft 范式——用 block diffusion 一次并行画出 γ 个 token,再把 target hidden 注入到 draft 的 KV cache;Qwen3-8B 上 ~6× ,比 EAGLE-3 再快 2.5×。
立场:EAGLE 三代是同一架构在四个维度上的逐步松绑(feature 级 → 动态树 → 多层融合 + 端到端 token 目标);DFlash 把 draft 的"自回归"维度本身砍掉,代表 draft 范式的下一步分叉。
1 · EAGLE-1 (2401.15077): 把 draft 抬到 feature 层
1.1 动机:before EAGLE,draft 模型卡在哪
2023 年的 speculative decoding 已经被 Leviathan 和 Chen 证明是 lossless 的,但工程上很尴尬:
- 找不到合适大小的 draft model。LLaMA2 系列里 7B 给 70B 当 drafter 行得通,但 7B 自己想加速,只能拿 TinyLLaMA(template 不一致)或自己再训一个小模型(3000B token 的预训练成本)。
- Medusa:用 target 的 second-to-top-layer feature f₂ 直接预测 t₄、t₅;但 t₄、t₅ 之间是相互独立的 head,acceptance 大约 0.6 就到顶。
- Lookahead:用 n-gram + Jacobi,完全不带训练,但 acceptance 更低。
EAGLE 的两个核心 observation:
- Feature 级序列比 token 级序列更"规则"。Feature 是连续高维向量,从 f₁ 推 f₂ 的轨迹比从 t₁ 推 t₂ 的离散跳变更平滑,小模型更容易拟合。
- 但 feature autoregression 自带 sampling 不确定性。同一个 fI 后面,sample 出 "am" 和 "always" 会得到完全不同的下一个 feature——这是分布而不是函数。
第二点是 EAGLE 论文里最锋利的洞察。它的解法很便宜:把"领先一个时间步的 token 序列"(即已经采样的 tokens)和 feature 序列一起喂给 draft model,把"未观测到的随机性"显式注入,从而把"由 fI 预测下一个 feature"这件事从 ill-posed 变成 well-posed。
1.2 核心方法
Worked example: "How can" → 一次 draft 步
| 张量 / 量 | 形状 | 含义 |
|---|---|---|
| f_how, f_can | ℝ4096 × 2 | target LLM 第 L−1 层 hidden(LLaMA2-7B) |
| e_can, e_I | ℝ4096 × 2 | embedding,token 已经领先 1 步 |
| concat 后 | (1, 2, 8192) | (bs, seq_len, 2·hidden) |
| FC → decoder layer | (1, 2, 4096) | ~0.3B param,output 即 f̂_I |
| LM head(共享) | (1, 2, |V|) | 取 top-k = 4 → 4 个候选 t_I⁽¹⁾ … t_I⁽⁴⁾ |
| Tree depth | m=5 | 5 次 draft forward → 26-token 静态树 |
1.3 关键结果
训练只用 ShareGPT(≤70k 对话),4×A100 1–2 天就可以训完 70B 的那一份 draft 头。MT-bench T=0:
- Vicuna 7B: 2.90× ; Vicuna 13B: 3.07× ; LLaMA2-Chat 70B: 3.01×
- 0-α(全精确输入下的 acceptance rate)≈ 0.78–0.82,显著高于 Medusa 的 ~0.6
- throughput(单卡 bs 上限)≈ 2× vanilla
1.4 局限(为后两代埋的坑)
- Tree 是静态的二叉树,context 无关。"10+2=" 的下一 token 几乎确定是 1,根本不需要分支;"10+2" 后却需要更宽的尝试。
- Feature 拟合 loss l_fea 是个隐性束缚。它强制 draft model 的输出去逼近 top-layer feature——但这块 feature 只承载"下一个 token"的信息,对"下下个" token 帮助有限。
- 训练数据加倍后基本不再涨。不存在 scaling law。
2 · EAGLE-2 (2406.16858): tree 结构本身要 context-aware
2.1 动机:静态树的两个 cherry-picked 假设
Sequoia / Medusa / EAGLE-1 都显式或隐式假设:draft token 的 acceptance rate 只依赖它在树里的位置。所以"上左多放、下右少放"就是最优 layout。
EAGLE-2 做了一个简单实验:在 Alpaca 上对 Vicuna 7B 跑 EAGLE-1,把每个位置 P1–P6 的 acceptance rate 按 query 画散点。结果是:
- 位置 P1 平均 acceptance ≈ 0.85,P6 ≈ 0.4 — "位置依赖" 部分成立。
- 但同一个位置上,不同 query 的 acceptance 跨越 [0.1, 1.0] — context 才是真正主导因素。
这就让"动态调整 tree 结构"成为必要。但有个鸡生蛋问题:要知道某个 draft token 的 acceptance,得让 target LLM 跑一遍,而 spec decoding 的目标恰恰是少跑 target。
EAGLE-2 的关键发现:EAGLE 的 draft model 是 well-calibrated 的。draft model 输出的 confidence(softmax 概率)与真实 acceptance rate 几乎落在 y=x 的对角线上(confidence 0.05 → accept 0.04;confidence 0.95 → accept 0.98)。所以可以用 confidence 当 acceptance 的代理。
2.2 核心方法:全局 value 排序 + expand/rerank 两阶段
定义节点 ti 的全局 value:
这是从根到 ti 路径上所有 confidence 的乘积,直觉是"只有 ti 的所有祖先全部被 verify 接受,ti 才有机会被检查"。所以局部 confidence 高但深度处于劣势分支的 token,V 自然会变小。
Worked example: "10+2=" vs "10+2"
| Query | 下一 token 顶 confidence | EAGLE-1 行为 | EAGLE-2 行为 |
|---|---|---|---|
| "10+2=" | "1" → 0.99 | 仍然加 2 个候选,第 2 个浪费 | 只扩 1 个候选 "1" |
| "10+2" | "=" → 0.55, " " → 0.30 | 同样加 2 个 | 加 2 个,且都深挖 |
2.3 关键结果
- MT-bench T=0,Vicuna 13B:4.26×(EAGLE-1 是 3.07×),平均接受长度 τ 从 3.98 涨到 4.83。
- HumanEval 上 Vicuna 13B 顶到 4.96×(代码模板高度可预测)。
- 所有 LLaMA3-Instruct 8B / 70B 上 v2 全面超越 v1,gain 在 +20% – +40%。
2.4 与 v1 的差异
关键在于 EAGLE-2 不动 draft model 训练、不动 verification,只重写"怎么从 draft model 多次 forward 的输出里挑出最值得 verify 的 token 集合"。所以一份 EAGLE-1 的 draft checkpoint 直接套上 v2 的 search 逻辑就能拿到收益——这也是 v2 论文反复强调"out-of-the-box"的原因。
§ Tree Verification 详解 — 从 chain 到 tree,再到动态树
这是一节穿插在 EAGLE-2 与 EAGLE-3 之间的"机制深读"。EAGLE-2 把 dynamic tree 推向主流之后,tree verification 已经成为整个 spec decoding 生态(SpecInfer / Medusa / EAGLE / Sequoia / DDTree / Mirror-SD)的共享原语。本节抽出来单独讲清三件事:它是什么、它为什么有用、它怎么设计。后面 EAGLE-3 / DFlash 都会反复引用本节。
A · 什么是 tree verification(对比 chain)
朴素的 speculative decoding 是单链(chain):draft model 串行生成 t̂₁ → t̂₂ → … → t̂_d,然后 target 一次 forward 把这条链 verify 一遍。问题是 acceptance 在深度上是乘性的——令 α 表示 per-token acceptance,d 步链全部通过的概率是 α^d。α=0.7、d=4 已经只剩 0.24,d=8 是 0.058。哪怕你 draft model 训得再好,链越长,"白干"概率指数级上升。
Tree 的思路:既然单条链的"赌注"很容易输,那就同时下多条注,而且让它们共享前缀。在 tree 里,每个 draft 步可以扩出 2、3 或 k 个候选;深度 d 的二叉树有 2^(d+1) − 1 个节点(d=4 是 31),它们的 KV 在共享前缀上是同一份。target 仍然只跑一次 forward,通过精心设计的 attention mask 一次性把整棵树都 verify 出每个节点的"假设我接受了我所有祖先,我自己被接受的概率是多少"。
注意一个常见误解:tree 并不要求 target 跑 31 次。target 永远是一次 forward,只是 input 长度从 4 变成 31。GPU 的瓶颈是 memory-bound decode,从 4 → 31 token 的 self-attention 在 H100 上几乎不增加 wall-clock(因为 KV 已经 cached,只需要算 query × 31 个 key 的点积),代价主要是 31 次 LM head 的 softmax。这正是 spec decoding 把"算力换 latency"的核心交易。
B · Tree attention mask 的具体形态
实现层面,tree verification 把整棵树flatten 成一个序列送入 target,然后用一个特制的 attention mask 强制每个节点只看到自己在树里的祖先(不能看兄弟、不能看堂表亲)。这样每个节点的 hidden state 都等价于"假设这条路径就是真实续写,我应该长成什么样"。
看一个具体的小树。flatten 顺序按 BFS,序列长度 7:
flash-attn 的 variable-length 接口或自定义 block-sparse kernel。前缀 prompt 那部分仍然是 full causal,只在 tree 这 7 个 token 上启用 ancestor-only mask。正确性论证:每个 tree 节点 ti 经过 self-attention 之后得到的 hidden state hi,实际上等价于把"prompt + Path(root, ti)"作为一个普通 causal 序列单独跑出来的结果——因为它能且只能 attend 到自己的祖先。这意味着 target 给出的 logit p_target(· | ti) 在数学上和"如果只 verify 这一条路径"得到的 logit 完全一致。所以 tree 没有引入任何额外近似,后面的 rejection sampling 可以照搬。
C · Tree 下的 lossless 保证
spec decoding 是 lossless 的(target 的 marginal token 分布和它单独 decode 一致),这一点在 chain 上由 rejection sampling 教程 给出严格证明。Tree 的版本只需要把"沿着唯一一条路径做 rejection"换成"沿着当前选中的路径做 rejection,reject 时切到对应 fallback":
这里关键点是每个 tree 节点的"draft 分布"是它父节点位置上的那次 draft forward 给出的(因为兄弟节点共享父亲)。验证算法:
- 从 root 的孩子里挑出 draft 概率最高的一个,运行 rejection 测试。如果通过,推进到这个孩子,继续向下。
- 如果这一层的某个孩子 reject 了,对照同层其它兄弟:把已 reject 节点的 draft 概率从父位置的 target 分布里"扣掉"(得到 residual),再用 residual 对剩下的兄弟做一次试探。这就是 SpecInfer 的 multi-candidate rejection。
- 若整层兄弟都 reject,则在父位置从 residual 分布里 sample 一个 token 作为 bonus,中止。
"longest accepted prefix" 输出规则:对每条 root → leaf 路径都跑这个 sequential rejection,沿着 tree 的连接逐级深入,直到第一次 reject 或到达 leaf。最终 commit 的就是第一次 reject 之前那段路径。注意这条 commit 路径在 tree 里是唯一的,所以输出是良定义的。
为什么这不破坏 marginal?——因为 rejection 在每一层都是独立进行的,而每一层"成功 sample 出的 token"的分布严格等于 ptarget。tree 只是降低了某层全部 reject 的概率(因为同一位置有多个候选,任意一个被接受就不需要 fallback),但 fallback 一旦被触发,它仍是从 residual 分布精确 sample 的。所以 marginal 一致。
D · 效率分析:加速从哪里来,代价是什么
D.1 Acceptance 概率的数学
令 α 为 per-token acceptance(独立同分布近似),d 为深度,w 为最深一层的"路径数"。
- Chain:全程通过 = α^d。期望接受长度 E[L] = (1−α^d) / (1−α) − 1 ≈ α/(1−α) 当 d 足够大。
- Binary tree(depth d, 完全二叉):最深层有 2^d 条路径,但它们彼此并不独立(共享前缀)。粗略上界 P(至少一条路径全过) ≤ 1 − (1 − α^d)^(2^d)。
下表用 α = 0.7 计算:
| 结构 | d=2 | d=4 | d=6 | d=8 |
|---|---|---|---|---|
| Chain α^d | 0.49 | 0.24 | 0.118 | 0.058 |
| Binary tree (1−(1−α^d)^(2^d)) | 0.84 | 0.98 | 0.999 | ~1 |
| 节点数 (2^(d+1)−1) | 7 | 31 | 127 | 511 |
| 期望接受长度(实测,Vicuna-7B) | ~1.7 | ~3.5 | ~4.8 | ~5.2 |
看到关键转折:从 chain 到 binary tree,d=4 时全程通过概率从 0.24 跳到 0.98——但 verify 的输入长度从 4 涨到 31,大约 8× 个 LM head softmax。这交易在memory-bound 的 decode 阶段是赚的(GPU 闲着也是闲着),所以才有 EAGLE-1 的 ~3× speedup。
D.2 成本分解
tree verification 的实际开销有三块:
- Tree-attention 的 attention 计算:N 个节点对 prompt(长度 P)做 cross-attn 是 O(N·P);tree 内部的 self-attn 是 O(N²) 但 mask 很 sparse(总共 O(N · avg_depth))。N 在 30~60 时,这一项几乎被 LM head 盖过。
- LM head + softmax:N · |V| 的矩阵乘。|V|=128k 时 N=60 大约 7.6M FLOPs(其实仍很便宜),但显存带宽是真瓶颈——LM head weight ~0.5GB,要被读 N 次。
- KV cache 内存:每个 tree 节点都要写入它自己的 K/V。N=60、layer=32、hidden=4096、bf16 → 60 × 32 × 4096 × 2 × 2 ≈ 30 MB。乘以 verify 步数,几百兆很常见;对长 prompt 的 server 是不可忽略的开销。如果 verify 失败的节点还把 KV 留下来,KV cache 会被污染——所以实现上必须做 KV rollback(EAGLE 用 page-level cache、Mirror-SD 用 paged-attention)。
D.3 Speedup 分解公式
其中 E[L] 是期望接受长度,t_target 是 target 单步 forward 时间,C_tree(N) 是 N 节点 tree 的额外开销(主要是 LM head 多算 N−1 次)。所以加大 N → E[L] 涨,但 C_tree(N) 也线性涨,直到某个 N* 后边际收益跌破 1。
EAGLE-1 论文的 ablation 显示:N=26(深度 5 的 binary tree)是 7B/13B 上的甜蜜点;EAGLE-2 通过 dynamic rerank 把有效 N 压到 8–16 同时 E[L] 反而提高。N=60 之后 throughput 普遍掉头向下(LM head 已经吃满,带宽打满)。
E · 怎么设计一棵 tree
设计的本质是给定 N(节点预算),怎么放才能最大化 E[L]。N 通常受 verify 的 GPU 显存与 LM head 带宽约束(典型 16–64)。
E.1 Static tree(EAGLE-1 / Medusa / SpecInfer 早期)
固定拓扑,常见的两种:
- 完全二叉树(Medusa, EAGLE-1): 深度 d,节点 2^(d+1)−1。简单但浪费——所有节点的预算分配相同,无视它们的局部 confidence。
- Sequoia-style 优化静态树: 用 offline benchmark 测每个 (depth, branch) 的平均 acceptance,再用动态规划把 N 个节点放到使 E[L] 最大的位置。结果通常是一个"上左密、下右稀"的不规则树。
静态树的根本问题已经在 EAGLE-2 被指出:同一个位置的 acceptance 在不同 query 上跨度很大(0.1–1.0)。静态最优解只是边缘分布意义上的最优,在任何具体 query 上都不会真正最优。
E.2 Dynamic tree(EAGLE-2): expand & rerank
把 tree 的设计拆成两步,关键是用 draft confidence 当 acceptance 的 proxy(2.1 节里讨论过 well-calibrated 性质)。给每个节点定义全局 value:
其中 cj 是 draft model 在节点 j 输出 token 时的 softmax 概率。
Expand 阶段(m 次迭代,m = 树深度):
- 初始化:tree = {root}。
- 循环 m 次:从 frontier(当前最深一层)选 V 最大的 top-k 个节点,送给 draft 模型 forward 一次,生成它们各自的 top-k 子节点;把这些子节点加入 tree。
- 结束时 tree 大概有 1 + k + k² + … + k^m 个节点;但因为 top-k 的剪枝,实际节点数远小于满二叉。
Rerank 阶段:把 tree 里所有节点(含未扩展的浅节点)按 V 排序,取 top-N 作为最终送 verify 的子树。一个关键引理: "按 V 取 top-N 得到的节点集合天然是连通子树"——因为 V 沿着路径单调下降,所以任何被选中的节点的祖先 V 都更大、必然也被选中。
E.3 Worked example: dynamic 扩展一步
假设当前 frontier 有 4 个节点,V 分别为 {0.95, 0.6, 0.3, 0.05}。budget k=2:
- 选 V=0.95 和 V=0.6 两个 expand,丢弃 V=0.3 和 V=0.05(它们的 path 太弱,继续挖也会很快掉到 0.1 以下)。
- 对前者 draft forward,假设 top-2 子 confidence = {0.9, 0.4} → 子节点 V 分别 = {0.95×0.9, 0.95×0.4} = {0.855, 0.38}。
- 对后者 draft forward,top-2 = {0.7, 0.2} → 子 V = {0.42, 0.12}。
- 新 frontier = {0.855, 0.42, 0.38, 0.12}。继续下一轮。
注意 0.42 > 0.38 — 更弱父亲的强孩子 反超 强父亲的弱孩子。这种跨支比较正是静态树做不到的地方。
E.4 Best-first heap(DDTree)
EAGLE-2 是 layer-synchronous 的(一层一层扩);DDTree(参见 16)进一步指出 layer 同步浪费:某些路径明显比其他路径强,应该只它们继续往下挖。DDTree 把 frontier 用 max-heap 维护,每次只 pop V 最大那一个 expand,不管它在哪一层。这相当于一个 best-first search,在 block diffusion drafter 上能拿到比 EAGLE-2 更高的 E[L](因为 diffusion drafter 一次出一整 block,best-first 能跨 block 比较 V)。
E.5 Node-budget 怎么调
实操经验:
- 对 Vicuna-7B / LLaMA-3-8B,N=24–32 是 throughput 拐点。
- 大模型(70B+)上,因为单 forward 时间长,N 可以放到 60+ 还赚。
- Code 任务(HumanEval、CommitMessage)上 acceptance 高,可以减小 N(20 左右),省 verify。
- Reasoning 任务(GSM8K、MATH)上 acceptance 低,需要更大 N;但深度上很难超过 6,因为 confidence 衰减太快。
F · 跨论文对比
| 方法 | Tree topology | Expansion 策略 | Drafter 类型 | 核心 trade-off |
|---|---|---|---|---|
| SpecInfer (Miao 23) | 静态 token tree | 多个独立 SSM,各产一条 chain → 拼成 tree | 多个小 AR draft model | 多模型成本高;tree 不共享前缀 |
| Medusa (Cai 24) | 静态笛卡尔积 tree | K 个独立 head 各 top-k,笛卡尔积 | 多 head,无 AR | head 之间独立 → acceptance 卡 0.6 |
| EAGLE-1 | 静态二叉,深度 5,~26 节点 | 每节点强制扩 2 子 | 1 层 AR drafter on feature | 结构 context-blind |
| EAGLE-2 | 动态子树,N=8–16 经 rerank | Layer-synchronous expand top-k by V | 同 v1,只改 search | 逐层同步,深路径浪费 budget |
| Sequoia (Chen 24) | 静态优化拓扑 | Offline DP 对边缘 acceptance | 独立 small AR | 静态,但优化粒度细 |
| DDTree (2604.12989) | 动态,heap-driven | Best-first by global V | Block diffusion drafter | 需要 frontier heap 实现;diffusion-aware |
| Mirror-SD (2510.13161) | Branch-complete tree | 每层 top-|V'| 全展(V' ≪ V) | Mirror drafter | 显存 / 带宽换 acceptance,extreme |
| DFlash (2602.06036) | 无 tree,平面 block | Block diffusion 一次出 16 token(平行不分支) | 5-layer block diffusion | 放弃 tree 的"多假设"换并行性 |
G · 完整 worked example: 一棵深度 2 的二叉 tree 全流程
假设 prompt = "积分等于",vocab = {一, 二, 三, 五},target 模型 LLaMA-7B-zh,draft 用 EAGLE-1 head。要求构造一棵深度 2、宽度 2 的完整二叉 tree(7 个节点),走完一轮 verify。
Step 1: Draft 第一层
Draft model 拿到 hidden state h_等于 + token "等于",输出 logit;softmax 后 top-2:
- node 1: "一", c₁ = 0.7
- node 2: "五", c₂ = 0.2
(其余 vocab confidence 太低,忽略)
Step 2: Draft 第二层(2 次 forward,batched)
对 node 1("一"),draft model 输入 h_一(由 EAGLE 的特征自回归算出来) + token "一",输出 top-2:
- node 3: "百", c₃ = 0.6 → V₃ = 0.7 × 0.6 = 0.42
- node 4: "千", c₄ = 0.3 → V₄ = 0.7 × 0.3 = 0.21
对 node 2("五"),输出:
- node 5: "百", c₅ = 0.4 → V₅ = 0.2 × 0.4 = 0.08
- node 6: "十", c₆ = 0.5 → V₆ = 0.2 × 0.5 = 0.10
Step 3: Flatten + 构 mask
Tree(BFS 顺序):[root, 一, 五, 百₁, 千, 百₂, 十],节点编号 0..6。Mask 与本节 B 部分给出的 7×7 矩阵相同(只是 token 内容不同)。
Per-node 全局 V: [1.0, 0.7, 0.2, 0.42, 0.21, 0.08, 0.10]。
Step 4: Target 一次 forward
Target 在 prompt + 7 个 tree token 上跑一次,得到每个节点位置的 next-token 分布 p_target(· | path)。具体数值假设:
| 节点 | token | p_draft | p_target(this token | parent path) |
|---|---|---|---|
| 1 | 一 | 0.7 | 0.85 |
| 2 | 五 | 0.2 | 0.05 |
| 3 | 百 | 0.6 | 0.55 |
| 4 | 千 | 0.3 | 0.25 |
| 5 | 百 | 0.4 | 0.30 |
| 6 | 十 | 0.5 | 0.20 |
Step 5: Sequential rejection,跟随最优路径
Layer 1:在 root 这一层,先比较 node 1 和 node 2(它们都对应 root 的 child)。
- 选 V 最大的 node 1("一"):accept w.p. min(1, 0.85/0.7) = 1.0 → 接受,推进到 node 1。
Layer 2:在 node 1 这一层,看它的 children(node 3, node 4)。
- 选 V 最大的 node 3("百"):accept w.p. min(1, 0.55/0.6) = 0.917。假设这次 sample 取 0.7 < 0.917 → 接受,推进到 node 3。
- (若 node 3 reject,会用 residual 试探 node 4,若都失败则在 node 1 位置 fallback sample。)
Step 6: 收尾
到达 node 3(leaf),tree 走完。Commit 的 token 序列 = ["一", "百"](沿 root → 1 → 3 的路径)。再加上 target 在 node 3 位置上 sample 出的bonus token(就是它在 node 3 hidden 上的 next-token 分布 sample)——假设 bonus = "万",最终输出三个 token。
对照:同样深度的 chain 只能尝试 ["一", "百"] 这一条;如果 chain 选错(比如 chain 第一步 sample 了 "五"),就会在第一步直接 reject、commit 0 个 token。Tree 通过同时下两个一层注 + 两个二层注,把"全程通过"概率从 α² ≈ 0.49 推到 ~0.84。一次 verify forward,3 个 commit token,wall-clock 约等于 1 次普通 decode + 30% 的 LM head + tree-attn overhead。
3 · EAGLE-3 (2503.01840): 拆掉 feature loss,引入 training-time test
3.1 动机:为什么 EAGLE 不能"加数据就更快"
到 2024 年下半年,LLaMA 系列每代训练数据都翻倍(LLaMA1: 1T → LLaMA3: 15T)。Yuhui 想:把 EAGLE 的训练数据也从 ShareGPT(~68K)扩到 ShareGPT + UltraChat(~530K),acceptance 应该会涨。
实测结果是个 anti-scaling 曲线:数据从 1× 加到 8×,EAGLE 的 speedup 几乎不动,acceptance 微涨然后饱和。问题出在 EAGLE 的 loss 本质上是两项 lockstep:
l_fea 强行让 draft model 的中间表示逼近 target 的 top-layer feature。这有两个隐性问题:
- top-layer feature 只承载"下一个 token"的信息(LM head 是满秩,所以 logit 对 feature 是单射);它不天然包含"下下个 token"的信息,因此哪怕 l_fea 训得很好,multi-step generation 时也不会变得更好。
- l_fea 这个约束剥夺了 draft model 的内部表达自由——加大数据后,模型本来可以学到更聪明的内部 routing,但被 l_fea 拽着不让走。
第一刀就是砍掉 l_fea。但这又引出第二个问题:Step 2 的输入需要 ft+1,而 train 时给的是 ground-truth f,test 时只有 draft 自己的输出 ât+1——分布不一致,所以 1-α(input 含 1 个估计值时的 acceptance)崩盘到 ~0.2。
解法是training-time test:训练时就让 Step 1 的 draft model 输出 â,然后把 â 而不是 f 喂给 Step 2 训练,模拟推理路径。这是标准的 scheduled sampling 思路在 spec decoding 上的具体化。HASS 论文同期也做了类似改动,但目的不同(HASS 是修 EAGLE 的误差累积,仍保留 l_fea)。
3.2 核心方法:三件套
Worked example: "How can" → "How can I do" 的 3 步 draft
| Step | Draft input | Draft output | Sample |
|---|---|---|---|
| 1 | g_how, g_can(target 给的多层融合) + e_can, e_I | a_I ∈ ℝ⁴⁰⁹⁶ | LM head(a_I) → "do" |
| 2 | g_how, g_can, a_I(注意:不是 g_I,target 还没验) + e_I, e_do | a_do | LM head(a_do) → "it" |
| 3 | ... + a_do + e_it | a_it | "." |
训练时 attention mask 要做"仿造"——同一行 ground-truth 序列要分别和 Step 1 的 a、Step 2 的 a 配对,但跨 step 之间不能互相穿越(否则模型会偷看后面的真值)。Figure 6 的稀疏 mask 用 vector dot 而不是矩阵乘可以省算力。
3.3 关键结果
- MT-bench T=0,Vicuna 13B:5.58×(v2 是 4.26×,v1 是 3.07×)。
- 5 个任务平均,Vicuna 13B 上 EAGLE-3 = 5.51×;LLaMA-Instruct 3.1 8B = 4.44×;LLaMA 3.3 70B = 4.12×;DeepSeek-R1-Distill-LLaMA 8B = 4.16×。
- 训练数据从 1× → 8× ShareGPT,τ 从 4.5 → 6.0,首次出现单调上升曲线(EAGLE-1/2 在 4× 后就饱和)。
- SGLang 集成下 batch size = 64 throughput 提升 38%。
3.4 与 v2 的差异
v2 是推理时的 search 优化(不动训练);v3 是训练目标 + 输入侧的根本重构。一句话总结:v2 教会"怎么挑 token",v3 教会"怎么生成更好的 candidate token"。两者正交,v3 的代码里 dynamic tree 仍照常用。
4 · DFlash (2602.06036): 把 drafter 本身换成 block diffusion
4.1 动机:autoregressive drafting 的天花板
EAGLE-3 已经把 acceptance 推到 ~6 token / cycle,但 DFlash 作者(Zhijian Liu et al. @ UCSD)指出一个被忽视的事实:即使 acceptance 再涨,autoregressive draft 的 T_draft 是线性增长的:
每多猜一个 token,draft 模型就多一次 forward。所以 EAGLE-3 不得不把 draft 模型限制成"1 个 transformer decoder layer",哪怕这意味着模型容量不够;一旦想加深,latency 立刻吃掉收益。整个 spec decoding 的实际 ceiling 卡在 2–3×(reasoning 模型上 EAGLE-3 平均 ~4× 已经是 outlier)。
另一边,2025 年的 dLLM(LLaDA / SDAR / Block Diffusion)展示了一种不同的生成方式:一次 forward 把一个 block 内所有 mask 位置并行解码。问题是 dLLM 端到端 quality 输给 AR 模型,且需要多步去噪。但作为 drafter——只要 verifier 把关,quality 不达标的 token 会被 reject,所以 lossless 仍然成立。
DFlash 的核心赌注:drafting 不需要去和 AR 模型卷质量,只要能在并行性上拿到工程红利就赢。
4.2 核心方法
Worked example: 一轮 draft–verify
| 组件 | 形状 / 数 | 说明 |
|---|---|---|
| Target hidden(5 层) | 5 × ℝ4096(per-token) | 从 layer {4,12,20,28,36} 抽 |
| fused context feature | ℝ4096(per-token) | concat → MLP 投影 |
| Draft layers | 5 layers(消融最优) | EAGLE-3 是 1 layer |
| Block size | 16(Qwen3),10(LLaMA3.1-8B) | 训练 16 推理可降到 8 |
| Draft forward | 1 次 | EAGLE-3 要 5–8 次 sequential |
| Acceptance length τ | ~6.5 (Q3-8B GSM8K) | EAGLE-3 在同模型 ~3.2 |
| End-to-end speedup | 5.15×(GSM8K) | EAGLE-3 同条件 1.99× |
4.3 训练设计的两个细节
- Random anchor sampling:不是均匀切 block,而是从 response 里随机选 anchor token 作为 block 的第一位(对应推理时 verify 通过的 bonus token),其余位置 mask。这样训练的 block 起点分布 ≈ 推理时的 block 起点分布。
- Loss 指数衰减加权:w_k = exp(−(k−1)/γ)。block 内第 k 位的权重逐位下降,因为只要前面位置 reject,后面就被 discard——前面错的代价高,所以早位置 weight 大。
4.4 关键结果
- Qwen3-8B,Transformers backend:5.15× (GSM8K) / 6.08× (MATH-500) / 5.62× (AIME25),EAGLE-3(60 树)对应 2.23×/2.05×/2.05×。
- Acceptance length τ:DFlash ~6.5,EAGLE-3 ~3.7。draft cost 上 5-layer DFlash @ 16 token 比 1-layer EAGLE-3 @ 8 token 还低(Figure 3)。
- 5-layer 是 sweet spot;3-H → 5-H(target 抽 3 层 vs 5 层)acceptance 能再涨一截但训练存储线性增加。
4.5 与 EAGLE-3 的差异
| 维度 | EAGLE-3 | DFlash |
|---|---|---|
| Drafter 类型 | autoregressive,1 层 decoder | block diffusion,3–8 层 bidirectional |
| Draft cost vs γ | 线性 γ·t_step | 近似常数 t_parallel |
| Target hidden 注入位置 | 仅作 input concat | 注入每层的 K/V cache |
| 训练稳定性 | 需要 training-time test 模拟 | 用 anchor sampling 直接对齐 |
| Tree? | dynamic tree(继承自 v2) | 纯 block,没有 tree |
5 · 演化轨迹: 一根线索 + 一次范式分叉
5.1 横向对比
| 维度 | EAGLE-1 | EAGLE-2 | EAGLE-3 | DFlash |
|---|---|---|---|---|
| Feature 输入粒度 | top-layer feature f | 同 v1 | 低/中/高 多层融合 g | 5 层 target hidden 通过 K/V 注入 |
| Draft tree topology | 静态 binary tree (~26 token) | context-aware dynamic + rerank | 继承 v2 dynamic tree | 无树,block diffusion 平面 16 token |
| Loss | l_fea + l_token | 无改动(只动 search) | 仅 l_token | l_token,带位置衰减加权 |
| 训练时模拟测试 | 否 | 否 | training-time test(scheduled sampling 风格) | random anchor + bidirectional mask |
| Drafter 架构 | 1 decoder + FC | 同 v1 | 1 decoder + 3k→k FC | 3–8 bidirectional layers |
| Drafter forward 数 / cycle | m 次 (m=5) | m 次 + rerank 后 m 减小 | m 次 | 1 次 / block |
| 是否 lossless | 是(保证 verifier 分布) | 是 | 是 | 是 |
| 典型 speedup | ~3.0× | ~4.2× | ~5.5× | ~6.0×(再 ÷ EAGLE-3 ≈ 2.5×) |
| 生态位置 | 开 feature-level autoregression 先河 | tree search 最佳实践 | data scaling 路径 | draft 范式分叉(non-AR drafter) |
5.2 一句话总结每代
- EAGLE-1(2024.01): "drafter 应该在 feature 层做自回归,且 sampling 不确定性必须用 shifted token 显式注入。"
- EAGLE-2(2024.06): "draft model 的 confidence 是 well-calibrated 的,所以 tree 应该 context-aware 地动态扩展。"
- EAGLE-3(2025.03): "feature regression loss 是隐性桎梏,拆掉它,加多层融合 + training-time test,就能解锁 draft 的 data scaling law。"
- DFlash(2026.02): "drafter 没必要继续 AR;block diffusion 把 T_draft 从 γ·t_step 压成 t_parallel,target hidden 通过 K/V 注入持续指导深 draft;ceiling 从 ~3× 推到 ~6×。"
5.3 趋势解读
EAGLE 三代是同一根线索的逐步松绑。v1 把 draft 抬到 feature 层用 shifted token 解决了"sampling 随机性引入的输入歧义";v2 注意到"verify 的搜索空间也应该 context-aware";v3 终于回头审视"我们当初为什么用 l_fea"并决定把它砍掉。每一代都解决了上一代的某个被掩盖的瓶颈,但结构上都属于"AR drafter + tree verify"这个家族。
DFlash 不在这条线上。它做的是范式分叉:既然 drafter 的核心问题是"AR 限制了 γ 和 model depth 之间的非耦合空间",那么换一个一次性并行解码 block 的生成模型(block diffusion),就能让 draft 模型变深(5 层而不是 1 层)而不付 latency 代价。注意 DFlash 仍然沿用了 EAGLE 系列的核心遗产——target hidden 注入(EAGLE-3 多层融合的演化版本)、共享 LM head、verifier 端 lossless rejection——它只换了"如何并行画出 γ 个候选 token"。
从工程角度看,接下来几个月可能的分叉点:
- DFlash 的 block 要不要内嵌 tree(同 token 多 candidate)?目前 DFlash 没有 tree,这与 EAGLE-2 的发现"context 决定 acceptance"看起来没冲突——block 内 bidirectional 已经是某种"软 tree"了。但 verify 层仍可重 rank 多 candidate。
- EAGLE-3 的 training-time test 如果搬到 DFlash,会不会再涨一截?DFlash 用 random anchor + bidirectional 隐式做了类似的事情,但没在 multi-cycle 之间显式模拟。
- DFlash 在大 batch 上的扩展性还没充分验证(EAGLE-3 在 SGLang B=64 时 throughput +38%,DFlash 论文 Table 2 显示 LLaMA3.1-8B 在 concurrency 提高时 EAGLE-3(60) 已经 sub-1× 而 DFlash 还能保持 1.4× — 这个对比点值得后续盯)。