系列文章:AI大模型知识体系 | 第五篇


一、引言:47B参数,却只花13B的钱?

2023年底,Mistral AI发布了 Mixtral 8x7B,号称拥有47B(470亿)参数量,但推理时的计算量仅相当于一个13B模型。

这听起来很不可思议——难道不应该参数越多、计算越慢吗?怎么可能"参数多但跑得快"?

答案就藏在一种叫做 MoE(Mixture of Experts,混合专家) 的架构里。不只是Mixtral,后来大火的 DeepSeek-V2/V3Qwen1.5-MoE 等模型也都采用了这种架构。可以说,MoE已经成为大模型领域的"显学"。

今天我们就来彻底搞懂:MoE到底是怎么做到的?


二、Dense模型的问题:每次都"全员出动"

要理解MoE,先要理解它要解决的问题。

目前主流的Transformer模型(比如LLaMA、GPT系列)都是 Dense(稠密)模型。什么叫稠密?就是每一个输入token,都要经过模型的全部参数

打个比方:你开了一家公司,有100个员工。不管来的是什么任务——写文案、做设计、搞财务——你都会让全部100个人一起上。写文案的时候,设计师和财务也在旁边"帮忙"(实际上是帮倒忙,白白消耗资源)。

这显然很浪费。一个token可能只是一句简单的"你好",它真的需要经过那几十亿参数吗?

MoE的核心思路就是:不是所有参数都要被激活,只让"对口"的专家来处理当前任务。


三、MoE的核心思想:公司部门分工

想象一下,你把公司重新组织了一下:

  • 研发部:负责技术问题

  • 市场部:负责营销推广

  • 财务部:负责资金核算

  • 法务部:负责合规审查

当一个"技术需求"来的时候,你只会把它分配给研发部,而不会让市场部和财务部也参与进来。

这就是MoE的核心思想——条件计算(Conditional Computation)

对于每个输入,只激活模型中一部分参数(专家),而不是全部。

模型总参数量很大(因为有很多专家),但每次推理时只用其中一小部分,所以计算量远小于同等参数量的Dense模型

用数字说话:Mixtral 8x7B有8个专家,每个token只激活其中2个。所以虽然总参数是47B,但每个token实际经过的参数大约只有 47B × 2/8 ≈ 13B。


四、门控网络(Router/Gate):谁来决定分给谁?

既然不是所有专家都参与,那总得有人来决定"这个任务该交给谁"吧?

这个角色叫做 门控网络(Router / Gate),通常就是一个简单的线性层。

你可以把它想象成公司的前台接待员:来了一个任务,前台快速判断一下"这个应该找谁",然后把任务分派给对应的专家。

Top-K路由:选最强的K个专家

最常见的路由策略是 Top-K

  1. 门控网络对每个专家打一个分(表示"匹配程度")

  2. 取分数最高的 K 个专家

  3. 只让这K个专家处理当前输入

  4. 最后把这K个专家的输出按权重加在一起

Mixtral 8x7B用的就是 Top-2 策略——每次选2个专家。

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

class MoERouter(nn.Module):
    """门控网络:决定每个token该去找哪些专家"""
    def __init__(self, hidden_dim, num_experts, top_k=2):
        super().__init__()
        self.top_k = top_k
        # 一个简单的线性层,输出维度 = 专家数量
        self.gate = nn.Linear(hidden_dim, num_experts, bias=False)

    def forward(self, x):
        # x shape: (batch_size, seq_len, hidden_dim)
        # 计算每个专家得分,然后softmax归一化
        gate_logits = self.gate(x)                      # (batch, seq, num_experts)
        gate_probs = F.softmax(gate_logits, dim=-1)     # 每个专家的"匹配概率"

        # 选出得分最高的 top_k 个专家
        topk_weights, topk_indices = torch.topk(
            gate_probs, self.top_k, dim=-1
        )
        # topk_weights: 被选中专家的权重
        # topk_indices: 被选中专家的编号

        # 对权重做归一化,保证加起来等于1
        topk_weights = topk_weights / topk_weights.sum(dim=-1, keepdim=True)

        return topk_weights, topk_indices

就这么简单!一个线性层 + Softmax + Top-K选择,就完成了路由。


五、专家网络(Experts):其实就是多个FFN

看到这里你可能会问:这些"专家"到底是什么?

答案出乎意料地简单——在Transformer-based MoE中,每个专家就是一个标准的FFN(前馈神经网络)

回忆一下Transformer的结构:每一层由 Attention + FFN 组成。MoE的做法是把一个FFN替换成多个并行的FFN(即多个专家),再加上一个Router。

标准Transformer层:
  Input → Attention → FFN → Output

MoE Transformer层:
  Input → Attention → Router → 选出Top-K个Expert(FFN) → 加权求和 → Output

每个Expert的结构完全一样,只是参数不同(训练过程中各自学到了不同的"专长")。

class MoELayer(nn.Module):
    """一个完整的MoE层"""
    def __init__(self, hidden_dim, ffn_dim, num_experts, top_k=2):
        super().__init__()
        self.router = MoERouter(hidden_dim, num_experts, top_k)
        self.top_k = top_k
        # 创建多个专家,每个都是独立的FFN
        self.experts = nn.ModuleList([
            nn.Sequential(
                nn.Linear(hidden_dim, ffn_dim),
                nn.SiLU(),  # 激活函数
                nn.Linear(ffn_dim, hidden_dim),
            )
            for _ in range(num_experts)
        ])

    def forward(self, x):
        # x shape: (batch, seq, hidden_dim)
        batch, seq, dim = x.shape
        x_flat = x.view(-1, dim)  # 展平成 (batch*seq, dim)

        # 1. 路由:决定每个token找哪些专家
        weights, indices = self.router(x_flat)  # (batch*seq, top_k)

        # 2. 让被选中的专家处理对应的token,然后加权求和
        output = torch.zeros_like(x_flat)
        for i in range(self.top_k):
            expert_idx = indices[:, i]           # 第i个被选中的专家编号
            expert_weight = weights[:, i]        # 对应的权重
            # 简化写法:假设所有token都路由到同一批专家(实际实现会更复杂)
            for e_id in range(len(self.experts)):
                mask = (expert_idx == e_id)      # 找出选了该专家的token
                if mask.any():
                    expert_input = x_flat[mask]
                    expert_output = self.experts[e_id](expert_input)
                    output[mask] += expert_weight[mask].unsqueeze(-1) * expert_output

        return output.view(batch, seq, dim)

注意:上面的代码为了可读性做了简化,实际工程中会用更高效的方式(比如 torch.scattergrouped GEMM)来实现批量路由和计算。


六、负载均衡:所有任务都去找同一个专家怎么办?

MoE有一个经典问题:路由坍塌(Routing Collapse)

什么叫坍塌?就是门控网络发现某个专家"特别好使",于是把所有任务都丢给它。结果就是:

  • 这个专家累死(计算瓶颈)

  • 其他专家闲死(参数浪费)

  • 模型退化成Dense模型(甚至比Dense更差)

这就好比一家餐厅,所有人都排队找1号厨师做菜,2号到8号厨师全在旁边刷手机。

解决方案:辅助损失函数(Auxiliary Loss)

最常用的办法是加一个负载均衡损失(Load Balancing Loss),在训练时"惩罚"分配不均的路由行为。

你可以把它想象成餐厅的排号系统

  • 如果1号厨师面前的排队人数远超平均值,系统就会"扣分"

  • 为了减少扣分,前台会自动引导顾客去排队少的厨师那里

  • 最终达到每个厨师的接待量大致均衡

在数学上,辅助损失通常是:

L_aux = num_experts * sum(f_i * P_i)

其中 f_i 是分配给专家 i 的token比例,P_i 是路由给专家 i 的平均概率。当分配完全均匀时,这个损失最小。

DeepSeek-V2还引入了更精细的策略——互补平衡(Complementary Balance)序列级辅助损失(Sequence-level Auxiliary Loss),进一步缓解了这个问题。


七、主流MoE模型盘点

模型

发布时间

总参数量

激活参数量

专家数

Top-K

亮点

Switch Transformer

2022 (Google)

最大1.6T

取决于专家数

可变

Top-1

最早将MoE大规模应用于LLM

Mixtral 8x7B

2023 (Mistral)

47B

~13B

8

Top-2

开源MoE的里程碑,性价比极高

DeepSeek-V2

2024 (DeepSeek)

236B

~21B

160+共享

Top-8

MLA注意力 + MoE,推理极快

DeepSeek-V3

2024 (DeepSeek)

671B

~37B

256+共享

Top-8

无辅助损失负载均衡,训练更高效

Qwen1.5-MoE

2024 (阿里)

~28B

~7B

60+共享

Top-4

小模型也能用MoE,移动端友好

趋势观察:从Switch Transformer到DeepSeek-V3,专家数量越来越多(从几个到几百个),但每次激活的专家比例越来越小。这说明MoE的发展方向是"更多的专家,更精准的分工"。


八、MoE vs Dense:到底谁更强?

维度

Dense模型

MoE模型

总参数量

等于激活参数量

远大于激活参数量

推理计算量

与参数量成正比

只与激活参数量成正比,更快

训练效率

所有参数都参与更新

只有被路由到的专家被更新

模型容量

受限于参数量

更大的"知识库"(专家各有所长)

同等算力下的效果

基线

通常更好(用更少的计算达到同等甚至更好的效果)

一句话总结:MoE用"参数多但计算少"的方式,实现了比Dense更高的性价比。你可以理解为"花13B的钱,办了47B的事"。


九、MoE的缺点:免费的午餐不存在

MoE虽然看起来很美好,但也有明显的短板:

1. 显存占用大

虽然推理时只激活一部分专家,但所有专家的参数都必须加载到显存里。Mixtral 8x7B的47B参数一个都不能少,FP16下大约需要94GB显存。计算量省了,但显存一点没省。

2. 通信开销

在分布式训练中,不同专家可能分布在不同GPU上。token在不同GPU之间路由和传输,会带来额外的通信开销。这也是为什么MoE模型的训练和部署对基础设施要求更高。

3. 训练不稳定

路由机制引入了离散选择(选哪个专家),这可能导致训练不稳定、梯度方差大。负载均衡问题如果没有处理好,模型效果会大打折扣。

4. 微调困难

对MoE模型做LoRA等参数高效微调时,由于每个token只激活部分专家,未被激活的专家不会被更新,可能导致微调效果不如Dense模型。


十、总结

让我们用一张图回顾MoE的核心思想:

          ┌─────────┐
 Input ──→│  Router  │──→ 选出Top-K个Expert
          └─────────┘
              │
     ┌────────┼────────┐
     ↓        ↓        ↓
 Expert 1  Expert 2  Expert 3  ...  Expert N
 (FFN)     (FFN)     (FFN)          (FFN)
     │        │
     └────┬───┘
          ↓
    加权求和 → Output

三个关键点

  1. 条件计算:每个token只经过一小部分参数,所以推理快

  2. 门控路由:一个轻量级网络负责"派单",决定哪些专家上场

  3. 负载均衡:必须保证专家们的工作量大致均匀,否则模型退化为Dense

MoE并不是要取代Dense,而是在"大模型需要更多参数"和"推理不能太慢"之间,找到了一个优雅的平衡点。随着DeepSeek-V3、Qwen2-MoE等新模型的涌现,MoE很可能成为未来大模型的标配架构。


参考与延伸阅读

如果你觉得这篇文章有帮助,欢迎点赞、收藏、关注三连!下一篇我们聊聊 KV Cache 与 Flash Attention——大模型推理加速的关键技术。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐