模型并行(Model Parallelism)原理

模型并行,说白了就是「一个模型太大,一张卡装不下,那就拆开装」。

我最早接触这个概念是在做GPT-3级别的模型训练时。当时我们团队拿到一个1750亿参数的模型,单张A100 80G连模型参数都放不下,更别提优化器和梯度了。嗯,这时候就必须上模型并行。

模型并行的动机:大模型放不进单卡

为什么大模型放不进单卡?我们来算笔账:

  • 一个175B参数的模型,用FP16存储,光参数就要 175B × 2 bytes = 350GB
  • 加上优化器状态(Adam需要8倍参数内存),总共要 350GB × 9 ≈ 3.15TB
  • 而目前最强的单卡H100也只有80GB显存

所以必须把模型切分到多张卡上。这就是模型并行的核心动机。

核心思想:将模型的计算图和参数分布到多个设备上,每个设备只负责一部分计算。

层内并行与层间并行

模型并行有两种基本切分方式,我分别讲讲。

层内并行(Intra-layer Parallelism)

把一层内的计算切分到多张卡上。比如Transformer的注意力头,可以拆到不同卡上计算。

举个例子:一个12头的注意力层,可以拆到4张卡上,每张卡算3个头。最后再合并结果。

我在项目中遇到过一个问题:层内并行的通信量很大。每层都要做一次all-reduce,网络带宽容易成为瓶颈。所以一般只在单机多卡场景用,跨机器的话延迟太高。

层间并行(Inter-layer Parallelism)

把不同层放到不同卡上。比如模型有48层Transformer,可以前16层放卡0,中间16层放卡1,后16层放卡2。

这种方式的通信量小很多——只需要在层间传递激活值和梯度。但有个致命问题:流水线气泡。

我的经验:层间并行更适合跨机器场景。因为通信量小,对网络带宽要求低。但要注意负载均衡,别让某张卡成为瓶颈。

Pipeline并行:GPipe与PipeDream

层间并行在实际中通常以Pipeline(流水线)的形式实现。这里有两个经典方案。

GPipe:同步流水线

GPipe的思路很简单:把mini-batch切成多个micro-batch,依次送入流水线。

假设有4张卡(4个stage),把batch切成4个micro-batch:

时间步 1: 卡0处理 micro-batch 0
时间步 2: 卡0处理 micro-batch 1, 卡1处理 micro-batch 0
时间步 3: 卡0处理 micro-batch 2, 卡1处理 micro-batch 1, 卡2处理 micro-batch 0
时间步 4: 卡0处理 micro-batch 3, 卡1处理 micro-batch 2, 卡2处理 micro-batch 1, 卡3处理 micro-batch 0
...

这样做的好处是实现简单,梯度更新是同步的。但问题也很明显:流水线气泡。

为什么会这样?因为流水线启动和排空阶段,有些卡是空闲的。比如上面例子中,时间步1只有卡0在工作,其他3张卡都在等。

气泡大小可以用公式算:bubble_ratio = (P-1) / (M+P-1),其中P是stage数,M是micro-batch数。

我算过一笔账:如果P=8,M=32,气泡占比约18%。也就是说近20%的计算资源被浪费了。

我曾经踩过的坑:在GPipe中,如果micro-batch数量太少,气泡会非常大。有一次我设了M=4,P=8,结果气泡占比超过60%,训练速度还不如单卡。所以一定要保证M >> P。

PipeDream:异步流水线

PipeDream的思路不同——它让流水线一直跑,不等待所有micro-batch完成。

具体做法是:每个stage处理完一个micro-batch,立刻把结果传给下一个stage,同时开始处理下一个micro-batch。

这样流水线始终是满的,气泡几乎为零。但代价是什么?

  • 梯度不一致:不同micro-batch用的参数版本不同,梯度更新有偏差
  • 需要权重存储:每个stage要保存多个版本的参数,内存开销大
  • 收敛不稳定:我试过几次,发现学习率要调得很小才能收敛

PipeDream后来出了改进版PipeDream-2BW,用双缓冲权重来缓解不一致问题。但说实话,工业界用得更多的还是GPipe的变体。

流水线气泡问题深度分析

气泡问题说白了就是「资源利用率低」。我们画个图看看:

流水线气泡示意图(4个stage,4个micro-batch) 时间→ Stage 0 mb0 mb1 mb2 mb3 Stage 1 空 mb0 mb1 mb2 mb3 Stage 2 空 空 mb0 mb1 mb2 mb3 Stage 3 空 空 空 mb0 mb1 mb2 mb3 气泡 气泡 气泡 绿色=Stage0 蓝色=Stage1 橙色=Stage2 紫色=Stage3 红色区域 = 流水线气泡(空闲等待)

从图上能清楚看到:Stage 3要等到第4个时间步才开始工作,前面3个时间步都在空转。这就是气泡。

怎么减少气泡?我总结几个实用方法:

  • 增加micro-batch数量:M越大,气泡占比越小。但要注意显存限制
  • 减少stage数量:P越小,气泡越小。但每张卡的负载会变大
  • 使用1F1B策略:PipeDream的1F1B(一个前向一个后向)策略,可以进一步压缩气泡
  • 交错式流水线:把多个micro-batch交错调度,减少空闲时间

我个人习惯:在实际项目中,我一般把micro-batch数量设为stage数量的4-8倍。比如8个stage,就用32-64个micro-batch。这样气泡能控制在10%以内,同时显存开销也还能接受。

总结一下

模型并行是解决大模型训练的必要手段。层内并行适合单机多卡,通信密集;层间并行适合跨机器,通信量小但要注意气泡问题。

GPipe和PipeDream代表了两种不同的设计哲学:一个追求简单稳定,一个追求极致效率。选哪个?我的建议是:如果团队经验不足,先用GPipe;如果对效率有极致要求,可以试试PipeDream的变体。

嗯,模型并行就讲到这里。记住一点:没有银弹,要根据你的硬件环境和模型特点来选方案。

核心要点:

  • 模型并行解决「模型太大放不进单卡」的问题
  • 层内并行切分层内计算,通信量大
  • 层间并行切分层间计算,通信量小但产生气泡
  • GPipe同步流水线,实现简单但气泡明显
  • PipeDream异步流水线,气泡小但梯度不一致
  • 气泡大小 ≈ (P-1)/(M+P-1),M要远大于P

想要更多资料,联系可可