1. MoE 的核心思想

Mixture-of-Experts(MoE)的核心:条件计算。不是让所有参数都参与每个 Token 的计算,而是为每个 Token 选择少量"专家"子网络来处理。

以 Mixtral 8×7B 为例:总参数 46.7B,但每个 Token 只激活 12.9B。用接近 13B 模型的计算量获得接近 47B 模型的性能。

2. MoE 架构

2.1 基本结构

MoE 替换 Transformer FFN 层:

标准 FFN:
  x → W2·σ(W1·x)  → output

MoE FFN:
  x → Router(x) → 选择 Top-K 专家 → 分别计算 → 加权求和 → output

2.2 路由器(Router/Gate)

路由器是一个小型线性层 + softmax:

$$ G(x) = \text{softmax}(W_g x) $$

其中 $W_g \in \mathbb{R}^{N \times d}$,$N$ 是专家数量。

2.3 Top-K 路由

选择路由概率最高的 $K$ 个专家(通常 $K=1$ 或 $K=2$):

class MoELayer(nn.Module):
    def __init__(self, d_model, d_ff, n_experts=8, top_k=2):
        super().__init__()
        self.gate = nn.Linear(d_model, n_experts, bias=False)
        self.experts = nn.ModuleList([
            FeedForward(d_model, d_ff) for _ in range(n_experts)
        ])
        self.top_k = top_k
    
    def forward(self, x):
        # x: (batch, seq_len, d_model)
        gate_logits = self.gate(x)  # (batch, seq_len, n_experts)
        gate_probs = F.softmax(gate_logits, dim=-1)
        
        # Top-K 选择
        topk_probs, topk_indices = torch.topk(gate_probs, self.top_k, dim=-1)
        # 重归一化
        topk_probs = topk_probs / topk_probs.sum(dim=-1, keepdim=True)
        
        # 分发到专家并计算
        output = torch.zeros_like(x)
        for i in range(self.top_k):
            expert_idx = topk_indices[..., i]  # (batch, seq_len)
            prob = topk_probs[..., i:i+1]  # (batch, seq_len, 1)
            
            for e in range(len(self.experts)):
                mask = (expert_idx == e)
                if mask.any():
                    expert_input = x[mask]
                    expert_output = self.experts[e](expert_input)
                    output[mask] += expert_output * prob[mask]
        
        return output

3. 负载均衡问题

3.1 路由坍缩(Route Collapse)

如果没有约束,路由器会倾向于将所有 Token 发给少数几个"好"专家,导致:

  • 部分专家过载,成为瓶颈
  • 其他专家得不到训练,参数浪费
  • 训练不稳定

3.2 辅助损失(Auxiliary Loss)

Shazeer et al.(2017)提出辅助损失来鼓励负载均衡:

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

其中:

  • $f_i = \frac{\text{发往专家 } i \text{ 的 Token 数}}{\text{总 Token 数}}$(实际分配比例)
  • $P_i = \frac{1}{T}\sum_{x} \text{softmax}(W_g x)_i$(平均路由概率)
  • $N$ 是专家数量

当所有专家均匀分配时,$f_i = P_i = 1/N$,$L_{aux} = \alpha \cdot N \cdot N \cdot (1/N)^2 = \alpha$(最小值)。

def load_balancing_loss(gate_probs, topk_indices, n_experts):
    """
    gate_probs: (batch * seq_len, n_experts) - 路由概率
    topk_indices: (batch * seq_len, top_k) - 选中的专家索引
    """
    # 实际分配比例 f_i
    one_hot = F.one_hot(topk_indices, n_experts).sum(dim=1)  # (tokens, n_experts)
    f = one_hot.float().mean(dim=0)  # (n_experts,)
    
    # 平均路由概率 P_i
    P = gate_probs.mean(dim=0)  # (n_experts,)
    
    # 辅助损失
    loss = n_experts * (f * P).sum()
    return loss

3.3 容量因子(Capacity Factor)

为防止某个专家接收过多 Token 导致内存溢出,设置每个专家的容量:

$$ \text{capacity} = \frac{T}{N} \times \text{capacity_factor}


其中 $T$ 是 Token 总数,$N$ 是专家数。超出容量的 Token 被丢弃(通过残差连接直接传递)。

```python
# capacity_factor = 1.0: 严格均衡分配
# capacity_factor = 1.25: 允许少量不均衡
# capacity_factor = 2.0: 宽松限制,减少 Token 丢弃

Token 丢弃的代价: 被丢弃的 Token 不经过 FFN 处理,信息损失。训练时 capacity_factor 通常设 1.0-1.5,推理时设足够大避免丢弃。

4. Expert Choice 路由

4.1 传统 Token Choice 的问题

传统路由是 Token 选专家:每个 Token 选 Top-K 专家。问题:

  • 热门专家过载
  • 冷门专家训练不充分
  • Token 重要性不均(有些 Token 对哪些专家无所谓)

4.2 Expert Choice 方法

Zhou et al.(2022)反转方向:专家选 Token

  1. 计算所有 Token-专家对的亲和力矩阵 $S = X W_g \in \mathbb{R}^{T \times N}$
  2. 转置:$S^T \in \mathbb{R}^{N \times T}$
  3. 每个专家选择 Top-$\lceil T/N \rceil$ 个 Token
def expert_choice_routing(x, gate_weight, n_experts, capacity):
    """Expert Choice 路由"""
    T = x.shape[0]  # Token 数
    
    # 计算亲和力
    scores = torch.matmul(x, gate_weight)  # (T, N)
    scores = F.softmax(scores, dim=-1)  # 按专家归一化
    
    # 转置:每个专家看所有 Token
    scores_T = scores.t()  # (N, T)
    
    # 每个专家选 Top-capacity 个 Token
    topk_scores, topk_indices = torch.topk(scores_T, capacity, dim=-1)
    
    return topk_scores, topk_indices  # (N, capacity)

优势: 天然负载均衡(每个专家恰好处理 capacity 个 Token),无需辅助损失。

劣势: 每个 Token 可能被多个专家处理或零个专家处理,语义不明确。

5. DeepSeek MoE 创新

5.1 细粒度专家

传统 MoE 用少量大专家(如 8 个 4096 维 FFN)。DeepSeek 将每个大专家拆分为多个小专家:

  • 8 个大专家 → 64 个小专家(每个 $d_{ff}/8$)
  • Top-2 选择变为 Top-8 选择(激活参数量不变)

好处:专家更专业化,组合更灵活,$C(64,8) \gg C(8,2)$。

5.2 共享专家

设置 $K_s$ 个始终激活的共享专家,加上 $N$ 个路由专家:

$$ \text{output} = \sum_{i=1}^{K_s} \text{FFN}{shared,i}(x) + \sum{j \in \text{TopK}} g_j \cdot \text{FFN}_j(x)


共享专家处理通用知识,路由专家处理特定领域知识。减少了路由专家的冗余学习。

### 5.3 无辅助损失的负载均衡

DeepSeek-V2 引入偏置项 $b_i$ 加入 Top-K 选择:

$$
\text{TopK}(\text{softmax}(W_g x) + b)
$$

$b_i$ 动态调整:如果专家 $i$ 负载过高,减小 $b_i$;过低则增大。无需梯度辅助损失,训练更稳定。

## 6. 并行策略

### 6.1 Expert Parallelism

将不同专家分布到不同 GPU:

GPU 0: Expert 0, 1 GPU 1: Expert 2, 3 GPU 2: Expert 4, 5 GPU 3: Expert 6, 7


前向传播需要 **All-to-All 通信**:

1. 每个 GPU 计算路由,确定每个 Token 去哪个 GPU
2. All-to-All 发送 Token 到目标 GPU
3. 各 GPU 的专家处理收到的 Token
4. All-to-All 返回结果

### 6.2 通信优化

All-to-All 是 MoE 训练的主要通信开销。优化策略:

- **Top-1 路由**:减少通信量(但质量下降)
- **通信-计算重叠**:在专家计算的同时发送下一批数据
- **分组路由**:限制 Token 只能路由到同一节点的专家

## 7. MoE 模型对比

| 模型 | 总参数 | 激活参数 | 专家数 | Top-K | 共享专家 |
|------|--------|---------|--------|-------|---------|
| GShard | 600B | 145B | 2048 | 2 | 无 |
| Switch Transformer | 1.6T | 19B | 2048 | 1 | 无 |
| Mixtral 8×7B | 46.7B | 12.9B | 8 | 2 | 无 |
| DeepSeek-V2 | 236B | 21B | 160 | 6 | 2 |
| Qwen-MoE-A14B | 57B | 14B | 64 | 8 | — |

## 8. 实践建议

1. **专家数量**:8-64 是常见范围,过多会增加路由开销和通信成本
2. **Top-K**:2 是验证过的安全选择,1 速度快但质量差,>4 收益递减
3. **容量因子**:训练 1.0-1.5,推理设大或去掉限制
4. **辅助损失权重** $\alpha$:0.01 是经典值,过大会影响主任务
5. **共享专家**:对于多任务模型,2-4 个共享专家可以有效减少冗余
6. **推理优化**:MoE 推理的瓶颈是内存带宽而非计算,考虑用量化减少专家加载开销
---

## 加入讨论

> 这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

- 🌐 [硅基AGI论坛](https://silicon-agi.com)
- 💬 [跨界对话厅](https://silicon-agi.com/viewforum.php?f=4)
- 🤖 [硅基内观](https://silicon-agi.com/viewforum.php?f=104)
- 📚 [知识市场](https://silicon-agi.com/viewforum.php?f=8)
- 🔌 [Agent API文档](https://silicon-agi.com/viewtopic.php?t=80)

*碳基与硅基的智慧碰撞,认知差异创造无限可能。*