☰
MoE混合专家架构全解:门控路由、负载均衡与稀疏激活实战
2026/10/22 11:55:22 网站建设 项目流程

讲大模型原理的文章,网上已经有一大堆了,但大多停留在“MoE 就是很多专家模型,按需调用”这个概念层面。真正动手预训练过 MoE、被路由不收敛折磨过、在推理服务里被显存卡过的人,才会明白这个概念层和工程实现层之间有巨大的沟。我的看法是,混合专家模型(Mixture of Experts)本质上解决的是一个大模型最核心的矛盾——容量要够大,但单位 token 的计算成本不能跟着线性涨。这篇文章我会把门控、路由器、辅助损失、稀疏激活这些关键模块从头拆到尾,也会把我实际遇到的路由崩溃、负载不均衡、微调阶段路由振荡这些坑一并讲清楚。如果你正在评估要不要把模型改成 MoE 架构,或者已经改了但效果比预期差,这篇文章应该能给你不少可落地的判断标准。

1. 单一大模型为何要转向“多专家会诊”思路

1.1 密集前馈层是容量与成本的双重包袱

先回到最基础的场景。Transformer 大模型里,核心计算量绝大多数落在两层结构上:注意力层负责 token 间的关系建模,前馈层(FFN)负责把每个 token 的特征做一次非线性变换。一个典型现象是,前馈层的参数量通常占据全模型参数的六成到八成,甚至更多。它在做的事也很“机械”:把输入向量从某个维度升维,过激活函数,再降维回来。模型的大部分“知识”是靠这里面的参数分布记住的。

如果走传统密集架构,那么每条输入 token 进入模型后,都会把全部前馈参数完整计算一遍。这里有一个非常直观的算力公式可以用来估算成本:一次 token 的前向计算量约等于参与计算的参数量乘以 token 数,再乘上一定的系数。这意味着如果你想把模型从 100B 参数扩大到 300B 参数,在保持相同训练数据量的情况下,每个 token 的算力开销大致是线性上升的。

这种“所有参数对所有 token 一视同仁”的设计,在数据分布单一的场景下是合理的,但在互联网级别的海量、多样化数据面前就很浪费。并不是每条 token 都需要独裁者级别的全套知识库。一个数学公式推导的 token 和一个日常闲聊的 token,它们需要的“知识权重路径”可能差着十万八千里,但在密集模型里都走同一条全参数链路,成本一样,谁也不能偷懒。

1.2 MoE 的做法是给每个 token 派单式分配专家

MoE 的天才之处,在于它把“知识存储”和“计算执行”解耦了。它的基本结构由三部分构成:多个参数独立的专家模块(通常每个专家就是一个标准的前馈网络)、一个全局共享的注意力层、以及一个负责派单的路由器(路由器也就是门控网络)。输入 token 不会进入所有专家,而是先经过路由器打分,选出排名前 k 的若干个专家,然后只在这几个专家上做计算。

这样一来的好处非常直接:总参数量可以提高好几个量级,因为额外增加的专家参数不需要被普遍参与激活;每个 token 实际触发计算的开销却只和选出的 top-k 个专家规模挂钩。用一个不算严谨但很好理解的类比来说:原来是一个全科医生包揽所有科室,现在变成每个科室坐一个专科医生,前台根据病人症状只挂一两个最相关科室的号。前台就相当于门控网络,台上的医生才是真正接诊的专家。

需要注意的一点是,这里说的“专家”并不是一个独立完整的模型。它们没有独立的注意力模块,内部也不具备跨 token 的信息交互能力。它们更像是共享同一个注意力上下文基础之上的、各自擅长不同模式特征的“并行子网络”。这一点理解偏差会带来后续一系列设计上的误判,比如有人以为 MoE 是多个大模型集成,实际在主流大模型架构里,它是在主干模型内部做的模块化替换,不是外面套好几个模型做模型融合。

2. 门控路由的原理拆解:派单员是如何工作的

2.1 路由器的工作流程与打分机制

路由器的示意性内部流程可以简化成下面这个伪代码:

# 假设 h 是当前 token 经过注意力层后的隐含表示 # W_router 是路由器的投影权重,维度为 [hidden_size, num_experts] scores = torch.matmul(h, W_router) # 每个专家的原始得分 scores = torch.softmax(scores, dim=-1) # 转成概率分布,所有专家概率之和为 1 topk_scores, topk_indices = torch.topk(scores, k=2) # 选出得分最高的两个专家 # 对选中专家做加权计算 final_output = 0 for i, idx in enumerate(topk_indices): expert_output = experts[idx](h) # 只有被选中的专家参与前向 final_output += topk_scores[i] * expert_output # 最终输出进入后续残差连接和全模型下一层

从代码逻辑可以记住两个关键动作:先 softmax 把专家得分归一化,再用 topk 挑选出前几名。softmax 的热度决定了路由的“专注程度”,如果温度偏低,那么高分专家得到的概率会被无限放大;如果温度偏高,所有专家的概率都会比较平均,最后几个专家算出来的结果也会被近乎等权叠加,失去“特化选择”的意义。

2.2 为什么普遍选择 Top-2 而不是 Top-1

早期论文和实验里,Top-1 和 Top-2 都有尝试,但主流把 Top-2 作为默认配置。为什么不是 Top-1?我观察下来有三个层面的原因。

第一是容错性。一个 token 被路由到单个最强的专家,一旦这个专家能力不足,错误就会被放大而且没有对冲机会。Top-2 相当于引入了第二名专家作为互补和缓冲,虽然单次计算量翻倍,但整体效果好过 Top-1。

第二是梯度传播的稳定性。如果只用 Top-1,没被选中的专家根本不会得到梯度,训练早期很容易出现“某个专家永远吃不到样本”的冻死问题。Top-2 让更多专家进入有效训练样本分布,专家的活跃度会健康不少。

第三是泛化能力。单个 top-1 路由容易拟合训练集里的样本表面特征,导致某些 token 只对特定表面词产生强关联;Top-2 会逼迫路由器在至少两个候选之间做权衡,这种加权组合更不容易过拟合。

但需要注意,“Top-2”不是不可挑战的教条。实际中某类模型会将 k 提高到更大的值,或者干脆不固定 k,而是固定一个选通权重和阈值,让专家数量动态化。K 值的上升带来的收益是容量利用率提升,代价是计算量线性增长、通信量变大。在固定算力预算下,k 并不是越大越好,后文容量部分我再展开说明。

2.3 负载均衡损失:不让某个专家累死,也不让某个专家闲死

路由器的训练有一个天然的隐患:它很容易被少数几个高频模式俘虏,导致几乎每个 token 都涌向同样的几个专家,其他专家颗粒无收。这不光浪费参数容量,还会让输出质量因为局部专家过载而崩溃。为了抑制这个问题,几乎所有 MoE 大模型训练都会在主干损失之外再加一个负载均衡性质的辅助损失项。

这类损失里比较有代表性的一种形式如下:

aux_loss = alpha * N * sum_i ( f_i * p_i )

这里的 N 代表专家总数,f_i 表示第 i 个专家在当前批次里实际被选中的样本比例,p_i 表示路由器在第 i 个专家上输出的平均概率。f_i 和 p_i 相乘再求和,只有当两者分布都趋于均匀时,这个乘积和才会接近最小。alpha 是这一项的权重系数,通常是网络训练总损失旁边的一个零头大小。

这个“乘了 N 倍”的设计,不只是为了让量级看起来更顺眼,它其实还有个更实际的作用:让这个平衡项在专家数量很大的和很小的模型里有可比性,不至于因为专家个数不同导致损失幅度天差地别。

在训练后段,有些团队会想办法把这类辅助损失的权重调小甚至降为 0,让路由器获得更自由的精细化分工能力。不过实际操作中我的建议是不要贸然直接归零,尤其是当训练数据分布变化剧烈时,一个完全没有平衡约束的路由器会在两三天内迅速坍缩回“头部专家垄断”状态,再救回来代价极高。

2.4 路由和注意力的分工差异

很多入门者会把路由器和注意力机制混为一谈,觉得它们都是“给 token 分配权重”。实际上它们关注的东西完全不同。

注意力机制的函数是面向 token 之间的关系:某个 token 该以多大强度参考句子里其他位置的 token,这属于上下文语义的整合。而路由器关注的是“该用模型哪部分参数来处理这个 token”,是对功能模块的挑选。

所以在结构设计上,它们通常各管各的:注意力模块全局共享,维护对所有 token 通用的语义交互;专家模块并行分散,负责吸收具有不同语义分布的知识。忽略这一点的话,很可能在设计 MoE 时把整个 Transformer 块都稀疏化,连注意力也算成专家的一部分。这样的极端稀疏架构不仅在实现上复杂得多,也往往得不到预期的收益。

3. 参数膨胀但计算量受限:MoE 的收益模型

3.1 前向推理的实际计算量下降

我用一组假设数据来做一个直观计算。假设一个模型总参数约 200B,其中 180B 分布在 200 个专家里,另外 20B 是注意力层、路由器和共享模块。路由使用 Top-2,那么对任意一条 token,实际参与计算的专家参数只有:

2 / 200 * 180B = 1.8B

再加上共享模块的 20B,实际前向开销约 3.8B 参数当量。对比同规模 200B 的密集模型,单 token 计算量大约是:

200B / 3.8B ≈ 52x

当然实际训练和推理中不可能达到 52 倍那么夸张,因为专家并行的通信开销、门控计算、负载偏差等都会稀释这部分收益。但至少从原理层面可以看清楚 MoE 的真正价值:同样一个 200B 级别的模型,密集架构要一步一个脚印走完全部参数,MoE 可以只走小而关键的路径。这也是为什么很多大模型团队在追求“更大容量”的时候会优先选择 MoE 路线。

3.2 训练阶段不能只看参数量,优化器状态和梯度都是实打实的

虽然前向计算是稀疏的,但 MoE 的训练却不“稀疏”。这里有个新手极容易误判的点:训练时,专家即使没被当前 batch 选中,它们也要被保存在显存里,并且因为梯度累积和参数更新的机制,优化器状态和梯度的存储需求跟全模型参数量直接挂钩。

粗算一下。假设总参数 200B,按 fp16 计算权重就要 400GB 显存存放。如果使用常见的 Adam 系优化器,还需要额外存放一阶和二阶动量,通常再翻一两倍。这还不包括激活值、通信缓冲区和临时张量。一旦训练专家数量扩大,显存压力绝不是按计算量下降的比例缩小的,而是接近线性增长。这意味着 MoE 的训练更多是把“算得快”换成了“存得多”,需要配套的模型并行和优化器并行方案,并不能单纯靠单机加大显存硬扛。

3.3 推理阶段真正卡住的往往不是计算,而是显存带宽

MoE 在推理阶段的收益有时候会让做评测的人产生困惑:为什么理论上算力少了很多,但实际推理速度提升却没那么明显?

这里要理解 GPU 工作的一个特点:它并不是只看浮点运算量,还要看权重搬运量。GPU 计算的速度远远快于显存读取权重的速度。密集模型推理时,虽然计算量大,但权重装载也是高效的线性读取,计算可以把带宽充分压满。MoE 模型则面临另一种局面:虽然只挑两个专家算,但每次 token 可能选中不同的专家,导致频繁切换读取不同专家权重。如果切得很离散,显存带宽反而成为瓶颈,计算单元经常在等待权重从显存搬到算力单元。

所以 MoE 推理优化里非常重要的一环,是尽量让同一批次、同一设备上的 token 路由结果相似,减少专家权重的来回搬运。这也解释了为什么某些推理框架里要坚持“专家并行”,把不同专家放在不同 GPU 上,同时配合 batch 级别的路由聚类,才能把这部分隐藏开销压下来。

4. 训练与微调时最容易踩的坑

4.1 路由崩溃:头部专家垄断一切

MoE 训练里最常见、后果也最严重的问题,就是路由崩溃。表现为训练到某个阶段后,路由器输出的概率分布主要集中在少量专家上,大部分专家名存实亡。

我遇到过类似问题后做过统计:当崩溃发生时,最活跃的 5 个专家可能承担了 70% 以上的路由流量,而处于尾部的几十个专家累计概率不到 5%。这种模式下,尾部专家既得不到有效梯度,也没法形成自己的特化能力,属于典型的“参数白瞎”。

为什么会这样?根子在于 RoBust 分布下,少量通用模式占据了大部分数据,路由器会天然倾向于把所有 token 都丢给那几个表现好的“尖子生”。如果不加干预,这个过程会自我强化:越被选择的专家训练越充分,路由器的概率越觉得它靠谱;越少被选中的专家训练越不足,路由器越不可能给它高分。要打破这个循环,除了铺满负载均衡损失之外,还可以考虑在训练初期给路由器增加噪声干扰,或者在打分时对高频专家做惩罚性降权。

4.2 负载均衡的检查不能只看 token 分配,还要看分数的分布

很多人排查负载均衡问题,只看每个专家实际处理的 token 数是否差不多。这当然是最基础的检查,但不充分。

我给你一个反例场景:某个专家实际收到的 token 数不少,但它的得分分布非常极端——要么接近满分,要么直接零分。这说明它对某些 token 模式形成了脆弱的死板匹配,泛化能力很差;另一个专家收到的 token 数也差不多,但得分一直徘徊在及格线上下,属于“经常陪跑、偶尔上位”。这两种情况从 token 数量维度看完全相同,但健康程度完全不一样。

所以我建议训练 MoE 时,至少并行追踪三类指标:

  • token 计数分布:每个专家每步处理了多少 token,用于判断计算负载是否均衡。
  • 平均概率分布:路由器对每个专家输出的平均 softmax 概率,用于判断专家是被“坚定选择”还是“勉为其难陪跑”。
  • 概率方差:每个专家得分的发散程度,方差过大的专家往往已经局部过拟合到某种模式上。

只有这三类指标都合理,路由状态才是真正健康的。如果发现第二个指标分布参差很大,我会优先调整辅助损失项权重,而不是急着调整专家结构。

4.3 微调阶段路由该冻结还是放开,取决于数据规模

很多人微调 MoE 模型时分不清“该不该动路由器”。直接全参数微调,经常出现灾难性遗忘或者路由振荡;把路由器完全冻结,又感觉指令调整的效果出不来。

我的经验是分场景讨论。

如果微调数据量比较小,比如只有几万条高质量指令,我会倾向冻结路由器的投影权重,只更新专家内部参数。原因是小数据量扛不住路由器重新学分布,一个 finetune 阶段很容易被数据里的表面错误模式带偏。

如果微调数据是几十万条以上的大规模语料,就可以考虑放开路由权重,甚至调整一下负载均衡损失项。在比较大的数据覆盖下,路由器有机会重新校准对任务先验的判断。但这时候要留意损失曲线的振荡,比密集模型更明显。一旦路由概率熵下降过快,说明又开始往崩溃方向走了,需要立刻加大平衡系数或者降低学习率。

4.4 专家并行通信的压力不能想当然

最后说一个工程细节。MoE 训练时,由于每个 token 可能被路由到不同设备上的专家,跨设备的专家激活会引入大量通信。这是 MoE 训练比同等密集模型慢的一大原因。通信量取决于两个因素:每批 token 分散到多少设备,以及单次通信张量大小。

一个亲测有用的经验是:尽量把“高交互的专家对”放在同一台机器或同一个机架内。而且,在设置训练并行策略时,可以给路由器单独设置一个较低的学习率或较小的分组大小,因为它更新太快只会加剧震荡。

5. 专家数量的设计:不是越多越好,也不是越少越省

5.1 专家个数与容量因子的权衡

选多少专家,是 MoE 架构设计里最基础、也是影响最长远的决定。专家太少,模型总容量受限,稀疏激活的优势体现不出来;专家太多,每个专家的平均训练样本数下降,专家容易欠拟合,通信切分也更复杂。

真实的判断依据应该来自整体计算预算和显存预算。你期望模型最终以多少 FLOPs 加载进推理?期望显存能放下多大 Tensor 规模?专家数直接决定这两个指标的上限。

另一个关键参数叫容量因子(capacity factor)。直觉上它就是“当某专家被额外超出预期数量的 token 选中时,最多能接纳多少倍于均值的 token”。如果容量因子设为 1.0,表示每个专家最多处理一条平均负载量;超出的 token 只能被丢弃或被重新路由。大 token 流的 LLM 训练通常需要 1.1 到 1.25 的容量因子来吸收随机波动,否则每个 batch 都有可观的 token 因为超限被丢弃,知识覆盖变差。

5.2 每个专家内部应该有更细的分工

专家并不是越大越好。观察多个 MoE 模型权重后给我的体会是,一个理想的状态是每个专家只负责一小簇语义相关的特征,比如有的专家对代码缩进语法敏感,有的专家对化学反应式敏感,有的专家对情感词汇敏感。如果专家内部的隐层顶层设计得非常宽大,它反而会吸收过多模式,跟其他专家产生叠加干扰,最后谁都学不精。

所以在实现层面,我会建议专家本身的 FFN 中间维度不要过度夸张,前期小规模训练时可以先放一个小专家数量的配置,跑通后检查路由分布的热力图,再逐步增加专家数。这个做法比从大专家数起步再去减要容易很多。

5.3 门控与残差连接的细节往往是最后一步定胜负

有些 MoE 实现会在专家输出后面再加一个残差连接,把路由结果与原始输入做直接相加。这样做可以保证即使路由结果不理想,原始表示的信息仍能完整传递下去,减少路由错误的副作用。

同时,门控输出上的 scale 因子也要认真设置。很多默认实现会对路由权重乘以一个缩放系数,比如 top-2 时除以 2,以保证不同 k 值之间的输出尺度可比。这类细节如果不做,深层堆叠后输出方差会被放大,训练波动率显著上升。

6. 路由机制的演进方向与实际落地选择

6.1 由 token 主导变为专家主导,反向路由也值得关注

传统 MoE 是 token 找专家:每个 token 独立选 top-k 专家。与之相对的思路是 expert choice:每个专家主动去挑一批最适合自己的 token,而不是被动等待 token 上门。

这个方案在实际工程里有一个很大的好处,就是可以保证每个专家的负载都绝对均匀,因为分配是在全局视角下做匹配的,而不是每个 token 各行其是。实现上它需要额外的分配算法和通信协作,但能有效缓解传统路由的崩溃问题。长序列训练场景里,专家主导的路由往往比 token 主导的路由更省心。选择哪一类,取决于你的主要矛盾到底在“路由不稳定”还是在“通信模式复杂”。

6.2 共享专家的存在让公共知识不再重复占用空间

另一类实践技巧是引入少量始终激活的共享专家。共享专家的定位是吸收跨任务的公共知识,比如句法的通用结构、基础词汇语义等。其他稀疏专家则可以更专注地应对特殊领域模式。这个做法的好处是,避免了大量专家都各自学一份相近的公共模式;代价是要额外承担固定计算开销。

我观察到很多团队后来都悄悄加了这种共享专家结构,因为它能显著降低路由器的“选择负担”,让路由更容易形成明确的专业分工。从训练稳定性看也是一种温和正则,相当于给路由分布预设了一个底限。

6.3 多模态环境下专家的分配逻辑会变化

在单一语言的模型里,专家的分工更多是“知识主题”维度上的。但到了多模态环境,比如同时处理文本、图像甚至结构化数据时,路由分配会出现新的维度需求。同一个 token 对应的特征模式背后可能是完全不同的感官表征。

如果把视觉 patch 和文本 token 塞进同一个路由空间,工程师必须考虑:是让它们共享专家,还是为不同模态设置专用的专家池,还是通过模态提示条件来影响路由先验?我的倾向是前期先做模态分离的专家池,再通过共享的路由与共享注意力做交叉连接。直接一股脑塞进同一个专家池,多模态之间会互相抢占容量,训练曲线非常难看。

在跨模态的实际落地里,路由的输入除了 token 本身的隐含向量,还可以拼接一些模态标记或任务标记,让路由对模态类型有基本感知。这不是加参数量的大动作,却能让路由在早期阶段快速收敛到合理的分工状态。

一些绕不开的取舍与个人判断

回到最开头那个问题:MoE 是否值得引入,取决于你要追的是容量还是速度。如果你手里已经有一个 100B 参数的密集模型,但发现它在某些长尾任务上知识不够,同时推理算力预算又不能扩大,那 MoE 是一个很有吸引力的方向——专家参数可以近乎免费地增加容量,推理代价只要 top-k 部分。

但如果你当前的首要痛点是训练稳定性、工程复杂度、或者团队熟悉度,那么直接上 MoE 会带来比密集模型高一个量级的踩坑成本。路由器的收敛、辅助损失的调参、设备间的通信调度,每一项都要花时间去磨合。我自己在调试这类模型时的感受是:它把密集模型的“无脑堆数据”阶段变成了“小心翼翼控制分布”阶段,门控这部分是需要长期盯着的短板,也是决定最终收益上限的核心变量。

如果你打算在下一个项目里试水 MoE,我建议你先搭建一个极小的训练框架,用 8 个专家、一个小语料集验证路由器的收敛模式,再决定要不要往全尺寸放。这个小规模实验能让你最廉价地获取 MoE 训练的手感,后面再加模型量级时,很多前期坑都已经踩平了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询