02 篇把 --moe-backend deep_gemm_mega_moe 与 --enable-expert-parallel 标成 Flash eval 的硬约束;06 篇讲完 KV 怎么占显存。本文想要分享的是:MoE 一层 forward 里,谁负责选 expert、谁负责算 expert,以及 MegaMoE 与 FusedMoE 在分片轴上的差别。跨卡 all2all、DeepEP 在 15 篇;权重 finalize_mega_moe_weights 在 14 篇。以下以 vLLM main 的 models/deepseek_v4/nvidia/model.py 为准。
一层 MoE forward 概览
DeepSeek-V4 的 MoE 不是「每个 token 过一遍全部 routed expert」。checkpoint 里 n_routed_experts 是路由表大小(具体数字看 config.json),num_experts_per_tok 才是每个 token 实际激活的 k;forward 只算这 k 路,其余 expert 权重不参与本次矩阵乘。
每个 decoder layer 里,FFN 前的 RMSNorm 已在 hyper-connection 融合 kernel 里与残差流一并处理(09 篇),再进入 DeepseekV4MoE。Flash eval 快路径上,一层 MoE 可收成下面这条链:
bash
hidden_states(已 ffn_norm)
→ GateLinear # hidden → fp32 router_logits
→ fused_topk_bias # sqrtsoftplus 选 expert;Hash 层另查 tid2eid
→ prepare_megamoe_inputs # EP symmetric buffer 上 pack hidden + topk
→ fp8_fp4_mega_moe # DeepGEMM MegaMoE kernel
→ (+ shared_experts) # 若有 n_shared_experts
→ reshape 回 token 维
Gate 对每个 token 的 hidden 打出 router logits;fused_topk_bias 根据 scoring 函数和(可选)Hash 查表,得到 top-k 个 expert 编号与权重;MegaMoE 再在 EP 组的 symmetric buffer 上 pack 输入,调用 DeepGEMM fp8_fp4_mega_moe 只算被选中的 expert。若 checkpoint 配置了 n_shared_experts,shared MLP 对全部 token 全量计算,结果加回 routed 输出。
把 --moe-backend 换成非 deep_gemm_mega_moe 的值,同一层会走 FusedMoE:路由仍可用同一套 GateLinear 与 fused_topk_bias(Hash 表经 hash_indices_table 传入),expert 计算改走 vLLM 通用 fused kernel,并按 tensor parallel 切 expert,而不是 EP。
MegaMoE 与 FusedMoE:同一份权重,两条分片轴
开关在 VllmConfig.kernel_config.moe_backend(CLI --moe-backend):
ini
self.use_mega_moe = (
vllm_config.kernel_config.moe_backend == "deep_gemm_mega_moe"
)
| 路径 | experts 类 | expert 权重怎么切 |
|---|---|---|
use_mega_moe == True |
DeepseekV4MegaMoEExperts |
EP :get_ep_group(),每 rank 持一段 physical expert |
use_mega_moe == False |
FusedMoE |
TP :n_physical_experts // tp_size,按 tensor parallel rank 切 |
假设某 token 路由命中 expert #12 和 #47。MegaMoE 路径上,每张 GPU 持若干 expert 的整份权重(按 EP 均分);若 #47 不在本卡,要把该 token 的 hidden 送到持 #47 的 rank,算完再加权汇总,矩阵乘走 DeepGEMM 的 fp8_fp4_mega_moe。FusedMoE 路径上,gate 仍打出同样的 #12、#47,但 expert 权重按 TP rank 切分,每张卡只存自己那份分片,用 vLLM 通用 fused MoE 算子完成 forward。
同一份 Flash checkpoint,换 backend 换的是 expert 算子与分片轴,不是只换一个 kernel 名字。Attention 仍可按 --tensor-parallel-size 切 head(02 篇);MegaMoE 路径下 MoE expert 用 EP,不用 TP 切 expert。规划卡数时要同时满足:TP 能整除 head 数,EP 组大小能整除 physical expert 总数(见下节,以 HF config 为准)。
开 MegaMoE 时,约束分两阶段出现,排障时不要混为一谈:
| 条件 | 何时检查 | 不满足时 |
|---|---|---|
enable_expert_parallel == True |
DeepseekV4MoE.__init__ |
NotImplementedError |
scoring_func == "sqrtsoftplus" |
init | MegaMoE 仅支持该路由 |
expert_dtype == "fp4"(Flash 典型) |
init | MegaMoE 仅支持 fp4 experts |
| CUDA SM100(capability major == 10) | finalize_weights |
DeepGEMM MegaMoE requires SM100 GPUs |
hidden_size / moe_intermediate_size 为 128 倍数 |
_check_runtime_supported(finalize 内) |
ValueError |
因此 eval 能过 init、首 token 或首次 MoE forward 才报错,常见是 SM100 或 128 对齐在 finalize 阶段才触发。关掉 deep_gemm_mega_moe 会落到 FusedMoE + TP 分 expert------不是「Flash 绝对不能纯 TP」,而是官方 eval 快路径绑定了 MegaMoE + EP + fp4 + sqrtsoftplus + SM100 这一组组合。
调 deep_gemm.fp8_fp4_mega_moe 之前,prepare_megamoe_inputs(nvidia/ops/prepare_megamoe.py)会在 EP group 的 symmetric buffer 上 pack 本 batch 的 hidden、scale 与 topk 索引/权重。这一步是在多卡 MoE 里把输入整理成 kernel 要的 layout,属于 EP 侧的输入 staging;token 如何派发到持有对应 expert 权重的 rank、all2all 走 DeepEP 还是别的 backend,由 parallel_config 决定。
路由:GateLinear、sqrtsoftplus、Hash 两层逻辑
Gate 是 GateLinear(gate_linear.py):对 hidden 做线性变换,输出 fp32 router logits。MegaMoE 路径在块外先 self.gate(hidden_states),再调 fused_topk_bias。
sqrtsoftplus 与 correction bias
Flash checkpoint 常用 scoring_func == "sqrtsoftplus"。CUDA 路径走 ops.topk_hash_softplus_sqrt(fused_topk_bias_router.py):先把 logits 变成 scores = sqrt(softplus(logits))。
非 Hash 层且在 topk_method == "noaux_tc" 时会挂 e_score_correction_bias(HF 权重名 .ffn.gate.bias 加载时映射到此字段)。选哪 k 个 expert 时,用 scores + bias 做 top-k;写进 topk_weights 的数值,是从未加 bias 的 scores 里,按选中 expert 做 gather 得到的。源码注释写明:bias 只参与 expert 选择,不参与权重计算。
Hash MoE:查表定 expert,gate 定权重
前 num_hash_layers 层是 Hash MoE(extract_layer_index(prefix) < config.num_hash_layers)。init 里挂 tid2eid,形状 [vocab_size, top_k],Hash 层不用 e_score_correction_bias:
ini
is_hash_moe = extract_layer_index(prefix) < config.num_hash_layers
if is_hash_moe:
self.gate.tid2eid = nn.Parameter(...) # [vocab_size, top_k]
Hash 层的路由分两步:
- Expert 编号 :由
input_ids查tid2eid[token_id],每个 token id 对应预定义的 k 个 expert id。 - Expert 权重:gate 仍要算;从 sqrt(softplus(logits)) 得到的 scores 里,按上一步查到的 expert id 做 gather,得到 topk_weights。
因此 forward 里若 tid2eid is not None 而 input_ids is None,会直接 ValueError------没有 token id 就无法查表。DeepseekV4Model.forward 在 MegaMoE 下会把 input_ids 转成 int64,每层 ffn(x, input_ids) 传入;prefill 与 decode 都需要这条链。排障「Hash 层路由全错」时,先确认 input_ids 是否传到 MoE,再查 gate logits。
EP:physical expert 怎么切到各张 GPU
Expert Parallel(EP)的含义是:不同 GPU 各持一部分 expert 权重;某个 token 激活的 expert 若不在本卡,需要跨卡通信把 token 送过去、再把结果收回来(通信细节在 15 篇)。--enable-expert-parallel 写入 parallel_config.enable_expert_parallel;MegaMoE 还要求此项为 True,否则 init 即失败。
MegaMoE 的 _init_mega_moe_experts 从 EP group 取 rank,按 physical expert 均分:
python
self.ep_group = get_ep_group()
self.ep_size = self.ep_group.world_size
self.n_local_physical_experts = self.n_physical_experts // self.ep_size
self.experts_start_idx = self.ep_rank * self.n_local_physical_experts
n_physical_experts = n_routed_experts + n_redundant_experts(EPLB 的 redundant replica 算进 physical 总数),必须能被 ep_size 整除,否则 assert。本 GPU 只加载 experts_start_idx .. experts_end_idx 这一段权重;DeepseekV4MegaMoEExperts.weight_loader 经 _map_global_expert_id 跳过不属于本 rank 的 global expert。
fused_topk_bias 输出的 topk_ids 先是 logical routed expert 编号。若开了 EPLB,MegaMoE forward 里还会经 eplb_map_to_physical_and_record 映射到 physical replica,再进 prepare_megamoe_inputs。
启动失败与 forward 异常怎么收窄
栈若在 DeepseekV4MoE.__init__,可先对照:
| CLI / checkpoint | 代码触点 |
|---|---|
--moe-backend deep_gemm_mega_moe |
kernel_config → use_mega_moe |
--enable-expert-parallel |
MegaMoE init assert |
expert_dtype=fp4 + scoring_func=sqrtsoftplus |
HF config.json → 同上 assert |
已通过 init、运行中数值或性能异常,可沿 router logits → topk_ids → Hash 层是否缺 input_ids → MegaMoE 是否已完成 finalize 往下查。
收尾
Flash eval 的 MoE 快路径是 MegaMoE + EP:路由侧 GateLinear 出 fp32 logits,再经 fused_topk_bias(sqrtsoftplus;早期 Hash 层用 tid2eid 定 expert id、gate 定权重,且必须传 input_ids);计算侧 prepare_megamoe_inputs 做 EP staging 后调 fp8_fp4_mega_moe。关掉 MegaMoE 则走 FusedMoE,expert 按 TP 切------路径存在,是否与 Flash checkpoint 匹配需自行 benchmark。
下一篇 08 ------ deepseek_v4_fp8 线性层量化与 MXFP4 experts(Flash 与 Flash-Base 在权重量化上的分水岭)。