大模型
MoE 混合专家架构详解:从路由机制到 DeepSeek 的工程创新
从"全员上阵"到"按需调度"
传统 Dense 模型(如 LLaMA、GPT-3)有一个硬约束:每个 token 都要经过所有参数的计算。参数量越大,推理越慢,显存占用越高——两者被死死绑在一起。
MoE(Mixture of Experts,混合专家)打破了这个绑定。它的核心思路很朴素:与其让所有参数都参与每次计算,不如准备一大批"专家",每次只调用其中几个。总参数量可以做得很大,但单次推理的激活参数量保持可控。
以 Mixtral 8x7B 为例:总参数量约 46.7B,但每个 token 只激活约 12.9B——大模型的容量,小模型的推理成本。
MoE 层的结构:门控路由 + 专家网络
在 Transformer 架构中,MoE 替换的是 FFN(前馈神经网络)层。原来一个 FFN 干的活,现在交给一组专家 FFN 加一个门控网络(Router/Gate)来分工。
门控网络本质是一个小的线性层,输入是当前 token 的隐状态,输出是每个专家的得分:
# MoE 门控路由(简化版)
# x: (batch, seq_len, d_model)
# W_gate: (d_model, num_experts)
scores = x @ W_gate # (batch, seq_len, num_experts)
weights = softmax(scores, dim=-1) # 每个专家的权重
# Top-K 选择:只保留得分最高的 K 个专家
topk_weights, topk_idx = weights.topk(K, dim=-1)
# 对选中的 K 个专家重新归一化
topk_weights = topk_weights / topk_weights.sum(dim=-1, keepdim=True)
# 只计算被选中的专家,其余跳过
output = sum(topk_weights[i] * experts[topk_idx[i]](x) for i in range(K))
关键在于 Top-K 选择:K 通常取 2(如 Mixtral 8x7B、DeepSeek-V3),意味着每个 token 只经过 2 个专家的计算。这个 K 值是 MoE 稀疏性的核心调节器——K 越小越省算力,但可能丢失信息;K 越大效果越好,但逐渐逼近 Dense 模型的成本。
路由策略:不止 Top-K 一种
Top-K 是最常见的路由方式,但不是唯一的。三种主流策略各有取舍:
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Top-K Routing | Token 选专家,取得分最高的 K 个 | 简单直接,效果稳定 | 易出现热门专家过载 |
| Expert Choice | 反过来:专家选 token,每个专家挑固定数量的 token | 负载天然均衡 | 破坏因果性(看到未来 token),训练可用但推理需特殊处理 |
| Hash Routing | 用哈希函数固定分配,不学习路由 | 零路由开销,确定性 | 效果较差,专家难以特化 |
目前主流大模型(Mixtral、DeepSeek、Qwen-MoE)基本都采用 Top-K 路由。简单、可控、不破坏自回归特性——工程上最好用。
负载均衡:别让专家"摸鱼"
Top-K 路由有一个经典问题:马太效应。少数专家被频繁选中,其他专家长期闲置。结果是部分专家过载、部分浪费容量,训练效率下降。
解决思路分两派:
辅助损失派(GShard、Switch Transformer):在训练损失中加一个均衡项,惩罚专家被选次数的不均匀分布。缺点是辅助损失需要调超参,且可能干扰主任务优化。
无辅助损失派(DeepSeek-V2/V3):不修改损失函数,而是在路由时给每个专家的得分动态加一个 bias 偏置。某个专家被选太多了,就降低它的 bias;太少了就提高。bias 只影响路由选择,不参与梯度回传,因此不干扰训练。
# DeepSeek 无辅助损失负载均衡(简化)
# 维护每个专家的 bias,根据负载动态调整
if expert_load[e] > avg_load:
bias[e] -= update_rate # 降低该专家的被选概率
else:
bias[e] += update_rate
# 路由时:得分 = 原始得分 + bias
adjusted_scores = scores + bias
topk_idx = adjusted_scores.topk(K)
这种方法更轻量,不引入额外损失项,也不需要调超参。DeepSeek-V3 的实践证明它在大规模训练中足够稳定。
DeepSeek 的两板斧:共享专家 + 细粒度分割
DeepSeek-MoE 在标准 MoE 上做了两个关键改进,后来被业界广泛借鉴:
共享专家(Shared Expert):从专家池中划出一部分作为"常驻专家",每个 token 必定经过它们。共享专家负责处理通用知识(语法、逻辑推理),路由专家则专注于特定领域。这样避免了通用能力在每个路由专家中重复学习,减少了参数浪费。
细粒度专家分割(Fine-grained):传统 MoE 用少量大专家(如 8 个),DeepSeek 改用更多小专家(如 64 个路由专家),每个 token 仍只选 6-8 个。专家更小、更多,组合空间呈指数增长,路由的灵活性大幅提升。
# DeepSeek-MoE 结构对比
# 传统 MoE(如 Mixtral 8x7B)
experts = [FFN_large] * 8
activated = top_k(x, k=2) # 2/8 = 25% 激活
# DeepSeek-MoE(简化)
shared_experts = [FFN_medium] * 2 # 常驻,必定激活
route_experts = [FFN_small] * 64 # 路由,选 6 个
activated = shared(x) + top_k(x, k=6) # 8/66 ≈ 12% 激活
更低的激活比例意味着同样的推理成本下可以塞进更多总参数——这正是 DeepSeek-V3 做到 671B 总参数、37B 激活参数的架构基础。
工程落地:显存、通信与推理
MoE 在推理部署上有三个绕不开的工程挑战:
显存墙:虽然每个 token 只激活少量专家,但所有专家的参数都要常驻显存。一个 671B 的 MoE 模型,即使只激活 37B,也需要约 1.3TB 显存来加载全部权重(FP16)。单卡装不下,必须多卡并行。
通信开销:多卡部署时,专家分布在不同的 GPU 上。一个 token 被路由到其他卡上的专家,就需要一次跨卡通信(All-to-All)。如果 batch 太小,通信时间可能超过计算时间,MoE 的推理效率反而不如 Dense 模型。
批处理策略:为了摊薄通信开销,MoE 推理需要凑够足够大的 batch。在实际服务中,通常会做 expert-aware batching——把路由到同一专家的 token 尽量凑在一起批量计算,减少碎片化。
# MoE 推理时的 token 分发(简化)
# 假设 4 张卡,每张卡上放 2 个专家
for token in sequence:
expert_ids = router(token, top_k=2)
for eid in expert_ids:
gpu_id = expert_to_gpu[eid]
send(token, gpu_id) # 跨卡通信
# 理想情况:大批次 + 同专家 token 聚集
# 现实:小批次 → 通信占比高 → 吞吐量骤降
这也是为什么 MoE 模型在 API 服务端(高并发、大 batch)表现优异,但在本地单卡推理(batch=1)时优势不明显甚至更慢。
MoE 的本质是用"选择性计算"换取"参数容量"——它不让模型变聪明,而是让大模型在有限算力下变得可行。路由器是调度员,专家是流水线上的工人,而负载均衡是让每个工人都别闲着也别累死的管理艺术。