1. Distributed Data Parallel
1. DDP 是什么
DDP(Distributed Data Parallel)是 PyTorch 官方提供的分布式数据并行训练机制;
它的核心思想是:
让多个训练进程各自持有同一个模型的副本, 分别处理不同的数据子集, 并在反向传播阶段自动同步梯度, 从而保持所有模型副本的一致更新;
一句话概括:
多进程 + 多副本 + 数据切分 + 梯度同步
2. DDP 要解决什么问题
深度学习训练通常面临两个核心限制:
- 单张 GPU 的计算吞吐有限
- 单张 GPU 每一步能处理的数据量有限
如果只用单卡训练, 随着模型和数据规模变大, 训练速度会越来越慢;
DDP 的目标是:
- 利用多张 GPU 并行处理更多数据
- 在提高吞吐的同时, 仍然保持"训练的是同一个模型"
- 让多卡训练的优化语义尽量接近单卡上的大 batch 训练
因此, DDP 本质上是在解决:
如何让多个 GPU 高效, 稳定地共同训练同一个模型;
3. DDP 的设计理念
DDP 的设计建立在一个基本前提上:
如果单张 GPU 能放下完整模型, 那么最自然的数据并行方式不是切模型, 而是复制模型;
所以 DDP 的基本设计不是"拆模型", 而是:
- 每个进程各自保存一份完整模型副本
- 每个进程只处理自己那部分数据
- 每个进程独立完成 forward 和 backward
- 在 backward 时把不同进程的梯度同步成一致
- 再由各进程各自完成参数更新
这意味着 DDP 本质上是 数据并行(data parallelism), 而不是模型并行(model parallelism);
4. DDP 的基本结构
DDP 的主干可以压缩成四件事:
-
多进程
通常是一张 GPU 对应一个训练进程; -
模型副本
每个进程中都有一份完整模型; -
数据切分
不同进程处理不同的数据子集; -
梯度同步
每个进程先算本地梯度, 再在 backward 中把梯度同步成一致;
这四个部分共同构成了 DDP 的核心机制;
5. DDP 为什么同步"梯度"而不是"参数"
在多进程训练中, 每个进程看到的数据不同, 因此各自算出来的局部梯度一般也不同;
DDP 的思路不是让每个进程先各自把参数更新完再对齐, 而是:
- 先各自计算本地梯度
- 再把这些梯度聚合成同一份全局一致梯度
- 然后每个进程都使用这份相同梯度去更新参数
这样做的好处是:
- 更符合优化器的基本逻辑
- 更容易保证所有进程更新结果严格一致
- 对带有 momentum, Adam 等状态的优化器更自然
因此, DDP 的核心不是"每步同步参数", 而是:
在 backward 阶段同步梯度;
6. DDP 的训练语义
设有多个训练进程, 每个进程处理一个局部 mini-batch;
一次训练 step 的核心逻辑可以概括为:
- 每个进程对自己的数据做 forward
- 每个进程对自己的 loss 做 backward, 得到局部梯度
- 不同进程的对应梯度被同步并平均
- 每个进程使用同步后的梯度各自执行参数更新
- 因为梯度一致, 所以更新后模型参数仍然一致
因此, 从优化角度看, DDP 相当于:
把多个局部 batch 的梯度合并成一个更大的全局 batch 梯度估计;
7. 为什么不同进程的梯度可以被平均
不同进程上的数据不同, 所以局部梯度通常不同;
但只要这些数据都来自同一个训练分布, 那么这些不同的局部梯度就都在估计同一个总体目标函数的真实梯度;
因此:
- 局部梯度不同是正常的
- 对它们做平均是合理的
- 平均后的梯度通常比单个局部梯度更稳定
这也是 DDP 在优化上成立的核心依据之一;
8. DDP 为什么高效
DDP 高效, 不是因为它"没有同步", 而是因为它把同步做得足够合理;
它的高效主要来自下面几个方面;
8.1 一 GPU 一进程
每张 GPU 由一个独立进程负责, 避免了单进程统一控制多卡带来的中心化瓶颈;
8.2 本地独立计算
每个进程都能独立完成自己的 forward 和 backward, 大部分计算都在本地完成, 只有梯度同步需要通信;
8.3 backward 中同步梯度
同步发生在反向传播阶段, 而不是等整个训练 step 完成后再统一处理, 这样更容易把通信嵌入现有计算流程;
8.4 bucket 机制
梯度不会等整网全部 ready 后再一次性同步, 而是被分成多个 bucket;某个 bucket 一旦 ready, 就可以立刻开始通信;
8.5 计算与通信重叠
后面层梯度开始同步时, 前面层的 backward 可能还在继续, 因此通信时间可以部分隐藏在计算后面;
所以 DDP 的高效本质是:
把不可避免的同步开销, 尽量通过局部化, 流水化和重叠化的方式隐藏起来;
9. DDP 为什么会被广泛使用
DDP 在现代深度学习训练中被广泛使用, 原因主要有以下几点;
9.1 训练语义清晰
它的逻辑直观: 多副本处理不同数据, 梯度同步后共同更新同一个模型;
9.2 工程实现相对自然
相较于更复杂的模型并行, 张量并行, 流水并行, DDP 的思路更简单, 也更容易和普通训练代码衔接;
9.3 扩展性强
它既适用于单机多卡, 也适用于多机多卡, 是从小规模到大规模训练都能使用的基础方案;
9.4 性能通常较好
在模型可完整放入单卡显存时, DDP 往往是性能和复杂度之间非常好的平衡点;
9.5 是更复杂并行方案的基础
很多更高级的大规模训练方案, 都是在 DDP 的思想基础上继续发展出来的;
因此, DDP 在现代训练系统中的地位可以理解为:
分布式训练的标准起点;
10. DDP 的局限
DDP 并不是万能的, 它有明确边界;
10.1 模型必须能完整放入单卡
因为每个进程都要保存一份完整模型副本;
10.2 同步训练天然存在等待
如果某个进程更慢, 其他进程在同步点就可能等待它, 这就是同步式训练的木桶效应;
10.3 通信成本会限制扩展收益
当 GPU 数量继续增加时, 梯度同步的通信开销可能成为新的瓶颈;
因此, DDP 适合的是:
模型能放入单卡, 希望通过数据并行提高吞吐的训练场景;
11. DDP 的最简洁总结
DDP 的核心可以浓缩成下面这段话:
DDP 通过为每个训练进程维护一份完整模型副本, 让多个进程并行处理不同数据; 在反向传播阶段, 不同进程会自动同步梯度, 从而保证所有模型副本始终执行一致更新;它之所以高效, 在于采用了一 GPU 一进程, 梯度分 bucket 同步, 以及计算与通信重叠等机制, 因此成为现代深度学习中最常用, 最标准的数据并行训练方案之一;
12. 一组用于自测的核心问题
- 为什么 DDP 的基本思路是"复制模型", 而不是"切分模型"?
- 为什么 DDP 同步的是梯度, 而不是每一步后强行同步参数?
- 为什么不同数据产生的局部梯度不同, 但仍然可以平均?
- 为什么说 DDP 的训练语义接近"大 batch 训练"?
- DDP 的高效主要来自哪些机制?
- 为什么 DDP 会有木桶效应?
- 为什么 DDP 在现代训练系统中如此常见?
- DDP 的适用边界是什么?
