Files
xserv/docs/25-speculative-methods-comparison.md
Gahow Wang 6485c87c5b docs: Phase 25 — three speculative-decoding paradigms compared
Contrast Small-Model (Phase 22-24, done), EAGLE3 (this phase's target),
and Multi-Token Prediction (DeepSeek-V3 style, not applicable here).

Includes the actual EAGLE3-Qwen3-8B weight tensor listing pulled from
AngelSlim/Qwen3-8B_eagle3 on dash5:
- 1 midlayer (attention + mlp) with hidden_size=4096
- fc.weight (4096, 12288) fusing 3 target hidden-state levels
- q_proj (4096, 8192) taking concat(embed, fused_h) as input
- lm_head only over draft_vocab_size=32000, mapped back with d2t table
- ~750 MB total (vs 1.2 GB for Qwen3-0.6B), draft cost ~1/10 of target

Also captures Qwen3-8B + EAGLE3 speedup benchmark on vLLM: ~1.97-2.02x
across MT-bench/HumanEval/GSM8K/Alpaca. That's the number to beat.

Next commits will implement Eagle3Head in xserv-model + hook target
hidden states out of Qwen3::decode_core.
2026-07-01 16:53:37 +08:00

301 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 25: 三种投机解码方法对比 — Small Model / EAGLE / MTP
> 目标:把 speculative decoding 三种主流范式(本项目已试过一种,另两种未实现)
> 讲清楚,并把 EAGLE3-Qwen3-8B 的实际权重结构展开来看。
## 1. 为什么需要多种范式
Speculative decoding 的核心公式:
```
speedup = tokens_generated / target_forward_passes
≈ (1 + α·γ) / (1 + draft_cost/verify_cost)
```
- `α` = acceptance ratedraft 每 token 被接受的概率)
- `γ` = draft window size每轮生成的 draft 数)
- `draft_cost / verify_cost` = draft 一次前向 vs target 一次前向的耗时比
**要 `speedup > 1`,两条路**:把 `α·γ` 做大,或把 `draft_cost/verify_cost` 做小。
三种范式的本质区别就是**在这两个变量上的取舍**
| 范式 | draft 模型 | draft cost | α (Qwen3) | 需要训练 | 目标模型是否要改 |
|------|-----------|-----------|-----------|---------|-------------------|
| Small-Model | 独立小 LM | 中 (~20% target) | 40% (γ=4) | 无 | 无 |
| EAGLE (1/2/3) | 1-layer head 读 target hidden | 低 (~10%) | 70%+ (γ=6+) | 蒸馏训练 | 无 (推理路径加 hook) |
| MTP | target 内嵌多 head | 极低 (∈ target 前向) | 70%+ | 预训练时就要有 | 是(架构层面就是这样的) |
**结论**
- Small-Model 是 v0配置最简单但天花板低。
- **EAGLE3 是当前性价比最高的落地方案**draft cost 极低,α 高,需要一次蒸馏训练(约 100k tokens 数据),但对目标模型无侵入。
- MTP 是 DeepSeek-V3 / DeepSeek-R1 那种"模型天生就懂"的方案,加速比最高但**必须在预训练时就设计进去**,无法事后加装到 Qwen3。
---
## 2. Small-Model Speculative本项目 Phase 22-24 已实现)
### 结构
- **Draft**: 独立的、小得多的同族 LM。要求**tokenizer 完全一致**vocab 也一致)。
- **Verify**: target 用 batched forward 一次算 γ 个位置的 logits从左往右比较
`draft_tokens[i] == target_argmax[i]`,接受最长匹配前缀。
### 算法伪代码
```python
for _ in gen_tokens:
round_start = len(committed)
# 1. draft γ steps
draft_tokens = [draft.decode(prev) for _ in range(gamma)]
# 2. target verify all γ positions in one forward
verify_logits = target.forward(committed + draft_tokens[:γ])
# 3. accept longest matching prefix
accepted = 0
while accepted < γ and draft_tokens[accepted] == argmax(verify_logits[accepted-1]):
accepted += 1
# 4. correction: use target's answer as the next token
correction = argmax(verify_logits[accepted-1] if accepted>0 else prev_target_logits)
committed.extend(draft_tokens[:accepted] + [correction])
```
### 优点
- **零训练**。任何同 tokenizer 的两个 LM 组合都能跑。
- 语义正确性直接保证:只要 accept 逻辑严格,输出等价于纯 target greedy。
- 代码简单,是理解 speculative decoding 最好的教学入口。
### 缺点(本项目实测在 dash5 上)
Qwen3-0.6B / Qwen3-8B 组合:
| γ | acceptance | speedup_e2e |
|---|---|---|
| 1 | 66.5% | 0.57× |
| 4 | 40.3% | 0.49× |
| 8 | 25.1% | 0.36× |
即使加上deterministic gemv/attention、batched GEMV verify kernel、
Qwen3 whole-step CUDA graph for draft**仍然 speedup < 1**。
根本原因两点
1. **Draft 太贵**0.6B 一次 decode ~ 2.5 mstarget 8B ~ 12 ms draft/verify 20%。
γ=4 draft 4×2.5=10 ms 单独就占了 verify (13 ms) 77%。
2. **Draft 太蠢**只用 next-token cross-entropy 训练的独立小模型
target top-1 一致率不高α 快速衰减γ=4 40%)。
理论上限假设 verify 免费`speedup ≤ (1 + α·γ) ≈ 2.6×`
实际上 verify 花掉了绝大部分预算跑到 0.5× 就到头了
### 什么时候能赢?
只有当 `draft_cost / verify_cost < acceptance_rate` 时才可能 >1×
Qwen3-0.6B 的 draft_cost 太高,需要 draft 是 target 的 **~1/40** 才行
8B target 需要 ~200M draft。Qwen3 没有官方 200M 的成员。
---
## 3. EAGLE3本 Phase 要做的方案)
### 3.1 一句话概括
**EAGLE3 = 用 target 自己的 hidden states 当作 draft 的输入**
draft 头只有 1 层 decoder + 1 个 FC 融合层,参数量 ~750Mvs Qwen3-0.6B 的 1.2 GB
且更重要的是draft 前向**不需要重跑 embedding、不需要多层 attention 累积**
成本大约是 target 一次 decode 的 **~1/10**。
### 3.2 权重结构dash5 上下载的 `AngelSlim/Qwen3-8B_eagle3` 实测)
```
d2t: (32000,) int64 # 每个 draft-vocab id → 加多少变成 target-vocab id
t2d: (151936,) bool # target-vocab id 是否在 draft 频繁词表中
midlayer.self_attn.q_proj.weight: (4096, 8192) bf16
midlayer.self_attn.k_proj.weight: (1024, 8192) bf16
midlayer.self_attn.v_proj.weight: (1024, 8192) bf16
midlayer.self_attn.o_proj.weight: (4096, 4096) bf16
midlayer.mlp.gate_proj.weight: (12288, 4096) bf16
midlayer.mlp.up_proj.weight: (12288, 4096) bf16
midlayer.mlp.down_proj.weight: (4096, 12288) bf16
midlayer.hidden_norm.weight: (4096,) bf16 # 融合特征的 pre-attn norm
midlayer.input_layernorm.weight: (4096,) bf16 # draft 嵌入的 pre-attn norm
midlayer.post_attention_layernorm.weight: (4096,) bf16
norm.weight: (4096,) bf16
fc.weight: (4096, 12288) bf16 # 3×hidden → hidden fusion
lm_head.weight: (32000, 4096) bf16 # 输出 draft-vocab
```
**关键观察**
- `fc.weight (4096, 12288)`**输入是 target 三个不同层的 hidden state 拼起来**
low + mid + high level一次 FC 融合成 EAGLE 内部的 hidden dim。这是 EAGLE3
跟 EAGLE1/2 最大的区别(前两代只用 target 最后一层)。
- `q_proj.weight (4096, 8192)`**8192 = 4096 × 2**。attention 输入是
`concat(embed(draft_token), fused_target_hidden)`,两个 4096 拼起来。
也就是每次预测下一个 token 时,"prompt" 是"上一个 draft token 的 embedding"
+"target 对上一个位置的隐状态"。
- `lm_head.weight (32000, 4096)`**只输出 32000 个高频 token**vs target 的 151936
预测出的 draft-vocab id 用 `d2t` 表查得到真实 target-vocab id
`real_id = draft_id + d2t[draft_id]`。这一步把 lm_head 从 622 MB 压到 131 MB。
### 3.3 推理时的数据流
```
target 前向(正常执行):
tokens t_0..t_n
→ embed → layer0 → layer1 → ... → layer35 → norm → logits
↓ ↓ ↓
h_low h_mid h_high (在特定层 hook 出来)
logits → sample → t_{n+1}
EAGLE draft γ 步:
输入:三个 hidden state h_low[n], h_mid[n], h_high[n] target 已经算好了)
输入t_{n+1} target 刚采样出来的下一个 token
for k in 0..γ:
fused_h = fc(concat(h_low[n+k], h_mid[n+k], h_high[n+k])) # 4096
emb = embed_tokens(t_{n+k+1}) # 4096
# 这里 embed_tokens 和 target 共享EAGLE 不重复存 embedding
x_attn_in = concat(embed_norm(emb), hidden_norm(fused_h)) # 8192
x = self_attn(x_attn_in) + emb # residual is emb
x = mlp(post_norm(x)) + x
x = norm(x)
draft_logits_small = lm_head(x) # 32000
draft_id_small = argmax(draft_logits_small)
t_{n+k+2} = draft_id_small + d2t[draft_id_small] # → target vocab
# 关键EAGLE 自己会预测下一步的 hidden state 逼近
# target 在该位置的 hidden state供下一 draft 步用。
h_low[n+k+1] = h_mid[n+k+1] = h_high[n+k+1] = x
```
**为什么快?**
1. 只有 1 层 decodervs Qwen3-0.6B 的 28 层)。
2. 每步计算量 = `attn(hidden=4096, kv_heads=8) + mlp(intermediate=12288) + lm_head(V=32000)`
≈ 1 层 Qwen3-8B decoder + 一个小 lm_head。整个 draft 步 ≈ target 单层 forward + 半个 lm_head
远小于 target 完整 forward。
3. Draft 的 KV cache 也只有 1 层vs 28 或 36
4. Embedding 表复用 target 的(不重复算)。
**Acceptance rate 高的原因**draft 直接使用了 target 的隐状态,
不是"用另一个小模型独立猜"α 通常 ≥70%。
### 3.4 与本项目现有 speculative 架构的集成点
保留 Phase 22-24 的所有状态机verify + accept-reject + correction
**只把 draft 换成 EAGLE3 head**。API 契约:
```rust
// 现在 (Qwen3 draft)
let draft_logits = draft_decoder.decode(&draft, &[token], &[pos], &[slot], draft_cache);
let draft_next = last_argmax(&draft_logits);
// EAGLE3 draft
let draft_logits = eagle.step(&target_hidden_low, &target_hidden_mid, &target_hidden_high, token, pos);
let draft_next_small = last_argmax(&draft_logits);
let draft_next = draft_next_small + eagle.d2t[draft_next_small as usize];
```
**新增到 xserv 的东西**
1. Target 侧:改造 `Qwen3::decode_core` 让它在特定 3 层(比如 1/3、2/3、末层的
`post_attention_layernorm` 之后)把 hidden state export 出来。
2. 新模块 `eagle3.rs`:加载 `AngelSlim/Qwen3-8B_eagle3` 权重,暴露 `step()` 方法。
3. `bench-speculative` 增加 `--drafter eagle3` 分支draft 改用 EAGLE head。
**不变的东西**verify path、accept-reject 逻辑、near-tie fallback、CUDA graph
框架、matched=true 的正确性验证。
### 3.5 Acceptance 上限
按 EAGLE3 paper 的报告Qwen3-8B 上 γ=6 acceptance ≈ 0.75speedup 通常 2-3×
本项目实测目标:`speedup_e2e > 1` 是保底,`> 2` 是 stretch goal。
---
## 4. Multi-Token Prediction (MTP)
### 4.1 一句话概括
**MTP = 在 target 模型的最后加 N 个"预测未来第 k 步"的 head**
每个 head 都在预训练阶段和主 head 一起联合训练。推理时这些 head 天然可以并行
生成 γ 个 draft然后主 head 一次前向验证。
代表实现:**DeepSeek-V3/R1、Meta MTP 论文Gloeckle et al., 2024**。
### 4.2 架构
DeepSeek-V3 的做法:
```
[ target 主 decoder61 层 ]
final hidden h (2048)
/ \
main_head MTP_head_1
(predict t_{n+1}) (predict t_{n+2}
given h and t_{n+1})
```
- 每个 MTP_head 是**一个完整的 transformer block** + linear head含 embedding
proj + attention + MLP
- 训练时MTP_head_k 的 target 是 `t_{n+k+1}`loss 加权求和DeepSeek-V3 训练时权重 0.3)。
- 推理时main_head 得到 `t_{n+1}` 后,用 MTP_head_1 得到 `t_{n+2}`(作为 draft
可以级联 MTP_head_2 得到 `t_{n+3}`……然后 target 主前向一次性验证。
**DeepSeek-V3 论文**arxiv 2412.19437)报告:
- MTP module 1 层depth=1参数占总模型 ~2%。
- MTP accept rate ≈ 85-90%。
- 端到端 tps 提升 1.8×。
### 4.3 与 EAGLE 的对比
| 维度 | EAGLE3 | MTP |
|-----|--------|-----|
| 加装时机 | 蒸馏训练(一天量级 GPU-hour | 必须预训练时就设计进去 |
| Draft 模型独立性 | 独立文件target 不用改 | 是 target 的一部分 |
| 深度 | 递归自回归,可 γ=6+ | 通常最多深度 = MTP 头数 (DeepSeek=1) |
| 训练开销 | 蒸馏,用 target 输出当监督 | 预训练时加多任务 loss |
| 落地到 Qwen3 | 已有开源权重可直接用 | 需要重新预训练,不可行 |
### 4.4 为什么我们不做 MTP
- Qwen3-8B 没有预训练的 MTP head。要 MTP 就得**自己重新预训练 Qwen3**,不现实。
- 若要用现成 MTP只能换到 DeepSeek-V3 这种自带 MTP 的模型;那对整个 xserv 目标
(Qwen3 + gpt-oss serving) 是绕道。
---
## 5. 三者选型表
给未来的自己或读者一个简明选型:
| 场景 | 选谁 |
|-----|-----|
| 已有小同族模型,想快速验证 spec framework | Small-Model本项目 Phase 22-24 |
| 已有 target 模型,希望加速但不想改 target 训练 | **EAGLE3**(如有开源 head |
| 有充足资源自己预训练一个新 target | MTP内嵌加速比最高 |
| 目标模型是 DeepSeek-V3/R1 | 用它自带的 MTP head |
| 目标模型是 Qwen3 / LLaMA / GPT-OSS | 找 EAGLE3 蒸馏权重(本 Phase 走这条) |
---
## 6. 本 Phase 的实施计划
1. **写这份文档**(正在做)。
2. **`xserv-model` 新增 `eagle3.rs`**:定义 `Eagle3Head` 结构,加载
`AngelSlim/Qwen3-8B_eagle3` 权重。
3. **修改 `Qwen3::decode_core`**:在 3 个位置 hook hidden state用 usize const
`EAGLE_LOW_LAYER`, `EAGLE_MID_LAYER`, `EAGLE_HIGH_LAYER`;对 36 层默认 12/24/35
返回值改成 `(Tensor, Option<[Tensor; 3]>)`,第二个 tuple 只在开启 eagle 时填。
4. **新增 `Eagle3Head::step(hidden_states, token, pos) -> Tensor`**:一层 attention+
MLP + lm_head输出 draft-vocab logitscaller 做 d2t 映射。EAGLE 自己也有
一个 1-层的 KV cache每轮 spec 结束时清空)。
5. **`bench-speculative``--drafter [qwen3|eagle3]` 开关**。EAGLE 分支复用现有
verify+accept 逻辑,只替换 draft 环节。
6. **γ 扫**:预期 γ=6 时 acceptance > 0.7、speedup_e2e > 1.5×。
## Sources
- EAGLE-3 paper (arxiv 2503.01840): "Scaling up Inference Acceleration of Large Language Models via Training-time Test"
- SafeAILab/EAGLE GitHub: reference implementation
- AngelSlim/Qwen3-8B_eagle3 on ModelScope/HuggingFace: pre-trained head we're using
- DeepSeek-V3 Technical Report (arxiv 2412.19437): MTP architecture
- Gloeckle et al. 2024 "Better & Faster Large Language Models via Multi-token Prediction"