📑 本页目录(点开跳转)
13 · 流水线并行
⏱ 26 分钟 | ⭐ 通信最省,但有个叫"气泡"的敌人
🎯 一句话
把模型按层切段,每张卡负责几层,数据像流水线一样流过去。 它的通信量比张量并行小两个数量级,所以它是唯一能舒服跨机的模型并行 —— 但它天生会浪费一部分算力,而这一章就是在和那个浪费搏斗。
🏭 一、基本结构
算一算
通信量对比(batch=8, seq=2048, hidden=8192):
张量并行:每层 4 次 AllReduce → 80 层共 86 GB/步
流水线并行:每个切分点传一次激活
3 个切分点 × 268MB × 2(前向+反向)≈ 1.6 GB/步
⭐ 差了 50 倍以上 —— 这就是它能跨机的原因
🫧 二、气泡:流水线的原罪
关键信息
解法:把 batch 切成 micro-batch
$$\text{气泡占比} = \frac{P-1}{M+P-1}$$
(P = 流水线级数,M = micro-batch 数)
算一算
P=4, M=4 → 气泡 3/7 = 43% 💀
P=4, M=16 → 气泡 3/19 = 16% ⚠️
P=4, M=64 → 气泡 3/67 = 4.5% ✅
P=8, M=64 → 气泡 7/71 = 10%
🔑 黄金法则:M ≥ 4P,最好 M ≥ 8P。 但 micro-batch 越多,需要缓存的激活也越多 —— 这就是下面 1F1B 要解决的。
⚡ 三、1F1B:现代标准调度
结果对照
💡 注意 GPU3(最后一级)几乎没有等待 —— 它的前向一做完就能立刻反向。 而 GPU0 要等最久。这种不均衡是流水线的固有特征。
⭐ 交错式 1F1B(Interleaved)
关键信息
⚖️ 四、切分策略:负载均衡是关键
关键信息
- ⚠️ 流水线的速度取决于【最慢的那一级】
- 常见的不均衡来源:
- 第一级要算 Embedding(词表很大)
- 最后一级要算 LM Head + 损失(词表 × hidden,很重)⭐
- 有些层结构不同(比如 MoE 层)
因果链
🔑 实践建议:先跑一次 profiling,测出每层的实际耗时,再切。 均匀切层数几乎总是次优的。
🔨 五、代码
# 🧩 骨架:`model` 来自你自己的代码,这一段只看写法
# PyTorch 原生(2.x)
from torch.distributed.pipelining import pipeline, ScheduleGPipe, Schedule1F1B
pipe = pipeline(module=model, mb_args=(example_input,),
split_spec={"layers.20": SplitPoint.BEGINNING,
"layers.40": SplitPoint.BEGINNING,
"layers.60": SplitPoint.BEGINNING})
stage = pipe.build_stage(rank, device)
sched = Schedule1F1B(stage, n_microbatches=64) # ⭐ M ≥ 4P
for batch in loader:
if rank == 0: sched.step(batch)
elif rank == last: losses = sched.step(target=labels)
else: sched.step()
💡 DeepSpeed 和 Megatron 也都提供流水线 —— Megatron 的
--num-layers-per-virtual-pipeline-stage就是交错式 1F1B。
⚠️ 六、四个坑
| 坑 | 说明 |
|---|---|
| micro-batch 太少 | 气泡吃掉一半算力 → M ≥ 4P ⭐ |
| 切分点不均衡 | 被最慢一级拖住 → 按实测耗时切 ⭐ |
| BatchNorm | 跨 micro-batch 的统计会错 → 用 LayerNorm |
| 调试困难 | 报错栈横跨多个进程 |
💥 一个容易忽略的点:流水线并行改变了梯度累积的语义。 M 个 micro-batch 的梯度是累加的,所以等效 batch = micro_batch × M, 学习率要按这个来调,而不是按 micro_batch。
📊 七、三种并行的对比(重要)
| 数据并行 | 张量并行 | 流水线并行 | |
|---|---|---|---|
| 切什么 | 数据 | 层内的矩阵 | 层(按深度) |
| 通信频率 | 每步一次 | 每层 4 次 | 每个切分点一次 |
| 通信量 | 2× 参数量 | 很大 | 很小 ⭐ |
| 能跨机 | ✅ | ❌ | ✅ ⭐ |
| 省参数显存 | ❌ | ✅ | ✅ |
| 主要缺点 | 不省显存 | 必须机内 | 气泡 |
| 单层装不下 | ❌ | ✅ | ❌ |
⭐ 三者互补,不是三选一 —— 第 14 章讲怎么组合。
🔗 和站内其他章的关系
| 相关的地方 | 这里的位置 |
|---|---|
| 第 5 章 跨机带宽小 | 流水线能跨机的原因 ⭐ |
| 第 12 章 TP 通信 86GB/步 | 对比:PP 只要 1.6GB |
| 第 9 章 梯度累积 | micro-batch 就是它 |
| 第 14 章 3D 并行 | PP 放在跨机维度 |
| 《Kaggle竞赛方法论》02 训练策略与正则化 梯度累加 | 那里它只是"省显存的招";在流水线里它是气泡公式的 M,直接决定效率 ⭐ |
| 《大模型全景导论》主线 2 · 模型怎样看懂一句话 一层长什么样 | 切分点必须落在层边界上;embedding / lm_head 比中间层重得多,是负载不均的主因 |
✅ 检查点
- 流水线并行怎么切?为什么它的通信量这么小?
- 什么是气泡?朴素做法下 4 卡的效率如何?
- 气泡占比的公式是什么?P=4、M=4 和 M=64 分别是多少?
- 黄金法则是什么?为什么不能无限增大 M?
- 1F1B 相比 GPipe 改进了什么?气泡率变了吗?
- 交错式 1F1B 的思路和代价是什么?
- 为什么均匀切层数几乎总是次优的?
- 流水线并行怎么改变了学习率的调法?
- 三种并行里,哪些能跨机?哪个能救"单层装不下"?
👀 答案
- 按层切段,每卡负责几层,数据像流水线流过。通信量小是因为卡之间只在切分点传递激活——3 个切分点约 1.6GB/步,而张量并行是 86GB/步,差 50 倍以上。
- 气泡是流水线填充和排空阶段的空闲。朴素做法下任何时刻只有一张卡在干活,4 卡的效率还不如 1 卡。
- (P−1)/(M+P−1)。P=4,M=4 → 3/7 = 43%;P=4,M=64 → 3/67 = 4.5%。
- M ≥ 4P(最好 8P)。不能无限增大是因为 micro-batch 越多需要缓存的激活越多(GPipe 下是 O(M))。
- 进入稳定态后每做一次前向立刻做一次反向,反向完就释放该 micro-batch 的激活 → 激活显存从 O(M) 降到 O(P),而气泡率完全不变,纯赚。
- 让每卡负责不连续的多段层,虚拟级数变成 P×v,气泡降到 (P−1)/(v·M+P−1),再降 v 倍。代价:通信次数变成 v 倍。
- 因为流水线的速度取决于最慢的一级,而第一级有 Embedding、最后一级有 LM Head(词表×hidden,很重)。均匀切会让最后一级拖慢所有卡。应该先 profiling 测每层实际耗时再切。
- M 个 micro-batch 的梯度是累加的,所以等效 batch = micro_batch × M,学习率要按等效 batch 调,不是按 micro_batch。
- 数据并行和流水线并行能跨机,张量并行不能。只有张量并行能救"单层装不下"。
🛑 可以停在这里
⚡ 走神救援
先记住这几件事
- 流水线并行按层分段,只有切分点需要交换相邻阶段的信息。
- micro-batch 让不同阶段交错工作,但填充和排空仍会留下气泡。
- GPipe 与 1F1B 的关键差异包括激活何时释放;按时间图比较,不只数前反向次数。
- 按实测耗时平衡各阶段,同时核对全局 batch 与调度要求。
下一节 👉 14-并行策略怎么组合.md ⭐