欢迎光临
我们一直在努力

从零理解 MoE 算子:路由、专家并行与 Token 重排

MoE(Mixture of Experts,混合专家模型)已经成为大模型扩展参数规模的重要技术路线。 本文从普通 FFN 出发,逐步解释 Router、Top-K 路由、专家计算、Expert Parallel、AllToAll 通信、Token Permute / Unpermute,以及 MoE 中常见的 Capacity、负载均衡和通信优化问题。


1. 为什么需要 MoE

Transformer Block 通常由 Attention 和 FFN 两个主要部分组成。为了理解 MoE,可以先忽略 Attention,只看 FFN。

一个常见的 SwiGLU FFN 可以写成:

FFN(x) = down_proj(silu(gate_proj(x)) * up_proj(x))

其中:

符号含义
T token 数量
H hidden size,模型隐藏维度
I intermediate size,FFN 中间维度

在这里插入图片描述

普通 FFN 的特点是:所有 token 都经过同一组参数。想增加模型容量,通常需要增大 FFN,这会直接增加每个 token 的计算量。

MoE 提供了另一种扩展方式:

准备多个 FFN 专家,但每个 token 只选择少数几个专家执行。

这类设计称为稀疏 MoE。

这里的“稀疏”指的是计算路径稀疏,而不是权重矩阵本身稀疏。


2. 普通 FFN 到 MoE FFN

普通 FFN 可以理解为:

所有 token -> 同一个 FFN

MoE FFN 可以理解为:

不同 token -> 不同专家 FFN

假设有 E=8 个专家,每个专家都是一个独立 FFN。对于每个 token,只选择得分最高的 top_k=2 个专家执行。

token x
-> Router
-> Top-K experts
-> Expert FFN
-> 按路由权重加权求和

例如:

总专家数 E = 64
每个 token 选择 top_k = 2

模型拥有 64 组专家参数,但每个 token 实际只经过其中 2 组。


3. Router:每个 Token 应该去哪里

Router 也常被称为 Gate。它通常是一个轻量级线性层:

router_logits = x @ router_weight.T

如果:

x : [T, H]
router_weight : [E, H]

那么:

router_logits : [T, E]

router_logits[t, e] 表示第 t 个 token 交给第 e 个专家处理的倾向。

随后,对每个 token 选择得分最高的 K 个专家:

topk_scores, topk_indices = topk(router_logits, k=top_k)
topk_weights = softmax(topk_scores)

得到:

topk_indices : [T, K]
topk_weights : [T, K]

例如:

token选中的专家路由权重
token_0 expert_1, expert_3 0.7, 0.3
token_1 expert_0, expert_3 0.6, 0.4
token_2 expert_1, expert_2 0.8, 0.2

4. Top-K 为什么会让 Token 变多

当 top_k > 1 时,一个原始 token 会被发送给多个专家。

逻辑上可以理解为:

原始 token 数 = T
进入专家计算的路由 token 数 = T * K

例如:

T = 1024
K = 2

专家侧总共需要处理:

1024 * 2 = 2048 个路由 token

不过需要注意:

Top-K 复制首先是一个逻辑概念。

在实际实现中,不一定一开始就真的复制完整的激活张量。很多系统会先保存:

expert_ids
source_token_ids
topk_weights

真正的数据复制或打包,可能发生在后面的 pack、dispatch 或 permute 阶段。


5. 单设备 MoE 的完整流程

如果所有专家都位于同一设备,MoE 的流程可以概括为:

Router
-> Top-K 选择专家
-> 按专家整理 token
-> 执行专家 FFN
-> 按原 token 位置和路由权重合并结果

更具体地说:

输入 tokens [T, H]
-> Router 得到 topk_indices 和 topk_weights
-> 构造路由 token [T * K, H]
-> 按专家编号重排 token
-> 专家 FFN 计算
-> 根据 source_token_ids 和 topk_weights 合并
-> 输出 tokens [T, H]


6. 为什么需要按专家分组

假设路由结果是:

路由 token目标专家
A expert_1
B expert_0
C expert_1
D expert_0

原始顺序是:

[A(exp1), B(exp0), C(exp1), D(exp0)]

为了批量执行专家计算,需要整理为:

[B(exp0), D(exp0), A(exp1), C(exp1)]

这就是 Token Permute:

按照专家编号重新排列 token,使同一个专家的 token 连续存放。

它不改变 token 的内容,只改变 token 的排列顺序。


7. GroupedMatMul:批量执行多个专家

每个专家都有一套独立权重。如果逐 token、逐专家调用小矩阵乘,硬件利用率通常很差。

例如有 64 个专家,如果每个专家单独调用一次矩阵乘,可能产生大量小 GEMM 和大量 kernel launch,整体效率很低。

更常见的做法是:

将同一专家的 token 连续排列,
然后用 GroupedMatMul 一次性执行多个专家的矩阵乘。

可以理解为:

Expert 0: X0 @ W0
Expert 1: X1 @ W1
Expert 2: X2 @ W2

GroupedMatMul 需要知道每个专家对应的 token 范围。常见做法是传入累计 token 数:

每个专家 token 数: [3, 2, 4]
group_list: [3, 5, 9]

它表示:

tokens [0, 3) -> expert 0
tokens [3, 5) -> expert 1
tokens [5, 9) -> expert 2

因此,group_list 本质上是专家 token 分段边界。


8. 专家内部仍然是 FFN

每个专家本质上仍然是一个 SwiGLU FFN:

Expert_e(x) = W2_e(silu(W1_gate_e(x)) * W1_up_e(x))

实际执行通常拆为:

GMM1
-> SwiGLU
-> GMM2

其中:

GMM1: 升维
输入 routed_tokens : [T_routed, H]
权重 up_weight : [E, H, 2I]
输出 gate_up : [T_routed, 2I]

SwiGLU: 激活
输入 gate_up : [T_routed, 2I]
过程 gate, up = split(gate_up)
hidden = silu(gate) * up
输出 hidden : [T_routed, I]

GMM2: 降维
输入 hidden : [T_routed, I]
权重 down_weight : [E, I, H]
输出 expert_out : [T_routed, H]

可以概括为:

按专家排列的 tokens
-> GMM1
-> SwiGLU
-> GMM2
-> 按专家排列的输出


9. 为什么需要 Expert Parallel

当专家数量很多时,单个设备可能无法容纳所有专家权重。

Expert Parallel,简称 EP,就是将不同专家分布到不同设备或 Rank 上。Rank 可以简单理解为“分布式系统中的一个进程编号”,一般情况下,一张GPU或者NPU就是一个Rank。

假设:

总专家数 E = 4
EP 并行度 = 2

可以分配为:

Rank 0: Expert 0, Expert 1
Rank 1: Expert 2, Expert 3

Router 仍可能将 Rank 0 上的 token 路由给 Expert 2。由于 Expert 2 位于 Rank 1,token 必须跨设备发送。

这就是 MoE 中 AllToAll 通信的来源。


10. AllToAll Dispatch:将 Token 发送给专家

在 EP 场景中,每个 Rank 都可能需要向多个 Rank 发送 token,同时也会从多个 Rank 接收 token。

Dispatch 阶段的目标是:

将每个路由 token 发送给持有目标专家的 Rank。

例如:

Rank 0 上的 token 可能要发送给 Rank 0 和 Rank 1
Rank 1 上的 token 也可能要发送给 Rank 0 和 Rank 1

AllToAll Dispatch 之后,每个 Rank 得到的是:

本 Rank 持有的专家需要处理的 token

也就是说,Dispatch 解决的是:

token 应该去哪台设备

而不是:

本地 token 是否已经按专家排好


11. Rank-Major 与 Expert-Major

Dispatch 之后,本地接收 buffer 可能按照来源 Rank 排列。

例如当前 Rank 持有两个本地专家:

Expert 0
Expert 1

它从两个 Rank 收到了 token:

Rank 0 -> Expert 0: [A, B]
Rank 0 -> Expert 1: [C]
Rank 1 -> Expert 0: [D]
Rank 1 -> Expert 1: [E, F]

Dispatch 后可能得到 rank-major 布局:

[A(E0), B(E0), C(E1), D(E0), E(E1), F(E1)]

也就是:

[R0-E0 | R0-E1 | R1-E0 | R1-E1]

这种布局称为 rank-major:

先按来源 Rank 分组,再按专家分组。

但是 GMM 更希望同一个专家的 token 连续:

[A(E0), B(E0), D(E0), C(E1), E(E1), F(E1)]

也就是:

[R0-E0 | R1-E0 | R0-E1 | R1-E1]

这种布局称为 expert-major:

先按专家分组,再按来源 Rank 分组。


12. Permute:从 Rank-Major 到 Expert-Major

Dispatch 解决了跨 Rank 发送问题,但本地为了高效执行 GMM,通常还需要一次 Permute。

rank-major
-> permute
-> expert-major

例如:

rank-major:
[A(E0), B(E0), C(E1), D(E0), E(E1), F(E1)]

Permute 后:

expert-major:
[A(E0), B(E0), D(E0), C(E1), E(E1), F(E1)]

对应的 permute 索引为:

[0, 1, 3, 2, 4, 5]

每个专家 token 数为:

[3, 3]

GMM 使用的累计 group_list 为:

[3, 6]

Permute 可以简化理解为:

permuted_tokens = tokens[permute_indices]


13. Unpermute:从 Expert-Major 恢复 Rank-Major

本地专家计算完成后,输出仍然是 expert-major:

[A', B', D', C', E', F']

但 Combine AllToAll 通常希望按照来源 Rank 连续发送回去,因此需要恢复为 rank-major:

[A', B', C', D', E', F']

这就是 Unpermute。

可以简化理解为:

restored = zeros_like_original_buffer()
restored[permute_indices] = expert_output

它是 Permute 的逆操作。


14. Dispatch 和 Permute 的边界不是固定的

需要特别注意:不同框架中,Dispatch 和 Permute 的边界并不完全一样。

有些实现会把流程拆得很清楚:

Dispatch = AllToAll 通信
Permute = 本地 token 重排

也就是:

AllToAll Dispatch
-> rank-major
-> Permute
-> expert-major

但也有些实现会把多个步骤融合在一起:

Dispatch = pack + permute + alltoall

甚至 Dispatch 输出就已经是适合专家计算的 expert-major 布局。

因此,阅读源码时不要只看函数名字,而要看这个函数的输入输出布局。

判断标准是:

输出 token 是否已经按专家连续排列?

如果已经按专家连续排列,那么后面可能不再需要单独的 Permute。

如果只是完成了跨 Rank 通信,那么后面通常还需要本地 Permute。


15. AllToAll Combine:将专家结果返回来源 Rank

专家计算完成后,每个 token 的专家输出需要回到原始 token 所在的 Rank。

Combine 阶段的目标是:

将专家输出发送回 token 的来源 Rank。

整体可以理解为 Dispatch 的反向通信:

Dispatch:
原始 Rank -> 专家所在 Rank

Combine:
专家所在 Rank -> 原始 Rank

Combine 完成后,每个 Rank 拿回自己原始 token 对应的专家输出。

如果 top_k = 2,每个原始 token 会对应两个专家输出。最后还需要根据路由权重做加权合并。


16. Top-K 专家输出如何合并

一个原始 token 可能被多个专家处理。

例如:

token_0 -> expert_1, weight = 0.7
token_0 -> expert_3, weight = 0.3

两个专家分别输出:

out_1
out_3

最终输出为:

output[token_0] = 0.7 * out_1 + 0.3 * out_3

一般形式为:

output[token] = sum(weight[token, k] * expert_output[token, k])

这里必须保证:

expert_output[token, k]

能够正确映射回原始 token 和对应的 top-k 位置。

因此实现中通常需要保存:

source_token_ids
topk_indices
topk_weights

或者保存等价的反向映射。


17. EP MoE 正向流程总览

现在可以把 EP MoE 的完整正向流程串起来:

Router
-> Top-K 选择
-> 构造路由 token
-> AllToAll Dispatch
-> Permute
-> GMM1
-> SwiGLU
-> GMM2
-> Unpermute
-> AllToAll Combine
-> 按原 token 合并 Top-K 专家输出

更具体地说:

输入 tokens [T, H]
-> Router 得到 [T, E] logits
-> Top-K 得到 [T, K] expert_ids 和 weights
-> 逻辑上形成 [T * K, H] routed_tokens
-> Dispatch 发送到专家所在 Rank
-> Permute 整理成本地 expert-major
-> GMM1 升维
-> SwiGLU 激活
-> GMM2 降维
-> Unpermute 恢复 rank-major
-> Combine 发送回来源 Rank
-> Weighted Merge 合并 Top-K 输出
-> 输出 tokens [T, H]


18. Capacity:每个专家最多处理多少 Token

Router 不会天然保证每个专家收到相同数量的 token。

可能出现:

Expert 0: 100 tokens
Expert 1: 900 tokens
Expert 2: 0 tokens
Expert 3: 300 tokens

如果不加限制,某些专家会收到过多 token,导致:

计算长尾
通信不均衡
显存占用不稳定
GMM 规模波动很大

因此很多 MoE 系统会引入 Capacity,也就是每个专家最多能接收多少 token。

一个常见形式是:

expert_capacity = ceil(tokens_per_rank * top_k / num_experts * capacity_factor)

例如:

tokens_per_rank = 1024
top_k = 2
num_experts = 8
capacity_factor = 1.25

平均每个专家接收:

1024 * 2 / 8 = 256

容量为:

256 * 1.25 = 320

也就是说,每个专家最多处理大约 320 个 token。

超过容量的 token 可能会被:

drop
reroute
保留残差路径
或者使用其他策略处理

Capacity 的核心作用是:

限制专家负载上界,让计算和通信更稳定。


19. Load Balancing:为什么需要负载均衡

如果 Router 完全自由选择专家,它可能会逐渐偏向少数专家。

极端情况下:

Expert 0: 很多 token
Expert 1: 很少 token
Expert 2: 几乎不用
Expert 3: 几乎不用

这会带来两个问题。

第一是性能问题:

部分专家过载,部分专家空闲。

第二是训练问题:

部分专家得不到充分训练。

因此 MoE 训练中通常会加入负载均衡机制。常见做法包括:

辅助负载均衡损失
容量限制
路由噪声
专家复制
更受约束的路由策略


20. Auxiliary Loss:辅助负载均衡损失

Auxiliary Loss,简称 Aux Loss,是 MoE 中常见的负载均衡手段。

它的目标是:

鼓励 Router 尽量均匀地使用不同专家。

直观地说,如果某个 batch 中大多数 token 都去了 Expert 0,而其他专家几乎没有 token,那么 Aux Loss 会变大。

训练时总损失变成:

total_loss = task_loss + aux_loss_weight * aux_loss

其中:

task_loss

是原本的语言模型损失,

aux_loss

是负载均衡损失。

Aux Loss 不直接提高单个 token 的预测能力,但它能改善专家使用分布,让训练更稳定。

不过 Aux Loss 也有代价:

过强的负载均衡约束可能影响模型自由选择最合适专家。

因此实际训练中需要调节它的权重。


21. Group Limited Routing:减少跨节点通信

最基础的 MoE 路由是:

从所有专家中选择 Top-K 专家。

但在大规模分布式训练中,这可能导致大量跨节点通信。

例如某个 token 在 Rank 0 上,但 Router 选择的专家都在远端 Rank 上,那么这个 token 必须跨设备发送。

为了降低通信开销,一些现代 MoE 系统会使用更受限制的路由策略,例如 Group Limited Routing。

它的大致思想是:

先把专家划分成若干组
Router 先选择少数几个专家组
再在这些组内选择具体专家

这样可以减少 token 被路由到任意远端专家的概率,从而降低 AllToAll 通信压力。

可以理解为:

普通 Top-K Routing:
token 可以从所有专家里自由选择

Group Limited Routing:
token 先选专家组,再在组内选专家

它牺牲了一部分路由自由度,换取更好的通信局部性和系统效率。


22. 反向传播如何理解

MoE 的反向传播可以从两个原则理解:

1. 通信方向与正向相反
2. 重排操作的反向是对应的逆重排

正向流程可以简化为:

Dispatch
-> Permute
-> Experts
-> Unpermute
-> Combine

反向传播大致对应:

Combine 的反向
-> Unpermute 的反向
-> Experts 的反向
-> Permute 的反向
-> Dispatch 的反向

其中:

Permute 的反向 ~= Unpermute
Unpermute 的反向 ~= Permute

专家内部 FFN 的反向包括:

GMM2 backward
-> SwiGLU backward
-> GMM1 backward

需要计算:

输入梯度 dX
升维投影权重梯度 dW1
降维投影权重梯度 dW2

对于 SwiGLU 专家:

hidden = silu(gate) * up

反向时需要分别计算:

d_gate
d_up

然后再通过 GMM1 的反向得到输入梯度和权重梯度。


23. MoE 为什么难优化

MoE 的性能问题不是单一矩阵乘问题,而是通信、计算和数据重排交织在一起。


23.1 AllToAll 通信开销

EP 引入两次主要通信:

Dispatch: 将 token 发送给目标专家
Combine : 将专家结果返回 token 来源 Rank

EP 规模增大后,通信延迟、通信带宽和网络拥塞都会影响性能。

对于大规模 MoE 来说,AllToAll 往往是最关键的瓶颈之一。


23.2 Token 重排开销

Permute 和 Unpermute 会读写大量激活值:

[T_routed, H]

它们不涉及复杂数学运算,但会消耗显著内存带宽。

尤其当:

T 很大
H 很大
top_k > 1

时,重排开销可能非常明显。


23.3 负载不均衡

Router 可能让不同专家收到不同数量的 token:

Expert 0: 100 tokens
Expert 1: 900 tokens
Expert 2: 0 tokens
Expert 3: 300 tokens

这会导致:

部分专家成为长尾
部分设备工作量更大
某些专家收到零 token
GMM 的矩阵规模波动
通信量不均衡

负载不均衡不仅影响性能,也会影响训练稳定性。


23.4 小算子和中间结果开销

MoE 流程中存在多个步骤:

Dispatch
-> Permute
-> GMM1
-> SwiGLU
-> GMM2
-> Unpermute
-> Combine

如果每一步都独立执行,可能产生额外的调度开销和中间结果读写。

因此 MoE 优化经常围绕以下方向展开:

算子融合
通信与计算重叠
减少中间激活读写
Token 重排优化
GroupedMatMul 优化
更好的负载均衡


24. 通信与计算重叠

朴素实现可能是:

等待 Dispatch 完成
-> 再开始专家计算
-> 等待专家计算完成
-> 再执行 Combine

这样通信和计算是串行的。

更高效的实现会尝试:

一边接收 token
一边对已经到达的 token 执行专家计算

也就是通信与计算重叠。

理想情况下:

通信开销被部分隐藏在计算开销后面。

例如:

Rank 先收到 Expert 0 的 token
就可以先执行 Expert 0 的 GMM
同时继续接收其他 Expert 的 token

这样可以减少总耗时。

不过通信与计算重叠会让实现复杂很多,因为需要处理:

buffer 管理
异步通信
执行顺序
专家 token 到达顺序
流同步
反向传播中的依赖关系


25. 算子融合与 Megakernel 思路

MoE 中很多步骤本身并不复杂,但每一步都会读写中间结果。

例如:

GMM1 输出 gate_up
SwiGLU 读取 gate_up,写出 hidden
GMM2 再读取 hidden

如果完全分开执行,中间激活会多次写入和读取显存。

优化方向之一是算子融合:

减少中间结果落显存
减少 kernel launch
提高数据局部性

更激进的方向是 Megakernel,即把多个阶段融合到一个更大的 kernel 或更紧密的执行单元中。

不过 Megakernel 不一定总是更好。它可能带来:

寄存器压力增加
代码复杂度上升
并行粒度变差
调度灵活性下降

因此实际优化需要结合硬件特点、token 分布、专家规模和通信模式综合判断。


26. 实现时容易忽略的边界条件

MoE 的基础流程看起来不复杂,但工程实现需要处理很多边界条件。


26.1 零 Token 专家

某个专家可能没有收到任何 token:

tokens_per_expert = [4, 0, 7, 2]

实现中不能假设每个专家都有输入。


26.2 某个 Rank 接收零 Token

在极端不均衡情况下,某个 Rank 可能完全没有收到 token。

通信、内存分配和反向传播仍然要保持正确。


26.3 Token 数无法整除批处理粒度

专家收到的 token 数通常不规则。

最后一个处理块可能不足完整大小,需要处理尾部 token。


26.4 Top-K 合并必须正确

一个原始 token 可能由多个专家共同处理。

输出需要按照路由权重正确合并:

output[token] = sum(weight[token, k] * expert_output[token, k])

如果 source token 映射错误,结果会直接错误。


26.5 Permute 映射必须可逆

Unpermute 需要知道每个 expert-major token 原本位于 rank-major buffer 的哪个位置。

常见方案有两类:

保存 Permute 索引
根据路由计数和偏移重新构造映射

无论采用哪种方案,都必须保证映射可逆。


26.6 Capacity 溢出

如果某个专家超过 capacity,需要明确处理策略:

丢弃
截断
重新路由
走残差路径
或者使用其他补偿机制

这部分必须和训练逻辑保持一致。


26.7 不同精度下的数值稳定性

MoE 中 Router、Softmax、Top-K、专家 FFN 可能使用不同精度。

常见组合包括:

FP32
FP16
BF16
FP8

Router 的 logits 和 softmax 对数值稳定性比较敏感,实际实现中经常需要特别处理。


27. 用伪代码串起整个过程

下面的伪代码省略了很多工程细节,但可以帮助建立整体认识:

def moe_forward(x, router_weight, expert_weights, top_k, ep_group):
# x: [T, H]

# 1. Router
logits = linear(x, router_weight) # [T, E]

# 2. Top-K routing
scores, expert_ids = topk(logits, k=top_k) # [T, K]
weights = softmax(scores, dim=1) # [T, K]

# 3. Expand routed tokens logically
routed_x, source_token_ids = expand_by_topk(
x,
expert_ids
) # [T * K, H]

# 4. Send routed tokens to ranks that own target experts
received_x, received_expert_ids = all_to_all_dispatch(
routed_x,
expert_ids,
group=ep_group
)

# 5. rank-major -> expert-major
permuted_x, permute_indices, group_list = permute_by_expert(
received_x,
received_expert_ids
)

# 6. Expert FFN
gate_up = grouped_matmul(
permuted_x,
expert_weights.up,
group_list
) # [T_routed, 2I]

hidden = swiglu(gate_up) # [T_routed, I]

expert_out = grouped_matmul(
hidden,
expert_weights.down,
group_list
) # [T_routed, H]

# 7. expert-major -> rank-major
rank_major_out = unpermute(
expert_out,
permute_indices
)

# 8. Send expert results back to source ranks
returned_out = all_to_all_combine(
rank_major_out,
group=ep_group
)

# 9. Merge top-k expert results for each original token
y = weighted_merge(
returned_out,
source_token_ids,
weights
) # [T, H]

return y


28. 常见术语速查

术语含义
MoE Mixture of Experts,混合专家模型
Expert 专家,通常是一个独立 FFN
Router / Gate 为 token 选择专家并生成路由权重
Top-K 每个 token 激活的专家数量
Sparse MoE 每个 token 仅激活少量专家的 MoE
EP Expert Parallel,专家并行
Rank 分布式通信中的一个进程或设备编号
Dispatch 将 token 发送到专家所在 Rank
Combine 将专家结果返回 token 来源 Rank
Rank-Major 先按来源 Rank,再按专家排列
Expert-Major 先按专家,再按来源 Rank 排列
Permute 将 Rank-Major 重排为 Expert-Major
Unpermute 将 Expert-Major 恢复为 Rank-Major
GMM GroupedMatMul,按专家分组执行矩阵乘
group_list 描述每个专家 token 范围的累计边界
Capacity 每个专家最多可处理的 token 数
Capacity Factor 控制专家容量上限的系数
Aux Loss 辅助负载均衡损失
Load Balancing 尽量避免专家间 token 数差异过大
Group Limited Routing 限制路由范围以减少跨节点通信
Overlap 通信与计算重叠执行
Megakernel 将多个计算阶段融合到更大的 kernel 中

29. 总结

理解 MoE,可以抓住四条主线。

第一条是数学结构:

Router
-> Top-K 专家
-> 加权合并

第二条是专家计算:

按专家分组
-> GMM1
-> SwiGLU
-> GMM2

第三条是分布式数据流:

Dispatch
-> Permute
-> Experts
-> Unpermute
-> Combine

第四条是系统优化:

Capacity
-> Load Balancing
-> AllToAll 优化
-> 通信计算重叠
-> Token 重排优化
-> 算子融合

其中最容易混淆的是:

Dispatch 负责跨 Rank 发送 token
Permute 负责将本地 token 整理为 GMM 需要的 Expert-Major 布局
Unpermute 负责恢复适合 Combine 的 Rank-Major 布局
Combine 负责将结果返回原始 Rank

但在具体实现中,Dispatch 和 Permute 的边界并不固定。有些系统会把 pack、permute、alltoall 融合进一个 dispatch 接口。因此阅读源码时,最重要的不是函数名字,而是看清楚:

输入是什么布局?
输出是什么布局?
token 是否已经按专家连续排列?
是否还需要恢复来源 Rank 的顺序?

只要抓住这些数据布局变化,MoE 的大部分工程实现都会变得更容易理解。

赞(0)
未经允许不得转载:171主机测评 » 从零理解 MoE 算子:路由、专家并行与 Token 重排
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址