起因#
机缘使然,最近在工作中接触到了 MoE(Mixture of Experts)。
有意思的是,我接触到的用法,和现在大模型里的 MoE 长得完全不一样。
我这边是:所有 expert 都算一遍,gate 只负责最后加权融合。
而一说 MoE,大家脑子里冒出来的多半是 DeepSeek / Mixtral 那种:几十个 expert,每个 token 只挑两三个来算。
差异大到,我一开始都怀疑是不是自己理解错了 MoE 这个词。
后来顺着这个反差梳理了一下,发现 MoE 压根就不是一种结构,而是一族结构。
把它们区分开的那把钥匙,就是 gate 和 expert 的关系。
这篇笔记就是我的梳理过程。
背景:MoE 从哪来#
MoE 最早能追溯到 1991 年。当时的结构就已经是现在这个样子了:多个 expert,外加一个 gating network。
动机是"分而治之":把输入空间划分成若干区域,每个 expert 只负责其中一片,gate 学会这个划分。这样每个 expert 只需要拟合一个较简单的局部函数,比一个复杂的全局模型好训。
注意这和 bagging/boosting 那种"一堆独立模型投票"不是一回事——MoE 的专家有分工,而且 gate 是按输入来加权,是动态的。这个后面还会再提。
核心公式长这样:
$$y = \sum_{i=1}^{N} g(x)_i \cdot E_i(x)$$其中 \(E_i(x)\) 是第 \(i\) 个 expert 的输出,\(g(x)\) 是 gate 输出的权重,\(N\) 是 expert 个数。输出就是所有 expert 输出的加权和。
注意,这个最原始的版本里,所有 expert 都要算一遍,gate 只是决定最后的加权比例。也就是说,最早的 MoE 是稠密的。
那它为什么沉寂了二十年?原因和当时整个神经网络的情况差不多:训练太难、算力不够、没有合适的场景。expert 学不出清晰分工,gate 也不稳定,看起来就是个更复杂的 MLP。
转机在 2017 年,一个关键改动:gate 不再给所有 expert 加权,而是只选出 top-k 个,只有被选中的 expert 才参与计算。
这个改动的意义在于,它把 MoE 从"加权融合结构"变成了"条件计算":参数总量可以疯狂涨,但每次 forward 只激活一小部分。
graph LR
subgraph 萌芽期[1991–2016 萌芽期]
A[1991
稠密 MoE
全部专家都算] --> B[1994
分层 MoE]
end
subgraph 复兴期[2017–2021 复兴期]
C[2017
稀疏 top-k
只激活部分] --> D[2020 GShard
大规模 Transformer] --> E[2021 Switch
top-1 更简化]
end
subgraph 爆发期[2024 爆发期]
F[Mixtral 8x7B] --> G[DeepSeek-V2/V3
细粒度 + shared expert]
end
B --> C
E --> F
先插一句名词辨析,不然读论文容易乱:gate、gating network、router,说的是同一样东西——那个决定"谁参与、权重多少"的组件。下文统一叫 gate。
中间还有一条常被忽略的支线:多任务学习里的 MMoE,用的恰恰是 1991 年那种稠密形态——所有 expert 都算,gate 只做加权。它在推荐系统里用得非常多,影响力不比 LLM 里的 MoE 小。
到 2024 年,Mixtral 和 DeepSeek-V2 让 MoE 真正走进工业界,从"论文里的结构"变成了"默认选项"。
所以到今天,“MoE” 这个词下面其实压着两种完全不同的用法:稀疏的和稠密的。这也是我写这篇笔记的初衷。
为什么要用 MoE#
先说 LLM 里的稀疏 MoE,它的动机一句话就能说清楚:在算力约束下换容量。
稠密模型的参数和 FLOPs 是严格绑定的:参数翻倍,算力基本也要翻倍。
而稀疏 MoE 把这两者解耦了:总参数可以很大,但每个 token 只经过 top-k 个 expert,FLOPs 只由激活的那部分参数决定。
graph LR
subgraph Dense[稠密模型]
D1[总参数 = 激活参数
671B 激活 671B]
end
subgraph Sparse[稀疏 MoE]
S1[总参数 671B
DeepSeek-V3] --> S2[每个 token 只激活 37B
top-k 专家]
end
Dense -->|容量相当| Sparse
S2 --> Cost[推理成本 ≈ 正比于激活参数
知识容量 ≈ 正比于总参数]
数字上感受一下:DeepSeek-V3 总参数 671B,每个 token 激活 37B;Mixtral 8x7B 总参数 46.7B,激活 12.9B。
参数涨了十几倍,单 token 算力基本没涨。
说白了就是:推理成本大致正比于激活参数,知识容量正比于总参数。花一样的钱,装更多的知识。
顺带一提,这也正是 MoE 和 bagging/boosting 这类集成的根本区别:集成对任何输入都走同一条路(所有模型都算再平均),是静态的;MoE 是 gate 根据当前输入动态决定走哪条路,不同输入走不同路径。
第二个顺带的好处是专家分工(expert specialization):路由会让不同的 expert 自然地学会处理不同类型的输入,比如有的对数学类 token 敏感,有的对代码类敏感。不少工作都在琢磨怎么让分工更彻底。
分工本身也带来一点可解释性的可能——可以看看哪些 token 被路由到了哪个 expert。不过这个属于锦上添花,不是 MoE 被大规模采用的核心理由。
至于 gate 在后的稠密 MoE(比如 MMoE),动机完全不同:不是省算力,而是让多个任务之间做软共享。
每个任务通过自己的 gate,从同一批 expert 里各取所需,而不是把所有任务硬塞进一个共享底层。这个后面展开讲。
分类学:gate 在哪,MoE 就是哪种#
前面那个公式 \(y = \sum_{i=1}^{N} g(x)_i \cdot E_i(x)\) 其实没有规定计算顺序。
而计算顺序,恰恰是最本质的分野。
- gate 在 expert 之前:先算 gate,根据 gate 的结果决定哪些 expert 参与计算。只有被选中的 expert 才动起来,这就是稀疏 MoE。
- gate 在 expert 之后:所有 expert 都先算完,gate 再对结果做加权。这就是稠密 MoE。
一句话:gate 在前,是"选人干活";gate 在后,是"都干完再汇总"。
这个位置差异决定了算力、训练方式、适用场景全都不一样。下面分别展开。
gate 在前:稀疏 MoE#
稀疏 MoE 的 forward 逻辑用伪代码表示大概是这样:
1def forward(self, x):
2 # x: [batch, dim]
3 logits = self.gate(x) # [batch, n_experts]
4 probs = F.softmax(logits, dim=-1)
5 topk_probs, topk_idx = probs.topk(self.top_k, dim=-1)
6
7 out = 0
8 for prob, idx in zip(topk_probs.unbind(1), topk_idx.unbind(1)):
9 out += prob.unsqueeze(1) * self.experts[idx](x)
10 return out三个关键点:
- gate 本身通常就是一个线性层加 softmax,输出 \(N\) 个权重。
- top-k 这一步是不可导的离散选择,所以梯度只能通过被选中的那几个 expert 传下去,这也是后面各种训练难点的根源。
- 没被选中的 expert 压根不 forward,这是省算力的来源,但也是负载均衡问题的来源。
顺带说一个实现细节:top-k 之后要不要把被选中 expert 的权重重新归一化(除以它们之和)?不同实现选择不同,有的保留原权重,有的会 renormalize。细节不影响理解,但看源码时别被这个差异绕晕。
graph TD
x["输入 x: [batch, dim]"] --> W["Gate 网络
linear + softmax
shape: [batch, n_experts]"]
W --> S[Softmax
row-wise]
S --> T[Top-k 选择
k=2]
T --> E1["Expert 1: E₁(x)"]
T --> E2["Expert 2: E₂(x)"]
E1 & E2 --> P["概率加权
p₁, p₂ = softmax 输出"]
P --> y["输出 y = p₁·E₁(x) + p₂·E₂(x)"]
这条线的主要区别,看 gate 怎么选就行:
- Shazeer 2017:noisy top-k。在打分上加一点噪声再取 top-k,让专家之间的选择有点随机性,不至于一上来就固定死。
- Switch Transformer:更激进,k 直接设成 1,只挑得分最高的那个。最简、最好训。
- Mixtral 8x7B:8 个 expert、top-2。
- DeepSeekMoE:把 expert 拆细,再加一批每个 token 都必过的 shared expert。这个放到后面"共享"那个维度细说。
graph TD
x["输入 x: [batch, dim]"] --> E["所有 Expert
并行计算 E₁(x)...Eₙ(x)"]
E --> W["Gate 网络
linear + softmax
shape: [batch, n_experts]"]
W --> S[Softmax
row-wise]
E & W --> F["加权求和
y = Σ gᵢ·Eᵢ(x)"]
gate 在后:稠密 MoE#
稠密 MoE 的 forward 逻辑是另一种写法:
1def forward(self, x):
2 # x: [batch, dim]
3 g = self.gate(x) # [batch, n_experts]
4 outputs = [expert(x) for expert in self.experts] # 每个 expert 都算
5 stacked = torch.stack(outputs, dim=-1) # [batch, dim, n_experts]
6 out = (stacked * g.unsqueeze(1)).sum(dim=-1)
7 return out对比上面那段代码,差别一目了然:这里没有 top-k,没有离散选择,所有 expert 都 forward 了,gate 只出现在最后一步的加权里。
所以它本质上是完全可微的,训练上没有稀疏 MoE 那些路由问题。
这也是它的代价:expert 的数量不能大。每个样本都要过全部 N 个 expert,N 一上来算力就线性爆炸,所以稠密 MoE 的 N 通常是个位数。
这决定了它的适用边界:任务是"融合几路视角",而不是"从一大堆专家里挑人"。
这条线的代表是 MMoE 和 PLE,场景基本都在多任务学习。
稠密 MoE 的代表:MMoE 和 PLE#
MMoE(Multi-gate Mixture-of-Experts)解决的是这样一个问题:多个任务共享一个底层网络时,任务之间会互相拉扯(比如 CTR 任务学到的表征对 CVR 任务有害),完全分开又浪费数据和参数。
它的做法是:底层换成一组 expert,每个任务 k 配一个自己的 gate \(g^k\),得到自己的融合结果,再走各自的任务塔:
$$y_k = h^k\left(\sum_{i=1}^{N} g^k(x)_i \cdot E_i(x)\right)$$其中 \(E_i\) 是共享的那批 expert,\(g^k\) 是任务 k 自己的 gate,\(h^k\) 是任务 k 自己的塔。
每个任务用不同的权重组合同一批 expert 的输出,任务之间既有共享(同一批 expert)又有隔离(各自的 gate 和塔),这比 hard sharing 灵活,比完全独立省参数。
注意这里的计算形态:所有 \(E_i\) 都要算(N 通常就几个),gate 在 expert 之后,只负责加权。
PLE(Progressive Layered Extraction)在 MMoE 基础上往前走了一步:它观察到 MMoE 里所有 expert 都是所有任务共用的,任务之间还是可能互相污染。
于是它把 expert 显式分成 shared expert 和 task-specific expert 两类——shared 部分每个样本都过,task-specific 部分只有对应任务用。gate 只负责 shared 部分的加权。
多任务里"哪些参数共享、哪些隔离"这个老问题,PLE 给了一个很工程化的答案。
这两个结构在推荐系统的 CTR/CVR 多目标建模里用得非常多。
它们和 LLM 稀疏 MoE 的差别,不在于"高级/低级",而在于目的:前者要的是任务间的软共享,后者要的是省算力。
一个特例:Soft MoE#
前面说 gate 在前的稀疏 MoE 会丢掉没被选中的 expert 的信息,gate 在后的稠密 MoE 又省不了算力。
有没有折中?有,Soft MoE 给出了一个很漂亮的答案。
它的核心是"slot":引入一个可学习矩阵 \(\Phi \in \mathbb{R}^{d \times m}\),可以理解成 m 个可学习的"位置"。
对输入 \(X\)(比如一帧图像的 n 个 token),先算每个 slot 对每个 token 的软权重:
$$\phi = \text{softmax}(X \cdot \Phi)$$然后用这些权重把 token 揉成 m 个混合 token:
$$\tilde{X} = \phi^{\top} \cdot X$$最后每个混合 token 过一个 expert,再按同样的权重散回原位置。
整个过程没有 top-k,没有离散选择,完全可导;但 expert 的输入从 n 个 token 变成了 m 个混合 token,算力还是省下来了(n 远大于 m 时)。
值得留意的是,Soft MoE 里的 \(\Phi\) 是可学习的,但它不依赖输入 \(x\)——也就是说,这里没有传统意义上"针对每个输入做决策"的 gate,而是一组学好的静态混合比例。
这和前面两种 MoE 的 gate 都不一样。严格来说它更接近"可学习的输入降采样",但大家都把它归进 MoE 家族,因为它保留了"多个 expert + 软分配"的内核。
Soft MoE 出来主要是为了视觉场景(ViT),它对稀疏 MoE 的几个训练问题(负载均衡 loss、专家坍塌)免疫,是个很有意思的观察点。
graph TD
X["输入 Token序列 X: [n_tok, dim]"] --> Φ["Slot矩阵 Φ:
可学习 [dim, m]"]
Φ --> W["权重计算
φ = softmax(X·Φ)
shape: [n_tok, m]"]
W --> Xh["混合 Token
X̃ = φᵀ·X
shape: [m, dim]"]
Xh --> E["每个 Slot 过一个 Expert
E₁(X̃₁), …, Eₘ(X̃ₘ)"]
E --> y["输出加权散布
y = Σ φᵢ · Eᵢ(X̃ᵢ)"]
其余三个次级维度#
gate 的位置是主刀,但看论文时还有三个维度值得一起问。
graph TD
subgraph D1[维度二:gate 的输入]
A1[只看 x
条件路由
LLM 常见]
A2[只看任务 id
静态路由
多任务常见]
A3[x + 任务 id
混合]
end
subgraph D2[维度三:expert 的粒度与共享]
B1[大 expert
一整个 FFN]
B2[细粒度 expert
拆小 + shared expert 必过
DeepSeekMoE]
end
subgraph D3[维度四:gate 结构]
C1[linear + softmax
基准]
C2[noisy top-k
Shazeer]
C3[top-1 argmax
Switch]
C4[aux-loss-free bias
DeepSeek]
C5[hash 路由
无参数]
end
- gate 的输入:只看 \(x\)(条件路由,每个样本路径不同);拼任务 id(静态路由,同任务内路径相同)。
- expert 粒度与共享:可以是一整个 FFN,也可以拆细(细粒度 expert + shared expert 承接公共知识)。
- gate 结构:从 linear+softmax 基准 → 加噪声 → top-1 → 用 bias 修正负载 → hash 路由(完全没有可学习参数)。
这四个维度组合起来,基本覆盖了主流 MoE 论文的设计空间。读论文的时候拿这四个问题套一遍,架构图很快就通了。
已知的坑#
这部分我没有在生产环境里实际踩过,属于"读论文读来的坑",简单记一笔。
稀疏 MoE 最大的两个坑是同一枚硬币的两面:
- 负载不均:gate 会逐渐"偏爱"少数 expert,大量 token 挤向同一批 expert,其他 expert 闲着。工程上每个 expert 的显存要按最忙的那个来分配,闲着的就是浪费。
- 专家坍塌:训练早期 gate 一旦形成赢家通吃,收不到 token 的 expert 就没有梯度,慢慢死掉,模型退化成一个小的稠密网络。
graph LR
T[Token 流] --> G[Gate
赢家通吃]
G -->|80% token| E1[Expert 1
过载]
G -->|15% token| E2[Expert 2
一般]
G -->|5% token| E3[Expert 3
饿死 → 梯度为 0 → 死掉]
G -->|0% token| E4[Expert 4
完全闲置]
主流的解法有几条路线:
- 加负载均衡 aux loss:让每个 expert 分到的 token 比例,和它拿到的平均 gate 权重都尽量均匀。缺点是这个 loss 会干扰主任务梯度,强度怎么调是个手艺活。
- 限制每个 expert 的 capacity,超出部分直接丢弃 token。简单粗暴,但要丢数据。
- DeepSeek 的 aux-loss-free 路线:给每个 expert 的 gate 打分加一个可调 bias,训练中按负载动态修正——超载的减一点,饿着的加一点。不干扰梯度,也不用丢 token。
工程侧还有 all-to-all 通信、expert 并行这些分布式训练的坑,和路由本身关系不大,这里不展开。
另外提一句:稀疏 MoE 对微调也不友好,负载均衡在继续训练时容易再次失衡,这也是社区里 MoE 微调经验帖多的原因。
常见用途速览#
graph TD
subgraph MoE[MoE 家族]
Sparse[稀疏 MoE
gate 在前
省算力换容量]
Dense[稠密 MoE
gate 在后
多任务软共享]
Soft[Soft MoE
slot 混合
视觉省算力]
end
Sparse -->|LLM| LLM[DeepSeek / Mixtral / Qwen]
Sparse -->|语音| ASR[MoE ASR]
Sparse -->|视觉| ViT[V-MoE]
Dense -->|推荐系统| Rec[MMoE / PLE
CTR/CVR]
Dense -->|金融领域| Fin[多任务加权融合]
Soft -->|视觉| SoftViT[Soft MoE / ViT]
- LLM:DeepSeek-V3/R1、Mixtral、Qwen。省推理算力,稀疏路由。
- 推荐系统多任务:MMoE/PLE 系。任务软共享,稠密加权。工业界存量应用可能比想象的多。
- 视觉:V-MoE(稀疏)、Soft MoE(软 slot)。给大 ViT 省算力。
- 语音:MoE ASR,结构上走 LLM 稀疏那套。
- 金融领域:我自己接触到的用法更接近 gate 在后的加权融合形态(场景就不展开了),和 LLM 那套完全不同。这也是我写这篇文章的最初动机。
几个容易混淆的概念#
顺带澄清几个名字里带 expert 或者和 MoE 长得很像、但本质不同的东西,免得看文章时对不上号:
- ensemble(集成):和 MoE 最本质的区别是静态 vs 动态。bagging/boosting 里不管输入是什么,所有基学习器都要算一遍再投票/平均,路径是静态的;MoE 的 gate 会根据当前输入决定走哪条路(稀疏的直接只激活部分 expert),不同输入走不同路径,是动态的。另外集成也没有"专家分工"这回事。
- Mixture-of-Agents (MoA):2024 年出现在 LLM 推理侧的一类方法,让多个 agent/LLM 轮流产出、互相参考再汇总。名字像 MoE,但它是推理时的多模型协作,不是模型内部的一个层,不共享参数也不做门控路由。
- 知识蒸馏/专家混合(routing in compression):有一些蒸馏工作把大模型的层"打包"成 expert 再路由,属于 MoE 的衍生用法,但目的不是省算力而是压缩/迁移。
这些都不影响本文的主线,只是"名字里带 MoE 的字"不等于就是 MoE。
总结#
三类用法放在一张表里对比:
| 稀疏 MoE | 稠密 MoE | Soft MoE | |
|---|---|---|---|
| gate 位置 | expert 之前 | expert 之后 | 静态可学习 slot |
| 计算形态 | 只算 top-k | 全算再加权 | 全算但输入是混合 token |
| 可导性 | 离散选择,不可导 | 完全可导 | 完全可导 |
| 主要动机 | 省算力换容量 | 多任务软共享 | 视觉大模型省算力 |
| 代表 | Switch / Mixtral / DeepSeek | MMoE / PLE | Soft MoE |
拿到任何一篇 MoE 论文,先问三个问题:
graph TD
Q1{gate 在哪?}
Q1 -->|expert 之前| S[稀疏 MoE
省算力换容量]
Q1 -->|expert 之后| D[稠密 MoE
多任务软共享]
S --> Q2{gate 输入?}
D --> Q3{expert 怎么组织?}
Q2 -->|只看 x| S1[条件路由
LLM 常见]
Q2 -->|拼任务 id| S2[任务路由
多任务常见]
Q3 -->|全共享| D1[MMoE 形态]
Q3 -->|shared + specific| D2[PLE 形态]
- gate 在哪:expert 之前还是之后?这决定了它是稀疏还是稠密。
- gate 的输入是什么:只有 x,还是带任务信息?
- expert 怎么组织:有没有共享,粒度多细?
问完这三个问题,任何 MoE 的架构图都能对号入座。剩下的都是这三个问题的具体答案。
参考资料#
- Adaptive Mixtures of Local Experts (Jacobs et al., 1991)
- Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer (Shazeer et al., 2017)
- GShard: Scaling Giant Models with Conditional Computation and Automatic Sharding (Lepikhin et al., 2020)
- Switch Transformers: Scaling to Trillion Parameter Models (Fedus et al., 2021)
- Hash Layers For Large Sparse Models (Roller et al., 2021)
- Scaling Vision with Sparse Mixture of Experts (Riquelme et al., 2021)
- Mixtral of Experts (Jiang et al., 2024)
- DeepSeekMoE: Towards Ultimate Expert Specialization in MoE Language Models (Dai et al., 2024)
- DeepSeek-V3 Technical Report (DeepSeek-AI, 2024)
- Modeling Task Relationships in Multi-task Learning with Multi-gate Mixture-of-Experts (Ma et al., KDD 2018)
- Progressive Layered Extraction (PLE): A Novel Multi-Task Learning Model for Personalized Recommendations (Tang et al., RecSys 2020)
- From Sparse to Soft Mixtures of Experts (Puigcerver et al., 2023)