FastMTP: Accelerating LLM Inference with Enhanced Multi-Token Prediction

Tencent BAC · Yuxuan Cai et al. · 2025-09-16 · arXiv:2509.18362
关键词: speculative decoding · MTP · self-distillation · shared-weight head · vocabulary compression · EAGLE-style verify
代码: github.com/Tencent-BAC/FastMTP · 模型: huggingface.co/TencentBAC/FastMTP

速读卡片 (TL;DR)

一句话: 把 DeepSeek-V3 那种"多个独立 MTP 模块"换成单个 position-shared 权重的 MTP head,在 self-distilled 数据上 fine-tune,让它学会被递归复用 K 次都还能保持高 acceptance,再叠一层语言相关的 dynamic vocabulary compression。

2.03×
vs NTP baseline (lossless)
+82%
vs vanilla MTP (1.21×→2.03×)
2%→36%
第三 token acceptance

立场: 很多 frontier 模型(DeepSeek-V3 / GLM-4.5 / MiMo)训练时已经送了 MTP 模块,但部署时只敢用第一个或干脆扔掉。FastMTP 是把这个"已付费但没用的赠品"真正变现的工程方案——只 fine-tune 一个 head,< 1 天 H20 训练,直接喂进 SGLang。


1 · 动机:已经训好的 MTP 为什么没人在 inference 时用?

1.1 历史脉络:MTP 的两个时代

Multi-Token Prediction 由 Gloeckle et al. 2024 提出(见 14_MTP_Gloeckle 的精读),最初是把它当成一种训练辅助任务:在主头之外挂 n 个独立的 head,各自负责预测 t+1, t+2, ..., t+n。其论点是 "提供更密集的监督信号 + 鼓励 planning ahead",主要好处兑现在训练时——下游任务质量轻微提升、收敛更快。

到 DeepSeek-V3 把它进一步演化成串行 MTP (sequential MTP):n 个 module 之间有 cross attention 形成因果链,每个都吃前一个 module 的 hidden state,而不是 Gloeckle 的并行独立设计。这一改保住了 autoregressive 性质,看起来"顺手就能拿来做 speculative decoding 的 draft"。

但实际部署中,几乎所有开放权重的 MTP 模型(DeepSeek-V3、GLM-4.5、MiMo)都做出了同一选择:

这就是 FastMTP 看到的"商机":这些已被付费训练的 MTP 模块,本来应该能加速 inference,但实际拿来一用就崩,作者要回答崩在哪、怎么修。

1.2 别的方案为什么都"够呛"

方案怎么用 MTP 做 draft问题
多 MTP module 串接 (DeepSeek-V3 原意)K=3 就加载 3 个独立模块,各自带 KV cache显存 × K · 每模块独立 weight + KV · 调度复杂 · serving 框架几乎不支持
只用一个 MTP module 验 1 个 token (现状)K=1 跑一次额外 tokenα ≈ 1.7,加速天花板 ~1.3×;浪费 MTP 多步预测潜力
递归复用同一个 MTP module 跑 K 步 (naive)把第 1 步预测的 hidden state 作为第 2 步输入这个 module 训练时只见过"输入 = target hidden state"——第 2 步起 acceptance 从 70% 崩到 11%、第 3 步崩到 2%
训一个独立 EAGLE-3 draft head额外蒸馏出小 transformer + projection需要从零训一个 head,放弃了已训好的 MTP module
FastMTP (本文)fine-tune 单个 shared-weight MTP head + self-distill单 head 内存常数; serving 等价 EAGLE-3 已支持; 复用 MTP 架构
关键观察: 论文标题是 "Enhanced MTP" 而不是 "Replace MTP" — 它不是发明新的 draft 架构,而是修复 vanilla MTP 在 K>1 时的 train-inference mismatch。论文反复强调 "aligning MTP training with its inference pattern"。

1.3 为什么这事不平凡 — train-inference mismatch 的根源

把 vanilla MTP 拿来递归用 K 次,本质上是 exposure bias 的极端版本。考虑训练时第 k 个 module 看见什么:

训练时 (vanilla MTP) main F → h_i (gold) M (k=1) 输入: h_i (gold) M (k=2) teacher forcing 输入还是 gold! 推理时 (递归复用) main F → h_i (gold) M (k=1) 输入: h_i ✓ M (k=2) 输入: ĥ (M 自己的) 从未见过的分布! ĥ_{i+1}
Vanilla MTP 的 train-inference mismatch:训练每一步用的都是 main model F 输出的 gold hidden state(teacher forcing),但推理 K=2 起没法再调用 F(否则就破坏了 spec decoding 的并行性),只能拿 MTP 自己第 1 步生出的 hidden state 喂进去。这是一个完全 OOD 的输入分布,所以崩。

那 train 时为什么不直接做 scheduled sampling / use ĥ_{k-1}?——因为 vanilla MTP 的设计目标是训练辅助,不是为 inference 优化的。每个 module 各管各的位置,就像 Gloeckle 的"n 头并行各预测一个 future token"——根本没想过递归。

FastMTP 的本质修补就两点:

  1. 架构层:把 K 个独立 module 改成同一个 head 在 K 个位置上反复用 → 强迫它在权重里融合"对不同 lookahead 距离都 robust"的能力
  2. 训练层:显式让训练时的输入分布 = 推理时的递归输入分布,不再单纯 teacher-force

2 · 背景速查

2.1 关键术语

术语含义
MTP head / module挂在主模型上、预测未来若干 token 的小型子网络;DeepSeek-V3 风格通常是 1 层 transformer + projection
Vanilla MTP预训练时按 DeepSeek-V3 方式训出来的、未做 inference-pattern fine-tune 的 MTP checkpoint
Position-shared weightsK 个 draft 步骤共用同一份 MTP head 权重;对比 Gloeckle 的 n 独立 head 和 V3 的 cascaded n module
Self-distilled data用 main model 自己生成的 (prompt, response) 当训练样本,而不是用原数据集中的人写 / 别的模型 response
EAGLE-style verify主模型对 K 个 draft token 一次并行 forward,从左到右接受到第一个不匹配位置;数学上 lossless(见 lossless proof tutorial)
FR-SpecFrequency-Ranked Speculative Sampling — 把 draft 的 vocab 限制到高频子集以省 logits 投影开销
Acceptance rate at step k第 k 个 draft token 通过 verifier 检查的概率(条件:前 k-1 都通过)
Acceptance length τ每次主模型 forward 平均产出 token 数 = 1 + 累积 accept 数

2.2 EAGLE-style verify 流程速记 (本文用的就是这个)

  1. 主模型 F 跑一次 prefill / 当前 step,得到 ĥ_i 和下一 token ĥ_{i+1}(这一个总是被接受)
  2. MTP head M 递归 K 次,生出 ĥ_{i+2}, ĥ_{i+3}, ..., ĥ_{i+K+1}
  3. 主模型 F 一次 forward 处理这 K+1 个位置,得到每个位置的 p_target
  4. 从左到右逐位置匹配 draft token vs argmax p_target;greedy 模式下"匹配 ≡ 接受",一旦不匹配就停,补一个 p_target 重采样的 token
  5. 本步产出 1..K+1 个 token

本文实验 greedy + temperature=0,所以"接受"就是 draft 与 target argmax 严格相等;非 greedy 情况标准 rejection sampling 一样适用,lossless 性质由 Leviathan 2023 / Chen 2023 证明。


3 · 方法① · Position-shared 单 head 架构

3.1 与 Gloeckle / DeepSeek-V3 的对比图

Gloeckle 2024 n 个独立并行 head trunk h_i H₁ H₂ H₃ → t+1 → t+2 → t+3 推理: 3 个独立预测,无递归 不互相 condition DeepSeek-V3 n 个 cascaded module (各自独立 weight) trunk h_i M₁ θ₁ M₂ θ₂ M₃ θ₃ → t+1, t+2, t+3 各自 推理: K module 全加载, KV × K 显存翻倍, 框架支持差 FastMTP (本文) 单 head + position-shared θ trunk h_i M (one θ) 递归 k=1,2,3 → t+1 → t+2 → t+3 单 KV cache, 兼容 EAGLE-style SGLang 直接吃 关键差别: Gloeckle 不递归 · V3 递归但 weight 不共享 · FastMTP 递归 + 共享 → 强迫单 θ 学习"对所有 lookahead 距离都 robust"
三种 MTP 架构对比。FastMTP 的设计可以理解成"DeepSeek-V3 cascaded MTP 在 θ₁=θ₂=θ₃ 约束下的特例 + 拿这个特例去 fine-tune"——架构上等价于把 V3 的多 module 退化成一个,推理时反复 unroll。

3.2 为什么 weight share 反而能涨 acceptance(反向论证)

"weight share"在 ML 史上往往是表达力的限制(每个 head 只能学同一个 mapping)。这里为什么能涨 acceptance?

  1. 训练 signal 在 K 个位置上叠加:同一份 θ 同时受到"预测 t+1"、"预测 t+2"、"预测 t+3"三种 loss 拉扯,被迫学一个对 lookahead 距离无关的"下一 token mapping",而不是 over-fit 到固定位置。
  2. 训练分布与推理分布严格对齐:推理时 M 在第 k 步看到的是 ĥ_{k-1}(自己生成的);训练时也按这个递归方式喂数据(下一节展开)。两边输入分布 i.d.d.。
  3. 推理时显存常数:1 个 KV cache、1 份权重。哪怕 K=7,显存与 K=1 相同,这让更长 draft变得可负担。

论文 §3.4 的 ablation 给的硬数据(详见 §8):同样 fine-tune,但 fix-data(用源数据集 response)的 K=3 acceptance length τ=2.54;换成 self-distilled 后 τ=2.73。再加 FR vocab compression τ=2.66 但 token/s 涨。这说明 share weight 是必要但不充分条件 — 配合 self-distill 才能解锁全部潜力。


4 · 方法② · Self-distilled 训练数据 — fine-tune 的核心 trick

4.1 数据流程图

原始数据集 {(x_n, y_n)} SFT/instruction 丢掉 y_n Main Model F MiMo-7B-RL temp=0.6, top-p=0.95 {(x_n, ỹ_n)} F 自己写的 response 389.4K samples 清洗 MinHash dedup 长度 / 重复过滤 Fine-tune 单个 MTP head M (210.8M params) F 全程冻结 · loss = Σ α_k · CE(ĥ_k, t_{i+k}) 3 epoch · lr 5e-5 · K=3 · β=0.6 · < 1 day on H20 数据组成: 42% general · 18% math · 13% code · 27% Chinese (中英双语)
Self-distillation 数据流。注意只有 prompt 复用,response 全部由 main model 重新生成。这一步把 "ground truth" 从"人写的"或"别的模型写的"换成了"当前 target 自己会写的",从而保证 MTP head 学的就是模仿这个 target 的下一 token 分布。

4.2 Self-distill vs Fixed-data 的硬数据 (论文 Table 1, K=3)

变体τ (mean)token/sSpeedup
Vanilla MTP (no FT)1.8338.041.21×
Fixed-data FT2.5452.681.67×
Self-data FT2.7357.011.81×
Self-data FT + FR2.6664.122.03×

从 Vanilla MTP → Fixed-data FT,τ 从 1.83 涨到 2.54(+39%);单单换数据(Fixed → Self),τ 从 2.54 → 2.73(+7%),speedup 从 1.67× → 1.81× (+8%)。看似不大,但乘进 1.81 → 2.03 这一步,self-distill 是必要的踏板。

4.3 反向思考 — 不 self-distill 会怎样?

假设你直接用原数据集的 (x_n, y_n) 来训 MTP head:

Self-distill 的本质就是把"语义等价"提升到"token 级一致"。这是 EAGLE 系列、Medusa 都用过的标准 trick(Cai 2024、Yang 2024c),只是 FastMTP 把它显式应用到 MTP fine-tune 上。


5 · 方法③ · Language-aware Dynamic Vocabulary Compression

5.1 问题:MTP head 的 vocab projection 是大头

MiMo-7B-RL 的 vocab size 是 152K(用 Qwen2 tokenizer)。MTP head 每生成一个 draft token,要做 logits = h · W^T,其中 W ∈ ℝ^{152K × 4096}。对于一个 7B 模型,这个 projection 在 draft 阶段(短序列、batch=1)不可忽略——FR-Spec (Zhao 2025a) 已经指出过这是个瓶颈。

5.2 机制图

输入 context "小明买了 3 个苹果..." 检测 语言判别 → ZH 语言相关 vocab pool V_high^EN: 32K (MT-Bench 最优) V_high^ZH: 16K (C-Eval 最优) 在不同 corpus 上离线统计 选 V_high^ZH W̃ ∈ ℝ^{16K × 4096} 152K → 16K 投影矩阵 draft step (一次) MTP head M → ĥ ∈ ℝ⁴⁰⁹⁶ logits = ĥ · W̃^T |V|=16K (压缩后) argmax → draft token 9.5× 投影量减少 Verify 阶段始终用完整 152K vocab → 接受标准基于 main model 的 full distribution → 即使 draft 在压缩 vocab 上做的判断,verify 是 lossless 的
语言相关动态压缩。两个关键点:① V_high 是按语言离线统计的(不是"每输入都算 top-K"),所以推理时只是 lookup 一个预存的子矩阵,无在线开销。② Verify 阶段恢复完整 vocab,所以最终输出分布与原模型严格相同——压缩只影响 draft 的"提议",不影响 acceptance criterion。

5.3 选 vocab size 的实验数据 (Table 2)

语言 / 任务|V|τtoken/sSpeedup
C-Eval (中)Full 152K2.55154.361.72×
16K2.525 (-0.026)62.931.99×
MT-Bench (英)Full 152K2.69056.331.80×
32K2.622 (-0.068)63.422.03×

反直觉结论:中文用 更激进的 16K 压缩,英文反而需要 32K。论文解释是:中文 token 的 head distribution 更集中(很多高频字 + 复合 token),英文 BPE 的长尾更长。

5.4 反向思考 — 为什么不直接用 top-K 的全局 vocab?

原 FR-Spec 用 SlimPajama (英语为主)统计 top-K → 中文 task 上 acceptance 跳水。FastMTP 的修补就是按语言切多个 V_high,运行时 detect 切换。这其实是个朴素工程,但效果显著——提醒我们 vocab compression 的"高频"要 conditional on 输入分布


6 · 公式 · 加权 cross-entropy 损失

6.1 损失定义

L_mtp = Σ_{k=1}^{K} α_k · L_mtp^k = Σ_{k=1}^{K} α_k · CE(ĥ_k, t_{shifted})

其中第 k 步的权重按指数衰减:

α_k = β^{k-1} / Σ_{j=1}^{K} β^{j-1} (β = 0.6, K = 3)

6.2 代入数值

kβ^{k-1}α_k (归一化后)
11.000.510
20.600.306
30.360.184
Σ1.000

6.3 物理直觉

权重指数衰减,代表"越远的 token 越不可能预测准,降低它的损失贡献"。如果不衰减(β=1),三个步骤等权——但第 3 步的 CE loss 自然就大(理论下界 = data entropy + 累积 mismatch),会主导梯度,反而让第 1 步没学好。β=0.6 把第 3 步权重压到 18.4%,让"近期对、远期凑合"成为优化目标。

6.4 β 的敏感度(论文未做完整 sweep, 反推)

βα_1α_2α_3预期 effect
1.0 (no decay)0.3330.3330.333第 3 步主导,第 1 步退化
0.80.410.330.26偏均匀
0.6 (本文)0.510.310.18论文选定,K=3 最优
0.30.710.210.07等于不学第 3 步
→ 0→ 1→ 0→ 0退回 vanilla MTP K=1
论文没报 β sweep,但 §3.3.1 显示 K=4..7 也训过(用同样的指数衰减),acceptance length 在 K=7 时还能涨到 ~3.2,只是 token/s 反降。这间接说明 β=0.6 在 K≤3 是足够的;更长 horizon 下也许该减小 β 让远期 loss 进一步弱化。

7 · Worked Example · 一个数学题片段在 K=3 上的轨迹

设输入 prompt 是 "求 sin²(x) 的不定积分。",main model 已经写到 "sin²(x)dx = (1−cos(2x))/2 dx, 所以 积分"。下一步 main model 会预测 "等于"

FastMTP 的一次 step:

Step 0: Main F forward → ĥ_i + 第一 token "等于" (always accepted) ĥ_i ∈ ℝ⁴⁰⁹⁶ · 这一 forward 同时给出剩余位置的 verify-target (后面用) k=1 M(ĥ_i, embed("等于")) → ĥ_{i+1}, draft = "x" FR vocab=32K (英文 token 也涵盖) · main F greedy 也会预测 "x" → acceptance@k=1: 81% (训练后) / 70% (vanilla) k=2 M(ĥ_{i+1}, embed("x")) → ĥ_{i+2}, draft = "/" 输入是 M 自己的 ĥ_{i+1} (不是 main F 的!) · main F 也想要 "/" → acceptance@k=2: 56% (训练后) / 11% (vanilla 崩!) k=3 M(ĥ_{i+2}, embed("/")) → ĥ_{i+3}, draft = "2" main F 预测的是 "2" — match → 接受 → acceptance@k=3: 36% (训练后) / 2% (vanilla 几乎归零)
本 step 总产出: 1 (always-accepted) + 3 (drafts all matched) = 4 个 token,即 τ=4。在 vanilla MTP 上等概率走完 3 步的概率是 0.70 × 0.11 × 0.02 ≈ 0.15%,实际平均产出 ~1.83;FastMTP 是 0.81 × 0.56 × 0.36 ≈ 16%,平均产出 ~2.73。

7.1 期望产出推导

记 a_k = step k 的条件接受率,τ = 1 + Σ_{k=1}^{K} ∏_{j=1}^{k} a_j。代入 FastMTP 的 (0.81, 0.56, 0.36):

τ = 1 + 0.81 + 0.81×0.56 + 0.81×0.56×0.36 = 1 + 0.81 + 0.454 + 0.163 = 2.43

论文实测 K=3 mean τ=2.73,略高,因为某些任务(数学)acceptance 显著偏高(数学第 3 步 acceptance >50%)。

同样代入 vanilla MTP (0.70, 0.11, 0.02):

τ = 1 + 0.70 + 0.70×0.11 + 0.70×0.11×0.02 = 1 + 0.70 + 0.077 + 0.0015 = 1.78

实测 1.83 接近。这就是 vanilla MTP 在 K=3 上 speedup 反而退回到 1.21× 的根本原因——多走的 step 几乎都不接受,白付 draft 计算。


8 · 实验关键结果

8.1 主结果 (Table 1, MiMo-7B-RL on A10 24GB, 7 任务平均)

方法K=1 speedupK=2 speedupK=3 speedup
Baseline (NTP)1.00× (31.55 token/s)
Vanilla MTP1.32×1.31×1.21× ↓
Fixed-data FT1.37×1.61×1.67×
Self-data FT1.40×1.71×1.81×
Self-data FT + FR (FastMTP)1.46×1.85×2.03×

看点:

8.2 Acceptance rate breakdown (Figure 4a) — 全篇最 killer 的图

0% 25% 50% 75% 100% 70% 81% k=1 11% 56% k=2 2% 36% k=3 Vanilla MTP FastMTP (Self-data FT) Acceptance Rate · 7 任务平均
论文最 killer 的发现。Vanilla MTP 在 k=1 已经接近 EAGLE-3 水平 (70%),但 k=2 跌 6 倍,k=3 几乎归零。FastMTP 在 k=1 微涨 11pp,但 k=2/k=3 拉升 5×、18×。

这张图的 take-away:vanilla MTP 不是"draft 不行",而是"递归不行"。修补方式不是换更强的 head,而是教它"被反复递归用"——FastMTP 的 fine-tune 改变的不是表达力,而是训练-推理输入分布对齐

8.3 Optimal draft length (Figure 3, on A100)

论文额外训了一个 K=7 的 head:

KFastMTP τFastMTP token/sVanilla τVanilla token/s
1~1.8~80~1.7~75
2~2.4~110~1.8~80
3~2.7~140~1.85~85
4~2.9~135~1.85~80
5~3.0~125~1.85~75
7~3.2~110~1.85~70

K=3 是 sweet spot。τ 单调涨但 token/s 在 K=4 就开始降——边际 acceptance 抵不过递归 forward 成本。


9 · 与同类工作对比

工作Draft 架构训练数据Lookahead 方式Vocab compress
Gloeckle MTP (2404.19737)n 独立并行 head预训练同分布无递归(直接 K 预测)
DeepSeek-V3 sequential MTPn cascaded module 各自权重预训练原生 cascade
Medusa多 MLP head 并行 + treeSFT-like无递归
EAGLE-3独立 transformer head + multi-layer hiddenself-distillautoregressive 递归
FR-Spec原 draft 不变原 draft 不变原 draft 不变top-K 全局
FastMTP1 个 shared MTP headself-distillautoregressive 递归 (训练时同分布)language-aware 动态

9.1 vs EAGLE-3

FastMTP 与 EAGLE-3 在形式上非常像:都是单个轻量 head + autoregressive 递归 + self-distill 数据。差别在 head 来源:

论文措辞:"ensures compatibility with EAGLE-style speculative decoding for seamless integration with existing inference frameworks such as SGLang"。

9.2 vs DeepSeek-V3 sequential MTP

V3 部署时一般只用 first MTP module 做 K=1,因为加载多个 module 需要框架特别支持。FastMTP 的视角是:既然多 module 部署难,就把 K 个权重合并成一个(用 share 约束),同时通过 self-distill fine-tune 把它训得递归 robust。从模型容量上确实失去了一部分(K 份独立 weight 退到 1 份),但部署上换来了 EAGLE-style 框架原生支持。

9.3 vs Medusa

Medusa 的 head 之间不递归:K 个 MLP head 各看 main 的同一个 hidden state,各自预测 t+1, t+2, ...。所以 Medusa 不会出现 FastMTP 担忧的 "递归输入 OOD",但反过来也吃了 t+k 没法 condition on t+1, ..., t+k-1 的亏 — 因此 Medusa 通常需要 tree-attention 来弥补。FastMTP 是另一个极端:严格 sequential,但靠 share weight + 训练对齐保证递归不崩。


10 · 局限 / 个人 take / 待验证问题

论文承认或暗含的局限

个人 take

我会想验证的问题

  1. K=3 的甜点会不会在大 batch 下后退?vLLM/SGLang 多并发场景下 draft 的固定开销摊薄了,理论上 K 甜点会前移(不是后退),但需实测。
  2. 把 share-weight 改成 LoRA-per-step(K 个 LoRA adapter,共享 base)能否进一步涨 acceptance 而几乎不增显存?这是一个介于"K 独立 module"和"完全 share"中间的折中。
  3. MiMo-7B-RL 本身已是 RL 后训过的模型;在没有 RL 后训的 base model 上(纯 pretrain)做 FastMTP 效果会不会差很多?数学上的高 acceptance 是不是模型本身被 RL 训得"模式化"了?
  4. 对 long-CoT(>4k)长文本的 acceptance 衰减如何?论文 max-length=1024,实际推理场景动辄数万 token,KV growth + 长上下文的 token 多样性可能让 acceptance 单调下降。
  5. 在 sampling 模式(temp>0)下,FR-Spec 的 vocab 截断会不会让"被截断的低概率 token"在 rejection sampling 时永远拿不到——是不是技术意义上仍然 lossless?(论文说 verify 用 full vocab 所以 lossless,但 draft 提议本身受截断影响,会改变 acceptance distribution。)

记忆点 (cold recall)

立场 把"已训好的 MTP 模块"从"训练辅助"兑现为"inference 加速"。
架构 单 head + position-shared θ + EAGLE-style verify(SGLang 直接吃)。
训练 Self-distilled 数据 (main model 自己生 response)+ exponentially weighted CE,β=0.6,K=3。
数字 2.03× e2e · acceptance k=2: 11%→56% · k=3: 2%→36% · 训练 < 1 day on H20。
陷阱 Vanilla MTP K=3 比 K=2 还慢 (1.21× vs 1.31×); 数学/代码加速 > QA。
vocab Language-aware FR: 中文 16K,英文 32K, +8% throughput,verify 仍用 full vocab 保 lossless。
看也 see also: Gloeckle MTP (14_MTP) · EAGLE-3 (DFlash 02) · Lossless proof (15)。

精读笔记 v1 · 2026-05-07 · PDF: /data/szhang967/papers/paper-notes/models/FastMTP_2509.18362.pdf