- 引言
- MoE 基本结构与数学原理
- 路由策略:Top-k、Expert Choice、Hash Routing
- 负载均衡与 Auxiliary Loss
- 从 Switch Transformer 到 DeepSeek-V3 的演进
- 训练稳定性与故障排查
- 推理优化:Expert Parallelism、Offloading
- 运维检查清单
- 故障案例与恢复
- 参考资料

引言
大语言模型(LLM)的参数量正在呈指数级增长,但每个 token 都要激活全部参数的 Dense 模型已经撞上了运算成本的墙。要训练 GPT-4 级别的 Dense 模型,需要数万块 GPU 运行数月,推理成本也随参数量成比例增加。为了解决这一根本性的低效,条件计算(Conditional Computation)范式应运而生,而 Sparse Mixture of Experts(MoE)架构正处于这一范式的核心。
MoE 的核心思路很简单:设置数百个专家(Expert)网络,但对每个输入 token 只激活其中少数几个专家,从而大幅削减运算量。总参数量决定模型容量(capacity),而激活参数量决定实际运算成本,因此可以同时实现高质量与低成本。Mixtral 8x7B 在总共 47B 参数中每个 token 只激活 13B,DeepSeek-V3 则在 671B 参数中只激活 37B。
本文将深入探讨 MoE 的数学基础、路由策略、负载均衡,从 Switch Transformer 到 DeepSeek-V3、Qwen3-235B-A22B 的架构演进,以及训练与推理的实战优化策略。每个主题都配以 PyTorch 代码说明,并涵盖生产环境中出现的故障案例与恢复流程。
MoE 基本结构与数学原理
Sparse Activation 的数学定义
MoE 层由 N 个专家网络 E_1、E_2、...、E_N 和一个门控网络 G 组成。对输入 token x,MoE 层的输出 y 定义如下:
y = sum_{i=1}^{N} G(x)_i * E_i(x)
这里 G(x) 是门控函数,针对输入 x 输出一个 N 维向量。在 Dense MoE 中,所有 G(x)_i 都是非零值,而在 Sparse MoE 中,只选择 Top-K 个专家,其余的门控值设为 0。
G(x)_i = softmax(W_g * x + noise)_i (if i in TopK)
G(x)_i = 0 (otherwise)
噪声项的作用是在训练时促进探索(exploration),提高专家利用的多样性。得益于这种稀疏性,总参数量按 N 的比例增长,而实际运算量(FLOPs)按 K 的比例增长,相比 Dense 模型效率提升 N/K 倍。
专家网络的结构
每个专家通常替代 Transformer 中的前馈网络(FFN)。标准设计是所有 token 共享 Self-Attention,只把 FFN 部分按专家拆分。这是因为 Self-Attention 承担的是捕捉 token 间关系的全局角色,而 FFN 承担的是转换单个 token 表示的局部角色。
import torch
import torch.nn as nn
import torch.nn.functional as F
from dataclasses import dataclass
@dataclass
class MoEConfig:
d_model: int = 1024
d_ff: int = 4096
num_experts: int = 8
top_k: int = 2
dropout: float = 0.1
aux_loss_weight: float = 0.01
class SwiGLUExpert(nn.Module):
"""使用 SwiGLU 激活函数的专家 FFN。
LLaMA、Mistral 等最新模型采用的标准结构。"""
def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
super().__init__()
self.w_gate = nn.Linear(d_model, d_ff, bias=False)
self.w_up = nn.Linear(d_model, d_ff, bias=False)
self.w_down = nn.Linear(d_ff, d_model, bias=False)
self.dropout = nn.Dropout(dropout)
def forward(self, x: torch.Tensor) -> torch.Tensor:
gate = F.silu(self.w_gate(x))
up = self.w_up(x)
return self.w_down(self.dropout(gate * up))
class TopKGating(nn.Module):
"""Top-K 门控网络。
实现 Noisy Top-K Gating(Shazeer et al., 2017)。"""
def __init__(self, d_model: int, num_experts: int, top_k: int = 2):
super().__init__()
self.num_experts = num_experts
self.top_k = top_k
self.gate = nn.Linear(d_model, num_experts, bias=False)
self.noise_linear = nn.Linear(d_model, num_experts, bias=False)
def forward(self, x: torch.Tensor):
# x: (batch * seq_len, d_model)
logits = self.gate(x)
if self.training:
noise = F.softplus(self.noise_linear(x))
logits = logits + noise * torch.randn_like(logits)
probs = F.softmax(logits, dim=-1)
top_k_probs, top_k_indices = torch.topk(probs, self.top_k, dim=-1)
# 重新归一化:使被选中专家的权重之和为 1
top_k_probs = top_k_probs / top_k_probs.sum(dim=-1, keepdim=True)
return top_k_probs, top_k_indices, probs
上面代码中的 SwiGLUExpert 是使用 LLaMA、Mistral、Qwen 等最新模型所采用的 SwiGLU 激活函数的专家。经验表明,相比传统的 ReLU 或 GELU,它的训练效率更高。TopKGating 实现了 Shazeer et al.(2017)提出的 Noisy Top-K Gating,训练时会给门控 logit 添加可学习的噪声,以促进专家探索。
Dense 与 Sparse MoE 定量比较
| 项目 | Dense 70B | Sparse MoE 8x7B (Top-2) | Sparse MoE 256x3B (Top-2) |
|---|---|---|---|
| 总参数量 | 70B | 47B | 768B |
| 激活参数量 | 70B | 13B | 6B |
| FLOPs/token | 140 TFLOPs | 26 TFLOPs | 12 TFLOPs |
| GPU 显存(FP16) | 140 GB | 94 GB | 1.5 TB |
| 训练成本比例 | 1.0x | 0.35x(按 FLOPs 计) | 0.17x(按 FLOPs 计) |
| 推理速度 | 基准 | 快 2-3 倍 | 专家加载瓶颈 |
值得注意的是 MoE 模型的显存需求。虽然激活参数量少,但必须把全部参数都加载进显存,因此当专家数量非常多时,显存占用反而可能比 Dense 模型更大。这正是需要 Expert Parallelism 与 Offloading 策略的根本原因。
路由策略:Top-k、Expert Choice、Hash Routing
路由(Routing)是 MoE 的核心,也是最难的设计问题。决定激活哪些专家的策略,会极大地影响模型质量、训练稳定性和推理效率。
Top-K Routing
这是最传统的路由方式,门控网络为每个专家计算分数,并选出得分最高的 K 个。Shazeer et al.(2017)提出了 Top-2,Switch Transformer(Fedus et al., 2022)将其简化为 Top-1,把通信成本降低了一半。
Top-1 的优点:由于每个 token 只使用恰好一个专家,分布式环境下的 All-to-All 通信量被降到最小,实现也很简单。
Top-1 的缺点:由于依赖单一专家,表达能力受限,而且门控决策是离散的(discrete),训练初期专家崩溃(expert collapse)的风险较高。
Top-2 的折中:Mixtral 8x7B 及众多最新模型都采用了 Top-2。由于对两个专家的输出做加权求和,表达能力更丰富,即使其中一个专家不稳定,也能由另一个专家来补偿。
Expert Choice Routing
Zhou et al.(2022)提出的 Expert Choice 路由转换了视角:不是由 token 选择专家,而是由专家选择自己要处理的 token。每个专家选出最适合自己的 K 个 token,因此负载均衡在结构上就得到了保证。
class ExpertChoiceGating(nn.Module):
"""实现 Expert Choice Routing。
每个专家直接选择自己要处理的 token,
从结构上保证负载均衡。"""
def __init__(
self,
d_model: int,
num_experts: int,
capacity_factor: float = 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: torch.Tensor):
# x: (num_tokens, d_model)
num_tokens = x.shape[0]
expert_capacity = int(
num_tokens * self.capacity_factor / self.num_experts
)
# 门控分数:(num_tokens, num_experts)
gate_logits = self.gate(x)
# 从专家视角计算分数:(num_experts, num_tokens)
gate_scores = F.softmax(gate_logits.T, dim=-1)
# 每个专家选择排名前 capacity 的 token
top_k_scores, top_k_indices = torch.topk(
gate_scores, expert_capacity, dim=-1
) # (num_experts, capacity)
# 生成调度掩码
dispatch_mask = torch.zeros(
self.num_experts, num_tokens,
device=x.device, dtype=x.dtype,
)
dispatch_mask.scatter_(1, top_k_indices, top_k_scores)
return dispatch_mask, top_k_indices, top_k_scores
Expert Choice 的核心优点是无需辅助损失(auxiliary loss)即可实现完美的负载均衡。由于每个专家被强制处理相同数量的 token,专家崩溃问题从根本上被消除了。不过它也存在一种不对称性:一个 token 可能被多个专家同时选中,也可能不被任何专家选中。
Hash Routing
Roller et al.(2021)提出的 Hash Routing 完全去掉了可学习的门控,改用哈希函数把 token 分配给专家。由于门控网络的参数和运算都不复存在,推理时的开销被降到最小。但由于分配规则是固定的,无法反映输入的语义,实践中质量低于可学习的路由方式,因此没有成为主流。
路由策略比较
| 策略 | 负载均衡 | 表达能力 | 通信成本 | 实现复杂度 | 代表模型 |
|---|---|---|---|---|---|
| Top-1 | 需要 Aux Loss | 低 | 最小 | 低 | Switch Transformer |
| Top-2 | 需要 Aux Loss | 中 | 中 | 中 | Mixtral 8x7B |
| Top-K (K=6,8) | 需要 Aux Loss | 高 | 高 | 中 | DeepSeek-V3 (Top-8/256) |
| Expert Choice | 结构性保证 | 高 | 中 | 高 | 研究用模型 |
| Hash Routing | 完美 | 低 | 最小 | 最小 | 研究用模型 |
负载均衡与 Auxiliary Loss
MoE 训练中最严重的问题是专家崩溃(Expert Collapse):门控网络把 token 集中发送给少数几个专家,其余专家因此失去训练机会,实际上变成了死参数。为了防止这种现象,人们开发了各种负载均衡技术。
Auxiliary Loss(辅助损失)
这是 Switch Transformer 提出的标准方法:在主语言建模损失中加入一个损失项,引导各专家的 token 分配趋于均衡。
def compute_load_balancing_loss(
gate_probs: torch.Tensor,
top_k_indices: torch.Tensor,
num_experts: int,
top_k: int,
) -> torch.Tensor:
"""计算 Switch Transformer 风格的负载均衡辅助损失。
Args:
gate_probs: 门控概率 (num_tokens, num_experts)
top_k_indices: 被选中的专家索引 (num_tokens, top_k)
num_experts: 专家数量
top_k: 被选中的专家数量
Returns:
辅助损失的标量值
"""
num_tokens = gate_probs.shape[0]
# f_i:分配给专家 i 的 token 比例
expert_mask = F.one_hot(top_k_indices, num_experts).float()
# (num_tokens, top_k, num_experts) -> (num_tokens, num_experts)
expert_mask = expert_mask.sum(dim=1)
tokens_per_expert = expert_mask.sum(dim=0) # (num_experts,)
f = tokens_per_expert / (num_tokens * top_k)
# P_i:专家 i 的平均门控概率
P = gate_probs.mean(dim=0) # (num_experts,)
# 辅助损失:N * sum(f_i * P_i)
# 均匀分配时取得最小值
aux_loss = num_experts * (f * P).sum()
return aux_loss
class MoELayerWithAuxLoss(nn.Module):
"""包含辅助损失的完整 MoE 层实现。"""
def __init__(self, config: MoEConfig):
super().__init__()
self.config = config
self.experts = nn.ModuleList([
SwiGLUExpert(config.d_model, config.d_ff, config.dropout)
for _ in range(config.num_experts)
])
self.gating = TopKGating(
config.d_model, config.num_experts, config.top_k
)
self.aux_loss_weight = config.aux_loss_weight
def forward(self, x: torch.Tensor):
batch_size, seq_len, d_model = x.shape
x_flat = x.view(-1, d_model)
top_k_probs, top_k_indices, gate_probs = self.gating(x_flat)
# 计算辅助损失
aux_loss = self.aux_loss_weight * compute_load_balancing_loss(
gate_probs, top_k_indices,
self.config.num_experts, self.config.top_k,
)
# 计算专家输出
output = torch.zeros_like(x_flat)
for k in range(self.config.top_k):
expert_indices = top_k_indices[:, k] # (num_tokens,)
expert_weights = top_k_probs[:, k] # (num_tokens,)
for i in range(self.config.num_experts):
mask = (expert_indices == i)
if mask.any():
expert_input = x_flat[mask]
expert_output = self.experts[i](expert_input)
output[mask] += expert_weights[mask].unsqueeze(-1) * expert_output
output = output.view(batch_size, seq_len, d_model)
return output, aux_loss
Auxiliary Loss 的权重 alpha 是一个非常敏感的超参数。Switch Transformer 推荐使用 alpha=0.01,但需要根据模型规模和专家数量进行调整。alpha 过大会降低语言建模质量,过小则负载均衡效果微弱。ST-MoE(Zoph et al., 2022)提出增加路由器 z-loss,直接约束门控 logit 本身的大小。
DeepSeek 的 Auxiliary-Loss-Free 策略
DeepSeek-V3(2024)提出了一种无需辅助损失即可实现负载均衡的创新方法:为每个专家添加一个不可学习的偏置项,并采用动态调整机制——训练过程中,token 过度集中的专家会被降低偏置,利用不足的专家则会被提高偏置。这种方法完全消除了辅助损失干扰主训练目标函数的问题,在训练稳定性和最终模型质量上都表现出优势。
从 Switch Transformer 到 DeepSeek-V3 的演进
Switch Transformer(Fedus et al., 2022)
Switch Transformer 是把 MoE 路由简化为 Top-1 的关键论文。它把原有 Top-2 路由的通信成本降低了一半,同时通过合适的 capacity factor 和 auxiliary loss 确保了训练稳定性。该论文训练出的 1.6T 参数模型,训练速度比 T5-XXL 快 4 倍,且达到了同等质量。
核心设计决策:
- Top-1 路由:将通信成本降到最低
- Capacity Factor:动态调整专家缓冲区大小,防止 token 被丢弃
- Selective Precision:门控使用 FP32、专家运算使用 BF16 的混合精度,同时兼顾稳定性与效率
GShard(Lepikhin et al., 2021)
Google 提出的 GShard 建立了 600B 参数 MoE 模型的分布式训练流水线。它采用 Top-2 路由和 Group-level 均衡,并提出了一套基于 SPMD(Single Program Multiple Data)编程模型、可在数千个 TPU 上高效训练的框架。
Mixtral 8x7B(Jiang et al., 2024)
Mistral AI 的 Mixtral 是证明开源 MoE 模型实用性的里程碑。它采用从 8 个专家中选出 Top-2 的结构,在总共 47B 参数中使用 13B 的激活参数。它展现出与 LLaMA-2 70B 相当甚至更优的基准测试性能,而推理 FLOPs 只有 70B 模型的三分之一。
DeepSeek-V3(DeepSeek, 2024)
DeepSeek-V3 在 MoE 架构的多个设计领域都实现了创新。
- Fine-grained Expert Segmentation:设置 256 个小规模专家,选出 Top-8。增加专家数量并缩小其规模,会加深专业化(specialization)程度,从而提升模型质量。
- Shared Expert:1 个共享专家处理所有 token,负责通用知识,而被路由到的专家则负责特化知识。
- Auxiliary-Loss-Free Load Balancing:通过前面介绍的基于偏置的动态均衡,消除了辅助损失带来的副作用。
- Multi-Token Prediction(MTP):增加了一次预测多个 token 的训练目标,提升了数据效率。
- FP8 训练:在 2048 台 H800 GPU 上以 FP8 精度训练 671B 参数模型,大幅降低了成本。
Qwen3-235B-A22B(Alibaba, 2025)
Qwen3-235B-A22B 是一个总参数量 235B、只激活 22B 的 MoE 模型,从 128 个专家中选出 Top-8。它把此前 Qwen2.5 系列的 Dense 架构转换为 MoE,以约十分之一的推理成本达到了 GPT-4o 级别的性能。
MoE 模型比较
| 模型 | 总参数 | 激活参数 | 专家数 | Top-K | 共享专家 | 路由策略 |
|---|---|---|---|---|---|---|
| Switch Transformer | 1.6T | 约 100B | 128 | 1 | 无 | Learned Top-1 |
| GShard | 600B | 约 20B | 2048 | 2 | 无 | Learned Top-2 |
| Mixtral 8x7B | 47B | 13B | 8 | 2 | 无 | Learned Top-2 |
| DeepSeek-V3 | 671B | 37B | 256+1 | 8 | 1 个 | Bias-adjusted |
| Qwen3-235B-A22B | 235B | 22B | 128 | 8 | 有 | Learned Top-K |
| DBRX | 132B | 36B | 16 | 4 | 无 | Learned Top-4 |
训练稳定性与故障排查
专家崩溃(Expert Collapse)诊断
专家崩溃是 MoE 训练中最常见也最致命的问题。当门控网络把 token 集中发送给特定的少数专家时,其余专家的梯度会收敛到 0 而停止学习,而这种不均衡会形成自我强化(self-reinforcing)的循环,使问题不断恶化。
import logging
from collections import defaultdict
logger = logging.getLogger(__name__)
class ExpertUtilizationMonitor:
"""专家利用率监控与崩溃检测工具。
在训练过程中追踪每个专家的利用率,
并及早发现崩溃迹象。
"""
def __init__(
self,
num_experts: int,
collapse_threshold: float = 0.01,
window_size: int = 100,
):
self.num_experts = num_experts
self.collapse_threshold = collapse_threshold
self.window_size = window_size
self.history: list[dict[int, float]] = []
def record(self, expert_counts: dict[int, int], total_tokens: int):
"""记录每个批次的专家利用率。"""
utilization = {
i: expert_counts.get(i, 0) / max(total_tokens, 1)
for i in range(self.num_experts)
}
self.history.append(utilization)
if len(self.history) > self.window_size:
self.history = self.history[-self.window_size:]
def detect_collapse(self) -> list[int]:
"""检测专家崩溃。返回利用率低于阈值(threshold)的专家。"""
if len(self.history) < self.window_size // 2:
return []
collapsed = []
for expert_id in range(self.num_experts):
recent_util = [
h[expert_id] for h in self.history[-self.window_size:]
]
avg_util = sum(recent_util) / len(recent_util)
if avg_util < self.collapse_threshold:
collapsed.append(expert_id)
if collapsed:
logger.warning(
f"Expert collapse detected! "
f"Experts {collapsed} have utilization below "
f"{self.collapse_threshold:.2%}. "
f"Consider increasing aux_loss_weight or "
f"reinitializing collapsed experts."
)
return collapsed
def get_load_imbalance_ratio(self) -> float:
"""计算负载不均衡比率。
1.0 表示完美均衡,数值越大越不均衡。"""
if not self.history:
return 0.0
latest = self.history[-1]
utils = list(latest.values())
max_util = max(utils) if utils else 0
min_util = min(utils) if utils else 0
avg_util = sum(utils) / len(utils) if utils else 0
if avg_util == 0:
return float("inf")
return max_util / avg_util
训练不稳定的原因与应对
| 症状 | 原因 | 应对方法 |
|---|---|---|
| Loss spike(损失骤增) | 门控 logit 爆炸 | 添加 Router z-loss,门控保持 FP32 |
| 专家崩溃 | Aux loss 不足、学习率过大 | 提高 Aux loss 权重,延长 LR warm-up |
| 专家间重复 | 初始化相似 | 专家正交初始化,多样性正则化 |
| token 丢弃 | Capacity factor 不足 | 将 Capacity factor 提高到 1.25-1.5 |
| 门控震荡 | 学习率过大 | 将门控 LR 单独设为主 LR 的 0.1 倍 |
稳定训练的超参数指南
保证训练稳定性的核心原则如下。第一,门控网络的运算必须以 FP32 执行,在 BF16 或 FP16 下,softmax 的数值不稳定会导致路由震荡。第二,学习率 warm-up 的时长要比 Dense 模型长 2-3 倍,如果在门控稳定之前就应用高学习率,会引发专家崩溃。第三,尽可能把批大小设得更大;在小批量下,门控的 token 分配对噪声很敏感,容易不稳定。
推理优化:Expert Parallelism、Offloading
MoE 模型的推理带来了与 Dense 模型根本不同的挑战。激活参数虽少,但必须让全部参数保持可访问状态,因此显存管理与专家部署策略是关键。
Expert Parallelism
Expert Parallelism(EP)是把专家分散部署到多个 GPU 上的策略。把 N 个专家分配到 P 个 GPU 上后,每个 GPU 只存储 N/P 个专家。当 token 被路由到特定专家时,通过 All-to-All 通信把 token 发送到对应的 GPU,运算结果再返回原来的 GPU。
import torch
import torch.distributed as dist
from typing import Optional
class ExpertParallelRouter:
"""实现 Expert Parallelism 所需的 token 分发/收集。
每个 GPU 负责一部分专家,
通过 All-to-All 通信路由 token。
"""
def __init__(
self,
num_experts: int,
ep_group: Optional[dist.ProcessGroup] = None,
):
self.num_experts = num_experts
self.ep_group = ep_group
self.ep_size = dist.get_world_size(ep_group) if ep_group else 1
self.ep_rank = dist.get_rank(ep_group) if ep_group else 0
self.experts_per_rank = num_experts // self.ep_size
def dispatch(
self,
tokens: torch.Tensor,
expert_indices: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
"""把 token 分发到负责的 GPU。
Args:
tokens: (num_tokens, d_model) 输入 token
expert_indices: (num_tokens,) 每个 token 的目标专家索引
Returns:
dispatched_tokens: 本 GPU 需要处理的 token
recv_counts: 从每个 GPU 收到的 token 数
"""
# 计算发往每个 GPU 的 token 数
send_counts = torch.zeros(
self.ep_size, dtype=torch.long, device=tokens.device
)
for rank in range(self.ep_size):
start_expert = rank * self.experts_per_rank
end_expert = start_expert + self.experts_per_rank
mask = (expert_indices >= start_expert) & (
expert_indices < end_expert
)
send_counts[rank] = mask.sum()
# 通过 All-to-All 交换接收计数
recv_counts = torch.zeros_like(send_counts)
dist.all_to_all_single(
recv_counts, send_counts, group=self.ep_group
)
# token 排序及 All-to-All 发送
sorted_indices = torch.argsort(expert_indices)
sorted_tokens = tokens[sorted_indices]
send_splits = send_counts.tolist()
recv_splits = recv_counts.tolist()
dispatched_tokens = torch.zeros(
int(recv_counts.sum()), tokens.shape[1],
dtype=tokens.dtype, device=tokens.device,
)
dist.all_to_all_single(
dispatched_tokens, sorted_tokens,
output_split_sizes=recv_splits,
input_split_sizes=send_splits,
group=self.ep_group,
)
return dispatched_tokens, recv_counts
Expert Offloading
当 GPU 显存不足时,这是一种把未激活的专家存放在 CPU 内存或 NVMe SSD 中、仅在需要时才加载到 GPU 的策略,在 DeepSpeed-MoE 和 Mixtral 的推理优化中被核心地采用。
Offloading 的关键在于预取(prefetching)。在当前层的专家运算进行的同时,异步地把下一层将被激活的专家加载到 GPU,就能隐藏专家切换的延迟。以 PCIe 4.0 x16 为准,可提供约 32 GB/s 的带宽,因此一个专家(数百 MB)可以在数毫秒内传输完成。
推理优化策略比较
| 策略 | GPU 显存 | 推理延迟 | 吞吐量 | 适用场景 |
|---|---|---|---|---|
| Full Model on GPU | 最大 | 最小 | 最大 | 高配置多 GPU 服务器 |
| Expert Parallelism | 分散 | 通信开销 | 高 | 多 GPU 集群 |
| CPU Offloading | 最小 | 加载延迟 | 中 | 受限 GPU 环境 |
| NVMe Offloading | 最小 | 较高的加载延迟 | 低 | 单 GPU 环境 |
| Speculative Expert Prefetch | 中 | 中 | 高 | 批量推理服务器 |
运维检查清单
整理了把 MoE 模型部署到生产环境时必须确认的检查项。
训练阶段
- 确认门控精度:验证门控网络的 forward/backward 是否以 FP32 执行。BF16 门控在训练初期看起来正常,但数万步之后可能引发不稳定。
- 搭建负载均衡指标看板:实时监控各专家的 token 分配量、最大/最小利用率,以及辅助损失的数值。
- 检查点策略:在专家并行环境下,检查点可能按 GPU 分开存储。要预先准备好将完整模型合并(consolidate)为一份的脚本。
- 调优 Capacity Factor:如果 token 丢弃率超过 1%,就提高 capacity factor。被丢弃的 token 只能通过 residual connection 传递,因此会降低质量。
- 设置专家崩溃告警:当某个专家的利用率跌至平均值的 10% 以下时触发告警,并在必要时对该专家重新初始化。
推理/部署阶段
- 显存分析:确认全部参数能否加载进 GPU 显存,如果不能,则选择 EP 或 Offloading 策略。
- 优化批大小:在 MoE 推理中,批大小会直接影响专家利用效率。小批量下只有部分专家被激活,导致 GPU 利用率下降。
- 管理 KV Cache:MoE 模型的 Attention 层与 Dense 模型相同,因此同样需要 KV Cache 管理,结合 PagedAttention(vLLM)会更高效。
- 路由一致性测试:确认对同一输入是否会选中相同的专家。尤其是在混合使用 Tensor Parallelism 和 Expert Parallelism 时,数值误差可能导致路由结果不同。
- Fallback 策略:实现当特定专家加载失败时,用次优先专家替代的 fallback 逻辑。
- A/B 测试流水线:在服务环境中验证 MoE 模型相对于 Dense 模型的质量等同性。
故障案例与恢复
案例 1:专家崩溃导致的质量下降
症状:训练 3 万步之后,基准测试分数突然下降。Loss 本身仍在正常下降,但生成质量变差。
原因分析:监控结果显示,8 个专家中有 2 个处理了全部 token 的 60% 以上,另有 3 个专家的利用率不到 2%。辅助损失权重(alpha=0.001)过低,导致均衡效果不足。
恢复流程:
- 回滚到专家崩溃发生前的检查点(2 万步)
- 把辅助损失权重从 0.001 提高 10 倍到 0.01
- 把崩溃专家的参数重新初始化为活跃专家的参数
- 把门控网络的学习率单独设为主学习率的 0.1 倍
- 重新训练后持续监控,直至专家利用率趋于均衡(以平均 12.5% 为基准,落在 8-17% 区间内)
案例 2:All-to-All 通信瓶颈
症状:用 Expert Parallelism 在 64 个 GPU 上训练时,GPU 利用率骤降到 40%。性能分析器显示,All-to-All 通信占据了全部训练时间的 45%。
原因分析:网络拓扑分析显示,专家部署没有考虑网络结构,导致节点间通信过度产生。同一节点内 GPU 间通信(NVLink,900 GB/s)与节点间通信(InfiniBand,400 Gb/s)的带宽差距超过 20 倍。
恢复流程:
- 切换为 Hierarchical All-to-All:把节点内通信与节点间通信拆分为两个阶段
- 把专家部署改为拓扑感知(topology-aware):将经常一起被激活的专家部署在同一节点
- 通信-运算重叠(overlap):把专家运算与下一批次的 token 分发以流水线方式重叠执行
案例 3:推理时专家加载延迟
症状:用 CPU Offloading 在单块 GPU(24GB)上为 Mixtral 8x7B 提供服务时,首 token 延迟(TTFT)超过 5 秒。
原因分析:每一层从 CPU 向 GPU 加载 2 个专家都需要 100-200ms,而 32 层是依次处理的,因此累计延迟达到 3.2-6.4 秒。
恢复流程:
- 实现专家预取:在处理当前层的同时预先计算下一层的门控分数,异步加载所需专家
- 热门专家缓存:把激活频率最高的前 2-3 个专家常驻在 GPU 上
- 专家权重量化:用 INT4 量化把专家体积缩小 75%,从而缩短传输时间
- 若 PCIe 带宽成为瓶颈,使用 pinned memory 优化 CPU-GPU 传输
案例 4:训练中出现 Loss Spike
症状:训练大规模 MoE 模型(100B 以上)时,每隔数千步就会反复出现 loss 骤增。虽然每次 spike 之后都会恢复,但会浪费训练时间。
原因分析:门控网络输入 softmax 的 logit 偶尔会出现非常大的数值,引发数值不稳定。尤其是在 BF16 训练时,门控 logit 的可表示范围比 FP32 更窄,更容易发生 overflow。
恢复流程:
- 添加 Router z-loss,直接约束门控 logit 的大小。
def router_z_loss(gate_logits: torch.Tensor) -> torch.Tensor:
"""ST-MoE 风格的 Router z-loss。
通过约束门控 logit 的大小来提高数值稳定性。
Args:
gate_logits: (num_tokens, num_experts) 门控 logit
Returns:
z_loss 标量
"""
log_z = torch.logsumexp(gate_logits, dim=-1) # (num_tokens,)
z_loss = (log_z ** 2).mean()
return z_loss
- 强制门控运算使用 FP32,以确保数值稳定性。
- 单独对门控网络应用梯度裁剪(max_norm=1.0)。
- 把学习率 warm-up 的时长扩展到整个训练过程的 5-10%。
参考资料
-
Fedus, W., Zoph, B., & Shazeer, N. (2022). Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity. JMLR, 23(120), 1-39. https://arxiv.org/abs/2101.03961
-
DeepSeek-AI. (2024). DeepSeek-V3 Technical Report. https://arxiv.org/abs/2401.06066
-
Cai, W. et al. (2024). A Survey on Mixture of Experts. https://arxiv.org/abs/2407.10671
-
FriendliAI. (2024). MoE Models Comparison: Architectures and Performance. https://friendli.ai/blog/moe-models-comparison
-
Zilliz. (2024). What is Mixture of Experts? A Complete Guide. https://zilliz.com/learn/what-is-mixture-of-experts
-
Wikipedia. Mixture of Experts. https://en.wikipedia.org/wiki/Mixture_of_experts
-
Shazeer, N. et al. (2017). Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer. ICLR 2017. https://arxiv.org/abs/1701.06538
-
Jiang, A. Q. et al. (2024). Mixtral of Experts. https://arxiv.org/abs/2401.04088
현재 단락 (1/384)
大语言模型(LLM)的参数量正在呈指数级增长,但每个 token 都要激活全部参数的 Dense 模型已经撞上了运算成本的墙。要训练 GPT-4 级别的 Dense 模型,需要数万块 GPU 运行数月...