Pipeline Parallel
流水线并行
核心解释
Pipeline Parallel 按模型深度切分,把连续的层放到不同阶段。一个 microbatch 的前向依次经过各阶段,反向再沿相反方向传回梯度。不同 microbatch 可以同时处于不同阶段。[1]
设模型由 个阶段组成。第 个阶段接收前一阶段的激活,计算自己的层,再把结果发送给下一阶段。每个阶段保留自己的参数与优化器状态。
流水线开始时要逐步填满,结束时要逐步排空。这些阶段没有工作的时间形成气泡。增加 microbatch 数可以摊薄气泡,但也会改变单次计算的大小和调度开销。
GPipe 先集中执行前向,再执行反向。1F1B 在预热后交替执行前向和反向,让激活更早释放。交错调度把每张卡上的层再分成多个模型块,增加调度机会,也增加跨块交接。[2]
更进一步的调度会拆开反向中的输入梯度与权重梯度,或让不同方向的工作重叠。判断收益时,需要把依赖、显存和通信放到同一条时间线上。[3]
资料
Ultra-Scale Playbook:从图解理解分布式训练 ↗
看 Pipeline Parallelism 的时间线,把 GPipe、1F1B 与交错调度对应到激活的保存和释放。
GPipe:用 microbatch 填充流水线 ↗
从原论文理解分层、microbatch 与梯度累积,关注气泡和重计算如何影响训练。
Megatron-LM:1F1B 与交错流水线 ↗
读第 2.2 节的调度图和公式,分清气泡减少、激活节省与通信增加各自来自哪里。
Zero Bubble:拆开反向传播的两项工作 ↗
输入梯度与权重梯度有不同的依赖。顺着调度图,看延后权重梯度如何填补空闲时间。
PyTorch:流水线阶段与调度实现 ↗
从 PipelineStage 进入 GPipe、1F1B 和交错调度,再沿源码链接核对 microbatch、通信和梯度缩放。
DualPipe:双向流水线与通信重叠 ↗
先看仓库中的 DualPipe 与 DualPipeV 调度图,再读示例和重叠接口,理解图上的重叠需要什么实现支持。
PP Schedule Visualizer:改变参数,观察时间线 ↗
调整阶段数、microbatch 和操作耗时,对照 1F1B、交错与 Zero Bubble 的依赖关系。
QA
气泡率的分母应该用哪段时间?
两者的分母不同。设流水线有 个均衡阶段,每步有 个 microbatch。用同步 GPipe 的简化模型,忽略通信,且各阶段的前向、反向耗时分别相同。气泡时间相对于理想工作时间的比例是 ;气泡占总时间的比例是 。
例如 ,两种口径分别约为 43.8% 和 30.4%。引用一个气泡比例时,先确认它除以的是哪段时间。
1F1B 的气泡没有消失,为什么仍然很有用?
GPipe 先完成一批前向,再统一做反向,需要同时保存多个 microbatch 的激活。1F1B 在预热后交替执行前向和反向,让较早进入的 microbatch 更早释放激活。
在阶段均衡的同步、非交错调度中,1F1B 与 GPipe 的理想气泡相同。它的直接收益是减少同时存活的激活,进而容纳更大的 microbatch 或减少重计算。
Zero Bubble 为什么可以把一部分反向计算放到后面?
一个线性层的反向至少包含输入梯度和权重梯度。上游阶段要等输入梯度,权重梯度则只需在对应的优化器更新前准备好。调度器因此可以先计算输入梯度,把权重梯度安排到后续空隙。
延后计算会延长部分激活的存活时间。能填多少气泡,取决于各项实际耗时、显存预算和跨阶段通信;论文中的不同调度正是在这些条件之间取舍。
流水线一轮训练到底传多少数据?
先看非交错流水线。设有 个阶段、 个 microbatch,每个边界传递的激活和反向梯度都为 字节。统计所有阶段的发送量之和,一次前向加反向为 。
例如 ,合计发送 3.5 GiB。它是所有边界的总发送量,每份传输只计一次;再把接收量相加会得到两倍。
真实模型的边界形状可能不同。此时逐边界求和。交错调度增加了模型块之间的交接,也需要重新统计,不能继续套用原来的阶段数。
把 microbatch 数量加大,为什么不一定更快?
固定全局 batch 时,增加 microbatch 数量会缩小每个 microbatch。流水线更容易填满,但 GEMM 也会变小,启动和通信次数会上升。
固定 microbatch 大小时,增加数量则会改变一次更新处理的样本数。比较速度时,要同时记录全局 batch、microbatch 大小、阶段数和数据并行度。
用了 1F1B,就意味着梯度来自旧权重吗?
1F1B 描述前向与反向的执行次序。训练语义还取决于什么时候更新权重。Megatron 的同步流水线会在本轮 microbatch 的梯度准备好后更新参数,因此本轮使用同一版本的权重。
PipeDream 等异步方案允许不同批次跨更新边界运行,需要处理权重版本和梯度陈旧性。读调度图时,要把优化器更新的位置也标出来。
推理只有前向,PP 还能降低单条请求的延迟吗?
单个 token 仍要按层经过所有阶段。把层分开本身没有缩短这条依赖链,还增加了阶段间传输。PP 的主要作用是容纳模型,并让不同请求或 microbatch 在不同阶段同时运行。
因此服务吞吐和单请求延迟可能朝不同方向变化。Decode 时还要考虑每条序列的逐 token 依赖,以及调度器能否持续为各阶段提供工作。
层数均分以后,怎样定位真正拖慢流水线的阶段?
待解
Embedding、输出层、MoE 路由和重计算会让阶段耗时不同。怎样在固定负载下记录计算、等待、通信及显存峰值,再调整切分?