🏠 总目录📚 本教程 流水线并行 ← →
📑 本页目录(点开跳转)

13 · 流水线并行

⏱ 26 分钟 | ⭐ 通信最省,但有个叫"气泡"的敌人


🎯 一句话

把模型按层切段,每张卡负责几层,数据像流水线一样流过去。 它的通信量比张量并行小两个数量级,所以它是唯一能舒服跨机的模型并行 —— 但它天生会浪费一部分算力,而这一章就是在和那个浪费搏斗。


🏭 一、基本结构

模型 80 层,4 张卡: 前向传播:激活一站一站往右传 GPU0 层 1-20 GPU1 层 21-40 GPU2 层 41-60 GPU3 层 61-80 反向传播往回传 ⭐ 卡之间只在【切分点】传递激活,通信量极小
真正跨卡的地方只有卡与卡之间那三处接缝;底下那条回路是反向传播沿原路折返,走的还是同样三处。

算一算

通信量对比(batch=8, seq=2048, hidden=8192):

张量并行:每层 4 次 AllReduce → 80 层共 86 GB/步

流水线并行:每个切分点传一次激活

3 个切分点 × 268MB × 2(前向+反向)≈ 1.6 GB/步

⭐ 差了 50 倍以上 —— 这就是它能跨机的原因


🫧 二、气泡:流水线的原罪

关键信息

朴素做法(一个 batch 走完再来下一个):
时间 →
GPU0 [F1] [B1]
GPU1 [F1] [B1]
GPU2 [F1] [B1]
GPU3 [F1][B1]
░░░░░ ← 大量空闲(气泡)░░░░░
⭐ 任何时刻只有【一张卡】在干活,其他 3 张在等 💀
4 卡的效率还不如 1 卡

解法:把 batch 切成 micro-batch

同一时间列代表同时发生的工作。micro-batch 按依赖逐级前进;开头和结尾的空格是流水线气泡。为便于读图,假设各阶段前向与反向耗时相同。GPipe:四阶段 × 四个 micro-batchGPU 0F1B4F2B3F3B2F4B1GPU 1F1B4F2B3F3B2F4B1GPU 2F1B4F2B3F3B2F4B1GPU 3F1B4F2B3F3B2F4B1时间向右推进F:前向 B:反向 空格:等待
同一时间列代表同时发生的工作。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:现代标准调度

结果对照

GPipe 的问题:所有 F 做完才开始 B
【M 个 micro-batch 的激活】全部要缓存 💀
✅ 1F1B(One Forward One Backward):
进入稳定态后,每做一次前向就【立刻】做一次反向
反向做完就能释放那个 micro-batch 的激活
⭐ 激活显存从 O(M) 降到 O(P)
而气泡率【完全不变】—— 纯赚 ⭐
最后一级较早进入前反向交替;前面的级先预热,再交替处理。B₁ 与紧邻它的 F 并非一定属于同一个 micro-batch,请对照编号。图中省略通信耗时,并假设各阶段计算等长。1F1B:四阶段 × 六个 micro-batchGPU 0F1F2F3F4B1F5B2F6B3B4B5B6GPU 1F1F2F3B1F4B2F5B3F6B4B5B6GPU 2F1F2B1F3B2F4B3F5B4F6B5B6GPU 3F1B1F2B2F3B3F4B4F5B5F6B6时间向右;空白格是等待,不是缺失内容F:前向 B:反向;B 完成即可释放对应激活
最后一级较早进入前反向交替;前面的级先预热,再交替处理。B₁ 与紧邻它的 F 并非一定属于同一个 micro-batch,请对照编号。图中省略通信耗时,并假设各阶段计算等长。

💡 注意 GPU3(最后一级)几乎没有等待 —— 它的前向一做完就能立刻反向。 而 GPU0 要等最久。这种不均衡是流水线的固有特征。

⭐ 交错式 1F1B(Interleaved)

关键信息

想法:让每张卡负责【不连续的多段】层
GPU0: 层 1-10 和 层 41-50
GPU1: 层 11-20 和 层 51-60
·
流水线"变长"了(虚拟级数 = P × v)
气泡占比降到 (P-1)/(v·M+P-1) ⭐ 再降 v 倍
⚠️ 代价:通信次数变成 v 倍

⚖️ 四、切分策略:负载均衡是关键

关键信息

因果链

❌ 均匀切:每卡 20 层
最后一级因为 LM Head 慢 30%
【所有卡都被它拖慢 30%】
✅ 按实测耗时切:
GPU0: 22 层(含 Embedding)
GPU1: 21 层
GPU2: 21 层
GPU3: 16 层 + LM Head ⭐ 层数少一些来补偿

🔑 实践建议:先跑一次 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 比中间层重得多,是负载不均的主因

✅ 检查点

  1. 流水线并行怎么切?为什么它的通信量这么小?
  2. 什么是气泡?朴素做法下 4 卡的效率如何?
  3. 气泡占比的公式是什么?P=4、M=4 和 M=64 分别是多少?
  4. 黄金法则是什么?为什么不能无限增大 M?
  5. 1F1B 相比 GPipe 改进了什么?气泡率变了吗?
  6. 交错式 1F1B 的思路和代价是什么?
  7. 为什么均匀切层数几乎总是次优的?
  8. 流水线并行怎么改变了学习率的调法?
  9. 三种并行里,哪些能跨机?哪个能救"单层装不下"?
👀 答案
  1. 按层切段,每卡负责几层,数据像流水线流过。通信量小是因为卡之间只在切分点传递激活——3 个切分点约 1.6GB/步,而张量并行是 86GB/步,差 50 倍以上。
  2. 气泡是流水线填充和排空阶段的空闲。朴素做法下任何时刻只有一张卡在干活,4 卡的效率还不如 1 卡。
  3. (P−1)/(M+P−1)。P=4,M=4 → 3/7 = 43%;P=4,M=64 → 3/67 = 4.5%。
  4. M ≥ 4P(最好 8P)。不能无限增大是因为 micro-batch 越多需要缓存的激活越多(GPipe 下是 O(M))。
  5. 进入稳定态后每做一次前向立刻做一次反向,反向完就释放该 micro-batch 的激活 → 激活显存从 O(M) 降到 O(P),而气泡率完全不变,纯赚。
  6. 让每卡负责不连续的多段层,虚拟级数变成 P×v,气泡降到 (P−1)/(v·M+P−1),再降 v 倍。代价:通信次数变成 v 倍。
  7. 因为流水线的速度取决于最慢的一级,而第一级有 Embedding、最后一级有 LM Head(词表×hidden,很重)。均匀切会让最后一级拖慢所有卡。应该先 profiling 测每层实际耗时再切。
  8. M 个 micro-batch 的梯度是累加的,所以等效 batch = micro_batch × M,学习率要按等效 batch 调,不是按 micro_batch。
  9. 数据并行和流水线并行能跨机,张量并行不能。只有张量并行能救"单层装不下"。

🛑 可以停在这里

⚡ 走神救援

先记住这几件事

下一节 👉 14-并行策略怎么组合.md ⭐

打卡记录保存在你的浏览器里,首页能看到总进度