第11章 MTP:多 Token 预测的训练放大器
“Predict not just the next token, but the next-next one too. You learn faster.” —— V3 技术报告,论 MTP
V4 把 V3 引入的 MTP 保留了下来——一个永远只预测下一下个 token 的辅助 head,训练时增加监督信号,推理时变成投机解码。
11.1 引子:next-token 预测的训练效率瓶颈
LLM 训练的标准目标是 next-token prediction——给定 token[0:n],预测 token[n]。loss 是 cross-entropy。
这套训练有个隐性低效:每个 token 只产生 1 个监督信号。一段 4096 token 的 batch 提供 4096 个监督。如果能把”下一下个 token” (next-next) 也作为监督,监督密度直接翻倍——同样的训练数据能让模型学得更好。
但是简单地”加一个 next-next prediction head”会有问题:
- 模型表达力被分散到两个目标
- next-next 的 hidden 与 next 的 hidden 是同一个——head 学到的”下下一个 token” 可能干扰主 head 的”下一个 token”
MTP(Multi-Token Prediction) 的解法:给 next-next 配一个独立的 transformer block,让它在主模型 hidden 的基础上做”额外的一层处理”,再预测 next-next。这种”独立 block + 共享 embedding” 的设计让监督信号翻倍但不干扰主任务。
V3 引入 MTP,V4 保留——num_nextn_predict_layers=1。本章拆 V4 中 MTP 的具体实现。
11.2 MTPBlock 类的源码
class MTPBlock(Block):
def __init__(self, layer_id: int, args: ModelArgs):
super().__init__(layer_id, args)
self.e_proj = Linear(args.dim, args.dim)
self.h_proj = Linear(args.dim, args.dim)
self.enorm = RMSNorm(args.dim, args.norm_eps)
self.hnorm = RMSNorm(args.dim, args.norm_eps)
self.norm = RMSNorm(args.dim, args.norm_eps)
self.hc_mult = hc_mult = args.hc_mult
hc_dim = hc_mult * args.dim
with set_dtype(torch.float32):
self.hc_head_fn = nn.Parameter(torch.empty(hc_mult, hc_dim))
self.hc_head_base = nn.Parameter(torch.empty(hc_mult))
self.hc_head_scale = nn.Parameter(torch.empty(1))
self.embed: ParallelEmbedding = None
self.head: ParallelHead = None
@torch.inference_mode()
def forward(self, x: torch.Tensor, start_pos: int, input_ids: torch.Tensor) -> torch.Tensor:
assert self.embed is not None and self.head is not None
e = self.embed(input_ids)
e = self.enorm(e)
x = self.hnorm(x)
x = self.e_proj(e).unsqueeze(2) + self.h_proj(x)
x = super().forward(x, start_pos, input_ids)
logits = self.head(x, self.hc_head_fn, self.hc_head_scale, self.hc_head_base, self.norm)
return logits
MTPBlock 继承自 Block——意味着它有完整的 attention + ffn + HC 处理。但它在 Block 之上加了几样东西:
新增组件 1:e_proj 与 h_proj
self.e_proj = Linear(args.dim, args.dim)
self.h_proj = Linear(args.dim, args.dim)
e_proj 处理 input embedding (e)、h_proj 处理主模型最后一层的 hidden (x)。两者在 forward 中相加:
x = self.e_proj(e).unsqueeze(2) + self.h_proj(x)
注意 e_proj(e).unsqueeze(2) 是 [B, S, 1, D]、h_proj(x) 是 [B, S, hc_mult, D]——unsqueeze + 广播让 e 被复制到 4 路。这相当于”input embedding 平均贡献到 4 路 hidden”。
新增组件 2:双 RMSNorm
self.enorm = RMSNorm(args.dim, args.norm_eps)
self.hnorm = RMSNorm(args.dim, args.norm_eps)
分别归一化 e 和 x,确保两者数值范围接近——避免某一方主导。
新增组件 3:独立的 hc_head 参数
self.hc_head_fn = nn.Parameter(torch.empty(hc_mult, hc_dim))
self.hc_head_base = nn.Parameter(torch.empty(hc_mult))
self.hc_head_scale = nn.Parameter(torch.empty(1))
MTPBlock 有自己的 hc_head 投影参数——把 4 路 hidden 混回 1 路,再过 lm_head 出 logits。
复用组件:embed 与 head
self.embed: ParallelEmbedding = None
self.head: ParallelHead = None
注意是 None——在 Transformer.__init__ 末尾被赋值:
self.mtp[-1].embed = self.embed
self.mtp[-1].head = self.head
MTPBlock 复用主模型的 embedding 和 lm_head——不重训这两个,直接共享。这让 MTP 几乎不增加参数(只增加一个 transformer block 的参数)。
11.3 MTP forward 的完整流程
把 MTPBlock.forward 一步步走一遍:
@torch.inference_mode()
def forward(self, x: torch.Tensor, start_pos: int, input_ids: torch.Tensor) -> torch.Tensor:
e = self.embed(input_ids) # 步骤 1: 重新算一遍 input embedding
e = self.enorm(e) # 步骤 2: 归一化 e
x = self.hnorm(x) # 步骤 3: 归一化主模型 hidden
x = self.e_proj(e).unsqueeze(2) + self.h_proj(x) # 步骤 4: 融合 e 和 x
x = super().forward(x, start_pos, input_ids) # 步骤 5: 跑一层 Block
logits = self.head(x, self.hc_head_fn, self.hc_head_scale, self.hc_head_base, self.norm) # 步骤 6: lm_head
return logits
输入:
x: [B, S, hc_mult, D]——主模型 60 层之后的 hidden(4 路)input_ids: [B, S]——原始 token id
输出:
logits: [B, vocab_size]——预测下一下个 token
flowchart TB
subgraph Inputs["输入"]
InputIds["input_ids"]
HiddenX["x (主模型 60 层后)"]
end
subgraph MTPInside["MTPBlock 内部"]
E["embed(input_ids) → e"]
Enorm["enorm(e)"]
Hnorm["hnorm(x)"]
Eproj["e_proj(e).unsqueeze(2)"]
Hproj["h_proj(x)"]
Combine["x = + (融合)"]
Block["super().forward(...)<br/>(完整 Block: attn + ffn + HC)"]
Head["head(x, hc_head_*)"]
end
Logits["logits"]
InputIds --> E --> Enorm --> Eproj
HiddenX --> Hnorm --> Hproj
Eproj --> Combine
Hproj --> Combine
Combine --> Block --> Head --> Logits
注意 super().forward(x, start_pos, input_ids)——MTPBlock 调用父类(普通 Block)的 forward,跑一遍完整的 attention + ffn + HC。这意味着 MTPBlock 拥有完整的 transformer block 能力,不是简单的”再多一个 head”。
11.4 MTP 在训练时的角色
V4 的训练源码不公开,但 MTP 的训练目标可以从架构反推:
训练目标:预测 token[n+1] 给定 token[0:n] 的 hidden state。
具体地,假设主模型对 token[n] 的预测是 P(token[n+1] | token[0:n]),MTP 的预测是 P(token[n+2] | token[0:n+1])——它在主模型预测的下一个 token 之上再多预测一个 token。
总 loss:
loss = main_loss + λ_mtp * mtp_loss
λ_mtp 是 MTP loss 的权重,DeepSeek-V3 论文报告的值约 0.1(让 MTP 提供”次要”监督,不主导训练)。
MTP loss 的具体计算:
- 主模型 forward 完,得到每个 token 位置的 hidden state x[n]
- MTPBlock 接收 x[n] 与 input_ids[n+1](“下一个 token 的 embedding”)
- MTPBlock forward 出 logits,与 token[n+2] 算 cross-entropy
注意第 2 步——MTP 用的 input_ids 是右移一位的。这是 next-next 预测的关键:MTP 看到主模型的 hidden + 下一个 token 的 embedding,预测下下一个 token。
这个设计的妙处:训练时 MTP 完全可微——主模型的梯度流过 MTP 路径回到主模型 backbone,让主模型学到”为 next-next 预测有用的 hidden representation”——间接提升主模型的表达力。
11.5 MTP 在推理时变成”投机解码”
V4 在推理时怎么用 MTP?答案是投机解码(Speculative Decoding)——
经典的投机解码用一个小型 draft 模型快速预测多个 token,然后用大模型验证。V4 的 MTP 让”draft”和”verify”用同一个模型——MTP 的输出就是”下一下个 token 的预测”,主模型再 verify 一下:
1. 主模型 forward token[0:n],得到 logits[n] → 采样 token[n+1] = T1
2. MTP forward (hidden[n], T1) → 得到 logits → 采样 token[n+2] = T2
3. 主模型再 forward token[0:n+1] (实际上 KV cache 增量加 T1),得到 logits[n+1] → 采样 token[n+2] = T2'
4. 比较 T2 vs T2':
- 如果一致:接受 T2,节省一次主模型 forward
- 如果不一致:拒绝 T2,重新从 T1 之后开始
V4 的 MTP 因为是用同一份训练数据训出来的,与主模型分布高度一致——T2 与 T2’ 一致的概率(“接受率”)很高。在 V3 的实测中,MTP 的接受率达到 80-90%(V3 论文公开)。这意味着:使用 MTP 投机解码,每个步骤平均能产出 1.8-1.9 个 token,吞吐率几乎翻倍。
V4 因为继承 V3 的 MTP 设计,预期接受率类似——但 1M 长上下文场景下的实测数据需要等 V4 GA 后社区报告。
11.5·补 MTP 投机解码的序列图
把 §11.5 描述的”MTP 投机解码 4 步”画成序列图:
sequenceDiagram
participant U as 用户
participant Main as 主模型 forward
participant MTP as MTPBlock forward
participant Sample as 采样器
U->>Main: input token[0:n]
Main->>Sample: logits[n]
Sample-->>Main: token[n+1] = T1
Main->>MTP: hidden[n], input_ids=[..., T1]
MTP->>Sample: logits (next-next)
Sample-->>MTP: token[n+2] = T2
Note over Main,MTP: 此时有 (T1, T2) 两个候选
Main->>Main: forward(KV cache + T1)
Main->>Sample: logits[n+1]
Sample-->>Main: token[n+2]' = T2'
alt T2 == T2'
Main-->>U: 接受 T1, T2 (节省一次 forward)
else T2 != T2'
Main-->>U: 接受 T1, 拒绝 T2, 用 T2' 替代
end
接受率 80-90% 意味着每 10 步 decode 平均省下 8-9 次主模型 forward——这是 V4 推理 throughput 接近翻倍的根本机制。
11.6 MTP 与 EAGLE / Medusa 对比
投机解码领域最近几年有几个流行方案——把 MTP 与它们对比:
| 方案 | 草稿模型 | 训练复杂度 | 推理 overhead | 接受率(短文本) |
|---|---|---|---|---|
| 单独小模型 draft | 独立训练的小 LLM | 高(多训一个模型) | 低 | 70-80% |
| MTP (V4) | 共享主模型 + MTPBlock | 低(单模型一起训) | 低 | 80-90% |
| EAGLE | 在主模型 hidden 上加 head | 中 | 中 | 80-95% |
| Medusa | 多个独立 head(next, next-next, …) | 中 | 中 | 70-85% |
| Lookahead | n-gram 启发式 | 低 | 低 | 50-70% |
V4 的 MTP 在三个维度上有优势:
- 训练成本极低:MTP 与主模型一起训,不需要额外训练阶段
- 接受率高:因为是同一份训练数据,分布对齐
- 推理 overhead 低:MTP 只有 1 个 transformer block,比独立 draft 模型快得多
V4 可能没有 EAGLE 那么高的接受率,但工程简洁性和成本控制更好。这是 V4 团队选 MTP 而非 EAGLE 的工程权衡。
11.7 MTP 的内存与计算开销
MTPBlock 在显存与计算上的代价:
显存:MTPBlock 是一个完整的 transformer block——参数量约 1 / 61 ≈ 1.6% 的总模型。对于 V4 Pro 来说约 25 B 参数。但因为 MTP 复用主模型的 embedding 和 lm_head(这两个加起来约几 B 参数),MTP 的”额外参数”实际只有 25 B 左右——占总 1.6T 的 1.5%。
计算:推理时 MTP 跑一次 forward 等于多跑一层 transformer——增加约 1.6% 的 FLOPs。但因为投机解码节省了主模型的 forward,净 FLOPs 反而降低。
KV cache:MTPBlock 内部的 attention 也有自己的 kv_cache buffer——但 MTP 在推理时通常 incremental decode(每次 1 个 token),KV cache 的累积大小与主模型相同。
总成本 / 总收益 = 1.5% / 80%-90% ——极划算。
11.8 动手实验:理解 MTP forward
import torch
import torch.nn as nn
class MockBlock(nn.Module):
"""占位 Block,模拟主 transformer block"""
def forward(self, x, start_pos, input_ids):
return x # 占位
class MiniMTPBlock(nn.Module):
def __init__(self, dim=128, hc_mult=4):
super().__init__()
self.dim = dim
self.hc_mult = hc_mult
self.e_proj = nn.Linear(dim, dim)
self.h_proj = nn.Linear(dim, dim)
self.enorm = nn.LayerNorm(dim)
self.hnorm = nn.LayerNorm(dim)
self.block = MockBlock()
self.norm = nn.LayerNorm(dim)
self.embed = nn.Embedding(1024, dim)
self.head = nn.Linear(dim, 1024)
def forward(self, x, input_ids):
# x: [B, S, hc, D]
# input_ids: [B, S]
e = self.embed(input_ids) # [B, S, D]
e = self.enorm(e)
x = self.hnorm(x) # [B, S, hc, D]
x = self.e_proj(e).unsqueeze(2) + self.h_proj(x) # broadcast
x = self.block(x, 0, input_ids)
x = x.mean(dim=2) # 简化:4 路 mean 成 1 路
logits = self.head(self.norm(x))
return logits
# 测试
mtp = MiniMTPBlock()
x = torch.randn(2, 10, 4, 128)
input_ids = torch.randint(0, 1024, (2, 10))
logits = mtp(x, input_ids)
print(logits.shape) # [2, 10, 1024]
跑通这个 mini 版本后再回看 V4 源码。会发现”原来 MTPBlock 比主 Block 的复杂度只多了 e_proj / h_proj 两个 Linear + 双 RMSNorm + 独立的 hc_head”。
11.8·补 MTP 在 long-context 场景下的特殊性
V4 的主模型支持 1M context。MTP 在这种长上下文场景下的工作方式有几个特殊性,值得专门说一下。
特殊性一:MTP 的 KV cache 与主模型独立
MTPBlock 继承自 Block——它有自己的 attention,有自己的 KV cache。这意味着:在长 prompt 场景下,主模型和 MTP 的 KV cache 是两份独立的内存。1M context 下主模型 KV ~8 GB,MTP KV 也约 130 MB 量级(MTP 只有 1 层,是主模型 1/61)——总占用约 8.13 GB。
这点在显存预算时容易被忽略——以为”MTP 共享主模型 KV”,结果部署时 OOM。V4 的设计里 MTP 必须有独立 KV,因为 MTPBlock 内部的 attention 也是稀疏 + 滑窗 + Compressor,与主模型的 attention 状态机不能共用。
特殊性二:MTP 在 1M context 下的接受率会下降
V3 报告在短上下文下 MTP 接受率 80-90%。但当 context 长度增加,MTP 的接受率会下降——主要原因是:长 context 下”下一下个 token”的不确定性增大,主模型与 MTP 在边界 token 上的分歧概率上升。
具体下降幅度需要 V4 GA 后社区独立测试。但工程上的应对:在长 context 场景下,用户可以选择关闭 MTP 投机解码,让主模型逐 token 跑——更稳定但较慢。这是一个”延迟 vs 稳定性” 的运行时选择。
特殊性三:MTP 与稀疏 attention 的协同
MTPBlock 的 attention 同样走 V4 的稀疏 + Compressor + Indexer 路径——不是 dense attention。这意味着 MTP 也能受益于 V4 attention 的所有红利(KV cache 小、长 context 性能稳)。
特殊性四:MTP 在 prefill 阶段不参与计算
prefill 时主模型逐 token 算完 hidden state,但 MTP 不需要为每个 prompt token 都算 forward——只在最后一个 token 之后启动,做”投机预测下一个 token 之后的 token”。这让 MTP 的推理 overhead 完全集中在 decode 阶段,prefill 完全不付 MTP 的成本。
这种”MTP 仅在 decode 工作”的设计让 V4 在长 prompt(如 RAG 场景)下的”first token latency” 不被 MTP 拖累——长 prompt 用户会感觉 V4 的 TTFT(time-to-first-token)与一个无 MTP 的等大模型一样快。
11.8·补·补 MTP 模块的训练数据流推演
为了让 MTP 训练机制更具象,把一个 batch 在训练中的数据流推演一遍:
给定 batch:32 个序列,每序列 4096 tokens。
步骤 1:主模型 forward
主模型逐层算完 60 个 Block,得到每个位置的 hidden state:hidden: [32, 4096, 4, 7168](4 路 HC)。
步骤 2:主 head 算主 loss
主 head 接收所有位置的 hidden,输出 logits [32, 4096, vocab]。与 input_ids 右移一位后做 cross-entropy——得到主 loss。
步骤 3:MTP forward
MTPBlock 接收:
- 主模型最后一层的 hidden
[32, 4096, 4, 7168] - 右移一位的 input_ids(即”下一个 token 的 id”)
MTP 内部融合 e_proj 与 h_proj,跑一层 Block,输出 logits [32, 4096, vocab]。
步骤 4:MTP loss
MTP logits 与”右移两位的 input_ids”做 cross-entropy——这是”预测下一下个 token”的目标。
步骤 5:合并 loss
total_loss = main_loss + 0.1 × mtp_loss
步骤 6:反向传播
梯度从 main_loss 流经主 head → 主模型 backbone → input embedding。 梯度也从 mtp_loss 流经 MTP head → MTPBlock → 主模型 backbone → input embedding。
注意:MTP 的梯度也回流到主模型 backbone——这是 MTP 训练让主模型表达力提升的核心机制。主模型 hidden state 不仅要为”预测下一个 token”服务,还要为”下一下个 token 预测”提供有用信号——这种”看更远”的目标隐式提升主模型的语义表征质量。
这个梯度共享是 MTP 与”完全独立的 draft 模型”的根本差别——独立 draft 不会反向影响主模型,但 MTP 会。
11.8·延展 MTP 与 vLLM 投机解码引擎的串接
V4 的 MTP 在 vLLM 中工作的方式与 vLLM 已有的”独立 draft 模型投机解码”完全不同——这是部署时需要专门理解的点。
vLLM 早期版本的投机解码(speculative decoding)支持”指定一个小型 draft 模型”——比如用 Llama-7B 作 draft、Llama-70B 做 target。这种模式下 vLLM 维护两个模型实例,每次 decode 跑两次 forward。
V4 的 MTP 是与主模型同进程、共享 KV 与 embedding 的——vLLM 不能把它当作”独立 draft 模型”处理。具体接口差异:
- 共享 KV cache:MTP 用主模型最后一层的 hidden 作输入。vLLM 的调度器必须确保 MTP 在主模型 forward 完成后才启动——不能并行。
- 共享 embedding 与 lm_head:MTPBlock 直接复用主模型的 embedding 和 head。vLLM 的”独立 draft” 假设不成立——必须让 MTP 知道主模型的这些层位置。
- 接受率高:因为 MTP 与主模型分布对齐,接受率比”独立 draft” 高。vLLM 的接受率监控指标对 V4 应该专门标注”V4 MTP” 类型。
vLLM 适配 V4 的 MTP 的 PR 大概率引入一个新的 SpeculativeConfig 类型 ——MTPSpeculator,与现有的 DraftModelSpeculator 并列。这部分工程细节会在《vLLM 推理内核深度解析》第 12 章 “投机解码”章节的后续更新中跟进。
读者如果想自己实现”V4-MTP 投机解码”,可以参考本书第 11 章 + vLLM 卷第 12 章的结合——前者讲 MTP 算法,后者讲 vLLM 投机解码引擎抽象。
11.9 延伸阅读
- DeepSeek-V3 报告(arXiv:2412.19437):MTP 在 V3 中的最早期工业化
- Speculative Decoding 论文(arXiv:2211.17192):投机解码的源头
- EAGLE 论文(arXiv:2401.15077):基于 hidden 的高接受率方案
- Medusa 论文(arXiv:2401.10774):多 head 投机解码
- 本书《vLLM 推理内核深度解析》第 12 章:投机解码在 vLLM 中的工程实现
11.9·补 MTP 在 fine-tune 时的 4 个常见误区
把 V4 fine-tune 到特定领域时,MTP 经常被忽略或误用。整理 4 个常见误区:
误区 1:fine-tune 时关掉 MTP loss
某些工程师把 fine-tune 看作”只优化主任务”,把 MTP loss 的权重设为 0。结果:MTPBlock 的参数停止更新,逐渐与主模型分布偏离——投机解码接受率从 80% 跌到 50% 以下。
正确做法:fine-tune 时保持 MTP loss 权重(典型 0.05-0.1),让 MTPBlock 跟随主模型同步更新。
误区 2:fine-tune 时只 fine-tune MTP,不动主模型
反过来的误区:只 fine-tune MTPBlock,主模型冻结。结果:MTP 学到与主模型不一致的分布,接受率反而下降。
正确做法:MTP 必须与主模型一起 fine-tune——它们是耦合的,不能拆开训。
误区 3:fine-tune 数据没右移
MTP 训练时输入 input_ids 是右移一位的(详见 §11.4)。fine-tune 时如果数据 pipeline 没正确处理这个位移,MTP 实际预测的是”下一个 token” 而非”下一下个 token”——接受率会大幅下降。
正确做法:检查 fine-tune 数据 collator 是否正确生成 MTP 监督信号。
误区 4:fine-tune 完关闭 MTP 推理
部署 fine-tuned V4 时如果不启用 MTP 投机解码,会损失 ~2x throughput。某些工程师以为”我没专门训 MTP,应该关掉它”——但 MTPBlock 在 fine-tune 时已经被同步更新,可以照常用。
正确做法:fine-tune 不影响 MTP 推理可用性,部署时保持启用。
这 4 个误区都来自”对 MTP 是什么”的误解——把它当成”独立的小模型” 而非”主模型的副 head”。理解 MTP 与主模型的紧密耦合,就能避免这些坑。
11.9·补·补 MTP 与”长 think 链生成” 的特殊协同
V4 的 Think Max 模式可以生成超长 think 链(接近 384K token)。MTP 在这种”超长生成” 场景下有特殊作用——加速 thinking 的生成。
普通生成 vs thinking 生成:
普通生成:用户问”什么是 V4”,模型给一段 200-500 token 回答。MTP 投机解码节省 ~50% 时间。
Thinking 生成:用户给数学题,模型生成 10K-100K token 的推理过程。MTP 在这种场景下的价值更大——节省 50% 时间意味着用户从等 60 秒变成等 30 秒。
Thinking 生成对 MTP 接受率的影响:
Thinking 链通常有较强的”逻辑连续性”——next 与 next-next token 的预测一致性比普通文本更高。这让 MTP 在 thinking 生成中的接受率甚至比普通生成更高。
Think Max 模式的工程意义:
如果你的产品场景是数学 / 编程 / 复杂推理(thinking 密集),MTP 的 ROI 比聊天产品更高。生产部署时不能为了简化关掉 MTP——会让 thinking 模式的延迟翻倍。
MTP 与 thinking 长度限制:
V4 的 thinking 默认上限是 384K token。MTP 的 KV cache 也要支撑这个长度——意味着 MTP 的 KV cache 也需要走 V4 attention 的滑窗 + 压缩段双段几何。这部分设计已经包含在 MTPBlock 继承的 Block 结构里——不需要额外工程。
理解 MTP 与 thinking 的协同,对设计 thinking 类产品(如 Cursor 的 Composer、Anthropic 的 Claude Code)极重要。
11.9·延展 MTP 的”接受率波动”工程经验
MTP 投机解码的接受率不是恒定的——它随场景、prompt、生成阶段而波动。理解这种波动让你正确设 SLA。
接受率波动来源 1:prompt 类型
- 常见 prompt(聊天、问答):接受率 80-90%
- 推理 prompt(数学、思考链):接受率 70-85%
- 创意生成(写诗、故事):接受率 60-75%
- 代码生成:接受率 75-85%
差异来自”分布的可预测性”——常见 prompt 的下一下个 token 容易预测,创意生成的 token 难预测。
接受率波动来源 2:生成阶段
- 生成开头(前 50 token):接受率较低,因为分布还在收敛
- 中段(50-1000 token):接受率最高,分布稳定
- 末尾(接近 EOS):接受率回落,因为模型在”决定何时停”
接受率波动来源 3:温度设置
- temperature=0(贪心):接受率最高(90%+)—— deterministic 行为
- temperature=1(默认):接受率 80% 左右
- temperature=2(高随机):接受率显著下降到 50% 以下
接受率波动来源 4:长 context 影响
- short context(< 4K):接受率正常
- mid context(4K-128K):接受率与 short 相当
- long context(128K-1M):接受率轻度下降,因为长 context 的 attention 误差累积
对 SLA 设计的指导:
不要按”平均接受率 80%” 设 SLA——按 P10(第 10 百分位)设。如果你的产品大量是创意生成(P10 接受率约 50%),SLA 必须按”几乎不投机”的延迟设——避免用户在某些场景下感受到延迟突跳。
这种工程经验是 V4 时代 MTP 部署的核心——不是技术问题,是产品设计问题。
11.10 本章小结
- V4 保留 V3 的 MTP 设计——
num_nextn_predict_layers=1 - MTPBlock 继承自 Block,加了 e_proj / h_proj / 双 RMSNorm / 独立 hc_head
- 复用主模型的 embedding 和 lm_head——参数开销极小(约 1.5% 总参)
- 训练时:MTP 提供 next-next 监督,让训练信号翻倍
- 推理时:MTP 变成投机解码的 draft 头,接受率 80-90%
- 与 EAGLE / Medusa / Lookahead 对比,V4 MTP 在训练成本 + 推理 overhead 上有优势
第 12 章我们离开模型结构,进入 V4 的精度链路:FP4 e2m1 + FP8 e4m3 + ue8m0 scale 的混合精度几何。
评论 0
还没有评论,来说两句吧。
评论加载失败,刷新重试。