📑 本页目录(点开跳转)
02d · MoE 混合专家
⏱ 64 分钟 | ⭐⭐ 教程里被反复引用、却从没讲过的那个东西
🎯 一句话
MoE 把「参数量」和「计算量」拆开了:一个模型可以有 6710 亿参数,但每个 token 只经过其中 370 亿。 代价也很直白——显存要按总参数算,速度才按激活参数算。 这不是免费午餐,是一次很具体的交易。
🚪 为什么要补这一章
翻一下这套教程,你会发现一件有点尴尬的事:
《全景导论》第 2 章:用「MoE 只替换 FFN」解释了三件事之一
《全景导论》第 7 章:本地部署的显存表里有一行「200B+ MoE」
《ML 基础》第 14 章:「Transformer 之后」那张表里有 MoE 一行
《AI基础设施》第 14 章:整整一节在讲 MoE 的专家并行
⚠️ 但全站没有任何一处讲过:MoE 到底是怎么工作的
这是典型的「用了没讲」:一个结论被当成公理反复引用,而它自己从来没被证明过。这一章补上它。
🔗 前置:读之前最好先有第 2 章里「注意力横向、FFN 纵向」那个分工的印象——这一章几乎全部建立在那句话上。
🎯 一、它想解决什么
Scaling Law 的结论很朴素:参数越多,效果越好(🔗 第 4 章)。但参数越多,每生成一个 token 的计算量也越大,推理成本跟着线性涨。
于是有了一个很自然的念头:
人在处理不同的问题时,也不是整个大脑一起上
→ 能不能让模型也这样:参数很多,但每次只用一小部分?
⭐ 这就是 MoE:把 FFN 换成【一组】FFN,每个 token 只走其中几个
两个必须分清的数字(面试和选型都会用到):
| 概念 | 是什么 | 决定了什么 |
|---|---|---|
| 总参数量 | 所有专家加起来的参数 | 显存占用 ⭐ |
| 激活参数量 | 一个 token 实际经过的参数 | 计算量、生成速度 ⭐ |
Mixtral 8×7B:总参数 46.7B,激活约 12.9B
DeepSeek-V3 :总参数 671B ,激活约 37B
⚠️ 注意 Mixtral 那个名字有误导性:
「8×7B」不等于 56B —— 因为【注意力那部分是所有专家共享的】,
只有 FFN 被复制了 8 份。所以是 46.7B 不是 56B ⭐
🧩 二、为什么只替换 FFN,不替换注意力
这是第 2 章直接给了结论、没给理由的那一条。三条理由,第三条才是决定性的。
理由 1:FFN 是参数大头
FFN 约占一层参数的 2/3。要稀疏化,当然挑最大的那块下手——把注意力稀疏化了,最多也就省下 1/3 的空间来做文章。
理由 2:FFN 本来就是「逐 token 独立」的
回想那句分工:注意力是横向的(token 之间交换信息),FFN 是纵向的(每个 token 单独深加工)。
FFN 对每个位置做【完全一样、互不相干】的变换
→ 那么把它换成"每个位置走不同的变换",
在语义上是完全成立的,不破坏任何结构 ⭐
理由 3:注意力根本没法这样稀疏化 ⭐
这条最关键。注意力要算 Q 和 K 的内积——而内积只有在两者处于同一个向量空间时才有意义。
假设 token A 走"专家1的注意力",token B 走"专家2的注意力"
→ A 的 Query 和 B 的 Key 来自两套不同的投影矩阵
→ 它们的内积【不代表任何东西】,就是两个不相干空间的数字硬乘 💀
而且注意力的本质是"所有 token 互相看",
它天然要求【所有 token 都在场、都用同一套坐标系】
🔑 一句话记住:FFN 是"每人自己想",所以可以每人想得不一样;注意力是"大家一起讨论",所以必须说同一种语言。
⚠️ 顺带一提:确实有人尝试过稀疏化注意力头(MoA 之类的研究),但没有成为主流,原因就在上面。
🚦 三、路由:谁决定 token 去哪
数学上就三步
$$g = \text{softmax}(W_g\,x), \qquad \mathcal{T} = \text{top-}k(g), \qquad y = \sum_{i \in \mathcal{T}} g_i \cdot E_i(x)$$
W_g是一个d × E的小矩阵(E = 专家数量),这就是全部的 router,小得可以忽略top-k挑出分数最高的 k 个- 每个被选中的专家
E_i就是一个普通的 FFN(现在通常是 SwiGLU,🔗 02b)
⭐ 为什么门控权重必须乘回去
这是个特别值得想明白的点,面试也常拿它筛人。
top-k 是【离散选择】—— 它不可导,梯度传不过去
如果你只是把选中的两个专家的输出【直接相加】:
y = E3(x) + E6(x)
→ router 的参数 W_g 对 loss 的影响路径【完全断了】
→ 它永远拿不到梯度,永远学不会该怎么路由 💀
乘上去之后:
y = 0.61·E3(x) + 0.28·E6(x)
→ g_i 这个【标量】是可导的,梯度能沿着它回到 W_g ⭐
→ router 学到的信号是:"我给专家3 打的分高了还是低了"
🔑 一句话:门控权重是 router 唯一的一条梯度通路。 去掉它,MoE 就变成了随机分派。
top-k 取多少
| 方案 | k | 代表 | 特点 |
|---|---|---|---|
| Switch Transformer | 1 | Google, 2021 | 论文的主要贡献之一就是证明 k=1 就够——此前大家以为至少要 2 个才能让 router 学到东西 |
| GShard / Mixtral | 2 | Mixtral 8×7B | 最常见的选择,k=2 的容错性比 k=1 好 |
| 细粒度 MoE | 6~8(从几十上百个里选) | DeepSeek-V3 | 专家更小更多,选得更多,见第五节 |
import torch
import torch.nn as nn
import torch.nn.functional as F
class MoEFFN(nn.Module):
"""一个能跑通逻辑的极简 MoE 层(教学用,不含容量限制和并行)。"""
def __init__(self, dim: int, hidden: int, n_expert: int = 8, top_k: int = 2):
super().__init__()
self.top_k = top_k
self.router = nn.Linear(dim, n_expert, bias=False) # ⭐ router 就这么小
self.experts = nn.ModuleList(
nn.Sequential(nn.Linear(dim, hidden), nn.SiLU(), nn.Linear(hidden, dim))
for _ in range(n_expert))
def forward(self, x): # x: (tokens, dim)
logits = self.router(x.float()) # ⭐ router 用 fp32 算,见第四节的坑
probs = F.softmax(logits, dim=-1)
topv, topi = probs.topk(self.top_k, dim=-1)
topv = topv / topv.sum(-1, keepdim=True) # 重新归一化
out = torch.zeros_like(x)
for e in range(len(self.experts)):
sel = (topi == e) # 哪些 token 选了这个专家
if not sel.any():
continue
tok, slot = sel.nonzero(as_tuple=True)
# ⭐ 门控权重乘回去 —— 这是 router 唯一的梯度通路
out[tok] += topv[tok, slot].unsqueeze(-1).to(x.dtype) * self.experts[e](x[tok])
return out, probs
layer = MoEFFN(dim=64, hidden=176, n_expert=8, top_k=2)
h = torch.randn(10, 64) # 10 个 token
y, p = layer(h)
print(y.shape, p.shape) # (10, 64) (10, 8)
print("每个专家分到的 token 比例:", (p.argmax(-1).bincount(minlength=8) / 10).tolist())
⚠️ 上面这份是教学实现:用 for 循环遍历专家,真实框架会用 grouped GEMM 一次算完(见第六节)。
⚖️ 四、负载均衡:不加会怎样
这是 MoE 里最容易出事、也最容易被跳过的一节。
坍缩是怎么发生的
router 是学出来的,而它有一个正反馈回路:
① 初始化时,专家 3 恰好比别人稍微好那么一点点
② 于是更多 token 被路由到专家 3
③ 专家 3 拿到更多梯度,训练得更好
④ 于是【更多】token 被路由到专家 3
⑤ 回到 ②……
💀 最终:8 个专家里只有 2~3 个在干活,
剩下的从初始化之后【几乎没被更新过】
💀 这个失败是静默的
发生了什么:8 个专家里 6 个从来没被路由到过
为什么没被发现:loss 曲线【看起来完全正常】,一直在降 ——
因为那 2~3 个活跃专家确实在学东西
代价:你用 8 倍的显存、训练了一个约等于 2 个专家的模型;
推理时也要把 8 份权重全加载进显存
该补什么:⭐ 训练时把【每个专家分到的 token 比例】打到监控面板上,
理想是接近 1/E 的平坦直方图。这一条比看 loss 有用得多
(🔗 《AI基础设施》第 14 章从并行的角度讲了同一件事的另一面:负载不均衡时,90% 的 token 挤到一张卡上,整个集群被它拖垮。)
辅助损失(auxiliary loss)
标准解法是在主损失外面加一项,惩罚"不均匀":
$$\mathcal{L}_{\text{aux}} = \alpha \cdot E \cdot \sum_{i=1}^{E} f_i \cdot P_i$$
f_i= 实际路由到专家 i 的 token 比例(用 top-k 的硬计数算,不可导)P_i= router 分给专家 i 的平均 softmax 概率(可导)α通常取 0.01
为什么是这两个相乘,这是设计里最精巧的一处:
f_i 提供"实际有多不均"的【强度】—— 但它不可导,单用它没有梯度
P_i 提供【可导的通路】 —— 但它是软的,单用它反映不了真实拥挤程度
⭐ 相乘:某个专家实际超载(f_i 大)时,
惩罚会通过 P_i 这条可导的路,把它的 softmax 分数压下去
数学上,这个和在 f 和 P 都等于 1/E(完全均匀)时取到最小值。
⚠️ α 的取值是个真实的权衡:
| α | 后果 |
|---|---|
| 太小(如 0.001) | 压不住,还是会慢慢坍缩 |
| 太大(如 0.1) | 辅助损失开始干扰主任务——模型为了均匀而均匀,路由变得没有语义 💀 |
容量因子与 token drop
即使有辅助损失,也不能保证每一步都均匀。所以还要一道硬限制:
每个专家的容量 C = capacity_factor × (本 batch 的 token 数 / 专家数)
capacity_factor = 1.0 → 完全没有余量,一旦不均就大量丢弃
capacity_factor = 1.25 → 常见取值,留 25% 余量
⚠️ 超出容量的 token 会被【丢弃】(token drop):
它这一层跳过 FFN,直接走残差通路过去
→ 它不是报错,是【悄悄少算了一层】⭐
⚠️ 一个真实的坑:训练和推理的 capacity factor 常常设得不一样(推理时为了省显存调小),导致同一个输入在训练和推理下走的路径不同。排查"离线评测好、线上效果差"时,这是一个值得看一眼的地方。
Switch Transformer 的三个具体改动
| 改动 | 为什么 |
|---|---|
| top-1 路由 | 计算量直接减半,且论文证明效果不掉。此前的共识是 k≥2 才行 |
| 容量因子 + token drop | 让每个专家的计算量固定,才能高效地做静态形状的批处理 |
| ⭐ router 用 fp32 算 | bf16 下 softmax + exp 的数值抖动会让路由决策本身翻转——同样的输入,这一步选专家 3、下一步选专家 5。这是低精度训练里少数必须留 fp32 的地方之一 🔗 混合精度 |
DeepSeek-V3 的另一条路:无辅助损失
辅助损失有个绕不开的问题:它是一个和主任务无关的梯度,多少会干扰模型学正事。
DeepSeek-V3 的做法是把它拿掉,换成一个偏置项:
给每个专家一个可调的偏置 b_i:
用于 top-k 筛选的分数 = s_i + b_i ← b_i 只在这里出现 ⭐
用于加权求和的权重 = s_i ← b_i【不进入】这里
训练中根据实际负载动态调 b_i:
这个专家过载了 → 把 b_i 调小一点(让它更难被选中)
这个专家太闲了 → 把 b_i 调大一点
⭐ 关键:b_i 是【手动按规则调的,不参与反向传播】
→ 均衡这件事完全不产生梯度,一点都不干扰主任务
💡 这个设计的思路值得单独记一下:把一个「本来要用梯度解决的问题」改成「用一条控制规则解决」,从而彻底避开它对主目标的污染。这类技巧在工程里到处能用。
🛑 读到这里可以停 —— 机制部分讲完了(约 31 分钟),刚好到全章一半。 你已经拿到最核心的三块:为什么只换 FFN(内积必须在同一个空间里)、门控权重是 router 唯一的梯度通路、不加负载均衡会静默坍缩。 后半章还有:DeepSeek-MoE 的细粒度 + 共享专家 · MoE 的五个代价(尤其是显存那条)· 它和 EM/高斯混合的亲戚关系。 回来的时候不用重读,直接从下一节接着看就行。
🔬 五、DeepSeek-MoE 的两个创新
2024 年之后的 MoE 基本都跟了这两条。
① 细粒度专家切分
传统:16 个大专家,每个 token 选 2 个
细粒度:把每个专家切成 4 份 → 64 个小专家,每个 token 选 8 个
⭐ 激活的总参数量【完全不变】(8 个小的 = 2 个大的)
但可能的组合数爆炸了:
C(16, 2) = 120 种组合
C(64, 8) ≈ 44 亿 种组合 💥
为什么这有用:专家越大,它就越被迫成为一个"什么都会一点"的通才;切小之后,每个专家可以专攻一个更窄的模式,而组合的自由度补回了表达能力。
② 共享专家隔离
留 1~2 个【所有 token 都必过】的共享专家,不参与路由
要解决的问题:
语法、常见搭配这类【每个 token 都需要】的通用知识,
在纯路由 MoE 里【每个专家都得学一遍】 → 参数冗余 💀
⭐ 把通用知识集中到共享专家里,
路由专家就腾出容量去学各自专精的东西
所以现在主流 MoE 层的结构是:输出 = 共享专家(x) + Σ 路由选中的专家(x)。
⚠️ 另一个 2025-26 的趋势:门控从 softmax 转向 sigmoid。softmax 强制所有专家分数加起来等于 1(专家之间在抢一个固定的蛋糕),sigmoid 让每个专家独立打分。专家数量很多时后者更稳定。知道有这回事就行。
💸 六、代价(这一节别跳过)
MoE 常被说成「白赚参数量」,那是销售话术。真实的账是这样的:
⭐ 代价 1:显存按总参数算
这是最重要的一条,也是本地部署时最容易被坑的一条。
稀疏的是【计算】,不是【存储】
下一个 token 会路由到哪个专家?不知道
→ 所以【全部 8 个专家的权重都必须在显存里待命】
Mixtral 8×7B:
速度像一个 13B 模型 ⭐
显存像一个 47B 模型 💀 ← 别被"8×7B"这个名字骗了
🔗 第 7 章本地部署那张显存表里「200B+ MoE → 100GB+」这一行,说的就是这件事。
代价 2:通信
多卡训练时,专家分散在不同的卡上(专家并行 EP)。每一层都要做两次 All-to-All:把 token 送到它该去的卡,算完再收回来。
⚠️ All-to-All 不是 AllReduce —— 它的通信量随卡数增长得更凶
⚠️ 跨机时(走 IB 而不是 NVLink)这一步能成为整个训练的瓶颈
🔗 《AI基础设施》第 14 章有完整的 EP 配置讨论;第 5 章互联与集群拓扑解释了为什么"跨机"这两个字这么贵。
代价 3:批处理效率下降
稠密模型:一个 batch 的所有 token 走同一个 FFN
→ 一次大矩阵乘,GPU 吃满 ✅
MoE:一个 batch 的 token 被打散到 8 个专家
→ 变成 8 次【小】矩阵乘,每次都吃不满 GPU ⚠️
→ 必须用 grouped GEMM 这类专门的算子才能拉回来
🔗 《AI基础设施》第 7 章算子融合讲的正是这类问题。
代价 4 和 5:训练更不稳、微调更难
| 问题 | 说明 |
|---|---|
| 训练不稳 | 路由是离散选择,梯度路径断断续续;loss spike 比稠密模型更常见 🔗 训练稳定性 |
| 微调容易废 | 小数据上微调 MoE,很容易让路由退化成"总选同几个专家"。⚠️ 一个常见做法是冻住 router 只微调专家,或者干脆只做 LoRA 🔗 第 5 章微调与 LoRA |
💡 什么时候 MoE 不划算
❌ 显存紧张的本地部署 —— 你付了大模型的显存,只拿到小模型的速度收益
❌ 极低延迟的单请求场景 —— MoE 的吞吐优势需要 batch 起来才体现
✅ 高吞吐 + 显存充裕的服务端 —— 这才是 MoE 的主场
🧠 七、它和你学过的一个东西是亲戚
《ML 基础》第 14 章在 MoE 那一行链到了《数学原理》第 13 章 EM 与高斯混合——这条链是对的,但过去链过去发现没有 MoE 正文。现在补上了。
| 高斯混合模型(GMM) | MoE | |
|---|---|---|
| 隐变量 | 这个样本属于哪个高斯分量 | 这个 token 该去哪个专家 |
| 怎么分配 | 软分配:所有分量都参与,按后验概率加权 | 硬分配 + 软加权:只有 top-k 参与 |
| 怎么学 | EM 算法,交替更新 | 端到端反向传播,router 和专家一起学 ⭐ |
🔑 同一个思想的两种实现:「用一个隐变量决定该用哪个子模型」。 区别在于 GMM 用 EM 做全量软分配,MoE 用 top-k 硬选——而硬选正是它省算力的全部原因。
🔗 这一章连到哪里
| 去哪 | 为什么 |
|---|---|
| 02 · Transformer 原理 | 「注意力横向 / FFN 纵向」那个分工是本章第二节的全部依据。那一章把「MoE 只替换 FFN」当结论用,这一章给出理由 |
| 02b · 现代 LLM 架构的四处改动 | MoE 里的每个专家现在基本都是 SwiGLU FFN,参数量怎么算在那一章 |
| 02c · 为什么都是 Decoder-only | 架构选型的另一个维度。定了 decoder-only 之后,MoE 是横在 FFN 这一层上的第二个选择 |
| 05 · 微调与 LoRA | 微调 MoE 容易让路由退化,LoRA 是常见的规避方式 |
| 07 · 本地部署与开源生态 | 那张显存表里「200B+ MoE → 100GB+」的由来就是本章第六节 |
| 《AI基础设施》14 · 并行策略怎么组合 | 专家并行 EP 的完整工程视角:All-to-All 为什么贵、负载不均衡怎么拖垮整个集群 |
| 《AI基础设施》05 · 互联与集群拓扑 | 「跨机做 All-to-All 是杀手」这句话背后的带宽数字 |
| 《AI基础设施》06 · 混合精度 | 「router 必须用 fp32 算」属于低精度训练里少数必须留 fp32 的地方,那一章有完整清单 |
| 《AI基础设施》07 · 算子融合与编译 | MoE 把一次大矩阵乘拆成一堆小的,grouped GEMM 要解决的正是这个 |
| 《数学原理》13 · EM 与高斯混合 | 「隐变量决定用哪个子模型」的祖师爷。想搞清 MoE 的统计学出身,去那里 |
| 《ML 基础》14 · 通往 Transformer | 那张「Transformer 之后」的表里,MoE 一行说的就是本章 |
✅ 检查点
- 「总参数量」和「激活参数量」各决定了什么?Mixtral 8×7B 这两个数字分别是多少,为什么不是 56B?
- 为什么 MoE 只替换 FFN 不替换注意力?三条理由里哪条是决定性的?
- 路由的三步公式写出来。router 本身有多大?
- 为什么门控权重
g_i必须乘回专家输出上?不乘会怎样? - 专家坍缩是怎么发生的?为什么说这个失败是「静默」的?该怎么监控?
- 辅助损失里
f_i和P_i分别是什么?为什么要把它们相乘?α 太大太小分别有什么后果? - 容量因子是什么?token 超出容量会发生什么——是报错吗?
- Switch Transformer 为什么要求 router 用 fp32 算?
- DeepSeek-V3 的无辅助损失方案,那个偏置项加在哪、不加在哪?为什么这么设计?
- 细粒度专家切分「激活参数不变」,那它到底赚在哪?共享专家又解决了什么问题?
- MoE 的五个代价里,本地部署时最容易踩的是哪个?
👀 答案
- 总参数量决定显存占用,激活参数量决定计算量和生成速度。 Mixtral 8×7B:总参数 46.7B、激活约 12.9B。不是 56B 是因为注意力那部分是所有专家共享的,只有 FFN 被复制成了 8 份。
- ① FFN 是参数大头(约 2/3),稀疏化收益最大 ② FFN 本来就是逐 token 独立的,换成"每个 token 走不同变换"语义上完全成立 ③ ⭐决定性的是第三条:注意力要算 Q·K 内积,内积只有在同一个向量空间里才有意义——如果不同 token 走不同的注意力投影,它们的 Q 和 K 就在不相干的坐标系里,内积不代表任何东西。FFN 是「每人自己想」,注意力是「大家一起讨论」。
g = softmax(W_g·x)→T = top-k(g)→y = Σ_{i∈T} g_i · E_i(x)。router 就是一个 d×E 的小矩阵,小到可以忽略。- 因为 top-k 是离散选择、不可导。如果只把选中专家的输出直接相加,router 的参数对 loss 的影响路径完全断了,永远拿不到梯度,MoE 就退化成随机分派。乘上
g_i这个可导的标量后,梯度才能回到W_g。门控权重是 router 唯一的一条梯度通路。 - 正反馈:某专家初始稍好 → 更多 token 去 → 训得更好 → 更多 token 去 → 坍缩。最终 8 个里只有 2~3 个在干活。静默是因为 loss 曲线看起来完全正常(那几个活跃专家确实在学)。代价是用 8 倍显存训了个约等于 2 个专家的模型。监控方式:⭐把每个专家分到的 token 比例打到面板上,理想是接近 1/E 的平坦直方图。
f_i= 实际路由到专家 i 的 token 比例(硬计数,不可导);P_i= router 给专家 i 的平均 softmax 概率(可导)。相乘是因为:f_i 提供"有多不均"的强度但没有梯度,P_i 提供可导通路但反映不了真实拥挤程度——相乘后,实际超载的专家会通过 P_i 这条路被压低分数。α 通常 0.01:太小压不住会慢慢坍缩;太大会干扰主任务,模型为了均匀而均匀、路由失去语义。C = capacity_factor × (batch 的 token 数 / 专家数),常见取 1.25(留 25% 余量)。超出容量的 token 不报错,会被悄悄丢弃——这一层跳过 FFN 直接走残差过去,等于少算了一层。⚠️ 训练和推理的 capacity factor 设得不一样时,同一输入走的路径会不同。- 因为 bf16 下 softmax + exp 的数值抖动会让路由决策本身翻转——同样的输入,这一步选专家 3、下一步选专家 5。这是低精度训练里少数必须保留 fp32 的地方之一。
- 偏置
b_i只加在用于 top-k 筛选的分数上(s_i + b_i),不进入最终加权求和的权重(仍用 s_i)。训练中按负载手动按规则调(过载调小、太闲调大),不参与反向传播。这样均衡完全不产生梯度,一点都不干扰主任务——把「本要用梯度解决的问题」改成「用一条控制规则解决」。 - 赚在组合数:
C(16,2) = 120种 vsC(64,8) ≈ 44 亿种。专家越大越被迫当通才,切小之后每个能专攻更窄的模式,而组合自由度补回了表达能力。共享专家解决的是参数冗余:语法这类每个 token 都要的通用知识,在纯路由 MoE 里每个专家都得学一遍;把它集中到共享专家后,路由专家腾出容量学各自专精的东西。 - ⭐ 显存。稀疏的是计算不是存储——下一个 token 会去哪个专家不知道,所以全部专家的权重都得在显存里待命。Mixtral 8×7B 速度像 13B,显存像 47B。另外四个代价是:All-to-All 通信(尤其跨机)、批处理效率下降(大矩阵乘被拆成一堆小的,要 grouped GEMM)、训练更不稳、微调容易让路由退化。
🛑 可以停在这里
⚡ 走神救援
MoE 把参数量和计算量拆开:⭐总参数量决定显存,激活参数量决定速度。Mixtral 8×7B = 总参 46.7B / 激活 12.9B(不是 56B,因为注意力是所有专家共享的,只有 FFN 复制了 8 份);DeepSeek-V3 = 671B / 37B。为什么只换 FFN:①FFN 是参数大头 2/3 ②FFN 本来就逐 token 独立 ③⭐决定性的一条:注意力算 Q·K 内积,内积只在同一个向量空间里才有意义——不同 token 走不同投影矩阵的话内积毫无意义。FFN 是"每人自己想",注意力是"大家一起讨论"。路由三步:
g = softmax(W_g x)→top-k→y = Σ g_i·E_i(x),router 只是个 d×E 的小矩阵。⭐门控权重必须乘回去——top-k 是离散不可导的,g_i 是 router 唯一的梯度通路,不乘 router 永远学不会。k 的选择:Switch 证明了 k=1 就够,Mixtral 用 k=2,DeepSeek 是"几十上百个里选 6~8 个"。💀负载均衡必须有:router 有正反馈(某专家稍好→更多 token→训得更好→更多 token),最终 8 个里只剩 2~3 个干活;⭐这个失败是静默的——loss 曲线完全正常,你得把每个专家的 token 占比打到面板上看直方图平不平才发现。辅助损失L = α·E·Σ f_i·P_i:f_i 是实际占比(不可导,提供强度),P_i 是平均 softmax 概率(可导,提供梯度通路),相乘才两者兼得;α 通常 0.01,太小压不住、太大干扰主任务。容量因子常取 1.25,⚠️超容量的 token 不报错,是悄悄跳过 FFN 走残差——等于少算一层。⭐Switch 要求 router 用 fp32,因为 bf16 下 softmax 抖动会让路由决策本身翻转。DeepSeek-V3 无辅助损失:偏置 b_i 只加在 top-k 筛选的分数上、不进加权权重,按负载手动调、不参与反传 → 均衡完全不产生梯度。DeepSeek-MoE 两创新:细粒度切分(激活参数不变但组合数从 C(16,2)=120 爆到 C(64,8)≈44 亿)、共享专家(把语法这类通用知识集中,避免每个专家重复学)。⭐代价别忽略:显存按总参数算(下一个 token 去哪不知道,所有专家都得在显存待命,Mixtral 速度像 13B、显存像 47B)、All-to-All 通信跨机是杀手、批处理效率下降(大矩阵乘被拆成一堆小的,要 grouped GEMM)、训练更不稳、微调容易让路由退化。MoE 的主场是高吞吐+显存充裕的服务端,不是本地部署。它和 GMM 是亲戚——都是"隐变量决定用哪个子模型",区别是 GMM 用 EM 软分配全部分量,MoE 用 top-k 硬选,而硬选正是它省算力的全部原因。
下一节 👉 03-Tokenizer与上下文.md