AIGC标识 MoE 的关键不是专家多,而是路由稳

MoE 的核心不是堆更多专家,而是用稳定路由把容量、计算、显存和通信重新做平衡。

大模型扩容时,最直接的办法是把 FFN 做大。FFN 是 Transformer 里真正吃参数的模块,也常被看作模型知识容量的重要来源。问题在于,稠密 FFN 有一个硬约束:每个 token 都要经过同一套参数。参数变大,单 token 的计算量也跟着变大。

MoE 想拆开的就是这根绑带。它不让每个 token 都访问全部 FFN 参数,而是准备一组专家 FFN,再用 Router 给每个 token 选择少数几个专家。总参数可以很大,单次激活的参数仍然保持较小。

这也是 MoE 最核心的工程判断:用稀疏激活扩大容量,用路由机制控制计算。它不是免费加参数,而是在显存、通信、训练稳定性之间重新做账。

MoE 替换的是 FFN 位置

MoE 不是挂在 Transformer 外面的附加系统。常见做法是把 Transformer Block 里的 FFN 子层替换为 MoE 层。Attention 仍然负责 token 间的信息交互,MoE 层负责在每个 token 的位置上做条件计算。mermaid-01.png

图:MoE 通常替换 Transformer Block 中的 FFN 子层

原生 Transformer 没有 MoE。MoE 是后来在 FFN 子层上的一种结构替换。理解这一点很重要,因为专家本身并不神秘,通常就是一组参数独立的 FFN。
inline-01.png

图:MoE 的工程关键在于 Router 如何稳定地分配 token。

MoE 解决的是容量和计算的绑定

稠密模型的逻辑很简单:模型参数越多,容量越大;但每个 token 都要穿过全部参数,推理成本也同步上涨。

MoE 改成条件计算:

  • 总参数很大:有很多专家,每个专家都是独立 FFN。
  • 激活参数很小:每个 token 只交给 Top-K 个专家处理。
  • 其他专家待命:它们占显存,但这一轮不参与该 token 的计算。
    mermaid-02.png

图:MoE 通过 Router 只激活少数专家

可以把稠密 FFN 看成一个全科医生,什么 token 都由同一个医生处理。MoE 更像专科医院:门口的 Router 负责分诊,每次只请最相关的几位专家出场。

一个 token 在 MoE 里怎么走

一个标准 MoE 层通常由两部分组成:

组件 职责
Router / Gating Network 给每个 token 对所有专家打分,选择 Top-K 专家
Experts 多个结构相同、参数独立的 FFN,负责实际变换 token 表示

对输入 token 向量 $x$,Router 先计算专家分数:

$$
g(x) = W_g \cdot x, \quad W_g \in \mathbb{R}^{N \times d_{model}}
$$

然后选出分数最高的 $K$ 个专家,并只在 Top-K 内做 Softmax:

$$p_i = \frac{\exp(g(x)​i)}{\sum​{j \in \mathrm{TopK}} \exp(g(x)_j)}, \quad i \in \mathrm{TopK}$$

最后把这些专家的输出加权合并:

$$
y = \sum_{i \in \mathrm{TopK}} p_i \cdot E_i(x)
$$

符号含义如下:

符号 含义 维度
$x$ 输入 token 向量 $d_{model}$
$W_g$ Router 权重 $N \times d_{model}$
$g(x)$ 各专家原始分数 $N$
$N$ 专家总数 标量,如 8、64、256
$K$ 每个 token 激活的专家数 标量,如 1 或 2
$E_i(x)$ 第 i 个专家 FFN 的输出 $d_{model}$
$p_i$ 第 i 个专家的合并权重 标量

Router 学的不是人类语义上的分类,而是这个 token 交给谁处理更合适。
mermaid-03.png

图:Router 选择专家并加权合并输出

教学版 MoE 代码

真实工程会用批处理、分组 dispatch、专家并行和 All-to-All 通信优化。下面这段代码只保留核心逻辑,方便看清 Router、Top-K 和专家合并的关系。

import torch
import torch.nn as nn
import torch.nn.functional as F

class MoELayer(nn.Module):
    def __init__(self, d_model, d_ff, num_experts, top_k=2):
        super().__init__()
        self.top_k = top_k
        self.gate = nn.Linear(d_model, num_experts)
        self.experts = nn.ModuleList([
            nn.Sequential(
                nn.Linear(d_model, d_ff),
                nn.SiLU(),
                nn.Linear(d_ff, d_model),
            )
            for _ in range(num_experts)
        ])

    def forward(self, x):
        logits = self.gate(x)
        weights, idx = torch.topk(logits, self.top_k, dim=-1)
        weights = F.softmax(weights, dim=-1)

        out = torch.zeros_like(x)
        for slot in range(self.top_k):
            for e in range(len(self.experts)):
                mask = idx[:, slot] == e
                if mask.any():
                    out[mask] += (
                        weights[mask, slot:slot + 1] *
                        self.experts[e](x[mask])
                    )
        return out

这段代码效率很低,但把 MoE 的核心动作写清楚了:先按 token 选专家,再让专家处理属于自己的 token,最后把结果加权写回。

Router 为什么是 MoE 的难点

专家只是 FFN,真正难的是 Router。

Router 要做离散选择:从 N 个专家里选 ​Top-K​。可训练时又希望整个系统可导。这会带来两个问题:

  1. Top-K 截断本身不可导,路由如何学得更好。
  2. Router 很容易把 token 分给少数热门专家,造成负载不均衡。

负载不均衡是 MoE 的头号顽疾。训练早期,某些专家偶然被选得更多,它们得到更多训练,效果更好,于是又更容易被 Router 选中。最后少数专家被挤爆,大量专家几乎不工作。
mermaid-04.png

图:路由不稳会造成专家过载和训练低效

结果很直接:冷门专家白占参数,模型实际容量缩水;热门专家变成计算瓶颈。MoE 训练必须主动干预负载分配。

路由方案的演进主线

MoE 路由技术一直在回答同一个问题:怎么既选得准,又分得匀。

方案 做法 好处 代价
Noisy Top-K Gating 打分时注入可学习噪声,再做 Top-K 避免早期过快锁死热门专家 仍需配合额外均衡机制
Aux Loss 在主任务损失外加入负载均衡损失 经典、稳定、工程上常见 和主任务目标存在张力,超参难调
Expert Choice Routing 从专家视角选择 token,每个专家固定容量 天然负载均衡 token 可能被多个专家选中,也可能没人选
Loss-Free Balancing 给每个专家加动态 bias,太忙调低、太闲调高 不引入额外主损失,选择前调节负载 仍需控制 bias 更新速率

Noisy Top-K:给冷门专家出场机会

早期 MoE 方案会在 Router 分数里加入噪声,让选择带一点随机扰动。这样可以避免模型在训练初期过早把流量锁死到少数专家上。

它解决的是探索问题:冷门专家至少有机会拿到 token,才可能被训练起来。

Aux Loss:用额外损失拉平负载

GShard、Switch Transformer 等方案常用辅助负载均衡损失。直觉形式是:

$$
L_{aux} = \alpha \cdot N \cdot \sum_{i=1}^{N} f_i \cdot P_i
$$

其中:

  • $f_i$ 是实际被路由到专家 i 的 token 比例。
  • $P_i$ 是 Router 分给专家 i 的平均概率。
  • $\alpha$ 控制均衡惩罚强度。

负载越集中,这项损失越大,训练就会把 Router 往更均匀的方向拉。问题也在这里:辅助损失不是主任务本身,调大了可能伤效果,调小了又压不住不均衡。

Expert Choice:让专家反过来选 token

传统路由是 token 选专家。Expert Choice 反过来,让每个专家在容量范围内选择自己分数最高的 token。

它的好处是负载天然均衡,因为每个专家拿到固定容量。代价是 token 维度不再规整:同一个 token 可能被多个专家选中,也可能没有专家选。这个思路更依赖序列级视角,适用场景和工程实现都要单独评估。

Loss-Free:在选择前调节专家热度

DeepSeek-V3 等较新的方案试图摆脱 Aux Loss,用动态 bias 调节每个专家被选中的概率。Router 原始分数是 $s_i$,选择前变成:

$$
s_i'=s_i+b_i
$$

用 $s_i'$ 做 Top-K 选择,但最终合并权重仍基于原始分数 $s_i$。这样 bias 只改变选择,不直接污染前向语义。

bias 的更新规则可以写成:

$$
b_i \leftarrow b_i + \gamma \cdot sign(\overline{c_i} - c_i)
$$

其中:

  • $c_i$ 是这一批里专家 i 收到的 token 数。
  • $\overline{c_i}$ 是所有专家的平均 token 数。
  • $\gamma$ 是 bias 更新速率。

某个专家太忙,bias 调低;太闲,bias 调高。它把负载均衡从损失函数里挪到路由选择前。

MoE 省的是 FLOPs,不是显存

MoE 最容易被误解的一点是省资源。它省的是计算量,不是参数显存。

一个 token 只激活 2 个专家,并不代表其他专家可以不加载。所有专家参数都要在显存里待命。一个总参数 200B、激活参数 30B 的 MoE,计算近似 30B 稠密模型,但显存占用接近 200B 量级。
mermaid-05.png

图:Router 选择专家并加权合并输出

所以 MoE 是拿显存换算力。

多卡训练里,All-to-All 是大头

大规模 MoE 往往使用专家并行。专家分布在不同 GPU 上,一个 token 被路由到哪个专家,不一定在当前卡。于是每个 MoE 层都可能发生两次通信:

  1. 把 token 发到目标专家所在 GPU。
  2. 专家计算完,再把结果发回来。
    mermaid-06.png

图:多卡 MoE 训练里的通信开销来自 token 分发和回收

在大集群里,All-to-All 通信经常比专家 FFN 计算更难优化。MoE 的稀疏计算收益,必须和路由、dispatch、combine、跨卡通信一起算。

稠密 FFN 和 MoE 的工程账

维度 稠密 FFN MoE
总参数容量 较小 很大
单 token 激活参数 全部 Top-K 少数专家
FLOPs 随参数同步增加 可保持相对较低
显存占用 参数量多大就加载多大 所有专家常驻,显存压力高
通信开销 较低 专家并行下 All-to-All 显著
训练稳定性 相对稳定 Router、负载波动、loss spike 都要处理
适用场景 中小规模或简单部署 超大容量、追求单位计算性价比

MoE 的价值不是让模型无成本变大,而是在总容量和单次计算之间打开一个新的折中空间。

常见误区

问题 判断
MoE 是多个模型投票吗 不是。它是模型内部的条件计算结构,专家通常是多个 FFN
参数多,计算就一定多吗 不一定。关键看每个 token 激活多少专家
专家会变成数学专家、代码专家吗 不一定。专家可能有分工,但未必符合人类标签
MoE 推理一定更快吗 不一定。还要算路由、分发、合并和跨卡通信
Expert 越多越好吗 不是。专家多会增加容量,也会增加路由和通信复杂度
Top-K 越大越好吗 不是。K 变大可能增强表达,也会提高计算和通信成本
每层都要换成 MoE 吗 不一定。可以只替换部分 Transformer Block 的 FFN 子层

真正要带走的判断

MoE 的核心不是专家越多越好,而是三个问题能否同时被控制住:

  1. 容量能否扩大:用多个 FFN 专家提供更大的总参数空间。
  2. 计算能否稀疏:每个 token 只激活少量专家。
  3. 路由能否稳定:热门专家不过载,冷门专家不饿死,跨卡通信可承受。

Router 是 MoE 的灵魂,也是它最难工程化的部分。Noisy Top-K、Aux Loss、Expert Choice、Loss-Free Balancing 这些方案,本质都在处理同一组矛盾:选择要准,负载要均衡,训练目标不能被过度干扰。

MoE 值得用,是因为它把参数容量和单 token 计算量解耦了;MoE 难用,是因为它把问题转移到了显存、通信、路由和稳定性上。理解这笔账,比记住“混合专家”这个名字更重要。

推荐阅读

Prime Agent 把 Coding Agent 从工具调用推向可恢复工作流

AI 改写技术文档,最容易悄悄改掉你的意思

Agent Team 真正缺的不是更多 Agent,而是同步协议

DeepSeek Harness 为什么能热换模型:插件依赖、事件日志与回滚机制

DeepSeek Harness:Agent 自我改进之前,先让 Harness 可观察、可组合、可回滚

aaa_compressed_under_1M.png

posted @ 2026-08-21 10:43  AI小老六  阅读(91)  评论(0)    收藏  举报