Expert Parallel / MoE
系统 · MoE 分布式训练 · 2020–2022

Expert Parallel:专家分卡,token 过 all-to-all

MoE 把 FFN 换成 E 个专家,每个 token 只走 top-k 个。EP 把专家归属按 rank 切开, token 通过 dispatch/combine 两次 all-to-all 找到专家;capacity factor 把动态路由压成静态形状, 代价是 drop 或 padding。TP 切专家内部矩阵,DP 复制/分片其余参数——三条轴各管一段。

GShard · Switch · DeepSpeed-MoE · Tutel · MegaBlocks · Megatron-Core 70f7a26 · DeepSpeed f47dc3e Switch ↗ GShard ↗ Megatron-Core ↗ DeepSpeed ↗
TL;DR · 30 秒抓住核心

稠密模型每 token 的 FLOPs 随总参数涨;MoE 只让 top-k 个专家参与,总参数随 E 涨,单 token 计算只随 k 个专家涨。 GShard 的 600B MoE 在 2048 张 TPU v3 上训练 4.0 天、avg BLEU 44.3;同文 dense T(96L) 要 42 天、235.5 TPU core-years,avg BLEU 36.9(Table 3)。

EP 切的是“哪个 rank 拥有哪个专家”,不切单个专家内部的矩阵。 每个 rank 只存 E/EP 个专家;token 先在本 rank 做 top-k,再按目标专家分组,经 dispatch all-to-all 送出, 专家算完再经 combine all-to-all 收回。all-to-all 路径上每层前向 2 次、反向 2 次,共 4 次(tensor_parallel/mappings.py:428;sharded_moe.py:100);Megatron-Core 默认走 all-gather,见 §04。

capacity factor 把动态路由压成静态槽位。 容量 = ceil((T_local × k / E) × cf);超出容量的分配被 drop,或补齐到容量后计算。 Megatron-Core 默认 moe_expert_capacity_factor=None(dropless),DeepSpeed 的 MoE 默认 capacity_factor=1.0 且 drop_tokens=True——默认值不同,账也不同。

EP 的收益不是免费的。 GShard 从 128 到 2048 个专家(16×),step time 只涨 1.7×,但 all-to-all 在 MoE+Transformer 层里的时间占比从 16% 到 36%(§5.2)。 DeepSpeed-MoE 在 128 张 A100 上把 1.3B+MoE-128 的训练吞吐做到 372 samples/s;对照的 6.7B dense 是 70 samples/s,两者达到相同模型质量(Table 3 的条件)。 比值 5.31× 是按表内 372/70 复算,原文 gain 列写 5×;代价是 52B 对 6.7B 的参数与显存。

44.3BLEU
GShard MoE(2048E, 36L),600B 参数,2048 TPU v3,4.0 天;同文 dense T(96L) 为 36.9
36%
2048 个专家时 all-to-all 在 MoE+Transformer 层中的时间占比;128 个专家时为 16%
5.31×
DeepSpeed-MoE:1.3B+MoE-128 372 vs 6.7B dense 70 samples/s(128 A100);两者达到相同模型质量,5.31× 为 372/70 复算(原文 gain 列写 5×)
01

稠密扩展把每个 token 都算一遍,稀疏扩展只算被选中的专家

先分清两种“变大”:稠密模型是每 token 计算量随参数涨;MoE 是总参数涨、单 token 计算只随 top-k 涨。

稠密 Transformer 的 FFN 每层只有一个 MLP,所有 token 都过同一个 MLP。把隐藏维、层数或词表做大,参数量上去, 每个 token 的 FLOPs 也一起上去。MoE 换掉 FFN:放 E 个专家,每个 token 只选 top-k 个。总参数可以随 E 增长, 单 token 的计算量约等于 k 个专家,而不是 E 个。

稠密:FLOPs/token ≈ 2 · Pdense
MoE:FLOPs/token ≈ 2 · (Pshared + k · Pexpert)
Ptotal ≈ Pshared + E · Pexpert
近似式,忽略 attention、路由与通信;Pshared 含非专家参数,Pexpert 是单个专家的 FFN 参数。
Dense

参数和计算一起涨

FFN 只有一份,E=1。想加容量就得加宽或加深,每 token 的矩阵乘也跟着变大。单卡放不下时,用 TP/PP/DP 把同一份计算切开或复制。

MoE

参数涨,激活的计算不涨

E 个专家只激活 k 个。总参数可以到 E × Pexpert,但每 token 只算 k 个专家的 FFN。代价是专家必须放在不同设备上,token 要跨设备流动。

GShard 的对照很直观:MoE(2048E, 36L) 有 600B 参数,在 2048 张 TPU v3 上训练 4.0 天,avg BLEU 44.3; 同文的 dense T(96L) 是 2.3B 参数(§1),训练 42 天、235.5 TPU core-years,avg BLEU 36.9(Table 3)。 这不是同参数量对照:dense 侧是 2.3B,MoE 侧是 600B。它说明的是“在相同设备数(2048 TPU v3)下,稀疏扩展能把参数做大,而每 token 计算不按参数比例涨”。

难点也随之出现:路由是数据相关的,专家却按设备静态放置。某个 token 选了 rank 1 上的专家,它的激活就得从 rank 0 发过去; 一个 expert 收到的 token 数每步都在变,但 GPU 上的 buffer 和 GEMM 形状需要提前确定。capacity factor、token drop、 all-to-all 和负载均衡损失,都是为了处理“动态路由”和“静态硬件”之间的这道缝。

02

TP 切专家内部,EP 切专家归属,DP 管副本

TP/EP/DP 三条轴不是替代关系:TP 让单个专家放得下,EP 让专家总数上得去,DP 给专家提供数据副本;PP 再切层。

TP-only

把每个专家都切小

对每个专家的 H×F 矩阵做列/行切分,每张卡算一部分,出口 all-reduce。每卡专家权重是 E × Pexpert / TP;TP 越大,单个专家的 GEMM 越小,每层仍要 all-reduce。

Megatron-Core 里专家 TP 组是独立参数:expert_tensor_parallel_size 默认等于 TP(parallel_state.py:951)。

EP + capacity

把专家整块分给 rank

每个 rank 拥有 E/EP 个完整专家。token 按目标专家分组,经 all-to-all 送到对应 rank;算完再 all-to-all 收回。容量因子决定每个专家最多收多少 token,超出就 drop 或 padding。

GShard、Switch、DeepSpeed-MoE 都走这条路线;DeepSpeed 论文建议 EP 等于专家数,避免每个专家分到的 token 变少(§4.1.3)。

Dropless

不设固定容量

不把每个专家补齐到容量,而是按实际 token 数构造 block-sparse 计算。MegaBlocks 用 128×128 的块稀疏 GEMM 处理不规则专家 batch,不丢 token;但每个 expert batch 仍会 pad 到 128 的整数倍(§5.1.2 / §5.2)。

它改的是“专家计算的形状”,dispatch/combine 的 all-to-all 仍在。MegaBlocks 在 dMoE-Medium 上比 padding-based Tutel 快 4.35×(Figure 7)。

分工

四条轴各自切什么、通信什么

  • DP:切 batch;非专家参数按普通 DP 组 all-reduce 梯度。专家参数另有一个 expert-DP 组,只同步专家参数的副本(parallel_state.py:2590;deepspeed/utils/groups.py:393)。
  • TP:切单层/单专家内部的矩阵;每层 all-reduce。专家 TP 组可以和普通 TP 组不同,默认相同(parallel_state.py:951)。
  • EP:切专家归属;dispatch/combine 各一次 all-to-all。专家权重不在 EP 组内做梯度 all-reduce——每个专家在 EP 维度上只属于一个 rank。
  • PP:切层;层间传激活。MoE 层可以落在不同 pipeline stage 上,PR-MoE 甚至在不同层用不同 EP 度数(DeepSpeed-MoE §4.1.3)。

所以“EP 与 DP 的组网约束”不是一句口号:EP 组负责 all-to-all,expert-DP 组负责专家梯度,普通 DP 组负责非专家梯度;三组各管一段,rank 排列必须让每一组都连续或按框架约定交错。

DeepSpeed-MoE 给了多专家、多数据并行的具体配置:一个在 128 张 GPU 上、不同 MoE 层分别有 32/64/128 个专家的 PR-MoE, 非专家部分用 128-way DP,专家部分用 {32,64,128} 的 EP 加 {4,2,1} 的 DP(§4.1.3)。 这样每张 GPU 在每层恰好负责 1 个专家,不因层而异。这个例子说明 EP 和 DP 不是二选一,而是可以按层组合。

03

路由器选 top-k,capacity 把动态路由压成静态槽位

先看 router 怎么给每个 token 选专家,再看容量怎么限制每个专家收多少 token,最后看 aux loss 和 z-loss 各自管什么。

Router:每个 token 算 E 个分数,取 top-k

路由器是一个小的线性层 Wg ∈ RH×E。每个 token 得到 E 个 logits,softmax 或 sigmoid 后取 top-k; 被选中专家的 gate 值用来加权专家输出。Switch 用 top-1;GShard 用 top-2,并按权重随机决定是否把 token 发给第二专家; Megatron-Core 默认 top-2、softmax,且 moe_router_pre_softmax=False(先选 top-k,再在选中项上归一化,transformer_config.py:965,1004,1013)。

logits = x · Wg  →  p = softmax(logits)
TopK(p) = {(e1, p1), …, (ek, pk)}
y = Σi=1..k pi · Expertei(x)
top-k 路由与加权求和;Switch 取 k=1(§2.1),GShard 取至多 2 个(§2.2)。

Capacity:每个专家最多收多少 token

Switch 的容量公式是“batch 里的 token 数 / 专家数 × capacity factor”(Eq. 3);GShard 的 expert capacity 是 O(N/E)(每个专家),每个 group 分到 2N/(G·E)(G 为 group 数,§2.2)。 把 top-k 写进分子后,就是 Megatron-Core 的 get_capacity:

Crank = ceil( (Tlocal × k / E) × cf )
Cexpert = Crank × EP   (TP=1)
Megatron-Core 的 get_capacity 用 math.ceil(moe_utils.py:265);dispatcher 以 T_local × topk 为分子,并把每个本地专家的 token 数乘以 TP × EP(token_dispatcher.py:534,544)。这里的 C_rank 是单个 rank 对每个专家的槽位,不是整个 rank 的 token 预算;GShard 按 group 分配容量,记法不同。

超出容量的分配有两种处理:drop 或 pad。drop 时,被丢的 token 不经过这个专家,其表示直接走 residual; Megatron-Core 的 moe_token_drop_policy 可选 "probs"(丢概率最低的)或 "position"(丢 batch 末尾的,transformer_config.py:1163)。 pad 时,把每个专家的输入补齐到容量,GEMM 形状固定,代价是白算 padding。Megatron-Core 默认 moe_expert_capacity_factor=None、moe_pad_expert_input_to_capacity=False,也就是默认不做容量限制、不 pad(transformer_config.py:1148,1152); DeepSpeed 的 MoE 默认 capacity_factor=1.0、min_capacity=4、drop_tokens=True(layer.py:44-49)。

Aux loss 和 z-loss:一个管均衡,一个管数值幅度

Switch/GShard 的辅助负载均衡损失是“每个专家的 token 比例”与“每个专家的平均路由概率”的点积,再乘专家数:

Laux = α · E · Σi fi · Pi
fi = (路由到专家 i 的分配数) / (T × k)
Pi = (1/T) · Σt pt,i
Switch Eq. 4–6(k=1);GShard §2.2 用 mean gates 作为可微近似;Megatron-Core 的 switch_load_balancing_loss_func 把 topk 写进分母(moe_utils.py:72)。

在均匀路由下,fi = 1/E、Pi = 1/E,所以 Laux = α。 它只通过 Pi 对 router 可微;fi 来自不可微的 top-k 选择。Megatron-Core 的 moe_aux_loss_coeff 默认是 0.0,注释建议从 1e-2 起步(transformer_config.py:1103); DeepSpeed 的 top1gating/top2gating/topkgating 直接返回 l_aux(sharded_moe.py:264,357,442)。

z-loss 不是负载均衡项,它压的是 router logits 的 log-sum-exp 幅度:

Lz = β · meant ( log Σj exp(logitst,j) )2
Megatron-Core 的 z_loss_func 对每个 token 的 logsumexp(logits) 平方后取均值,再乘系数(moe_utils.py:170);默认 moe_z_loss_coeff=None,注释建议 1e-3 起步(transformer_config.py:1108)。
实现差异

DeepSpeed 的 top-2 l_aux 缩放与 Megatron 不同

DeepSpeed 的 top1gating 用 sum(me·ce)·E,top2gating 用 mean(me·ce)·E², topkgating 用 mean(me·ce)·E²/k(sharded_moe.py:264,357,442)。 Megatron-Core 的 switch_load_balancing_loss_func 统一把 topk 放进分母(moe_utils.py:163)。 按代码逐项对照,DeepSpeed 的 top-2 路径没有除以 2,而 top-k 路径除了 k;两套实现给出的 l_aux 数值不能直接横向比较。

单 rank 对每专家槽位 C_rank
40
每专家总容量 C_rank × EP
320
最热专家本 rank 负载 / 本 rank 分配
512.0 / 2048
单 rank 溢出 / 丢弃率
472.0 / 23.0%
容量账本(T_global = 8192,E = 64,k = 2)
T_local = 1024;A = 2048;C_rank = 40;C_expert = 320;单 rank 最热 512 / 溢出 472 / 丢弃率 23.0%;EP 对称时总负载 4096、总溢出 3776(总容量 320);pad 每 rank 2560 行 / 20.0 MiB;dropless 2048 行 / 16.0 MiB。

简化模型:只用“最热专家在本 rank 的份额”描述不均衡,假设每个 EP rank 上该专家的负载相同(各 512 条分配)。 C_rank 是单 rank 对每个专家的槽位,整个 rank 的发送槽位还要乘 E;单 rank 溢出 472,EP 对称时每专家总负载 512×8=4096、总容量 320、总溢出 3776。 未覆盖真实路由分布、top-k 中一条分配被丢但另一条仍处理、probs 的第二次 all-to-all、TP>1、共享专家与网络 overlap。 丢弃率按 assignment 计,分母是本 rank 的全部 2048 条分配,不是最热专家的 512 条;也不是按 token 计。

手算对账 A:capacity factor 1.25 下的容量与 A2A 字节 默认参数:EP=8,T_global=8192

参数:T_global=8192,EP=8 → T_local=1024;E=64;k=2;cf=1.25;最热专家在本 rank 的份额 25%;H=4096;bf16 每元素 2 B。

1. 路由分配:A = T_local × k = 1024 × 2 = 2048。

2. 单 rank 对每个专家的槽位:C_rank = ceil((A / E) × cf) = ceil((2048 / 64) × 1.25) = ceil(40) = 40。

3. 每个专家从 EP 个 rank 收到的总容量:C_expert = C_rank × EP = 40 × 8 = 320(TP=1)。

4. 单 rank 上最热专家的分配:0.25 × 2048 = 512;单 rank 对每专家容量 40,所以单 rank 溢出 512 − 40 = 472,丢弃率 472 / 2048 × 100 = 23.0%。若各 rank 对称,每专家总负载 512 × 8 = 4096,总容量 320,总溢出 4096 − 320 = 3776,丢弃率 3776 / 16384 × 100 = 23.0%,与单 rank 口径相同。

5. pad 模式每 rank 发送行数:E × C_rank = 64 × 40 = 2560;字节:2560 × 4096 × 2 = 20,971,520 B = 20.0 MiB。dropless 行数:A = 2048;字节:2048 × 4096 × 2 = 16,777,216 B = 16.0 MiB;比值:20 / 16 = 1.25。

显示规则:容量取整;百分比与 MiB 保留 1 位(四舍五入);参数个数用千分位。交互默认输出(单 rank 溢出 472 / 23.0%;EP 对称时总负载 4096、总溢出 3776)与以上逐项相同。

覆盖:单 rank 与 EP 对称两种口径、pad 与 dropless 两种行数、每层 dispatch 字节。未覆盖:真实路由分布、top-k 中一条被丢但另一条仍处理、probs 的第二次 all-to-all、TP>1、共享专家、网络延迟与 overlap。

04

每层 4 次 all-to-all;专家权重按 EP 除,梯度按 expert-DP 同步

把通信量和显存写成字节账,才能判断 EP 是在省时间,还是把时间换成了网络。

All-to-all 字节:每层前向 2 次、反向 2 次

dispatch 把 token 按目标专家分组送出,combine 把专家输出按源 rank 收回。设每个 rank 每层要处理的隐藏状态行数为 R, 隐藏维为 H,元素字节为 b:

V = R × H × b
dropless:R = Tlocal × k
pad:R = E × Crank
前向 = 2V(dispatch + combine) ·  反向 = 2V  →  每层 4V
b=2(bf16);TP=1、未计 probs 的标量 all-to-all;dropless 的 combine 行数按实际路由行数计,pad 的 combine 按容量计。

反向为什么也是 2 次?框架把 all-to-all 包在 autograd 里:combine 的反向是 dispatch 形状的 all-to-all, dispatch 的反向是 combine 形状的 all-to-all。Megatron-Core 的 _AllToAll.backward 交换 input/output splits 再调用一次 all-to-all(tensor_parallel/mappings.py:474);DeepSpeed 的 _AllToAll.backward 直接对 grad 再做一次 all-to-all (sharded_moe.py:108)。所以一个 MoE 层在训练步里共有 4 次 all-to-all。

专家权重显存:每卡只放 E/EP 个专家

Mexpert/rank = (E / EP) × 3 × H × F × b
按 TP=1;若专家 TP>1,每卡权重再除以专家 TP。3 个矩阵是 gate / up / down,按 gated FFN 计;若专家是 2 矩阵 FFN,把 3 换成 2。b=2(bf16);只算专家 FFN 权重,不含优化器状态、梯度、激活、embedding 与 attention。
Megatron-Core

默认 all-gather,可选 all-to-all

默认 moe_token_dispatcher_type="allgather"(transformer_config.py:1119): 在 TP×EP 组内 all-gather token,本地按专家 permutation,combine 用 reduce-scatter 收回(token_dispatcher.py:380)。 也可切到 "alltoall":dispatch 用变长 all_to_all_single,支持 pad-to-capacity(token_dispatcher.py:697)。 all-gather 路径把每个 rank 的 token 复制到组内所有 rank,all-to-all 路径只把 token 送到目标专家所在 rank;两条路径的字节账不同。

DeepSpeed-MoE

EP>1 时走等分 all-to-all

MOELayer.forward 在 ep_size > 1 时调用 _AllToAll.apply(sharded_moe.py:701), combine 再调用一次(sharded_moe.py:730)。_AllToAll.forward 用等分 all_to_all_single(sharded_moe.py:104),dispatch buffer 按容量 [E, C, H] 组织; top-1/top-2 的 l_aux 在 gate 内计算并返回(sharded_moe.py:264,357)。

专家梯度:不在 EP 组内 all-reduce

EP 把专家分给不同 rank;在 EP 维度上,每个专家只属于一个 rank,只在该 rank 的专家副本上计算前向和反向,所以专家权重梯度不需要跨 EP 组 all-reduce。 需要同步的是专家参数的 DP 副本:Megatron-Core 把它们放在 expert-DP 组里,文档字符串写明该组用于 “expert grad all-reduce, optimizer state, checkpoint replicas”(parallel_state.py:2590)。 DeepSpeed 在 deepspeed/utils/groups.py 里也把 EP 组与 expert-DP 组分开:例如 world=16、EP=2 时, expert-DP 组是 [0,2,4,…,14] 和 [1,3,5,…,15],注释写明 “all reduce is only on MoE params” (deepspeed/utils/groups.py:393)。非专家参数仍走普通 DP 组。若专家还做 TP 切分,专家 TP 组内仍有 all-reduce;这里只讨论 EP 与 expert-DP 的分工。

每卡专家数 E / EP
8
每卡专家权重(bf16)
2.625 GiB
dispatch 每卡每层
16.0 MiB
4 次 A2A 合计 / 层
64.0 MiB
EP 预算账本(T_global = 8192,E = 64,k = 2)
T_local = 1024;C_rank = 40;dropless dispatch 16.0 MiB;前向 32.0 MiB;含反向 64.0 MiB。

简化:每卡专家数按 E/EP 均分;按 gated FFN 的 3 个矩阵计(2 矩阵 FFN 数值不同);只算 bf16 专家 FFN 权重; A2A 只算隐藏状态,未计 probs 标量、优化器状态、激活、共享专家、专家梯度 all-reduce 与网络 latency。EP 变化时保持 T_global 不变,所以每卡 token 数按 1/EP 缩小。

手算对账 B:EP=8 的专家权重显存与每层 A2A 字节 默认参数:EP=8,H=4096,F=14336

参数:E=64,EP=8 → 每卡 8 个专家;每个专家 3 个矩阵(gate / up / down),每个 H×F;bf16 每元素 2 B。

1. 每个专家参数:3 × 4096 × 14336 = 176,160,768。每卡 8 个:8 × 176,160,768 = 1,409,286,144 参数。

2. bf16 权重字节:1,409,286,144 × 2 = 2,818,572,288 B;2,818,572,288 / 1,073,741,824 = 2.625 GiB。

3. T_local = 8192 / 8 = 1024;单 rank 对每个专家的槽位 C_rank = ceil((1024 × 2 / 64) × 1.25) = 40。

4. dropless dispatch:1024 × 2 × 4096 × 2 = 16,777,216 B = 16.0 MiB;pad dispatch:64 × 40 × 4096 × 2 = 20,971,520 B = 20.0 MiB。

5. 每层前向 = dispatch + combine = 2×;反向 = 2×;合计 4×。dropless:前向 2 × 16.0 = 32.0 MiB,合计 4 × 16.0 = 64.0 MiB;pad:前向 2 × 20.0 = 40.0 MiB,合计 4 × 20.0 = 80.0 MiB。

6. 64 个专家的总权重:64 × 176,160,768 = 11,274,289,152 参数;2 × 11,274,289,152 = 22,548,578,304 B = 21.000 GiB(仅 bf16 权重)。

显示规则同上。交互默认(EP=8、H=4096、F=14336、dropless)输出 8 个专家/卡、2.625 GiB、16.0 MiB、32.0 MiB、64.0 MiB,与以上逐项相同。

覆盖:FFN 三矩阵的 bf16 权重、EP 均分专家、每层 4 次 A2A 的字节。未覆盖:优化器状态、梯度、激活、临时 buffer、embedding/attention、TP>1、专家数不被 EP 整除、网络带宽与 latency、专家梯度 all-reduce。

Table 2(Tutel):All-to-All 在 MoE 层开销中的占比,以及完全重叠后的理论上限。模型设置:fflayer hidden 4K、channel 4K、2 experts/GPU、64K tokens/iteration。

GPU 数MoE 开销 (ms)All-to-All (ms)All-to-All 占比潜在加速
16560.9189.133.7%1.51×
64698.9323.846.3%1.86×
256866.4491.356.7%1.76×

潜在加速是“All-to-All 与计算完全重叠”时的上限(Table 2 原表最后一列),不是实测端到端加速。占比随规模上升;原表的 potential overhead saving 从 46.3% 到 43.3%,所以 256 GPU 的潜在加速(1.76×)低于 64 GPU(1.86×)。

Tutel 另外报告了单层 MoE 的显存:32,768 tokens/step 时,Fairseq MoE 是 57.9 GiB,Tutel 是 5.7 GiB,表内写作 -90.2%(Table 5);4,096 tokens/step 时是 3.7 GiB 对 2.9 GiB(-21.6%)。条件是 D=H=4096、top-k=2、E_g=2 的静态设置。 Table 5 只记录单层 MoE 的 GPU 显存;Tutel 在 §4.2 把 fast encode/decode 列为显存优化点,表内没有把差距拆成 padding 与 buffer 两部分。

05

前向两次、反向两次,token 在 dispatch/combine 之间往返

跟着一个 MoE 层走一遍:router 选专家,dispatch 送到专家所在 rank,专家算完,combine 收回并加权求和。

MoE 层的 token 路径:token 进 router 选 top-k,经 dispatch all-to-all 送到专家所在 rank,专家计算后经 combine all-to-all 收回 rank r token T_local × H Router top-k 选择 softmax p, expert id All-to-All dispatch 按目标专家分组 变长 / 补齐 EP all-to-all EP rank 0 · experts 0–1 E0 FFN E1 FFN EP rank 1 · experts 2–3 E2 FFN E3 FFN All-to-All combine 按源 rank 收回 Σ p_i · E_i(x) 回源 rank 后 unpermute
本页重绘:一个 MoE 层的 token 路径。图中 EP=2、每 rank 2 个专家、top-k=2;只画 rank r 的 token,其他 rank 走同一流程。dispatch 与 combine 各一次 all-to-all;反向各自再走一次反向 all-to-all。

前向:路由、派发、专家、收回

  1. Router:每个 token 算 E 个 logits,取 top-k,得到专家 id 和 gate 概率。Megatron-Core 默认先 top-k 再 softmax(moe_router_pre_softmax=False,transformer_config.py:1004)。
  2. Permute:按目标 rank/专家排序,把 token 重排成发送 buffer。dropless 时行数是 T_local × k;pad 时每 rank 发送 E × C_rank 行。
  3. Dispatch all-to-all:每个 rank 把发往其他 rank 的 chunk 送出,同时接收自己本地专家需要的 token。Megatron-Core 的 all-to-all 支持变长 splits(token_dispatcher.py:720);DeepSpeed 用容量大小的等分 buffer(sharded_moe.py:104)。
  4. Experts:本地 E/EP 个专家处理自己收到的 token。Megatron-Core 可用 TEGroupedMLP 或 SequentialMLP(experts.py:282,1942);DeepSpeed 用 Experts/GroupedExperts(experts.py:13;ep_experts.py:439)。
  5. Combine all-to-all:专家输出按源 rank 发回;unpermute 恢复原顺序,乘 gate 概率并求和,最后加 residual。

反向:4 次 all-to-all 的来历

  1. Combine 的反向:输出梯度按 combine 的逆方向做一次 all-to-all,回到专家所在 rank。
  2. Expert 反向:本地专家算权重梯度和输入梯度。专家权重梯度不跨 EP 组同步;跨 expert-DP 副本做 all-reduce/reduce-scatter(parallel_state.py:2590)。
  3. Dispatch 的反向:输入梯度再按 dispatch 的逆方向做一次 all-to-all,回到源 rank。
  4. Router 反向:gate 概率的梯度穿过加权求和回到 router;aux loss 的梯度只通过 Pi 回传,z-loss 直接作用在 logits 上(moe_utils.py:170)。
rank r: [Tlocal, H]
top-k: [Tlocal × k, H] + probs
dispatch: pad [E × Crank, H] / dropless [Tlocal × k, H]
experts: pad [E × Crank, H] → [E × Crank, H] / dropless [Tlocal × k, H] → [Tlocal × k, H]
combine: pad [E × Crank, H] / dropless [Tlocal × k, H]
output: [Tlocal, H]
形状按 TP=1 书写;pad 的 E × C_rank 是每 rank 发送/接收的槽位数,dropless 是实际路由行数(均衡时约 T_local × k);combine 的槽位数与 dispatch 相同。实际变量长度由 routing map 决定。

共享专家不在上图里:它不经过 router,也不走 all-to-all,而是对每个 token 直接计算,再与 routed 专家输出相加。 Megatron-Core 用 shared_experts.py 的 SharedExpertMLP;DeepSpeed-MoE 的 Residual-MoE 用 use_residual 加一个 dense MLP(layer.py:81)。图中未画这条分支,容量账和 A2A 字节账也不含它。

06

报告数字:EP 把 step time 压到亚线性,但通信占比上升

下面每一行对应一个来源与配置;不同来源的行指标口径不同,不能跨来源比较或相加。同一论文内的对照(如 Switch 的 CF、GShard 的 MoE vs dense)只在原文设定的指标内成立。模型、并行配置、硬件和指标都写在同一行。

Table 1(Switch):128 experts、experts every other FFN、32 TPUv3 cores、相同计算量。质量列是 100k steps 的 Neg. Log Perp.,越低越好;时间列是到达 -1.50 阈值的时间。

模型capacity factor质量 @100k (Neg. Log Perp.)到 -1.50 时间 (h)速度 (examples/s)
Switch-Base2.0-1.55472.8860
Switch-Base1.25-1.55365.0910
Switch-Base1.0-1.56162.81000
T5-Large dense—-1.550131.1470

Switch-Base 与 T5-Large 不是同参数量模型;Table 1 的对照是在相同硬件、相同计算量(32 TPUv3 cores)下:质量列是 100k steps 的 Neg. Log Perp.,时间列是各模型到达 -1.50 阈值的时间(原文明确说模型需要训练超过 100k steps 才能到阈值)。Switch-Base 从 CF 2.0 到 1.25 的时间是 72.8→65.0 h,速度是 860→910 examples/s;CF 1.0 进一步到 62.8 h / 1000 examples/s。

跨论文结果:来源分别是 Table 3(GShard)、Table 3(DeepSpeed-MoE)、Table 7(Tutel)、Figure 7–8(MegaBlocks;8 A100、8-way EP、64 experts、top-1 的设置见 §6)、Table 9(Switch)。DeepSpeed 两行是 Table 3 的同质量对照:1.3B+MoE-128 与 6.7B dense 达到相同模型质量,372/70 是吞吐比,原文 gain 列写 5×。不同来源的行指标口径不同,禁止跨来源比较或相加。

来源模型 / 设置并行 / 硬件指标数值
Switch Table 1Switch-Base,128 experts,CF 1.2532 TPUv3 cores到 -1.50 时间 / 速度65.0 h / 910 ex/s
Switch Table 9Switch-C,2048 experts,EP only,1.6T(§5.4)TPUv3,EP only到固定 perplexity4× vs T5-XXL
GShard Table 3MoE(2048E, 36L),600B2048 TPUv3avg BLEU / core-years44.3 / 22.4
GShard Table 3dense T(96L),2.3B(§1)2048 TPUv3avg BLEU / core-years36.9 / 235.5
DeepSpeed Table 31.3B+MoE-128(52B)128 A100training samples/s372
DeepSpeed Table 36.7B dense128 A100training samples/s70
Tutel Table 7SwinV2-MoE128 A100,1 expert/GPUtrain / infer images/s226 / 792
Tutel Table 7Fairseq MoE128 A100train / infer images/s146 / 375
MegaBlocks Figure 7dMoE-Medium vs padding-based Tutel8 A100,8-way EPend-to-end 加速4.35×
MegaBlocks Figure 8dMoE-Medium vs 逐模型调 CF 的 Tutel8 A100,8-way EPend-to-end 加速1.18×
MegaBlocks Figure 7dMoE vs Megatron-LM dense8 A100(dense 基线)end-to-end 加速1.8–2.4×

MegaBlocks 的两条对照不同:Figure 7 的 4.35× 是相对“padding-based Tutel”路径;Figure 8 的 1.18× 是相对“为每个模型调过 capacity factor 的 Tutel”。论文摘要写 “up to 40% over MoEs trained with Tutel”,对应 Figure 8 中 XS 的 1.38×。这几个数字不能合并成一个“对 Tutel 的加速”。

Switch 的 Table 1 在 C4、128 experts、32 TPUv3 cores 的设定下报告:CF 1.0 和 1.25 比 2.0 更快。 T5-Large 到阈值要 131.1 h,Switch-Base CF 1.25 要 65.0 h,时间比是 2.02×;速度 910 vs 470 examples/s, 速度比是 1.94×;时间少 50.4%(相对口径:(131.1−65.0)/131.1)。这些只在 Table 1 的设定内成立。

GShard 的扩展账更直接:专家数从 128 到 2048(16×),执行时间只涨 1.7×,但 all-to-all 在 MoE+Transformer 层里的占比从 16% 到 36%(§5.2)。 600B MoE 的 22.4 TPU core-years 对 dense T(96L) 的 235.5,core-year 比是 10.5×;avg BLEU 44.3 对 36.9,差 7.4(Table 3)。 这不是同参数量对照,前面已经说明。

DeepSpeed-MoE 在 128 张 A100 上把 1.3B+MoE-128 的训练吞吐做到 372 samples/s;6.7B dense 是 70 samples/s,两者达到相同模型质量(Table 3 的条件)。按表内数字复算 372/70 ≈ 5.31×,原文 gain 列写 5×。 代价是模型从 6.7B 参数变成 52B(§3.3 / Table 2),大约 7.8×;MoE 的显存和通信压力都随专家数上去。

Tutel 在 128 张 A100、1 expert/GPU 的 SwinV2-MoE 上,训练 226 vs 146 images/s、推理 792 vs 375 images/s, 分别是 1.55× 和 2.11×(Table 7)。同一篇论文的 Table 2 显示,64 GPU 时 all-to-all 已占 MoE 层开销的 46.3%, 完全重叠的潜在加速是 1.86×;到 256 GPU,占比 56.7%,潜在加速 1.76×。

MegaBlocks 在 8 张 A100、64 experts、top-1 的设置下(§6),dMoE-Medium 比 padding-based Tutel 快 4.35×(Figure 7), 但相对调过 CF 的 Tutel 只有 1.18×(Figure 8)。相对 Megatron-LM 的 dense Transformer,Figure 7 报告 1.8–2.4×。 Table 3 里 Tutel 在 Medium 上只能用 micro-batch 1,MegaBlocks 能用 8——padding 的显存代价直接压低了前者的 batch。

07

边界:drop、失衡、小 batch、组网,任何一条都可能吃掉 EP 收益

EP 的前提是“每个专家都有足够多的 token、且 token 能便宜地到达专家”。下面四个边界里任何一个不成立,账就要重算。

Token drop:容量是静态的,路由是动态的

capacity factor 把每个专家的 token 数固定住,但 router 的选择是数据相关的。容量小,溢出多,token 被丢后只走 residual; 容量大,padding 多,白算的 GEMM 和 all-to-all 字节都上去。Switch 的 Table 1 在 C4、128 experts、32 TPUv3 cores 下报告 CF 1.0/1.25 比 2.0 更快;MegaBlocks 的 Figure 2 在 The Pile、64 experts 下报告容量越高 loss 越低,最高容量(动态不丢 token)最低。 两处是不同数据、不同模型、不同容量策略,本页并列,不合并成一条通用规则。

负载失衡:aux loss 只约束期望,不保证每一步均衡

aux loss 鼓励 router 把 token 平均分给专家,但它不能保证每一步都均衡。Tutel 的 Table 1 在 ImageNet-22K、SwinV2-S、32 experts、top-1、 capacity factor 为无穷大的设定下,给出 LB loss 权重与 top-1 精度的关系:

LB loss 权重0.0010.010.11.0
Acc@1 (%)37.3237.7837.1634.71

Table 1(Tutel):权重 1.0 时 34.71%,0.01 时 37.78%,差 3.07 个百分点。表内结论是“过大的 LB 权重会伤精度”;这里只转录这四个点,不外推到其他模型。

z-loss 不解决均衡,它压的是 logits 的幅度,让 router 的 softmax 不至于进入数值不稳的区域。Megatron-Core 把两者分开: moe_aux_loss_coeff 管均衡,moe_z_loss_coeff 管 logits 幅度,默认分别是 0.0 和 None(transformer_config.py:1103,1108)。 两个系数都需要按模型扫,不能互相替代。

小 batch:专家数上去,每个专家的 batch 变小

如果全局 token 数不变,专家数 E 增大时,每个专家分到的 token 数按 1/E 缩小;只增大 EP 而 E 不变时,每卡 token 数按 1/EP 缩小,但每个专家的全局 token 数不变。 DeepSpeed-MoE §4.1.3 的建议是让 EP 等于专家数,用 DP 把全局 batch 补回来,避免每个专家的 batch 变小;但专家数很多时,单专家 batch 仍然可能小到 GEMM 不高效。 MegaBlocks 的 Table 3 是一个具体例子:Medium 模型上,Tutel 的 padding 路径只能用 micro-batch 1,MegaBlocks 能用 8。 batch 小的时候,all-to-all 的 latency 更容易成为瓶颈;Tutel Table 2 的 16 GPU 场景,all-to-all 已占 33.7%,64 GPU 到 46.3%。

组网:EP 组、expert-DP 组、普通 DP 组要同时满足整除

Megatron-Core 在初始化时要求 world_size 能被专家网格的 TP × EP × PP 整除(GTP remat 默认为 1),并单独计算 expert_data_parallel_size(parallel_state.py:960);配置校验要求 num_moe_experts % expert_model_parallel_size == 0(transformer_config.py:2752), 且 EP>1 时 num_moe_experts 不能为空(transformer_config.py:2232)。 DeepSpeed 的 deepspeed/utils/groups.py 也把 EP 组和 expert-DP 组分开生成,rank 排列随 use_data_before_expert_parallel 变化 (deepspeed/utils/groups.py:563,570)。

常见组网里,EP 组的 all-to-all 会跨节点,TP 的 all-reduce 多在节点内。DeepSpeed-MoE 的推理系统用两级 all-to-all(节点内再节点间)来减少跨节点跳数(§5.3); Tutel 的 2DH all-to-all 也是同一思路。工程判断把 EP 组铺到慢速跨节点链路上,all-to-all 的 latency 会直接吃掉专家计算的收益。

什么时候别用 EP

工程判断 本节最后的列表是工程建议,不是论文实验结论;依据是上面引用的约束与字节账。具体收益仍要在目标集群上测 all-to-all 带宽、每专家 batch 和 GEMM 形状。