我最早以为 MoE 不过是一种“多模型集成”——多个专家各管一摊,路由负责分活。既然如此,那每个 token 只走一小部分专家,计算量小了,训练应该更轻松、更快。直到我真正跑了一次 MoE 训练,才意识到完全不是那么回事:损失曲线像心电图一样抖动,某个专家突然就“罢工”了,整个训练几乎进行不下去。

问题出在“稀疏激活”这个词上。它不只是说推理时能省计算,更关键的是它改变了训练时梯度流动的方式。理解这一点,才算真正理解了 MoE 与 Dense 模型的分水岭。
Dense 模型的稳定,来自“全量更新”
一个普通的 Dense Transformer,给定一个 token,它要经过所有层、所有参数。反向传播时,每个参数对 loss 的梯度都被计算出来,然后按优化器更新。所有参数同时看到一批数据,更新的方向是全局协商的结果——虽然没有明确的协商,但参数的梯度都是往整体 loss 更低的方向走。所以它的训练动态相对平稳:损失下降曲线看起来光滑,很少出现一步暴跌、一步暴涨的情况。
我举个例子。你可以把 Dense 训练想象成一群人一起推一辆车,每个人都同时用力,车往哪个方向走其实是合力决定的。哪怕个别人用了怪力,也会被其他人的力量抵消掉,整体运动很平滑。
Sparse 激活:每个 token 只“抽”一部分专家
MoE 不同。每个 token 进来,先经过一个路由(router),路由根据 token 的内容给它分配几个专家。比如 Switch Transformer 用的是 Top-1,也就是每个 token 只选一个专家;Mixtral 用 Top-2,选两个。选中之后,token 才进入这些专家的 FFN 层,其他专家完全不参与。
这意味着梯度被“打碎”了。在反向传播时,只有被路由选中的专家才能收到这个 token 产生的梯度。未被选中的专家,在这个 step 上拿到的梯度是零。而且路由本身也是一个神经网络,它的参数也会被更新。于是,你和环境之间形成了一个动态系统:路由决定谁被训练,被训练的专家又反过来影响路由的偏好。
野生的 MoE 训练会怎样?
如果你不管负载均衡,直接训练一个 MoE,很快就会发现:某些专家成了“网红”,几乎所有 token 都往它们那儿跑;另外一些专家则是“冷宫”,长时间没有 token 光顾,梯度为零,它们就再也学不到东西了。这种“专家贫富分化”在文献里叫负载不均(load imbalance)。更严重的是,一旦某个专家长期不被选中,它的输出对 loss 的贡献是固定的,路由学不到关于它的梯度信号,它就可能永远被边缘化——这就是“僵尸专家”。
Switch Transformers 的论文里提到,受限于路由的不稳定性,训练的过程中,路由器可能在不同状态之间反复横跳,导致 loss 曲线震荡,训练不稳定。这也是为什么他们专门引入了一个辅助的负载均衡损失,把这个作为标准训练配置。相关论文见这里。
Dense vs Sparse:一张表看穿训练动态差异
| 对比维度 | Dense 模型 | MoE 稀疏模型 |
|---|---|---|
| 梯度流动 | 每个 step 所有参数都获得梯度 | 只有被路由选中的专家获得梯度 |
| 计算效率 | 算多少参数量就用多少计算 | 参数量大,但每个 token 只激活一部分 |
| 负载均衡 | 天然均衡,所有参数一视同仁 | 容易出现专家分化和“僵尸专家” |
| 训练稳定性 | 损失曲线相对平滑 | 路由波动可能导致震荡甚至崩溃 |
| 内存占用 | 所有参数都需要被加载 | 所有专家都要驻留内存,虽然只算部分 |
| 训练通信 | 并行中每个参数都有固定归属 | 需要 all-to-all 通信把 token 送到对应专家 |
表格里有两行你可能觉得奇怪:为什么 MoE 内存占用比 Dense 大?因为所有专家参数都必须保留在显存里,虽然每次只算一部分。举个例子,Mixtral 有 46.7B 参数,激活 12.9B,但显存开销是按 46.7B 算的。还有通信,因为 token 分配到的专家可能在不同 GPU 上,所以需要 all-to-all 通信把 token 发送过去,这在 Dense 里不存在,也是 MoE 训练时的一个重要瓶颈。
一个 token 的旅程:稀疏训练背后的代价
- token 先进入一个共享的注意力层,得到一个隐藏表示。
- 路由(一个线性层)基于这个表示打分,选出分数最高的 top-k 个专家。
- token 被发送到选中的专家,专家计算自己的 FFN 变换。
- 多个专家的结果被加权合并,继续后续层。
- 反向传播时,梯度只流向路由和被选中的专家。
你看,稀疏并不是让专家“不训练”,而是让专家“用不到的时候就不训练”。这就像一个公司的员工,有活的时候才被叫来干活,没活的时候就闲着。但公司还得给他们发工资(存储参数)。休息的员工学不到新技能,要是分配不均,有些员工永远没活干,逐渐就废了。
想稳定训练?得给路由戴上“紧箍咒”
为了让训练动态可控,MoE 训练的标配是加一个辅助的负载均衡损失(auxiliary load balancing loss)。核心思路很简单:统计每个专家在一批数据中接收到的 token 数量,鼓励路由让每个专家接收的数量接近平均。Switch Transformer 的一个版本中,这个损失会和原来的语言模型损失加权相加。这相当于给路由一个“公平分配”的课业,让它不能随意偏心。
还有别的手段。例如专家容量(expert capacity)。限制每个专家最多接收多少个 token,超出的 token 就直接丢掉或跳过。这相当于给“网红专家”设上限,强制别人分担流量。不过太小的容量会丢太多 token,影响效果。有的模型也用基于噪声的 Top-k 路由(如 Shazeer et al. 2017),在路由打分上加入噪声,迫使模型探索不同的专家分配,也算是一种正则化。参考Shazeer 等人 2017 年的论文。
关于 MoE 训练的四个常见误区
- 误区一:MoE 参数量大,训练就一定更慢。实际上训练吞吐可能下降,因为通信和路由开销。
- 误区二:稀疏激活意味着专家可以并行独立训练。实际上它们通过共享注意力层和路由耦合在一起。
- 误区三:只要加大专家数量,效果就越好。实际上过大的专家数量会增加路由难度,导致收益递减。
- 误区四:负载均衡损失是万能的。它只是约束了分配比例,不能保证专家学到不同的知识。
为什么训练动态差异如此关键?
这意味着什么?当你决定把一个 dense 模型换成 MoE 时,你不仅要考虑参数量,还要重新调整超参数。比如学习率、batch size 甚至优化器都可能需要改。Switch Transformer 论文里提到,MoE 对学习率和正则化更敏感,他们用了 dropout 和调整后的 init 才稳定下来。Mixtral 团队也是做了很多工程上的努力,才能在 8 个专家的 46.7B 参数上训练成功(实际激活约 12.9B)。见Mixtral of Experts 论文。
我自己的一个领悟是:训练动态的差异,本质上来自“条件计算”带来的反馈循环。Dense 模型的梯度传播是前馈的,每个参数对自己的更新有明确责任;MoE 中路由成了一个额外的中介,中介可以决定让谁学习,而谁又通过学习反过来影响中介的选择。这个循环如果不加约束,就会产生极不稳定的动态。这比单纯增加参数量带来的困难要大得多。
MoE 不是免费的午餐
所以,MoE 不是免费的午餐。它为同等算力下扩大参数提供了可能,但代价是训练时要面对稀疏梯度、负载均衡、通信开销和更敏感的超参。现阶段,像 DeepSeek-MoE 这样的工作已经通过细粒度专家和共享专家隔离来缓解一些问题,但路由不稳定的根本难点仍在。如果你打算投入 MoE 训练,请做好心理准备:你得到的不是一个更容易训练的模型,而是一个更难训练、但推理效率更高的模型。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/559.html