第四节:数据并行原理

数据并行,说白了就是「把数据切开,分给多张卡一起算」。

我刚开始接触分布式训练时,觉得这概念挺简单的——不就是数据分片嘛。但真正上手才发现,里面的门道比想象中多得多。尤其是梯度同步那部分,踩过的坑能写满一页A4纸。

4.1 数据并行的核心思想

先说说最朴素的想法:

你有一批训练数据,比如1024张图片。单卡训练时,一张卡要算完这1024张,才能更新一次参数。

数据并行是怎么做的?

  • 把1024张图片平均分给4张卡,每张卡分到256张
  • 每张卡独立做前向传播,算出各自的损失
  • 每张卡独立做反向传播,算出各自的梯度
  • 关键一步:把4份梯度「合并」成一份,更新模型参数

嗯,这里要注意:每张卡上存的是同一个模型的完整副本。不是每张卡只存一部分参数,而是每张卡都有完整的模型。

核心要点:数据并行 = 数据分片 + 模型复制 + 梯度同步

每张卡的计算量是原来的 1/N,但显存占用基本不变(因为模型是完整的)

我在项目中遇到过一个问题:有同学问,既然每张卡算的是不同数据,那梯度方向不一样怎么办?

答案是:取平均。把所有卡的梯度加起来,除以卡数,得到的就是「全局梯度」。这个梯度等价于用完整batch size算出来的梯度方向。

4.2 梯度同步机制:AllReduce

梯度同步是数据并行中最核心、也最容易出问题的环节。

每张卡算完自己的梯度后,需要把梯度「汇总」到一起。这个汇总操作,在分布式训练里叫 AllReduce。

我最早接触AllReduce时,以为就是简单的「把梯度发到一台机器上求和,再广播回去」。后来发现,这种参数服务器架构在大规模集群上效率很低——所有卡都在等那台中心节点。

现在主流用的是 Ring AllReduce,它的思路很巧妙:

  • 把所有GPU排成一个环
  • 每张卡只和相邻的卡通信
  • 分两步走:先做Reduce-Scatter(分散归约),再做AllGather(全收集)

这样做的好处是:通信量均匀分摊到每张卡,没有瓶颈节点。

我的经验:在8卡以内的场景,Ring AllReduce和参数服务器架构的差距不大。但一旦超过16卡,Ring AllReduce的优势就非常明显了。我曾经在32卡集群上测试过,Ring AllReduce比参数服务器快了将近3倍。

下面这张图展示了Ring AllReduce的核心流程:

Ring AllReduce 梯度同步流程 GPU 0 GPU 1 GPU 2 GPU 3 Step 1: Reduce-Scatter 每张卡将梯度分块,沿环传递并累加 Step 2: AllGather 将累加后的完整梯度广播给所有卡 g0 g1 g2 g3

你想想看,如果每张卡都要和所有其他卡通信,通信量是 O(N²)。但Ring AllReduce只需要 O(N) 的通信量,这就是它在大规模集群上胜出的原因。

4.3 Batch Size 与学习率缩放

这是数据并行里最容易踩坑的地方。

单卡训练时,batch size = 32,学习率 = 0.001。现在换成4卡数据并行,每张卡还是算32张,那全局batch size就变成了 32 × 4 = 128。

问题来了:学习率要不要变?

我见过不少同学直接拿单卡的学习率去跑多卡,结果模型要么不收敛,要么loss炸了。

警告:全局batch size变大后,学习率必须相应调整。否则梯度更新的步长会「相对变小」,导致收敛变慢甚至不收敛。

目前主流的缩放策略有两种:

策略名称 公式 适用场景
线性缩放 lr_new = lr_base × N batch size ≤ 1024 时效果较好
平方根缩放 lr_new = lr_base × √N batch size 很大(>2048)时更稳定

其中 N 是GPU数量。

我个人习惯先用线性缩放,然后观察loss曲线。如果loss震荡厉害,就换成平方根缩放,或者加一个warmup阶段。

避坑指南:我曾经在一个视觉模型上,把batch size从256直接拉到4096(16卡),学习率按线性缩放设了0.016。结果训练直接炸了——loss变成了NaN。后来加了5个epoch的warmup,从0.001线性增长到0.016,问题就解决了。

为什么会这样?因为大batch size意味着梯度更「平滑」,方差更小。如果学习率太大,很容易跳过最优点。warmup的作用就是让模型慢慢适应大的更新步长。

4.4 实践中的注意事项

最后聊几个实际工程中容易忽略的点:

  • BN层的处理:BatchNorm在数据并行下需要特殊处理。每张卡单独算均值和方差,还是全局同步?我建议用SyncBN,否则batch size变小后BN效果会打折扣
  • 数据加载:每张卡的数据要保证不重叠,且shuffle要正确。我曾经遇到过数据没打乱,导致8张卡学到的分布不一样
  • 梯度累积:如果显存不够,可以用梯度累积来模拟更大的batch size。但要注意,梯度累积和真正的数据并行在BN统计量上是有区别的

嗯,数据并行这块内容不少,但核心就三点:数据分片、梯度同步、学习率调整。把这三点吃透了,多卡训练的基本功就算打牢了。

一句话总结:数据并行让训练变快了,但代价是你要多操心梯度同步和学习率缩放。好在这些都有成熟的方案,照着做就行。

想要更多资料,联系可可