混合专家 MOE 是什么

混合专家 MOE封面

你有没有想过:为什么一家大医院要分那么多科室?如果让一个「全能医生」从头到脚、内内外外全包,他要么累垮,要么哪科都不精。医院的做法是分而治之——按病症分诊到不同科室,每科医生在自己领域深耕,最后综合各科意见给出诊断。

混合专家模型(Mixture of Experts,简称 MoE)就是把这个「术业有专攻」的思路搬进神经网络:不再用一个又大又全的网络处理所有输入,而是设置多个小网络(专家),再让一个「门控 router」决定当前输入该交给哪些专家、各占多少比重,最后把各专家的输出加权融合。

这是当下大模型(如 Mixtral 8x7B、DeepSeek-V3;业界普遍推测 GPT-4 也采用了类似架构)背后最核心的架构创新之一,属于深度学习里「让模型变大、但不让计算成本跟着爆炸」的关键思路。

三个核心要素

MoE 的全部秘密就藏在这三件套里:

  1. 专家(Expert):每个专家是一个独立的小网络(最简单可以就是一个线性层 Linear)。它并不是真的懂某个领域,而是在训练过程中逐渐「擅长」某一类输入,自然形成分工。
  2. 门控 Router(Gate):一个小网络,输入和专家们看到的是同一个 xx,输出是「该给每个专家多少权重」。最常见的实现就是一个 Linear 加 softmax。
  3. 加权融合:让每个专家都跑一遍自己的小网络,得到各自的输出,再用 router 给的权重做加权求和,得到最终结果。

本文讲的是基础版,叫稠密 MoE:所有专家都会被激活(即参与计算)、都参与加权融合。后面会提一句工程上的升级版——稀疏 MoE(只挑 top-k 个专家),但思想完全一致。

公式:三步走

把上面的流程写成数学,就两条核心公式。

第一步,router 算权重:

g=softmax(Wgx)g = \mathrm{softmax}(W_g \, x)

逐项解读:

  • xx:当前输入向量(比如一个词的特征表示)。
  • WgW_g:router 的权重矩阵,形状是「专家数 × 输入维度」。
  • WgxW_g \, x:得到每个专家的一个原始打分(logit,就是 softmax 之前的那层分数,有正有负,本身没有概率含义)。
  • softmax\mathrm{softmax}:把原始打分转成「概率」——非负、所有专家权重加起来等于 1。
  • g=(g1,g2,,gn)g = (g_1, g_2, \dots, g_n):第 ii 个分量 gig_i 就是「第 ii 个专家该占的比重」。

第二步,每个专家各自计算:

ei(x)=第 i 个专家对 x 的输出e_i(x) = \text{第 } i \text{ 个专家对 } x \text{ 的输出}

最简单时,ei(x)=Aix+bie_i(x) = A_i x + b_i,也就是一个线性层。

  • AiA_i:第 ii 个专家这个线性层的权重矩阵,决定输入各维怎么组合成输出。
  • bib_i:第 ii 个专家这个线性层的偏置向量,相当于该专家自带的「默认值」,让输出能整体平移。

第三步,加权融合:

y=i=1ngiei(x)y = \sum_{i=1}^{n} g_i \cdot e_i(x)

把每个专家的输出乘以 router 给的权重,全部加起来,就是最终输出 yy。直觉上:权重大的专家「话语权」大,对结果影响大;权重小的几乎被忽略。

  • nn:专家总数,也是求和上限——公式把全部 nn 个专家的加权输出累加起来。

一个小算例

假设有 3 个专家,router 对某次输入算出的原始打分是 [1,2,3][1,\, 2,\, 3]

先过 softmax(公式 softmax(z)i=ezi/jezj\mathrm{softmax}(z)_i = e^{z_i} / \sum_j e^{z_j},其中 e2.718e \approx 2.718 是自然对数底,jj 是遍历所有专家打分的求和指标,ziz_i 是第 ii 个专家的原始打分/logit):

  • 分母:e1+e2+e32.72+7.39+20.09=30.20e^1 + e^2 + e^3 \approx 2.72 + 7.39 + 20.09 = 30.20
  • 权重:g10.09,  g20.24,  g30.67g_1 \approx 0.09,\; g_2 \approx 0.24,\; g_3 \approx 0.67

自检:0.09+0.24+0.67=1.000.09 + 0.24 + 0.67 = 1.00,非负且和为 1,符合概率含义。

假设三个专家对这条输入的输出分别是 e1=10e_1 = 10e2=20e_2 = 20e3=30e_3 = 30,加权融合:

y=0.09×10+0.24×20+0.67×30=0.9+4.8+20.1=25.8y = 0.09 \times 10 + 0.24 \times 20 + 0.67 \times 30 = 0.9 + 4.8 + 20.1 = 25.8

可以看到,专家 3 的权重最大(0.67),最终结果也明显偏向它的输出 30。Router 就这样通过权重「调控」了每个专家的话语权。

3 专家算例的 softmax 权重比例条与加权贡献分解

图 1:3 专家算例——softmax 把打分归一成权重,再按权重加权各专家输出求和

为什么要有 MoE?

传统的「一个网络通吃」做法,要让模型更强就得加宽加深,但计算量随参数线性增长——参数翻倍,算力也翻倍,很快就把显卡压垮。

MoE 的妙处在于:参数可以加到很大(多放几个专家),但每次前向(即模型算一次输出)只需激活其中一部分。稀疏 MoE(如 Mixtral 8x7B,8 个专家只用 2 个)正是利用这一点——总参数量很大,但单次计算量只相当于一个小模型,从而在「容量」和「速度」之间鱼和熊掌兼得。

基础版稠密 MoE 虽然没省计算量(所有专家都跑),但它把一个复杂的「通才」网络拆成了多个「专才」小网络,让每个专家专注于自己擅长的输入子集,整体效果往往比单一网络更好——这就是「分而治之」的价值。

传统单网络与稀疏 MoE 的参数量 vs 单次计算量对比

图 2:参数量与单次计算量的对比——稀疏 MoE 让两者解耦

稠密 vs 稀疏:一句话区分

  • 稠密 MoE(本文基础版):所有专家都激活,全部加权融合。简单直观,适合学原理。
  • 稀疏 MoE(大模型实际用的):router 只挑权重最高的 top-k 个专家(如 8 选 2),其他专家直接跳过,省算力。

两者只是 router 多了一步「取 top-k」,核心思想一致。

稠密 MoE 与稀疏 MoE 的专家激活模式并排对比

图 3:稠密 vs 稀疏——左边全部专家激活,右边只激活 Top-K 个

完整代码:基础版 MoE

下面是一个最简实现,复制即可跑:BasicExpert(一个 Linear 当专家)+ BasicMOE(gate 是一个 Linear,输出过 softmax 得权重,再加权求和)。代码里每一步都对应上面公式的某一行。

python
# PyTorch 是最常用的深度学习框架;nn 是神经网络模块,F 是常用函数(如 softmax、mse_loss)
import torch
import torch.nn as nn
import torch.nn.functional as F


# 单个专家:最简实现就是一个线性层 Linear(feature_in -> feature_out)
class BasicExpert(nn.Module):
    def __init__(self, feature_in: int, feature_out: int):
        super().__init__()
        # 一个 Linear 就是一个最简「小网络」,对应公式里的 e_i(x) = A_i x + b_i
        self.linear = nn.Linear(feature_in, feature_out)

    def forward(self, x):
        return self.linear(x)


# 基础版稠密 MoE:gate 算权重 + 所有专家都跑 + 加权求和
class BasicMOE(nn.Module):
    def __init__(self, feature_in: int, feature_out: int, expert_number: int):
        super().__init__()
        # 用 ModuleList 注册多个专家,PyTorch 才会把它们的参数纳入 model.parameters()
        self.experts = nn.ModuleList(
            [BasicExpert(feature_in, feature_out) for _ in range(expert_number)]
        )
        # 门控 router:一个 Linear,输出维度 = 专家数,对应公式里的 W_g
        self.gate = nn.Linear(feature_in, expert_number)

    def forward(self, x):
        # x 形状: (batch, feature_in),batch 是这一批的样本数
        # 第一步:router 算每个专家的权重,过 softmax 得到 g(对应公式 g = softmax(W_g x))
        expert_weights = F.softmax(self.gate(x), dim=-1)  # (batch, expert_number)

        # 第二步:每个专家都对 x 跑一遍,得到 e_i(x)
        # torch.stack 把列表沿新维度拼起来: (batch, feature_out, expert_number)
        expert_outputs = torch.stack(
            [expert(x) for expert in self.experts], dim=-1
        )

        # 第三步:按权重加权求和(对应公式 y = Σ g_i · e_i(x))
        # expert_weights.unsqueeze(1) 形状变成 (batch, 1, expert_number),便于广播逐元素相乘
        weighted = expert_outputs * expert_weights.unsqueeze(1)
        output = weighted.sum(dim=-1)  # 沿专家维度求和: (batch, feature_out)
        return output, expert_weights


# === 假数据跑一遍前向 + 训练一步 ===
torch.manual_seed(42)  # 固定随机种子,保证每次运行结果一样(便于复现)

model = BasicMOE(feature_in=4, feature_out=3, expert_number=3)
x = torch.randn(2, 4)  # 生成 2 个样本,每个 4 维特征(数值为标准正态分布随机数)

# 前向:把 x 喂进模型,算出输出 y 和专家权重 weights
y, weights = model(x)
print("输出 y:", y)            # 形状 (2, 3)
print("专家权重 g:", weights)   # 形状 (2, 3),每行加起来等于 1

# 训练一步:用 MSE 损失(均方误差)让模型输出逼近全 1 的目标,反向传播更新参数
target = torch.ones(2, 3)  # 目标输出:2 个样本,每个 3 维,全为 1
loss = F.mse_loss(y, target)
loss.backward()  # 反向传播:算出 loss 对所有参数(专家 + gate)的梯度

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)  # SGD 优化器,学习率 0.01
optimizer.step()  # 用梯度更新一次参数
print("loss:", loss.item())  # .item() 把单元素 tensor 转成 Python 浮点数

读代码时抓住这三个对应关系就够了:

  • self.gate = nn.Linear(feature_in, expert_number) 对应 WgW_g
  • F.softmax(self.gate(x), dim=-1) 对应公式第一步 g=softmax(Wgx)g = \mathrm{softmax}(W_g x)
  • expert_outputs * expert_weights.unsqueeze(1).sum(dim=-1) 对应第三步 y=giei(x)y = \sum g_i \cdot e_i(x)

还有几个易忽略的细节:

  • nn.ModuleList:把多个专家装进 ModuleList(而不是普通 Python list),PyTorch 才会注册它们的参数,model.parameters() 才能拿到、才能训练。
  • unsqueeze(1):把权重形状从 (batch, expert_number) 变成 (batch, 1, expert_number),才能和 (batch, feature_out, expert_number) 逐元素相乘(广播机制)。
  • 训练时专家和 router 是联合训练的——router 学「该把什么输入分给谁」,专家学「接到分配的输入后该怎么处理」,二者共同进化。

小结

一句话:MoE = 多个专家 + 一个门控 router + 加权融合。它把「一个通才大网络」拆成「多个专才小网络」分工合作,是「分而治之」「术业有专攻」在神经网络里的具体实现。基础版稠密(所有专家都跑、全部加权),大模型实际用的稀疏版只在 router 上多了「取 top-k」一步,思想完全一致。理解了这个基础版,再去读 Mixtral、DeepSeek-V3 的架构就轻松多了。

参考资料

  1. Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer - Shazeer et al., ICLR 2017 https://arxiv.org/abs/1701.06538
  2. Mixture of Experts Explained - HuggingFace Blog https://huggingface.co/blog/moe
  3. Mixture of experts - Wikipedia https://en.wikipedia.org/wiki/Mixture_of_experts