阶段 5 · 大模型怎么炼成

MoE:一个参数几十亿的模型
每次只用一小块

《缩放定律》说"越大越好"。但参数量一大,每个 token 都要过一遍全部参数—— 推理成本直接线性爆炸。MoE(Mixture of Experts,混合专家)的说法是: 参数量可以大,但每个 token 只看其中几个专家就够了。

1

先看它改的是哪一块

Transformer 的每一层(一个 Block)由两部分组成:注意力和前馈层(FFN)。 我们在《Transformer 自注意力》那章算过:FFN 占了整层三分之二的参数。

所以想让参数量变大、又不让计算量跟着变大,唯一值得动刀的地方就是 FFN。 原因也简单:FFN 对每个 token 各算各的,正好拆得开;注意力要让所有 token 互相交换信息,拆成专家收益小。

普通模型(叫「稠密」模型:每个 token 都要用到全部参数)把这一个大 FFN 用满。 MoE 换成 N 个小的(叫"专家"),每个 token 只挑其中 k 个走。 N 是专家总数,k 是每个 token 用几个——两个数不是一回事。

稠密 FFN  每个 token → 1 个 FFN(全部参数都用上)
MoE FFN   每个 token → top-k 个专家(其余专家完全不参与计算)
💡 关键区分:总参数 vs 每个 token 用到的参数

总参数决定模型能装多少知识(显存要装下全部)。
每个 token 用到的参数(active parameters)决定它要花多少计算(速度看这个)。

比如 Qwen3-30B-A3B(30.5B 总 / 3.3B 用上):总参数 300 亿,但每个 token 只用其中 30 亿。 它的知识容量高于用到的参数、又达不到总参数——每个专家只见到分给它的那部分 token, 学到的比同样大的稠密模型少(所以「有效参数」落在中间;这是本章的说法,不是论文指标)。 官方博客的对照是:它以约十分之一的「每个 token 用到的参数」超过了 QwQ-32B (一个 32B 的稠密模型;Qwen3 博客), 推理速度则和 3B 的稠密模型差不多。 总参数是容量上限,不是等值兑换。

图 · 总参数是整库,每个 token 只借其中一小块

总参数:显存要装下全部 比如 30B → 每个 token 用到的:只亮这一小块 比如 3B 方格子只是示意;真实参数是连成一片的数,不是这么整齐的块。
🎯 像一家医院的分诊台

MoE 像一家有很多专科医生的医院:分诊台(路由器)看完你这个病人,只把你送去最对口的两位医生。 全医院的知识都在,但你这一个病人只花两位医生的时间。
这个类比管到「怎么分工」为止:医生的专科是事先定的,MoE 的专家不是——哪个专家擅长什么,是训练中自己长出来的。

互动 · 动刀的地方只有 FFN:把 1 个大 FFN 换成 8 个小的

总参数
—
每 token 用到
—
用到的比例
—

2

路由器:一个很简单的选择器

怎么决定哪个 token 去哪个专家?靠一个叫路由器(Router)的小矩阵 Wr: 它给每个 token 算一组分数,取最高的 k 个,再用 softmax 归一化成权重 g。

路由器是可以训练的,而且它只占极少的参数——Wr 是一个 d×N 的矩阵 (d 是 token 向量的长度,N 是专家个数,k 是每个 token 用几个)。 下面沙盘里每个 token 带着一个 x(它的特征向量,这里是 6 个数), 路由分数就是 Wr · x(x 的每一项乘 Wr 相应一列再相加), 再取分数最高的 k 个——每一步怎么算,后面的数学节会逐格走一遍。

沙盘里,左边每一行是一个 token,右边每一行是一个专家;每条线 = 这个 token 被送去了那个专家, 线的粗细就是它的权重 g。

沙盘里的 token 特征是按 8 个专家方向手工造的,所以它们一定会落到不同专家—— 这里演示的是路由怎么算,不是专家怎么学会分工。 真实模型里专家到底专在哪?研究者事后检查过 Mixtral 的路由:没有出现明显的「这个专家管数学、那个管生物」, 更像是在词法、句法层面分了工(Mixtral 论文第 5 节)。

互动 · 亲手路由一批 token

每个 token 用到的参数
—
不均衡度(越接近 0 越均匀)
—
最忙 / 最闲专家
—

3

真正的难点:让专家们别偷懒

上面的沙盘看起来一切正常。但 MoE 有一个致命的自增强陷阱:富者越富。

第 1 步:随机初始化,某个专家恰好稍微好一点点
第 2 步:路由器更愿意把 token 给它 因为它当下表现更好——这是合理的短期选择
第 3 步:它拿到更多 token,于是训练得更充分,变得更强
第 4 步:回到第 2 步。循环强化,直到其他专家彻底没人用 这就是 专家坍塌(Expert Collapse)——大多数专家变成死参数, 模型退化成一个只有一两个专家的稠密网络,MoE 白做了

解法是往损失函数里加一项:负载均衡损失(Load Balancing Loss)。 它惩罚"分配不均",逼路由器把 token 摊开。

互动 · 加上负载均衡损失,看路由怎么被掰回来

专家负载分布

损失随训练下降

互动 · 富者越富:拖「自我强化」强度,看负载什么时候塌成一个专家

4

一路修补过来的 MoE

版本关键改动解决了什么
最初的 MoE
1991
层内并列多个专家,用一个小网络(门控)来挑 理论上更省算力,但训练极不稳定
稀疏门控 MoE
2017
第一次把稀疏门控真的堆到 137B 参数 参数量大幅涨、算力几乎不涨;但通信开销巨大
GShard
2020
top-2 token-choice 门控(token 选专家)+ 自动分片(把模型切到上千张卡上) 第一次把 MoE 做到 600B 级并跑通分布式训练
Switch Transformer
2021
top-1 路由(只选一个专家)+ 简化负载均衡损失 第一次把 MoE 训到 1.6 万亿参数(并用半精度 bfloat16 稳住训练)
Expert Choice
2022
反过来:专家选 token(expert-choice) 天然负载均衡。但每个 token 会被不同数量的专家处理;而且专家要拿一整批 token 来挑,生成时后面的 token 还没出来,不能直接用于自回归生成
Mixtral 8×7B
2023
top-2,共 8 个专家 第一个用 Apache-2.0 许可(一种允许商用的开源协议)发布、被社区广泛验证的 MoE 大模型
DeepSeekMoE
2024
细粒度专家(专家更多更小)+ 共享专家(几个专家所有 token 都走) 共享专家负责通用知识,路由专家负责专门知识, 大幅减少"每个专家都得重复学一遍常识"的浪费
DeepSeek-V3 / R1
2024–25
671B 总参数 / 37B 用上;无辅助损失的路由(不额外加一项惩罚)+ 通信优化 证明 MoE 在超大规模上不仅省算力,效果也能打赢同级别的稠密模型
💡 "共享专家"这个改动值得单独理解

标准 MoE 有个隐性浪费:像"这个词是名词""这句话是疑问句"这类所有 token 都需要的常识, 会因为路由分散而在每一个专家里都被重复学一遍。
DeepSeekMoE 的解法是留出几个共享专家——不管路由结果如何,所有 token 都会过它们。 于是专门知识交给路由专家,通用知识交给共享专家,不再重复劳动。

互动 · 三个真实机型:总参数和每个 token 用到的参数差了多少

5

MoE 到底省了什么、没省什么

资源稠密模型MoE(总参数 8×,每 token 用 1×)
每个 token 的计算量1× 约 1×(只走 k 个专家)—— 这是 MoE 唯一真正省的东西
显存占用1× 约 8×(所有专家都要装在显存里,即使某个 token 不用)
训练时的通信低 很高。专家分散在不同 GPU 上,token 要在显卡之间发送再收回来
训练稳定性稳 需要精心调负载均衡、路由温度和容量因子(后两样后面讲)
知识容量1× 约 8×(总参数量决定容量)
推理延迟取决于参数量 计算快,但要处理动态路由,一次要算的 token 很少(batch 小)时反而可能更慢
⚠️ 一句话总结这个权衡

MoE 省算力,但很费显存和通信。
所以它特别适合"推理量巨大、但显存相对宽裕"的场景—— 比如在线服务的基础模型。反过来,如果你要在手机或边缘设备上部署, MoE 是最糟糕的选择:装不下,而且动态路由的开销完全掩盖了计算量的节省。

互动 · 省了什么、没省什么:两笔账摆在一起(以 8 专家 top-1 为例)

M

数学 · 一个 token 是怎么被挑走、又怎么合起来的

前面说「路由器给每个 token 打分,取 top-k,再 softmax 加权」。 这十几个字,读的人脑子里常常是空的——分数从哪算出来、挑中之后是几个什么数、 几个专家的答案又怎么变成一个。一张图走完这三步,这一章的公式你就全看懂了。 (图上那个「路由温度 T」藏在 softmax 里:分数先除以 T 再归一化——T 越小,高分的优势被放得越大。)

核心大图 · 拖 top-k 和温度:看这个 token 被打了几分、被谁处理、最后怎么合起来

被选中的专家
—
权重 g(softmax 后)
—
每个 token 用到的参数占比
—

互动 · 每个符号管图上的哪一块(卡片下面那张是「均衡损失为什么是个碗」的那一步)

🎬 自己验一遍

把「温度」从 1 拖到 0.2:分最高的那个专家的权重会迅速吃掉全部, 输出几乎就等于它自己——这就是 top-1 的极端。
再把温度拖到 3:被选中的两个专家权重变得接近,输出成了两者的平均。 同一个 token、同一组分数,只因为温度变了,它走的路就变了—— 这就是为什么「路由温度」是 MoE 要专门调的一个超参数。

6

MoE 的四个坑

坑说明什么时候真的会痛 → 换什么
① 专家坍塌 富者越富的自增强循环:被选中的专家学得更好,于是更容易被选中。解法是负载均衡损失, 但均衡得太狠又会让路由失去区分能力——所有专家变得一样,等于没分 训练数据分布单一、或 λ 怎么调都不对时 → 调大 λ、或让共享专家分摊通用知识(DeepSeekMoE 的做法)
② 容量因子与 token 丢弃 专家硬件上容量有限,超出容量的 token 只能被丢弃:这些 token 对专家参数的梯度是 0 (token 本身经残差——原样加回上一层——直通下一层,见 《归一化》) 负载天生偏、数据长尾(大部分数据集中在少数类型,其余类型各只有一点点)时最痛 → 把容量因子调大(费显存),或看第 4 节 expert-choice 那种反过来的路由
③ batch 小的时候不划算 MoE 的优势建立在"不同 token 走不同专家、可以并行"上。 一次要算的 token 很少时,它们可能都挤到同一个专家,并行度丢失,反而比稠密模型慢 端侧、单条请求、实时交互 → 直接用每个 token 计算量和它差不多的稠密模型
④ 微调更难 LoRA(一种常见的省显存微调法)之类的方法在 MoE 上不那么好使——该调路由器还是该调专家? 专家之间的负载还会随微调数据分布漂移,目前仍是开放问题 要频繁换任务微调 → 得连专家一起调、并盯住负载平衡;想省事就用稠密模型

看到「X 总参数 / Y 用到的参数」时,先看这几个数

看的数意味着什么代价
专家越多越小(细粒度)组合空间更大、更灵活通信和路由开销上升
top-k 越大每个 token 用的专家越多,质量通常更好每个 token 用到的参数变多,算力跟着涨
比例(总参数 ÷ 用到的参数)常见区间是 3.6:1 到 18:1比例越大,显存和通信的账越贵

互动 · 拖「容量因子」:设小了,有多少 token 会被直接丢掉

7

小结

这些工程细节底下,只压着一个赌注。这一节把它挖出来。

它对应哪条线 ② 没有免费午餐 → 必须有归纳偏置(先给模型定一条规矩)——MoE 往结构里焊进了一条断言: 「不同的 token,该由不同的参数来算」
一句话 这一章的做法,本质上是在赌「一个 token 只需要一小部分知识」, 然后花全部力气去让这个赌注不要塌成一个没有专家的稠密网络。
它牺牲了什么 牺牲了稠密模型那份「每个参数都为每个 token 干活」的确定性。 计算确实省下来了(每个 token 用到的参数只要 1/3.6 到 1/18), 但总参数一点没省:知识容量要全额占显存,专家之间还要 all-to-all 搬运。 更麻烦的是,那条「让专家别偷懒」的正则项本身也在消耗模型的表达力—— 拉过头,专家就全长成一个样了(回扣暗线 B)。
💡 检验一下:你现在能指着哪个互动说这句话

回到第 2 节那张路由沙盘,点一下「让专家 0 一开始就领先」那个按钮—— 专家 0 的柱子立刻比别的长出一大截,读数报出「专家坍塌」。

再把 token 数量留在默认的 24 个,把「每个 token 走几个专家」拖到 top-1: 连线的条数少了,专家 0 的份额还会更高(49% → 54%)。 那个瞬间你看到的就是赌注塌掉的样子——流量全挤向一个专家,MoE 白做了,实际退化成一个小得多的稠密网络。

然后走到第 3 节,把 λ 从 0.01 拖到 0.05,点「▶ 开始训练」—— 负载柱会被压成一条平线。那条平线就是那场拉扯:均衡的力量和分工的力量互相抵消, MoE 的全部工程难度都挂在这个滑块上。如果你刚才没想指着某个东西说这句话,回去再拖一次。

把「均衡 vs 分工」写成公式

路由器在做的事,和负载均衡损失在做的事,各是一行。把鼠标移到公式里的符号上, 对应的滑块会亮起来。

👆 先悬停,再拖那个滑块

鼠标停在 λ 上,看它指向第几节的那个滑块;f 是每个专家实际分到的比例,P 是路由器给它的平均概率,悬停它们也能看到各自指哪。
然后把 λ 从 0.01 拖到 0.05——右边那条均衡项曲线会掉到贴着虚线。 那条虚线是「N 个专家各拿 1/N」时 N·Σf² 的最小值 1.0(还没乘 λ);曲线贴着它,正说明负载摊匀了。
注意这条曲线只画了 N·Σf² 这一项,不是模型的总损失,也还没乘 λ:λ 拖大,这一项确实贴近最小值 1.0, 但路由被逼着不看内容了,模型整体反而变差——这才是「不能一直拖大」的原因。

为什么「均衡」和「分工」不能同时要满

λ 太大λ 刚好(≈0.01)λ 太小或为 0
路由器被逼着不看内容、均匀分配。每个专家拿到一样的流量, 但也就变成了 8 个相同的小 FFN——等于没有专家 token 较均匀地分到各个专家,同时没有一个专家垄断流量。 这就是要的那个点 富者越富的自增强循环:被选中的专家学得更好 → 更容易被选中 → 最后只剩一两个专家在干活(专家坍塌)

λ 是一个超参数——它不在模型里,得人来定。 DeepSeek-V3 主要绕开了这件事:不靠惩罚项,改成给每个专家挂一个随负载自动上下调的偏置(只保留一个极小的序列级辅助损失,防单条序列内极端不均衡)。

它在六条暗线里站在哪

下面的 A–F 是本站拆解任何一个方法时通用的六个问题,不是 MoE 专属的。

暗线这一章的回答
A 信息流动 输入 (n_token, d_model)(n_token 个 token,每个是一条 d_model 长的向量),输出还是 (n_token, d_model)—— MoE 不改变张量的形状。
它改变的是那条路径:稠密 FFN 里每个 token 走同一套参数, MoE 里每个 token 走一条自己选的专家路径。 形状不变、计算图变成随 token 而变的动态图——这就是 MoE 所有工程麻烦的源头: batch 里没有两个 token 保证走同一条路,所以你没法把它们简单拼成一次大批量矩阵乘
B 什么被牺牲了 牺牲的不是精度,是稠密模型那份「每个参数都为每个 token 干活」的确定性。 同时丢掉三样:显存(总参数要全额加载)、 带宽(token 要在卡间搬运)、 路由的稳定性(必须靠一个正则项按住,而正则项会吃掉区分度)。 第 3 节那个 λ 滑块量的就是最后一笔账
C 参数账本 看三个真实机型:
Mixtral 8×7B:8 个专家、top-2,总参数 46.7B,每个 token 用 12.9B(约 3.6:1)
DeepSeek-V3:256 个专家、top-8,总参数 671B,每个 token 用 37B(约 18:1)
Qwen3-30B-A3B:就是第 1 节那台(比例约 9:1)
「每个 token 用到的参数」才是 FLOPs(计算量)的账,「总参数」才是显存的账—— 两个数差着一个数量级,这正是 MoE 的全部卖点
D 跑在什么上 卡在「搬数据」上,不是卡在算力上。
MoE 省的正是 FLOPs(计算量),可它把省下的钱全花在了搬运上:专家分布在不同卡上时, 每个 token 要被 两两互送(all-to-all)到它专家所在的那张卡,算完再搬回来—— 最贵的动作是「把 token 从一张卡搬到另一张卡」,不是乘加。
更反讽的是:每张卡分到的 token 变少了,一次搬进来的数据只够算几下—— 算力再快也得等数据送到,GPU 反而更容易被「数据进出的速度」(内存带宽)卡住。 完整的搬运 vs 计算账在 《硬件与算力账本》
E 它假设了什么 假设「知识是可分块的、输入决定了该用哪一块」。 这条假设成立时,MoE 就是白捡的算力;不成立时(所有 token 其实都需要同一套知识), 路由学不出分工、专家全长成一个样,MoE 退化成稠密网络还多背一堆参数。
第 2 节那个沙盘演示的是这条假设成立时的样子(token 特征是我们按专家方向造的); 真实模型里它成不成立,要看数据
F 违背了哪个直觉 「参数越多越慢」是错的。常识里 671B 的模型该比 37B 慢一个数量级, 可 MoE 让它跑出 37B 的速度。
第二个反直觉:「更大的模型必然要花更多算力」也不完全对—— MoE 把「容量」和「速度」解耦了,代价是它们各自需要的东西(显存 vs 通信) 不再是同一笔账
一句话带走 MoE

MoE = 把 FFN 换成多个专家,每个 token 只走 top-k 个。
它唯一真正省的是计算量(每个 token 用到的参数),总参数(知识容量)一点没省,反而更费显存和通信。
核心难点是路由会让富者越富——所以必须加负载均衡损失, 但均衡过度又会毁掉专家的区分度。
它的工程含义很明确:适合显存宽裕、推理量巨大的服务场景,不适合端侧部署。 取舍那笔账,《缩放定律》算过;数据从哪来,看《预训练数据工程》。

8

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

∑ 更严格的形式