v6:长序列和 MoE:Context Parallel 与 Expert Parallel
v5 用 TP + PP 把大模型放下了,还有两件事没管。视频很容易到几万 token,activation 跟着 s 涨。想多要参数又不多花 compute 就得用 MoE,expert 一多,每张卡各放一份就放不下。这一课加两维,CP 沿 sequence 切,EP 把 expert 分到不同的卡上。
问题:sequence 一长,activation 先爆
还是 Qwen3-VL-8B 的 LLM 部分(h = 4096、36 层、b = 1),按 Korthikanti 公式粗估:
- activation 随 s 线性涨:开 FlashAttention 后每层约 34·s·h bytes。s = 8K 时每层 1.14 GB,36 层 41 GB;s = 128K 时 ×16,约 657 GB
- attention 随 s² 涨:FlashAttention 不存 s × s 的 score(否则 8K 时每层 10.7 GB),但 attention 的 compute 照样随 s² 涨
- 前几课的办法不够:TP + SP 最多切 8 份,PP 的第一个 stage 仍存 L 层的量(第 6 课)。要再切,只能沿 sequence 切
CP:每张卡只管一段 token
Context Parallel(CP)把所有 activation 沿 sequence 切成 cp 份。LayerNorm、MLP 逐 token 算,只有 attention 需要看别人的 token。
- 效果:128K、cp = 16 → 每卡 8K token,activation 回到约 41 GB
- Ring attention:每张卡的 Q 块不动,K、V 块沿着环传。每一步用自己的 Q 和手上那块 KV 算一次,同时把它传给下一张卡,像旋转寿司,转 cp 步就尝过所有盘子。每块的局部输出按 log-sum-exp(LSE)加权相加,结果和不切时完全一样,就是 FlashAttention 的 online softmax
- 传输藏得住:一块 8K token 的 K、V 是 32 MiB,走 IB 约 0.67 ms,而这块 attention 的计算约 2.7 ms(按 400 TFLOPS)。边算边传,通信几乎不露出来
- Causal load balancing:causal mask 下越靠后的 token 要看的 KV 越多,cp = 4 按顺序切时最慢的卡是平均的 1.75 倍。改成切 2·cp 块、一头一尾配对,每卡计算量就相同
另一种做法 Ulysses,以及 CP 和 SP 的区别
DeepSpeed-Ulysses 不让 KV 绕环,先对 Q、K、V 做一次 all-to-all,把“按 sequence 切”换成“按 head 切”,每卡拿到完整序列、a/cp 个 head,本地算普通 attention,再 all-to-all 换回来,所以 cp 不能超过 head 数。Qwen3-8B 有 32 个 query head,但只有 8 个 KV head,K、V 要切成 8 份以上就得复制 KV head。
| SP(第 5 课) | CP(ring) | Ulysses | |
|---|---|---|---|
| 切什么 | 只切 TP 没切的 LayerNorm、dropout 的 activation | 全部 activation | 同 CP |
| 和 TP 的关系 | 依附 TP,份数是 t | 独立的一维,可叠加 TP + SP | 同 CP |
| 通信 | 和 TP 一样多 | 能和计算 overlap | 要等 all-to-all 完才能算 |
| 上限 | t ≤ 8 | 每块要够长,算得比传得久 | cp ≤ head 数 |
Megatron 用 --cp-comm-type 选:p2p(ring,默认)、a2a(Ulysses 式)、a2a+p2p(机内 a2a,机间 p2p)。
你的经验视频 VLM 的长序列正是 CP 要解决的场景。去查一下:你 TikTok 任务里最长的样本有多少 token?当时靠什么放下的(截断、少抽帧、gradient checkpointing)?verl 的 FSDP 后端自带 Ulysses(ulysses_sequence_parallel_size),看看 OpenManus-RL 的配置里开没开,这是你的项目接到 CP 的入口。
MoE:参数很多,每个 token 只算一点
- dense FFN:每个 token 都过完整的 FFN,Qwen3-8B 一层(SwiGLU)是 3 × 4096 × 12288 ≈ 151M 参数
- MoE:把 FFN 换成 E 个 expert FFN,再加一个 router,每个 token 只走分数最高的 k 个 expert,像医院分诊,每个病人只去最相关的科室
- 例:E = 8、k = 2,每个 expert 和原来的 FFN 一样大。这一层参数 ×8(约 1.21B),每个 token 的 FFN 计算只 ×2
NVIDIA 原题:Implement MoE and show the dimensions
设一个 micro-batch 有 T = 4096 个 token,h = 4096,E = 8,k = 2,capacity factor cf = 1.25:
| 步骤 | 做什么 | shape |
|---|---|---|
| 输入 | 把 batch 和 sequence 两维拍平 | x:[T, h] = [4096, 4096] |
| router | x 乘 Wr([h, E]),再 softmax | [T, E] = [4096, 8] |
| top-k | 每个 token 取最大的 k 个 expert 和 gate 值 | [T, k] = [4096, 2] |
| dispatch | 按 expert 分组,每组最多 C 个 token,不满的 pad | [E, C, h] = [8, 1280, 4096] |
| expert | 每个 expert 跑自己的 FFN:h → 12288 → h | [E, C, h] |
| combine | 放回原位置,k 份输出按 gate 加权求和 | y:[T, h] = [4096, 4096] |
- Capacity:\(C = \text{cf}\cdot kT/E = 1.25 \times 2 \times 4096 / 8 = 1280\),像每个科室限号,超出的 token 被 drop。Megatron 默认不设 capacity,也就是 dropless
- Load balancing loss:router 稍微偏爱某个 expert,它就被训练得更好、更被偏爱,最后少数几个包揽一切,这叫 expert collapse。Switch Transformer 加一项 \(\alpha E\sum_i f_i P_i\)(\(f_i\) 是分到 expert i 的 token 比例,\(P_i\) 是 router 给 i 的平均概率),分得均匀时最小。DeepSeek-V3 的 aux-loss-free 改成给每个 expert 加一个只在选 top-k 时用的 bias,不经过 loss
EP:expert 分到不同的卡上,用 all-to-all 送 token
- Expert Parallel(EP):EP = 8 时,8 张卡各放 1 个 expert,attention 部分照常每卡一份。expert 的参数和 optimizer state 都缩成 1/8
- dispatch 和 combine:每张卡的 token 要去的 expert 散在所有卡上,所以 dispatch 用 all-to-all(第 3 课)把 token 发过去,expert 算完,combine 再反向 all-to-all 发回原卡
- all-to-all 常是瓶颈:每卡 4096 个 token、k = 2 时一次 dispatch 共 64 MiB,其中 7/8 发往别的卡,走 IB 约 1.2 ms。forward 一层 dispatch、combine 两次约 2.3 ms,而这层 expert 的计算约 6.2 ms(400 TFLOPS)。热门 expert 所在的卡还会变成 straggler
- 办法:EP 尽量留在机内走 NVLink(64 MiB 约 0.15 ms),用 grouped GEMM 把 E 个小 GEMM 合成一个,all-to-all 再和另一个 micro-batch 的计算 overlap(DeepEP、DualPipe)
30 秒英文回答
Long sequences blow up activation memory, so context parallelism shards every activation along the sequence across cp GPUs. Only attention needs other GPUs' tokens: each GPU keeps its query block and passes KV blocks around a ring, merging partial results with online softmax while the transfer hides behind compute. MoE replaces the FFN with E experts and a router that picks the top-k per token, so parameters grow with E but compute only with k. Expert parallelism puts experts on different GPUs; dispatch and combine are all-to-alls, often the bottleneck. A load-balancing loss prevents expert collapse.
自测
MoE 不加 load balancing loss 会怎样?
少数 expert 包揽一切(expert collapse),其余参数白占。有 capacity 时热门 expert 大量 drop token,EP 下它所在的卡成为 straggler。
CP 和 SP 有什么区别?
SP 依附 TP,只切 LayerNorm、dropout 的 activation,进 attention 前要 all-gather 回完整序列。CP 是独立的一维,切全部 activation,attention 靠 KV 绕环完成。两者可以同时开。
EP 为什么用 all-to-all,不用 all-gather 或 all-reduce?
每张卡发给每张卡的 token 不同,去向由 router 决定。all-reduce 是同形状求和,语义不对。all-gather 会把每个 token 发给所有卡,大部分用不上。
某个 expert 收到的 token 超过 capacity 会怎样?
超出的 token 在这个 expert 上被 drop,只经 residual 进下一层(k = 2 时另一个 expert 可能还在算它)。没收满的位置 pad。cf 调大能少 drop,但多算多传。
写出 MoE forward 每一步的 shape。(NVIDIA SWE 原题)
就是上面的表,[T, h] → [T, E] → [T, k] → [E, C, h] → [T, h]。延伸(自拟):launch overhead 大时,先按 expert 给 token 排序,用一个 grouped GEMM 算完。
想看细一点 6.7 Context Parallel(ring 和 Ulysses 的通信公式)· 6.8 MoE 与 Expert Parallel(NumPy 手撕代码、Mixtral 参数账)· 6.3 Sequence Parallel · 6.25 FlashAttention(online softmax)