长椿街网站建设,制作人是干嘛的,网站vps,网站悬浮广告素材Pytorch DistributedDataParallel#xff08;DDP#xff09;教程一#xff1a;快速入门理论篇 目录 一、 写在前面二、什么是分布式并行训练1. 并行训练2. 数据并行 三、DDP的基本原理1. DDP的训练过程2. Ring-All-Reduce算法 四、如何搭建一个Pytorch DDP代码框架1. 与DDP有…Pytorch DistributedDataParallelDDP教程一快速入门理论篇 目录 一、 写在前面二、什么是分布式并行训练1. 并行训练2. 数据并行 三、DDP的基本原理1. DDP的训练过程2. Ring-All-Reduce算法 四、如何搭建一个Pytorch DDP代码框架1. 与DDP有关的基本概念2. 与DDP有关的一些操作3. 要实现DDP训练我们需要解决哪些问题4. 一个最简单的DDP代码框架 五、查资料过程中的一个小惊喜 一、 写在前面
随着深度学习技术的不断发展模型的训练成本也越来越高。训练一个高效的通用模型需要大量的训练数据和算力。在很多非大模型相关的常规任务上往往也需要使用多卡来进行并行训练。在多卡训练中最为常用的就是分布式数据并行DistributedDataParallel, DDP。但是现有的有关DDP的教程和博客比较少内容也比较分散繁琐。在大多数情况下我们只需要学会如何使用即可不需要特别深入地了解原理。为此写下这个系列博客简明扼要地介绍一下DDP的使用抛开繁杂的细节和原理帮助快速上手使用All in one blog。
篇幅较长分为上下两篇这篇简要介绍相关背景和理论知识下篇详细介绍代码框架和搭建流程。
二、什么是分布式并行训练
1. 并行训练
在Pytorch中有两种并行训练方式
1模型并行。模型并行通常是指你的模型非常大大到一块卡根本放不下因而需要把模型进行拆分放到不同的卡上。
2数据并行。数据并行通常用于训练数据非常庞大的时候比如有几百万张图像用于训练模型。此时如果只用一张卡来进行训练那么训练时间就会非常的长。并且由于单卡显存的限制训练时的batch size不能设置得过大。但是对于很多模型的性能而言由于BN层的使用都会和batch size的大小正相关。此外很多基于对比学习的训练算法由于其对负样本的需求性能也与batch size的大小正相关。因此我们需要使用多卡训练不仅可以训练加速并且可以设置更大的batch size来提升性能。
2. 数据并行
在Pytorch中有两种方式来实现数据并行
1数据并行DataParallelDP。DataParallel采用参数服务器架构其训练过程是单进程的。在训练时会将一块GPU作为server其余的GPU作为worker在每个GPU上都会保留一个模型的副本用于计算。训练时首先将数据拆分到不同的GPU上然后在每个worker上分别进行计算最终将梯度汇总到server上在server进行模型参数更新然后将更新后的模型同步到其他GPU上。这种方式有一个很明显的弊端作为server的GPU其通信开销和计算成本非常大。它需要和其他所有的GPU进行通信并且梯度汇总、参数更新等步骤都是由它完成导致效率比较低。并且随着多卡训练的GPU数量增强其通信开销也会线性增长。 不过DataParallel的代码十分简洁仅需在原有单卡训练的代码中加上一行即可。
model nn.DataParallel(model) 如果你的数据集并不大只有几千的规模并且你多卡训练时的卡也不多只有4块左右那么DataParallel会是一个不错的选择。
关于Parameter Server更详细的原理介绍可以参考
深度学习加速算法、编译器、体系结构与硬件设计
一文讀懂「Parameter Server」的分布式機器學習訓練原理
2分布式数据并行DistributedDataParallelDDP。DDP采用Ring-All-Reduce架构其训练过程是多进程的。如果要用DDP来进行训练我们通常需要修改三个地方的代码数据读取器dataloader日志输出print指标评估evaluate。其代码实现略微复杂不过我们只需要始终牢记一点即可每一块GPU都对应一个进程除非我们手动实现相应代码不然各个进程的数据都是不互通的。Pytorch只为我们实现了同步梯度和参数更新的代码其余的需要我们自己实现。 三、DDP的基本原理
1. DDP的训练过程
DDP的训练过程可以总结为如下步骤
1在训练开始时整个数据集被均等分配到每个GPU上。每个GPU独立地对其分配到的数据进行前向传播计算预测输出和反向传播计算梯度。
2同步各个GPU上的梯度以确保模型更新的一致性该过程通过Ring-All-Reduce算法实现。
3一旦所有的GPU上的梯度都同步完成每个GPU就会使用这些聚合后的梯度来更新其维护的模型副本的参数。因为每个GPU都使用相同的更新梯度所以所有的模型副本在任何时间点上都是相同的。
2. Ring-All-Reduce算法
Ring-All-Reduce架构是一个环形架构所有GPU的位置都是对等的。每个GPU上都会维持一个模型的副本并且只需要和它相连接的两个GPU通信。
对于第k个GPU而言只需要接收来自于第k-1个GPU的数据并将数据汇总后发送给第k1个GPU。这个过程在环中持续进行每个GPU轮流接收、聚合并发送梯度。
经过 N 次的迭代循环后N是GPU的数量每个GPU将累积得到所有其他GPU的梯度数据的总和。此时每个GPU上的梯度数据都是完全同步的。
DDP的通信开销与GPU的数量无关因而比DP更为高效。如果你的训练数据达到了十万这个量级并且需要使用4卡及以上的设备来进行训练DDP将会是你的最佳选择。
关于DDP和Ring-All-Reduce算法的更多实现原理和细节可以参考
Bringing HPC Techniques to Deep Learning
Pytorch 分散式訓練 DistributedDataParallel — 概念篇
Technologies behind Distributed Deep Learning: AllReduce
四、如何搭建一个Pytorch DDP代码框架
1. 与DDP有关的基本概念
在开始使用DDP之前我们需要了解一些与DDP相关的概念。
参数含义查看方式group分布式训练的进程组每个group可以进行自己的通信和梯度同步Group通常在初始化分布式环境时创建并通过torch.distributed.new_group等API创建自定义groups。world size参与当前分布式训练任务的总进程数。在单机多GPU的情况下world size通常等于GPU的数量在多机情况下它是所有机器上所有GPU的总和。torch.distributed.get_world_size()rankRank是指在所有参与分布式训练的进程中每个进程的唯一标识符。Rank通常从0开始编号到world size - 1结束。torch.distributed.get_rank()local rankLocal rank是当前进程在其所在节点内的相对编号。例如在一个有4个GPU的单机中每个GPU进程的local rank将是0, 1, 2, 3。这个参数常用于确定每个进程应当使用哪个GPU。Local rank不由PyTorch的分布式API直接提供而通常是在启动分布式训练时由用户设定的环境变量或者通过训练脚本的参数传入。
2. 与DDP有关的一些操作
在DDP中每个进程的数据是互不影响的除了采用Ring-All-Reduce同步梯度。如果我们要汇总或者同步不同进程上的数据就需要用到一些对应的函数。
1all_reduce
all_reduce操作会在所有进程中聚合每个进程的数据如张量并将结果返回给所有进程。聚合可以是求和、取平均、找最大值等。当你需要获得所有进程的梯度总和或平均值时可以使用all_reduce。这在计算全局平均或总和时非常有用比如全局平均损失。
一个示例代码如下
import torch.distributed as disttensor_a torch.tensor([1.0], devicedevice)
# 所有进程中的tensor_a将会被求和并且结果会被分配给每个进程中的tensor_a。
dist.all_reduce(tensor_a, opdist.ReduceOp.SUM)2all_gather
all_gather操作用于在每个进程中收集所有进程的数据。它不像all_reduce那样聚合数据而是将每个进程的数据保留并汇总成一个列表。当每个进程计算出一个局部结果并且你需要在每个进程中收集所有结果进行分析或进一步处理时可以使用all_gather。
一个示例代码如下
import torch
import torch.distributed as dist# 每个进程有一个tensor_a其值为当前进程的rank
tensor_a torch.tensor([rank], devicedevice) # 假设rank是当前进程的编号
gather_list [torch.zeros_like(tensor_a) for _ in range(dist.get_world_size())]
# 收集所有进程的tensor_a到每个进程的gather_list
dist.all_gather(gather_list, tensor)
3broadcast
broadcast操作将一个进程的数据如张量发送到所有其他进程中。这通常用于当一个进程生成了某些数据需要确保其他所有进程都得到相同的数据时。在在开始训练之前可以用于同步模型的初始权重或者在所有进程中共享某些全局设置。一个示例代码如下
import torch.distributed as disttensor_a torch.tensor([1.0], devicedevice)
if rank 0:tensor_a.fill_(10.0) # 只有rank 0设置tensor_a为10
dist.broadcast(tensor_a, src0) # rank 0将tensor_a广播到所有其他进程3. 要实现DDP训练我们需要解决哪些问题
1如何将数据均等拆分到每个GPU
在分布式训练中为了确保每个GPU都能高效地工作需要将训练数据均等地分配到每个GPU上。如果数据分配不均可能导致某些GPU数据多、某些GPU数据少从而影响整体的训练效率。
在PyTorch中可以使用torch.utils.data.DataLoader结合torch.utils.data.distributed.DistributedSampler。DistributedSampler会自动根据数据集、进程总数world size和当前进程编号rank来分配数据确保每个进程获取到的数据互不重复且均衡分布。
2如何在IO操作时避免重复
在使用PyTorch的分布式数据并行DDP进行模型训练时由于每个进程都是独立运行的IO操作如打印print、保存save或加载load等如果未经特别处理将会在每个GPU进程上执行。这样的行为通常会导致以下问题重复打印每个进程都会输出同样的打印信息到控制台导致输出信息重复难以阅读、文件写入冲突如果多个进程尝试同时写入同一个文件会产生写入冲突导致数据损坏或者输出不正确、资源浪费每个进程重复加载相同的数据文件会增加IO负担降低效率和浪费资源。
一个简单且可行的解决方案是只在特定进程中进行相关操作例如只在rank为0的进程中执行如有必要再同步到其他进程。
3如何收集每个进程上的数据进行评估
在DDP训练中每个GPU进程独立计算其数据的评估结果如准确率、损失等在评估时可能需要收集和整合这些结果。
通过torch.distributed.all_gather函数可以将所有进程的评估结果聚集到每个进程中。这样每个进程都可以获取到完整的评估数据进而计算全局的指标。如果只需要全局的汇总数据如总损失或平均准确率可以使用torch.distributed.reduce或all_reduce操作直接计算汇总结果这样更加高效。
4. 一个最简单的DDP代码框架
篇幅太长见下篇。
五、查资料过程中的一个小惊喜
在查找DDP有关过程中发现了一些博客和视频做得很不错而且这里面有一部分是女生做的。博客和视频的质量都很高内容安排合理逻辑表达清晰参考资料也很全面。我看到的时候还是很惊艳的巾帼不让须眉链接如下
国立中央大学的李馨伊
复旦大学的_Meilinger_