1.1. 数据并行
1.1.1. 数据并行的概念和原理
数据并行是一种分布式训练技术,它允许我们将大规模的数据集分割成多个小份,并将它们分配到多个 GPU 节点上进行处理。 每个 GPU 节点都持有一个完整的模型副本,并基于其分配的数据独立进行梯度计算。 在这种设置中,通常有一个 GPU(如 GPU0)作为参数服务器,负责聚合各个 GPU 节点的梯度,并更新模型参数,然后广播更新后的参数到所有节点(也可以将参数服务器分布在所有节点上,每个 GPU 只更新其中一部分梯度)。 这种并行方式不仅可以应用于训练数据,还可以扩展到模型梯度、权重参数以及优化器状态等多个方面,以提高训练效率。
1.1.2. 数据并行(PyTorch DP)
PyTorch中的数据并行(DP)是最早提供的一种并行方式,它基于单进程多线程实现。 在 DP 中,数据和模型被分发到每个 GPU 上,每个 GPU 独立进行前向传播和梯度计算,然后将计算结果返回到主 GPU 上进行损失函数计算和梯度更新。 DP 的优点是实现简单,只需一行代码即可完成设置。 然而,它的缺点也很明显,比如受制于全局解释器锁(GIL),导致性能开销较大,且只能在单台服务器上使用(单机多卡),不支持分布式训练和 Apex 混合精度训练。
1.1.3. 分布式数据并行(PyTorch DDP)
分布式数据并行(DDP)是 PyTorch 中一种更高级的数据并行方案,它基于多进程实现,每个进程都有自己的优化器,独立执行更新过程。 DDP 通过在进程间传递梯度来减少网络通信的瓶颈,适用于单机和多机情况,并且避免了 GIL 带来的性能开销。 DDP 的每个进程都会创建一个本地的梯度同步器,确保在训练过程中,每个 GPU 都能独立进行反向传播和参数更新,从而实现更高效的负载均衡和运行效率。
1.1.4. DP 和 DDP的区别
DDP(torch.nn.DistributedDataParallel
)是PyTorch中用于分布式训练的模块,它允许在多个GPU甚至多个节点上并行训练模型。
以下是 DDP 的一些详细特点和工作原理:
- 多进程管理:DDP 通过启动多个进程来实现分布式训练,每个进程控制一个 GPU,并加载模型的一个副本,每个进程都有独立的优化器,执行自己的更新过程;
- 数据分发:在每个 GPU 上分发参数并建立模型副本,每个进程都会加载不同的数据,DDP 会自动处理跨 GPU 的梯度同步;
- 梯度同步:每个 GPU 上的模型都会在自己的数据上进行前向和反向传播,然后通过梯度同步机制更新模型参数,确保所有GPU上的模型状态保持一致;
- Ring-Reduce方法:DDP 使用 Ring-Reduce(组内 reduce -> 组间 all reduce -> 组内 broadcast)的方法进行通信,交换梯度,每个进程获取所有进程梯度,并用平均后的梯度更新参数。
DP 和 DDP 的主要区别在于它们的实现方式和参数更新机制。
- DP 基于单进程多线程,而 DDP 基于多进程,每个 GPU 对应一个进程。
- 在参数更新方面,DDP 在各进程梯度计算完成后,会将梯度汇总平均,然后由主进程广播到所有进程,各进程用该梯度独立更新参数。 相比之下,DP是将梯度汇总到主卡,然后在主卡上更新参数,再广播给其他GPU。
- DDP 由于避免了 GIL 和减少了数据传输量,训练效率更高,且不存在 DP 中的负载不均衡问题。
- DDP 支持模型并行,而 DP 并不支持,这意味如果模型太大单卡显存不足时,只能使用DDP。
1.1.5. 完全分片数据并行(PyTorch FSDP)
完全分片数据并行(FSDP)受到 DeepSpeed ZeRO 优化策略的启发。 FSDP 通过分片模型参数、梯度和优化器状态,进一步减少了模型训练过程中的冗余数据。 FSDP 可以选择将模型参数分片卸载到 CPU,以提高内存效率。 FSDP 的出现打破了大模型训练中模型分片的障碍,同时保持了数据并行的简单性,使得训练更大的模型成为可能。
ZeRO 优化策略
ZeRO 是 DeepSpeed 提出的一种优化策略,它通过分片模型状态参数来减少冗余数据。 ZeRO 有三个不同的级别:
- ZeRO-1 只对优化器状态进行分片,
- ZeRO-2 对优化器状态和梯度进行分片,
- ZeRO-3 则进一步对模型权重参数也进行分片。 这种分片策略可以显著减少GPU上需要存储的数据量,从而提高内存效率和训练大模型的能力。
DDP 和 FSDP 的区别
DDP 和 FSDP 在模型副本和梯度聚合方式上有所不同。 在 DDP 中,每个 GPU 上都有一个完整的模型副本,而在 FSDP 中,每个 GPU 上仅存在模型的分片。 在梯度聚合方面,DDP 在局部计算后需要将梯度同步给主节点,而 FSDP 则通过 all-gather 操作从其他 GPU 收集所有权重,以在本地计算前向传播。 FSDP 在内存效率上有优势,因为它可以在每层前向传播后丢弃全部权重,为后续层节省内存。 这种设计使得 FSDP 特别适合训练大型模型。