从0训练LLM · 现代大模型体系 岔路 阅读 + 实验 ≈ 30 分钟 页内实验 ×2
C10 并行:一张卡不够怎么办 岔路课

MoE 的几百个专家放在哪?token 怎么送过去?

专家并行与 all-to-all
MoE 把 FFN 换成几百个专家,每个 token 只选几个。专家太多,复制到每张卡放不下;专家并行把它们分到不同卡上,用两次 all-to-all 把 token 送去、再收回来。跨机的带宽只有机内的约 1/3,DeepSeek-V3 让每个 token 最多去 4 个节点,再用 DualPipe 把通信藏到另一批计算后面。
🔀

这是一堂岔路课。第 4 课的 DualPipe 说它要把「专家并行的 all-to-all」藏起来。这个 all-to-all 从哪来?MoE 本身(为什么这样设计、专家怎么不抢活)是 C11 的主题;这一课只回答一个系统问题。

本课一问:MoE 有几百个专家,放在哪?每个 token 怎么送到它选中的专家那里?
这一课先不碰:路由为什么是 top-k、负载均衡怎么做(C11);MoE 省了什么算力(C11)。

从已有出发

从已有出发 · 训练一步摊到多张卡 已学本课还没学
取一批
前向 ZeROTPPPCPEP
loss
反向
梯度同步 DP
优化器更新
每课多切一维:
数字口径:构建时由锚点 Qwen3.8-27B 与 DeepSeek-V3 的官方 config 按公式现算;卡数按 8 张计。本章没有我们自己的多卡实测。

只需要 MoE 的一个事实(C11 会从头讲):MoE 把每层的一个大 FFN 换成很多个小 FFN(专家),每个 token 由一个路由器挑出其中几个来算。DeepSeek-V3 的官方 config:61 层里前 3 层是普通 FFN,其余 58 层每层 256 个路由专家(另有 1 个共享专家),每个 token 选 8 个;每个专家是 3 × 7,168 × 2,048 = 44,040,192 个参数。

这一课的组件用合成的路由打分(没有真实模型的路由数据),贯穿例句不出场。

先自己设计:每张卡放一份全部专家

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案把全部专家复制到每一张卡上
DeepSeek-V3 · 路由专家数
每个专家(gate、up、down 三个矩阵)3 × 7,168 × 2,048 = 44,040,192
每层 256 个11.27B
58 个 MoE 层653.9B,BF16 权重 1.31 TB

数字口径:frontier-models.json 的 DeepSeek-V3 官方 config 现算。

新的失败:token 要跨机

all-to-all 的麻烦在于跨机。每个 token 选 8 个专家,它们散在 8 个节点上;跨节点走 IB,只有节点内 NVLink 的约 1/3(DeepSeek-V3 报告:160 GB/s 对 50 GB/s)。报告写得很直白:跨节点专家并行让计算与通信的比例接近 1 : 1 ——算多久,就要传多久。

DeepSeek-V3 的改进是 node-limited routing:每个 token 最多发往 M = 4 个节点。节点按「这个节点上最高的 K/M = 2 个亲和分数之和」挑,再在选中的节点里选 8 个专家 。下面用它的真实布局和路由规则,在合成打分上跑一遍:

每个 token 的 8 个专家分布在几个节点上(token 数)
64 张卡各收到多少份 token(8 节点 × 8 卡,颜色越深越多)
🎯

试三件事:① 在「直接选前 8」下读直方图:8 个专家大多落在 5、6 个节点上(红柱是超过 4 个节点的 token);② 切到「先选 4 个节点」,最多 4 个节点,跨节点副本明显变少,每个节点平均分到约 2 个专家——报告里的 3.2 不是这个平均数,而是「每个节点最多选这么多也不增加 NVLink 开销」的余量(见下一段);③ 把「热门专家」调到 +2:0 号节点那张卡收到的 token 是平均的十几倍,all-to-all 要等它——这就是 C11 负载均衡要解决的问题。

每个跨节点副本到了目标节点,再由 NVLink 转发给具体持有专家的那张卡;报告说这样 IB 与 NVLink 的传输可以完全重叠,每个 token 平均在每个节点选 3.2 个专家也不增加 NVLink 的开销——换句话说,同样的通信成本最多能支持 4 × 3.2 ≈ 13 个专家。发出去的激活先量化成 FP8,收回来的保留 BF16;整套通信只占 20 个 SM 。

最后一招:把 all-to-all 藏到另一批计算后面

限了节点,通信还是很重。DeepSeek-V3 的 DualPipe(第 4 课)把一个前向块和一个反向块配成一对:把每个块拆成注意力、dispatch、MLP、combine 几段,让这一个块的 all-to-all 和另一个块的计算同时进行,报告称 all-to-all 与流水线通信都可以「完全隐藏」 。这和第 1 课「分桶把梯度同步藏到反向后面」是同一种思路,只是这次藏的是 token。

⚠

常见误解之一:「专家并行和数据并行只能二选一。」

DeepSeek-V3 同时用 64 路专家并行、16 路流水线并行和 ZeRO-1 数据并行:专家按 EP 切,其余部分(注意力、共享专家)照常做数据并行。实验课会看到几维怎么叠。

常见误解之二:「MoE 省算力,所以也省显存和通信。」

MoE 省的是每个 token 的计算(只算 8 个专家);全部 256 个专家的参数都要放下,token 还要跨卡跑两趟。显存靠专家并行摊开,通信靠限节点和重叠去藏。

常见误解之三:「限制每个 token 最多去 4 个节点,一定会让模型变差。」

报告没有给出这项限制对质量的消融,这里只能说「不知道」;它给出的是工程上的动机(IB 只有 NVLink 的约 1/3)和「训练中不丢任何 token」的结果。

证据三问

问本课的回答
① 它解决什么问题?MoE 的专家太多,每张卡放不下全部;把专家分到各卡,token 发过去算
② 数学上为什么可能有效?每个专家只处理分给它的 token,专家之间互不相干,所以可以放在不同的卡上;通信量由路由决定,限节点直接截断了跨机副本数的上限
③ 实验上为什么最后选了它?GShard 首先在 600B 规模上用它;DeepSeek-V3 用 64 路 EP 加限节点与 DualPipe 训完了 671B 的模型,报告称训练中没有丢 token

演进地图:你在这里

并行的演进:每切一维,都是被上一代的某个真数字逼出来的
  1. 起点 一张卡 C9 的结论
    解决了训练一步的全部东西都在一张卡上,不需要任何通信。
    卡在锚点的模型状态 16 字节 × 27,781,427,952 = 444.5 GB,是 80 GB 的 5.6 倍;就算放得下也太慢:DeepSeek-V3 预训练用了 2,664K H800 GPU 小时,一张卡约 304 年。
  2. 第一代 数据并行 + ring all-reduce ring 来自 HPC(2009),2017–2018 进入深度学习
    解决了每卡一份完整模型、算 1/N 的数据,每步把梯度加起来。ring 让每卡只收发 2(N−1)/N 倍梯度:锚点 8 卡 97.2 GB、64 卡 109.4 GB(汇总到 0 号卡的朴素做法:388.9 GB → 3,500 GB)。
    卡在只解决「慢」:每张卡仍要放下完整的 444.5 GB。
  3. 第一代 · 工程 分桶,边反向边同步 PyTorch DDP(Li et al. 2020)
    解决了反向逐层产出梯度,攒满一桶(默认 25 MiB)就先发,通信藏到后面几层的反向里。
    卡在DP 的通信量每步固定:锚点 8 卡每卡 97.2 GB,按 IB 50 GB/s 要 1.94 s;每卡 4,096 个 token 的反向只有约 1.05 s(推演)——token 太少就藏不住。
  4. 第二代 ZeRO-1 / 2 / 3 · FSDP Rajbhandari 2019;PyTorch FSDP 2023
    解决了不再每卡存一份:8 卡上 444.5 → 152.8 → 104.2 → 55.6 GB(依次切优化器状态、梯度、参数)。
    卡在切参数后每步多一次 all-gather,通信是 DP 的 1.5 倍;激活不切:s = 4,096 的选择性重计算要 45.6 GB,放不进剩下的 24.4 GB。
  5. 第三代 张量并行(层内切矩阵) Megatron-LM(Shoeybi 2019)
    解决了第一个矩阵按列切、第二个按行切,一个 MLP 只要一次 all-reduce;t = 8 配序列并行,激活 45.6 → 5.7 GB。
    卡在每层前向 2 次、反向 2 次同步:每 4,096 个 token 每卡要发 18.8 GB,每层都要等——只能放在机内。ZeRO 原文实测:跨两台 DGX-2,每卡约 5 TFLOPS,不到峰值 5%。
  6. 解决了按层切,跨机只传段与段之间的激活;切 micro-batch 填满流水线;1F1B 把在途激活从 m 份压到 p 份;interleaved 把气泡除以 v。
    卡在首尾必然空等:p = 8、m = 32 时气泡 (p−1)/m = 21.9%(占总时间 17.9%),interleaved v = 4 也还有 5.5%。
  7. 2023–2024 Zero Bubble · DualPipe Qi 2023;DeepSeek-V3 2024
    解决了反向拆成「对输入」B 与「对权重」W,W 拿去填空泡(F = B = W 时 ZB-H1 的气泡是 1F1B 的 1/3);DualPipe 从两头喂 micro-batch,并把 all-to-all 藏到另一批计算后面。
    卡在DualPipe 要存两份参数;调度更复杂;通信只是被藏住了,量一点没少。
  8. 岔路 · 长序列 序列并行 / 上下文并行 2021–2023:SP、Ring Attention、Ulysses
    解决了128K 的序列按序列切给多张卡;zig-zag 头尾配对,把因果负载 10 : 26 : 42 : 58 配平成每卡一样多。
    卡在每个全注意力层都要在卡之间传 K、V:锚点 s = 131,072、8 卡时每层 0.5 GB;Ulysses 按头切,KV 只有 4 个头。
  9. 岔路 · MoE 专家并行 + all-to-all GShard 2020 → DeepSeek-V3 2024 你在这里
    解决了DeepSeek-V3 的 654B 路由专家参数分到 64 张卡,每卡每层 4 个;每个 token 最多去 4 个节点。
    卡在跨节点 IB 50 GB/s 只有 NVLink 160 GB/s 的约 1/3,跨节点 EP 的计算与通信约 1 : 1;热门专家所在的卡成为掉队者(→ C11)。
  10. 2021 起 组合:3D / 4D / 5D 并行 Narayanan 2021;Llama 3;DeepSeek-V3
    解决了Llama 3 405B:[TP, CP, PP, DP] = 8 × 1 × 16 × 128,16,384 张 H100,BF16 MFU 41%;DeepSeek-V3:PP 16 × EP 64 × ZeRO-1 DP,不用 TP。
    卡在没有万能配置:每种组合都要先算清每卡显存、每一维的通信走哪条链路、气泡多大(实验课)。
数字口径:锚点与 DeepSeek-V3 的数字由 anchor-model.json / frontier-models.json 的官方 config 在构建时按公式现算(GB = 10⁹ 字节,卡按 80 GB 计);「分桶」一行的时间是推演(有效算力 400 TFLOPS、IB 50 GB/s);5 TFLOPS 出自 ZeRO 原文 §1;GPU 小时、并行配置、带宽出自 DeepSeek-V3 报告 §1、§3.1–3.2(本地 PDF);Llama 3 的配置出自其报告表 4。本章没有我们自己的多卡实测。

一句话

📌

专家并行把专家分到不同卡,用两次 all-to-all 把 token 送去再收回;跨节点带宽只有节点内的约 1/3,所以 DeepSeek-V3 让每个 token 最多去 4 个节点,再用 DualPipe 把通信藏到另一批计算后面。

回到主线

实验课:几种切法怎么组合成一套方案? → 实验课。MoE 本身——为什么用它、专家为什么会抢活、专家该大还是小——在 C11。

你可能会问