- Published on
Mixture of Experts(MoE)架构深度解析:从 Switch Transformer 到 Mixtral 的演进与高效扩展策略
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 引言
- MoE 架构基本原理
- Switch Transformer 解析
- Mixtral 8x7B 架构
- DeepSeek-MoE:细粒度专家切分
- 路由机制比较
- MoE 模型对比表
- 训练与推理优化
- 运营注意事项
- 故障案例与解决方案
- 生产环境检查清单
- 参考资料
- 结语

引言
提升大语言模型(LLM)性能最直观的方法是增加参数量。但在 Dense 模型中,参数量翻倍,训练和推理所需的运算量也几乎随之翻倍。Mixture of Experts(MoE) 架构为这一问题提供了一个优雅的解法:大幅增加模型的总参数量,但对每个输入 token 只激活全部参数中的一部分,从而将运算成本维持在恒定水平。
这一想法可以追溯到 1991 年 Jacobs 等人提出的原始 MoE 论文,但近几年才取得了飞跃式发展。Google 的 Switch Transformer(2021)用单专家路由大幅降低了 MoE 的复杂度;Mistral AI 的 Mixtral 8x7B(2024)用 Top-2 路由为开源 LLM 树立了新标准;DeepSeek-MoE(2024)则通过细粒度的专家切分将专家特化推向极致。而 DeepSeek-V3(2024)在 671B 参数中只激活 37B 的结构下,实现了顶尖水平的效率。
本文将从 MoE 架构的基本原理出发,综合梳理主要模型的设计差异、路由机制比较、训练/推理优化策略、运营注意事项、故障案例与生产环境检查清单。
MoE 架构基本原理
门控机制
MoE 层的核心是门控网络(Gating Network)。给定输入 token x,门控网络会输出各个专家上的概率分布。
其中 W_g 是可学习的门控权重矩阵。输出向量 G(x) 的每个元素表示对应专家被选中的概率。
import torch
import torch.nn as nn
import torch.nn.functional as F
class GatingNetwork(nn.Module):
"""MoE 基础门控网络"""
def __init__(self, d_model, num_experts, top_k=2):
super().__init__()
self.top_k = top_k
self.gate = nn.Linear(d_model, num_experts, bias=False)
def forward(self, x):
# x: (batch_size, seq_len, d_model)
logits = self.gate(x) # (batch_size, seq_len, num_experts)
# 选择 Top-k 专家
top_k_logits, top_k_indices = torch.topk(logits, self.top_k, dim=-1)
top_k_gates = F.softmax(top_k_logits, dim=-1)
return top_k_gates, top_k_indices
Sparse Activation
在 Dense 模型中,每次输入都会激活全部参数;而在 Sparse MoE 中,只有 Top-k 个专家会被激活。由于 N 个专家中只有 k 个参与运算,模型容量可以扩大到 N 倍,而运算量仍能维持在 k 倍的水平。
MoE 层的输出由被选中专家的加权和计算得出。
其中 E_i(x) 表示第 i 个专家网络的输出。
class MoELayer(nn.Module):
"""基础 Sparse MoE 层"""
def __init__(self, d_model, d_ff, num_experts, top_k=2):
super().__init__()
self.num_experts = num_experts
self.top_k = top_k
self.gate = GatingNetwork(d_model, num_experts, top_k)
# 每个专家都是独立的 FFN
self.experts = nn.ModuleList([
nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model)
)
for _ in range(num_experts)
])
def forward(self, x):
B, T, D = x.shape
gates, indices = self.gate(x) # gates: (B,T,k), indices: (B,T,k)
# 初始化输出
output = torch.zeros_like(x)
# 按专家逐一进行批处理
for i in range(self.num_experts):
# 分配给第 i 个专家的 token 掩码
expert_mask = (indices == i).any(dim=-1) # (B, T)
if not expert_mask.any():
continue
# 提取对应 token 并执行专家运算
expert_input = x[expert_mask]
expert_output = self.experts[i](expert_input)
# 应用门控权重
gate_values = gates[indices == i]
output[expert_mask] += gate_values.unsqueeze(-1) * expert_output
return output
Capacity Factor
每个专家能处理的 token 数上限由 Capacity Factor(CF) 控制。理想的均匀分配下,每个专家处理 T / N 个 token,再乘以 CF 得到实际容量。
CF 为 1.0 时假设完全均匀分配,实际中通常使用 1.25~1.5。CF 太低会导致 token 被丢弃(overflow),太高则会浪费内存。
Switch Transformer 解析
单专家路由的革新
Google Brain 的 Switch Transformer(Fedus et al., 2021)打破了以往 MoE 的常识。此前普遍认为,至少要激活 Top-2 个专家才能实现稳定训练,而 Switch Transformer 证明了仅用 Top-1(单专家) 路由也能达到出色的性能。
单专家路由的优势有三点。
- 路由计算减少:由于只选择一个专家,门控计算被简化
- 专家容量效率化:由于每个 token 只分配给一个专家,在相同 Capacity Factor 下可以将批大小提高一倍
- 通信成本降低:分布式训练时只需把 token 发送到一个专家所在的设备
class SwitchRouter(nn.Module):
"""Switch Transformer 路由器(Top-1)"""
def __init__(self, d_model, num_experts, capacity_factor=1.25):
super().__init__()
self.num_experts = num_experts
self.capacity_factor = capacity_factor
self.gate = nn.Linear(d_model, num_experts, bias=False)
def forward(self, x):
B, T, D = x.shape
# 门控 logits
logits = self.gate(x) # (B, T, num_experts)
probs = F.softmax(logits, dim=-1)
# 选择 Top-1
gate_values, expert_indices = probs.max(dim=-1) # (B, T)
# 计算专家容量
capacity = int(self.capacity_factor * T / self.num_experts)
# 处理超出容量的 token 丢弃
dispatch_mask = torch.zeros(B, T, self.num_experts, dtype=torch.bool)
for i in range(self.num_experts):
expert_mask = (expert_indices == i)
# 超出容量时丢弃超出部分
positions = expert_mask.nonzero(as_tuple=True)
if len(positions[1]) > capacity:
drop_indices = positions[1][capacity:]
expert_mask[positions[0][capacity:], drop_indices] = False
dispatch_mask[:, :, i] = expert_mask
return gate_values, expert_indices, dispatch_mask
负载均衡损失
Switch Transformer 引入了辅助损失(Auxiliary Loss)来防止专家间的负载不均衡。它被定义为各专家分配到的 token 比例(f_i)与该专家平均路由概率(P_i)的内积。
def load_balancing_loss(gates, expert_indices, num_experts, alpha=0.01):
"""Switch Transformer 负载均衡辅助损失"""
B, T = expert_indices.shape
# f_i:分配给各专家的 token 比例
f = torch.zeros(num_experts, device=gates.device)
for i in range(num_experts):
f[i] = (expert_indices == i).float().sum() / (B * T)
# P_i:各专家的平均路由概率
probs = F.softmax(gates, dim=-1) # 针对全部专家的概率
P = probs.mean(dim=[0, 1]) # (num_experts,)
# 均衡损失
loss = alpha * num_experts * (f * P).sum()
return loss
性能结果
Switch Transformer 在与 T5-Base 相同运算量的条件下,实现了快 7 倍的预训练速度。即便扩展到 1.6 万亿(1.6T)参数,也保持了训练稳定性,这要归功于 bfloat16 训练与路由器 z-loss 的引入。
Mixtral 8x7B 架构
结构设计
Mistral AI 的 Mixtral 8x7B(2024)是开源 MoE 模型的代表案例。它以与 Mistral 7B 相同的 Transformer 架构为基础,将每层的 FFN 模块替换为 8 个专家,并采用 Top-2 路由。
核心规格如下。
- 总参数量:46.7B(含专家参数)
- 激活参数量:12.9B(每个 token 实际参与运算的参数)
- 专家数量:8 个(每层)
- 激活专家数:2 个(Top-2 路由)
- 注意力机制:使用 Grouped Query Attention(GQA)
- 上下文长度:32K(应用 Sliding Window Attention)
class MixtralMoELayer(nn.Module):
"""Mixtral 8x7B 风格 MoE 层"""
def __init__(self, d_model=4096, d_ff=14336, num_experts=8, top_k=2):
super().__init__()
self.num_experts = num_experts
self.top_k = top_k
# 路由器:线性门控
self.gate = nn.Linear(d_model, num_experts, bias=False)
# 8 个独立的 SwiGLU FFN 专家
self.experts = nn.ModuleList([
SwiGLUExpert(d_model, d_ff) for _ in range(num_experts)
])
def forward(self, x):
B, T, D = x.shape
x_flat = x.view(-1, D) # (B*T, D)
# 计算路由概率
logits = self.gate(x_flat) # (B*T, 8)
weights, indices = torch.topk(logits, self.top_k, dim=-1)
weights = F.softmax(weights, dim=-1) # 在 Top-k 内做归一化
# 对专家输出做加权求和
output = torch.zeros_like(x_flat)
for i in range(self.top_k):
expert_idx = indices[:, i] # (B*T,)
gate_weight = weights[:, i] # (B*T,)
for j in range(self.num_experts):
mask = (expert_idx == j)
if mask.any():
expert_out = self.experts[j](x_flat[mask])
output[mask] += gate_weight[mask].unsqueeze(-1) * expert_out
return output.view(B, T, D)
class SwiGLUExpert(nn.Module):
"""Mixtral 使用的基于 SwiGLU 的 FFN 专家"""
def __init__(self, d_model, d_ff):
super().__init__()
self.w1 = nn.Linear(d_model, d_ff, bias=False)
self.w2 = nn.Linear(d_ff, d_model, bias=False)
self.w3 = nn.Linear(d_model, d_ff, bias=False)
def forward(self, x):
return self.w2(F.silu(self.w1(x)) * self.w3(x))
Top-2 路由的优势
Mixtral 采用 Top-2 的原因如下。
- 训练更稳定:结合两个专家的输出,使梯度流动更加稳定
- 专家利用率:相比单专家,会激活更多样的专家,从而降低专家崩溃的风险
- 性能与效率的平衡:以 12.9B 的激活参数达到 Llama 2 70B 级别的性能,同时推理速度快 6 倍
DeepSeek-MoE:细粒度专家切分
核心策略
DeepSeek-MoE(2024)用两项核心策略将专家特化推向极致。
策略 1:细粒度专家切分(Fine-grained Expert Segmentation)
如果说传统 MoE 是从 16 个大型专家中选出 Top-2,那么 DeepSeek-MoE 则把相同的总参数量切分成 64 个小型专家,并选出 Top-8。它将每个专家 FFN 的中间维度缩小到 1/4,专家数量增加到 4 倍,从而让可能的专家组合数急剧增加。
- 从 16 个中选 2 个:C(16,2) = 120 种组合
- 从 64 个中选 8 个:C(64,8) ≈ 44 亿种组合
class DeepSeekMoELayer(nn.Module):
"""DeepSeek-MoE 风格层:细粒度专家 + 共享专家"""
def __init__(
self,
d_model=2048,
d_ff=10944,
num_routed_experts=64,
num_shared_experts=2,
top_k=6,
):
super().__init__()
self.top_k = top_k
# 细粒度路由专家(较小的 FFN)
expert_d_ff = d_ff // 4 # 相比原来缩小到 1/4
self.routed_experts = nn.ModuleList([
SwiGLUExpert(d_model, expert_d_ff)
for _ in range(num_routed_experts)
])
# 共享专家(始终激活)
self.shared_experts = nn.ModuleList([
SwiGLUExpert(d_model, d_ff)
for _ in range(num_shared_experts)
])
# 路由器
self.gate = nn.Linear(d_model, num_routed_experts, bias=False)
def forward(self, x):
B, T, D = x.shape
x_flat = x.view(-1, D)
# 共享专家输出(始终激活)
shared_out = sum(expert(x_flat) for expert in self.shared_experts)
# 路由专家输出
logits = self.gate(x_flat)
weights, indices = torch.topk(logits, self.top_k, dim=-1)
weights = F.softmax(weights, dim=-1)
routed_out = torch.zeros_like(x_flat)
for i in range(self.top_k):
expert_idx = indices[:, i]
gate_weight = weights[:, i]
for j in range(len(self.routed_experts)):
mask = (expert_idx == j)
if mask.any():
out = self.routed_experts[j](x_flat[mask])
routed_out[mask] += gate_weight[mask].unsqueeze(-1) * out
return (shared_out + routed_out).view(B, T, D)
策略 2:共享专家隔离(Shared Expert Isolation)
将部分专家指定为共享专家(Shared Experts),对所有 token 始终保持激活。共享专家负责通用知识(常见语言模式、句法结构等),使路由专家能够专注于特化知识。这样一来,路由专家之间的知识冗余减少,特化程度也随之提高。
性能
DeepSeek-MoE 16B 仅用约 40% 的运算量,就达到了与 Dense 模型 DeepSeek 7B 相当的性能。DeepSeek-MoE 2B 则展现出与专家参数和运算量多出 1.5 倍的 GShard 2.9B 相当的性能。
路由机制比较
Top-k 路由
这是最常见的方式,选择门控网络输出中排名靠前的 k 个专家。
- Top-1(Switch Transformer):运算最少、效率最高,但存在训练不稳定的风险
- Top-2(Mixtral、GShard):稳定性与效率的平衡
- Top-k(DeepSeek-MoE,k=6~8):与细粒度专家搭配使用时具有很高的灵活性
Expert Choice 路由
Google Research(2022)提出的 Expert Choice 方式,不是由 token 选择专家,而是由专家选择自己要处理的 token。由于每个专家都挑选 Top-k 个 token,因此能保证完美的负载均衡。
class ExpertChoiceRouter(nn.Module):
"""Expert Choice 路由:由专家选择 token"""
def __init__(self, d_model, num_experts, capacity_factor=1.0):
super().__init__()
self.num_experts = num_experts
self.capacity_factor = capacity_factor
self.gate = nn.Linear(d_model, num_experts, bias=False)
def forward(self, x):
B, T, D = x.shape
x_flat = x.view(-1, D) # (N, D) where N = B*T
N = x_flat.shape[0]
logits = self.gate(x_flat) # (N, num_experts)
scores = F.softmax(logits, dim=0) # 在 token 维度上做 softmax
# 每个专家要处理的 token 数
k = int(self.capacity_factor * N / self.num_experts)
# 各专家选择 Top-k 个 token
top_k_scores, top_k_indices = torch.topk(
scores.t(), k, dim=-1
) # (num_experts, k)
return top_k_scores, top_k_indices
Hash 路由
这是一种用哈希函数取代可学习门控、将 token 分配给专家的方式。由于不存在路由计算,开销接近于零,路由不稳定问题也从根本上消失。但由于无法根据输入特征做出最优分配,性能会略低一些。
MoE 模型对比表
| 项目 | Switch Transformer | Mixtral 8x7B | DeepSeek-MoE 16B | DeepSeek-V3 |
|---|---|---|---|---|
| 发布时间 | 2021.01 | 2024.01 | 2024.01 | 2024.12 |
| 总参数量 | 1.6T(最大) | 46.7B | 16.4B | 671B |
| 激活参数量 | 可变 | 12.9B | 2.8B | 37B |
| 专家数量 | 128 | 8 | 64 路由 + 2 共享 | 256 路由 + 1 共享 |
| 激活专家数 | 1(Top-1) | 2(Top-2) | 6(Top-6) | 8(Top-8) |
| 路由方式 | Learned Top-1 | Learned Top-2 | Learned Top-k | Learned Top-k |
| 负载均衡 | Auxiliary Loss | 未公开 | Auxiliary Loss | Auxiliary-Loss-Free |
| 共享专家 | 无 | 无 | 有(2 个) | 有(1 个) |
| 基础架构 | T5(Encoder-Decoder) | Mistral 7B(Decoder) | Decoder-only | Decoder-only |
| 主要成果 | 7 倍训练速度提升 | 相比 Llama 2 70B 推理快 6 倍 | 40% 运算量达到与 Dense 7B 相当 | 开源最佳性能 |
训练与推理优化
Expert Parallelism
在 MoE 模型的分布式训练中,Expert Parallelism(EP) 是核心。它把每个专家部署在不同的 GPU 上,通过 All-to-All 通信把 token 发送给对应的专家。
# DeepSpeed MoE 配置示例
deepspeed_config = {
"train_batch_size": 256,
"fp16": {"enabled": True},
"zero_optimization": {"stage": 2},
"moe": {
"enabled": True,
"ep_size": 8, # Expert Parallelism:将专家分散到 8 个 GPU 上
"num_experts": 64,
"top_k": 2,
"capacity_factor": 1.25,
"min_capacity": 4,
"use_residual": True, # 残差 MoE
"moe_param_group": True # 专家参数单独分组
}
}
通常会把 EP 和 Data Parallelism(DP)结合使用。例如在 64 个 GPU 上配置 EP=8、DP=8,则由 8 个 GPU 负责一个专家组,同时 8 个副本并行处理数据。
负载均衡策略
下面整理用于训练稳定性的负载均衡技巧。
1. Auxiliary Loss(Switch Transformer)
这是最基本的辅助损失,最小化专家分配比例与路由概率的乘积。系数 alpha 通常在 0.01~0.1 范围内调优。
2. Router z-loss(ST-MoE)
通过对门控 logits 的量级施加惩罚来提升训练稳定性。它抑制输入路由器的过大 logits 值,从而减少浮点舍入误差。
3. Auxiliary-Loss-Free(DeepSeek-V3)
完全去掉辅助损失,转而为每个专家添加一个可学习的偏置项,动态调整路由概率。这从根本上消除了辅助损失对模型性能的负面影响。
推理优化
MoE 模型推理中最大的挑战是内存占用。由于必须把所有专家都加载进 GPU 内存,即便是实际不参与运算的专家也会占用内存。
主要的优化技巧如下。
- Expert Offloading:把未激活的专家移到 CPU/NVMe,在需要时再加载到 GPU。由于会增加延迟,通常结合基于预测的预取。
- Expert Quantization:将未激活的专家量化为 INT4/INT8 以降低内存占用。
- Expert Pruning:剔除使用频率较低的专家以缩小模型体积。
- Speculative Expert Loading:预测下一层将被激活的专家并提前加载。
运营注意事项
内存管理
MoE 模型的总参数量远大于激活参数量,因此在内存规划上需要格外留意。以 Mixtral 8x7B 为例,激活参数只有 12.9B,但仍需把全部 46.7B 参数加载进 GPU。按 FP16 计算,大约需要 93GB 的 GPU 内存,单张 A100 80GB 无法满足。
Expert Collapse
Expert Collapse(专家崩溃) 是 MoE 训练中最常见的问题。它表现为大部分 token 集中到少数专家身上,其余专家几乎不被使用。原因与对策如下。
- 原因:训练初期,如果某个专家偶然表现较好,就会被分配更多 token,进而获得更多训练机会、变得更强 —— 这是一个正反馈循环
- 对策:加强负载均衡损失、给路由器添加噪声、使用 Expert Choice 路由
路由不稳定性
训练初期可能会出现路由决策剧烈变化的 Routing Instability。为防止这一现象,会使用以下技巧。
- 将路由器的学习率设置得比主模型更低(通常为 0.1 倍)
- 使用 bfloat16 以确保数值稳定性(相比 fp16 表示范围更宽)
- 通过 Router z-loss 限制 logits 的量级
- 在训练初期的预热阶段将路由固定为均匀分配
故障案例与解决方案
案例 1:推理时特定专家 OOM
症状:在某个批次中,token 过度集中到某一个专家身上,导致该专家所在 GPU 发生 OOM
原因:当输入数据的分布与训练数据差异较大时,路由器会做出偏向特定专家的决策
解决方案:
- 设置 Capacity Factor 以限制每个专家的最大 token 数
- 在推理阶段也应用简单的负载均衡逻辑
- token 溢出时不丢弃,而是通过残差连接(residual)绕行
案例 2:训练中 Loss 发散
症状:训练初期数千步之后,Loss 突然发散
原因:路由器的 logits 值过大,导致 softmax 输出趋近于 0 或 1,进而引发梯度爆炸
解决方案:
- 应用 Router z-loss(系数 0.001~0.01)
- 使用 bfloat16(相比 fp16 数值范围更宽,可防止溢出)
- 将路由器权重初始化为较小的值
- 应用 Gradient clipping(max norm 1.0)
案例 3:Expert Parallelism 通信瓶颈
症状:GPU 利用率低,大部分时间都消耗在 All-to-All 通信上
原因:相对于专家数量而言 GPU 数量过多,导致通信开销超过了运算时间
解决方案:
- 优化 EP 规模与专家数量的比例(建议 专家数量 / EP 规模 >= 2)
- 使用通信与运算重叠(overlap)技巧
- 在配备 NVLink/NVSwitch 的节点内构建 EP
生产环境检查清单
以下是将 MoE 模型部署到生产环境时需要确认的项目。
内存与基础设施
- 按总参数量估算 GPU 内存(依据完整模型体积,而非激活参数量)
- 配置 Expert Parallelism 时确认 GPU 间通信带宽(NVLink 为必需)
- 设置 Capacity Factor 并监控 token 丢弃比例
- 验证 KV Cache 与专家参数的内存总和
训练稳定性
- 调优负载均衡损失系数(过大会降低性能,过小则会导致崩溃)
- 确认是否启用 Router z-loss
- 使用 bfloat16 训练(相比 fp16 训练稳定性更优)
- 搭建按专家统计 token 分配比例的监控看板
推理优化
- 应用 Expert Offloading 时进行延迟基准测试
- 按批大小分析专家激活模式
- 特定专家过载时的回退策略(丢弃 vs 残差)
- 应用量化(INT8/INT4)后验证精度
监控
- 跟踪各专家的激活频率(尽早发现 Expert Collapse)
- 监控路由熵(过低意味着崩溃,过高意味着随机路由)
- 跟踪各 GPU 的内存占用与运算负载不均衡
- 监控 All-to-All 通信时间与运算时间的比例
参考资料
- Fedus, W., Zoph, B., and Shazeer, N. "Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity." JMLR 23, 2022.
- Jiang, A. Q. et al. "Mixtral of Experts." Mistral AI, 2024.
- Dai, D. et al. "DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models." ACL, 2024.
- DeepSeek-AI. "DeepSeek-V3 Technical Report." 2024.
- Zhou, Y. et al. "Mixture-of-Experts with Expert Choice Routing." NeurIPS, 2022.
- Zoph, B. et al. "ST-MoE: Designing Stable and Transferable Sparse Expert Models." 2022.
- Shazeer, N. et al. "Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer." ICLR, 2017.
- Jacobs, R. A. et al. "Adaptive Mixtures of Local Experts." Neural Computation, 1991.
结语
MoE 架构是应对「用更大的模型、花更少的成本」这一 LLM 扩展核心难题最实用的解法。Switch Transformer 打开了单专家路由的可能性,Mixtral 在开源生态中证明了 MoE 的实用性,DeepSeek-MoE/V3 则用细粒度专家策略树立了效率的新标准。
但 MoE 依然存在有待解决的挑战。专家崩溃与路由不稳定性需要在训练过程中持续管理,相对总参数量偏高的内存需求也会推高部署成本。Expert Choice 路由、Auxiliary-Loss-Free 均衡等最新技巧正在逐步解决这些问题,但完美的答案尚未出现。
展望未来,MoE 的发展方向大致可以预见如下:专家的动态生成与移除、多模态专家特化、推理时自适应调节专家数量,以及与硬件紧密 co-design 的 MoE 架构都将陆续出现。正如 DeepSeek-V3 这样的模型所展示的那样,MoE 已经成为最前沿 AI 模型的核心组成部分,这一趋势今后还将进一步加速。