MOE负载均衡问题

为什么需要 MoE

同样的总参数量,MoE 每次推理只激活其中一部分参数——用更少的计算量撬动更大的模型容量。

方案总参数每次推理激活计算量知识容量
Dense 模型 7B 7B 基线 7B 级别
Dense 模型 35B 35B 35B 级别
MoE 模型 35B (含多专家) ~3.6B ~0.5× 35B 级别

核心:MoE 把"参数规模"和"计算量"解耦了。35B 总参、只激活 3.6B——知识容量向 35B 看齐,推理成本向 7B 看齐。这就是 Qwen3.6-35B-A3B 名字的含义:35B 总参,3B 活跃参数。

MoE 核心架构

两个核心组件

1、路由器(Router / Gate)

一个轻量的线性层,输入每个 token 的 hidden state,输出每个专家的"适配分数"。Top-K 选择分数最高的 K 个专家。
s = Wr · h   →   TopK(s, k=2)

2、专家(Expert)

每个专家是一个独立的前馈网络(FFN),通常是标准 Transformer 中 FFN 的副本。不同专家在训练中自然分化,各自擅长不同类型的输入模式。

标准 MoE 层的计算流程

# 标准 Top-K MoE:Token 选专家
h = input_hidden          # shape: (n_tokens, d_model)

# Step 1: 路由打分
logits = router(h)        # (n_tokens, n_experts)

# Step 2: Top-K 选择
scores, indices = topk(logits, k=2)   # 每个 token 选 2 个专家
weights = softmax(scores)              # 对选中的专家分数做 softmax

# Step 3: 各专家并行计算
output = 0
for expert_id in range(n_experts):
    mask = (indices == expert_id)      # 哪些 token 分配给了这个专家
    if mask.any():
        expert_out = expert[expert_id](h[mask])
        output[mask] += weights[mask] * expert_out

return output

MoE 层在 Transformer 中的位置


Multi-Head Attention  →  Add & Norm  →  MoE FFN Layer  →  Add & Norm
                                                          ↑
                                              Attention 权重是共享的
                                              只有 FFN 被替换成了 MoE

MoE 只替换 Transformer 中的 FFN 层,Attention 层保持不变。一个典型的 MoE Transformer 可能每隔一层才放 MoE(比如 Mixtral 的做法),或者每层都放(比如 DeepSeek-V2/V3)。

核心缺陷:负载不均衡

什么是负载不均衡

在标准 Top-K 路由中,每个 token 自己选择专家。训练过程中,路由器可能学会"偷懒"——把大量 token 分配给少数几个专家,而其他专家几乎闲置:

// 8 个专家,某一步的 token 分配情况(batch=64 tokens)

专家0: ████████████████████████████  31 tokens  ← 过载!
专家1: ██████████                  10 tokens
专家2: ████████                    8 tokens
专家3: ████                        4 tokens
专家4: ███                         3 tokens
专家5: ██                          2 tokens
专家6: █                           1 token  ← 几乎闲置
专家7:                             0 tokens  ← 完全闲置!

不均衡带来的后果

后果一:计算效率崩塌

MoE 的并行策略是每个专家跑一块 GPU(Expert Parallelism)。如果专家0 有 31 个 token 而专家7 有 0 个,那么 GPU0 忙死、GPU7 闲死——其他 GPU 必须等 GPU0 算完才能进行下一步。负载最重的专家决定了整个 batch 的延迟。

31 tokens 的专家决定了所有人的等待时间 → 实际并行效率可能不到 50%。

后果二:训练不充分

被冷落的专家(4-7)每步只看到极少的 token,梯度更新不充分——它们永远追不上热门专家。这在训练早期尤其严重——一旦某几个专家"抢跑",整个路由就固化了。

后果三:Token 丢弃

实际部署中,每个专家有容量上限(expert capacity),超出的 token 直接丢弃——这些 token 的信息在这一层完全丢失。容量公式通常是:

capacity = (tokens_per_batch / n_experts) × k × capacity_factor

capacity_factor 通常设为 1.25~1.5,意味着允许 25%~50% 的溢出缓冲。但如果某些专家严重过载,缓冲也不够用,token 就被强制丢弃了。

为什么会不均衡——路由坍塌

根源在于路由器的正反馈循环

Step 1: 初始化时路由器随机分配 → 略有偏差
    ↓
Step 2: 热门专家看到更多 token → 梯度更新更充分 → 输出质量更高
    ↓
Step 3: 路由器学会"热门专家=好输出" → 分配更多 token 给它们
    ↓
Step 4: 冷门专家 token 更少 → 梯度更稀疏 → 差距继续拉大 → GOTO Step 2

这就是所谓的"富者愈富"效应(rich-get-richer):路由坍塌一旦开始,很难靠模型自己恢复。必须从架构层面介入。

解法一:细粒度专家(Fine-Grained Experts)

核心思想:把一个大专家切成多个小专家 → 每个专家处理能力更小 → 负载更分散

传统的"粗粒度"专家

标准 MoE 中,每个专家就是一个完整的 FFN 层。以 7B 模型为例:

# 粗粒度:8 个专家,每个专家 = 完整 FFN(~110M 参数)
n_experts = 8,  expert_size = 110M
总 MoE 参数 = 8 × 110M = 880M(仅 FFN 部分)

细粒度的做法

DeepSeek-V2 提出:把 FFN 内部再切分,让专家的粒度更细:

# 细粒度:64 个专家,每个专家 = FFN 的 1/8
n_experts = 64,  expert_size = 13.75M
总 MoE 参数 = 64 × 13.75M = 880M(总参不变!)
粗粒度细粒度
专家数量 8 64
每个专家大小 110M 13.75M
总参数 880M 880M
每个专家处理能力 强(完整 FFN) 弱(1/8 FFN)
负载分布 容易集中 自然分散

为什么细粒度能缓解不均衡

原因一:容量上限自然降低

专家变小了,每个专家的处理上限也随之降低。即使某些专家被分配了更多 token,因为本身"胃口小",很快就会达到容量上限,溢出的 token 会被分配给其他专家。这就像自助餐换成了小盘子——每个人拿的少了,排队自然均衡。

原因二:激活更多专家

细粒度下通常 Top-K 也相应增大(比如 k=8 而不是 k=2)。每个 token 看到更多专家,路由决策的"容错率"更高——即使 router 把某个 token 分配给了一个"不太合适"的专家,还有 7 个其他专家兜底。

原因三:专家专业化更灵活

小专家更容易学到细粒度的专长。比如"法律合同条款"可以由专家12+专家28+专家45 组合处理,而不是一个巨大的"法律专家"把所有法律相关 token 全吃掉。

细粒度 + 共享专家(DeepSeek-V2/V3 的实际方案)

DeepSeek 还引入了一个关键设计:共享专家(Shared Expert)。所有 token 都会经过共享专家,而路由专家只负责"增量":

# DeepSeekMoE 架构
h = input_hidden

# 共享专家:所有 token 都经过
shared_out = SharedExpert(h)

# 路由专家:细粒度 + Top-K,每个 token 选 k 个小专家
routed_out = MoE_Router(h, topk=6, n_experts=64)

# 最终输出 = 共享 + 路由
output = shared_out + routed_out

共享专家的妙处:它保证了每个 token 都有"底线能力"(常识、基础语言能力),路由专家只负责"特长"。这样即使路由完全失败,共享专家也能保证输出不崩溃——这是一个非常重要的鲁棒性设计

解法二:专家选Token(Expert-Choice Routing)

核心思想:反过来——不是 token 选专家,而是专家选 token。每个专家主动挑选自己最擅长处理的 K 个 token。

Token-Choice vs Expert-Choice

Token-Choice(标准)Expert-Choice(反转)
谁做选择 每个 token 选 top-k 专家 每个专家选 top-k token
路由计算 softmax(router(h)) per token softmax(router(h)) per expert
负载保证 不保证——可能全部 token 选同一个专家 天然均衡——每个专家固定处理 k 个 token
token 覆盖 每个 token 一定被处理 部分 token 可能无人选择

 

专家选Token 的计算流程

# Expert-Choice Routing
h = input_hidden          # (n_tokens, d_model)

# Step 1: 计算所有 token 对所有专家的适配分数
scores = router(h)        # (n_tokens, n_experts)

# Step 2: 对每个专家维度做 softmax(注意:和 Token-Choice 相反!)
scores = softmax(scores, dim=0)  # 沿 expert 维度归一化 → 每个专家对 token 的偏好

# Step 3: 每个专家选 top-k 个 token
for expert_id in range(n_experts):
    top_tokens = topk(scores[:, expert_id], k=capacity)
    # expert_id 处理这 k 个 token

关键区别:softmax 的方向变了。Token-Choice 是每个 token 在自己这行做 softmax(选专家),Expert-Choice 是每个专家在自己这列做 softmax(选 token)。这个看似微小的变化,彻底改变了负载特性。

为什么能天然均衡

每个专家固定处理 K 个 token → 绝对均衡

在 Expert-Choice 中,每个专家的 token 数 = capacity = K。这不再是"希望"均衡,而是架构上强制均衡——无论路由器学成什么样,负载一定是均衡的。

# Expert-Choice 下,负载天然均衡

专家0: ████████  8 tokens
专家1: ████████  8 tokens
专家2: ████████  8 tokens
专家3: ████████  8 tokens
专家4: ████████  8 tokens
专家5: ████████  8 tokens
专家6: ████████  8 tokens
专家7: ████████  8 tokens

# 完美均衡!但注意:total = 64 tokens,不是所有 token 都覆盖了
# 如果有 100 个 token,那 36 个没人选——这就是 Expert-Choice 的代价

两种解法的局限性

细粒度专家的局限

局限一:通信开销激增

细粒度意味着更多专家(64→128→256)。在分布式训练中,不同专家分布在不同 GPU 上。每个 token 需要和更多专家通信——All-to-All 通信量随专家数线性增长。当专家数超过某个阈值(比如 256),通信开销会吃掉所有加速收益。

局限二:缓解不等于解决

细粒度只是让不均衡"没那么严重",并不能消除。Top-K 路由器依然存在——k 可以调大、专家可以切小,但路由坍塌的本质没有变:路由器仍然可以学会把所有 token 分配给某几个专家。细粒度是治标:让溢出的影响更小,但根因还在。

局限三:辅助损失(Load-Balancing Loss)的副作用

细粒度方案通常需要配合负载均衡损失(Load-Balancing Loss)——在训练 loss 中加一项,惩罚负载偏差。但这引入了一个新问题:

Ltotal = Ltask + α · Lbalance

α 太大 → 路由过于"平均主义",专家学不到专长。α 太小 → 不均衡依然严重。α 是一个需要精细调参的超参,不存在普适的最优值。

局限四:专家总数有上限

细粒度不能无限切——每个专家至少要保持一定容量才能学到有意义的模式。DeepSeek-V2 用的是 64 个路由专家 + 2 个共享专家,已经是实践中比较激进的设置了。如果再切到 256 个,单个专家太小,反而学不到有效的特征变换。

专家选Token 的局限

局限一:Token 丢弃——最大的硬伤

这是 Expert-Choice 最致命的问题。每个专家只选 top-K 个 token,那么:

覆盖的 token 数 = n_experts × K    如果 n_experts × K < n_tokens → 有 token 被丢弃

被丢弃的 token 在这一层完全不做任何处理——相当于信息直接丢失。在深层网络中,信息丢失会逐层放大,严重影响模型质量。

局限二:"你情我不愿"——质量 vs 均衡的矛盾

Token-Choice 的问题:token 选了专家,但专家可能"不擅长"这个 token(负载不均衡)。Expert-Choice 解决了负载问题,但引入了相反的质量问题:为了填满容量,专家不得不选一些自己并不擅长的 token。

# 专家 0 最擅长数学类 token,但 batch 里只有 3 个数学 token
# Expert-Choice 强制专家 0 选 k=8 个 token
# → 另外 5 个 token 属于"赶鸭子上架"

专家0 擅长: [数学] [数学] [数学] ✓
专家0 被迫: [文学] [代码] [历史] [生物] [地理] ← 质量下降

局限三:推理时的动态性困境

Expert-Choice 需要看到整个 batch 的所有 token才能决定每个专家选哪些——因为 softmax 在 token 维度上计算,必须有全局视野。这在推理时尤其麻烦:

  • 自回归生成时,token 是一个一个来的,没有"整个 batch"可选
  • 要么等攒够一批再处理(增加延迟),要么退化为 Token-Choice
  • DeepSeek-V3 在推理时就退回了 Token-Choice + 辅助损失的方式

局限四:Top-K 选择的梯度断裂

Top-K 选择本身是不可导的——选中的 token 有梯度,没选中的没有。在 Expert-Choice 中,大量 token 可能长期不被任何专家选中,这些 token 的梯度信号完全中断。模型的训练效率取决于"token-专家的匹配质量",而这个质量本身又需要充分训练才能获得——先有鸡还是先有蛋。

三种路由方案对比

维度Token-Choice
(传统 Top-K)
细粒度 + Token-Choice
(DeepSeek-V2)
Expert-Choice
(反转路由)
选择方向 Token → 专家 Token → 专家 专家 → Token
负载均衡 差,容易坍塌 中等,靠辅助损失 天然均衡
Token 覆盖率 100% 100% 取决于 n_experts × K
推理适用性 差(需攒 batch)
通信开销 中高
训练稳定性 中等 中等
代表模型 Mixtral 8×7B
Switch Transformer
DeepSeek-V2/V3
Qwen3-MoE
Google Sparsely-Gated
(论文方案,未大规模采用)

业界最终选择:DeepSeek-V2/V3 的方案——细粒度 + 共享专家 + 辅助损失 + 训练时用 细粒度Token-Choice + 推理时退回简单路由——是目前 MoE 领域被验证最成功、最实用的路径。Expert-Choice 在理论上很优雅(天然均衡),但 token 丢弃问题在工程上难以接受。这也是为什么它至今没有被任何大规模生产模型采用。

常见问题汇总

Q1:MoE 最大的工程挑战是什么?
负载不均衡。Top-K 路由下,专家之间 token 分配极度不均衡——少数专家过载、多数闲置。这导致:① 分布式训练中 GPU 利用率低下(负载最重的 GPU 决定全局延迟);② 冷门专家训练不充分;③ 超出容量的 token 被强制丢弃。细粒度 + 辅助损失是目前的主流缓解方案,但无法根治。
 
Q2:细粒度专家为什么能缓解负载不均衡?有什么局限?
细粒度专家的本质是降低每个专家的容量,增加专家数量。每个专家变小(胃口小),溢出的 token 自然分散到更多专家。配合增大 Top-K,每个 token 看到的专家更多,路由容错率更高。局限:① 通信开销线性增长;② 辅助损失 α 需要精细调参;③ 不均衡的根因(路由坍塌)仍然存在;④ 专家不能无限切小。
 
Q3:Token-Choice 和 Expert-Choice 的本质区别是什么?
Token-Choice(常规):每个 token 做 softmax 选专家 → 保证 token 全部被处理,但不保证负载均衡。Expert-Choice(反转):每个专家做 softmax 选 token → 保证每个专家负载相同,但不保证 token 全部被覆盖。两者是"公平性 vs 覆盖率"的 trade-off,没有银弹。
 
Q4:为什么 Expert-Choice 没有被大规模采用?
三个致命伤:① Token 丢弃——n_experts × K < n_tokens 时必然丢 token,信息损失逐层放大;
② 推理不友好——需要攒够 batch 才能做 softmax,自回归生成时不可用;
③ 质量妥协——专家被迫处理不擅长的 token 以填满容量。DeepSeek-V3 训练时用过 Expert-Choice 的变体(auxiliary-loss-free),但推理时退回了 Token-Choice。
 
Q5:DeepSeek-V2/V3 的 MoE 方案有什么独特之处?
三大创新:
① 细粒度专家——64 个路由专家 + 2 个共享专家,每个专家是常规 FFN 的 1/8;
② 共享专家——所有 token 必过共享专家,保证底线能力,路由专家负责增量特长;
③ 辅助损失自由(auxiliary-loss-free)的负载均衡策略——在训练中通过动态调整 expert bias 来实现均衡,不需要额外的辅助损失项。这三个设计组合在一起,是目前 MoE 领域最成功的工程实践。

 

总结:

MoE 是当前扩大模型规模最具性价比的路线——DeepSeek-V3 671B 总参只激活 37B,训练成本约为同规模 Dense 模型的 1/5。但负载不均衡仍然是核心瓶颈。未来方向可能在动态路由(不是固定的 Top-K,而是根据输入复杂度动态决定激活多少专家)、硬件级稀疏计算支持(GPU/NPU 对稀疏专家计算的 native 支持)、以及MoE + 蒸馏(训大 MoE,蒸馏到小 Dense 部署)。

 

参考文献

#论文核心贡献
1 Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer
Shazeer et al., Google Brain, 2017
首次将 MoE 引入深度神经网络,提出 Sparsely-Gated 路由、Top-K 选择 + 辅助负载均衡损失,奠定整个领域基础。
2 GShard: Scaling Giant Models with Conditional Computation and Automatic Sharding
Lepikhin et al., Google, 2020
将 MoE 扩展到 600B 参数,提出 Expert Parallelism + All-to-All 通信,验证了 MoE 在大规模下的工程可行性。
3 Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity
Fedus et al., Google, 2021
Top-1 路由 + 可微分 Load-Balancing Loss。首次系统对比 Token-Choice 与 Expert-Choice 两种路由策略(§3.2),指出 Expert-Choice 天然均衡但导致 token 丢弃。
4 MegaBlocks: Efficient Sparse Training with Mixture-of-Experts
Gale et al., Stanford & Google, 2022
提出 block-sparse 计算方案,量化分析了 Expert-Choice 路由的 token 丢弃问题,指出这是其难以大规模部署的核心瓶颈。
5 DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model
DeepSeek-AI, 2024
细粒度专家里程碑。细粒度专家切分 + 共享专家隔离 + 负载均衡策略。236B 总参、21B 激活,训练成本仅为 Dense 的 1/3。
6 DeepSeek-V3 Technical Report
DeepSeek-AI, 2024
Auxiliary-Loss-Free 负载均衡。通过动态调整 expert bias 实现均衡,完全消除辅助损失。671B 总参、37B 激活。
posted @ 2026-07-23 15:29  黄艺龙  阅读(0)  评论(0)    收藏  举报