2. 分布式训练基础

聊到分布式训练,很多人第一反应就是“多卡跑起来”。但说实话,我见过太多团队一上来就堆显卡,结果发现训练速度反而更慢了。为什么会这样?

说白了,分布式训练不是简单地把模型扔到多张卡上。你得先搞清楚一个问题:你的瓶颈到底在哪?是模型太大装不下?还是数据太多算不完?不同的瓶颈,对应的并行策略完全不同。

我个人习惯把并行策略分成三大类:数据并行、模型并行、流水线并行。今天咱们就把它们掰开揉碎了讲清楚。

一、数据并行:最常用的“人海战术”

数据并行,顾名思义,就是把数据切开来并行处理。每张卡上都放一份完整的模型副本,然后各自处理不同的数据批次。

核心思想:数据分片,模型复制,梯度同步。

举个例子。假设你有4张GPU,每张卡能塞下整个模型。你把一个batch size为1024的数据,切成4份,每份256。4张卡同时跑前向和反向,各自算出梯度。然后通过AllReduce操作,把梯度汇总平均,再更新所有卡上的模型参数。

我在项目中遇到过这样一个场景:训练一个BERT-large模型,单卡batch size只能开到16。用数据并行扩展到8卡,batch size直接变成128,收敛速度快了将近7倍。嗯,这里要注意,数据并行的加速比不是线性的,通信开销会吃掉一部分收益。

适用场景:

  • 模型能完整塞进单张GPU显存
  • 训练数据量巨大,需要提高吞吐量
  • GPU数量在8-64卡之间,通信开销可控

避坑指南:我曾经遇到过梯度同步时通信卡死的问题。原因是AllReduce的环拓扑在跨节点时,网络带宽成了瓶颈。后来改用分层AllReduce,先节点内同步,再节点间同步,问题就解决了。

二、模型并行:把大模型“拆开”放

当模型大到单卡放不下时,数据并行就失效了。这时候你需要模型并行——把模型的不同层或不同参数,分布到多张卡上。

模型并行有两种常见形式:

  1. 层间并行:把网络的不同层放在不同卡上。比如前3层在GPU0,中间3层在GPU1,后3层在GPU2。数据像流水一样依次经过各卡。
  2. 层内并行:把某一层的参数切分到多张卡上。比如一个超大矩阵乘法,拆成多个小矩阵乘法并行计算。

你想想看,层间并行有个明显的缺点:同一时刻只有一张卡在工作,其他卡在干等。这其实是一种“伪并行”。我建议除非模型实在太大,否则优先考虑层内并行。

核心思想:模型分片,数据流动,计算依赖。

我记得有一次训练一个千亿参数的MoE模型,单卡根本放不下。我们用了模型并行,把专家网络分布到32张卡上。每张卡只负责一部分专家,输入数据通过路由机制分发到对应的专家卡上。效果不错,但通信量确实大。

并行方式 显存占用 通信开销 典型场景
层间并行 低(每卡只存部分层) 高(逐层传递激活值) 超深网络,如GPT-3
层内并行 中(每卡存部分参数) 中(AllReduce梯度) 超大单层,如超大FFN

适用场景:

  • 模型参数超过单卡显存(比如>80GB)
  • 单层计算量极大,需要拆分计算
  • GPU间通信带宽高(如NVLink)

三、流水线并行:让“流水线”转起来

流水线并行,其实是对层间模型并行的一种优化。它解决了“同一时刻只有一张卡在工作”的问题。

怎么做的?把数据也切成多个微批次(micro-batch)。第一个微批次进入GPU0,计算完后传给GPU1,同时GPU0开始处理第二个微批次。这样,不同GPU就能同时处理不同微批次的不同阶段。

核心思想:模型分片,数据微批次,流水线调度。

我刚开始用流水线并行时,踩过一个坑:微批次数量设得太少,导致流水线气泡(bubble)太大,GPU利用率不到50%。后来把微批次数量从4增加到16,气泡占比从30%降到了5%左右。说白了,微批次越多,流水线越满,效率越高。

常用的流水线调度策略有两种:

  • Gpipe:先填满流水线,再稳定运行。实现简单,但气泡较大。
  • 1F1B(One-Forward-One-Backward):前向和反向交错执行。气泡更小,内存占用也更低。

避坑指南:我曾经在流水线并行中遇到过显存爆炸的问题。原因是每个微批次的前向激活值都要保留到反向计算。后来用了激活值重计算(activation recomputation),用时间换空间,才把显存压下来。

四、三种并行策略怎么选?一张图看懂

下面这张图是我自己总结的决策流程。你遇到一个训练任务,可以按这个思路来选并行策略。

并行策略选择决策树 训练任务 模型能塞进单卡显存吗? 是 否 数据并行 数据分片,模型复制 适合数据量大、模型适中 模型并行 模型分片,数据流动 适合超大模型 通信带宽够吗? 单层计算量大吗? 是 否 是 否 直接数据并行 数据并行+梯度压缩 层内模型并行 流水线并行

五、三种并行策略的对比总结

维度 数据并行 模型并行 流水线并行
模型能否装下 能(单卡) 不能(需拆分) 不能(需拆分)
主要瓶颈 通信(梯度同步) 计算(层内拆分) 气泡(流水线空闲)
扩展性 好(8-64卡) 中(受层数限制) 好(可跨节点)
实现难度 低(框架原生支持) 高(需手动切分) 中(需调度策略)
典型框架 PyTorch DDP, Horovod Megatron-LM, Mesh-TensorFlow Gpipe, PipeDream

最后说一句,实际项目中很少只用一种并行策略。我见过的大规模训练任务,基本都是混合并行——数据并行+模型并行+流水线并行一起上。比如训练GPT-3时,用了数据并行(跨节点)+模型并行(节点内)+流水线并行(跨层)。

嗯,这就是分布式训练的三大基础并行策略。你想想看,你的任务属于哪种情况?选对了策略,训练效率能翻好几倍。

我的建议:刚开始做分布式训练时,先从数据并行入手。等遇到显存瓶颈了,再考虑模型并行和流水线并行。别一上来就搞复杂的混合并行,容易把自己绕进去。