怎么使用Pytorch进行多卡训练


这篇“怎么使用Pytorch进行多卡训练”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用Pytorch进行多卡训练”文章吧。当一块GPU不够用时,我们就需要使用多卡进行并行训练。其中多卡并行可分为数据并行和模型并行。具体区别如下图所示:由于模型并行比较少用,这里只对数据并行进行记录。对于pytorch,有两种方式可以进行数据并行:数据并行(DataParallel, DP)和分布式数据并行(DistributedDataParallel, DDP)。在多卡训练的实现上,DP与DDP的思路是相似的:1、每张卡都复制一个有相同参数的模型副本。2、每次迭代,每张卡分别输入不同批次数据,分别计算梯度。3、DP与DDP的主要不同在于接下来的多卡通信:DP的多卡交互实现在一个进程之中,它将一张卡视为主卡,维护单独模型优化器。所有卡计算完梯度后,主卡汇聚其它卡的梯度进行平均并用优化器更新模型参数,再将模型参数更新至其它卡上。DDP则分别为每张卡创建一个进程,每个进程相应的卡上都独立维护模型和优化器。在每次每张卡计算完梯度之后,进程之间以NCLL(NVIDIA GPU通信)为通信后端,使各卡获取其它卡的梯度。各卡对获取的梯度进行平均,然后执行后续的参数更新。由于每张卡上的模型与优化器参数在初始化时就保持一致,而每次迭代的平均梯度也保持一致,那么即使没有进行参数复制,所有卡的模型参数也是保持一致的。Pytorch官方推荐我们使用DDP。DP经过我的实验,两块GPU甚至比一块还慢。当然不同模型可能有不同的结果。下面分别对DP和DDP进行记录。Pytorch的DP实现多GPU训练十分简单,只需在单GPU的基础上加一行代码即可。以下是一个DEMO的代码。其中model = DataParallel(model, device_ids=[0, 1])这行将模型复制到0,1号GPU上。输入数据x无需指定设备,它将会被均匀分配至各块GPU模型,进行前向传播。之后各块GPU的输出再合并到GPU0中,得到输出y。输出y在GPU0中计算损失,并进行反向传播计算梯度、优化器更新参数。为了对分布式编程有基本概念,首先使用pytorch内部的方法实现一个多进程程序,再使用DDP模块实现模型的分布式训练。首先使用pytorch内部的方法编写一个多进程程序作为编写分布式训练的基础。以上代码主进程创建了两个子进程,子进程之间使用NCCL后端进行通信。每个子进程各占用一个GPU资源,实现了所有GPU张量求和的功能。细节注释如下:1、为每个子进程定义相同名称的张量,并分别分配至不同的GPU,从而能进行后续的GPU间通信。2、定义一个通信组,用于后面的all_reduce通信操作。3、all_reduce操作以及其它通信方式请看下图:4、定义编号(rank)为0的ip和端口地址,让每个子进程都知道。ip和端口地址可以随意定义,不冲突即可。如果不设置,子进程在涉及进程通信时会出错。5、初始化子进程组,定义进程间的通信后端(还有GLOO、MPI,只有NCCL支持GPU间通信)、子进程rank、子进程数量。只有当该函数在size个进程中被调用时,各进程才会继续从这里执行下去。这个函数统一了各子进程后续代码的开始时间。6、执行子进程代码。7、由于创建子进程会执行本程序,因此主进程的执行需要放在__main__里,防止子进程执行。8、开始创建子进程的方式:spawn、fork。windows默认spawn,linux默认fork。具体区别请百度。9、由于是以NCCL为通信后端的分布式训练,如果不同进程中相同名称的张量在同一GPU上,当这个张量进行进程间通信时就会出错。为了防止出错,限制每张卡独占一个进程,每个进程独占一张卡。这里有两张卡,所以最多只能创建两个进程。10、创建子进程,传入子进程的初始化方法,及子进程调用该方法的参数。11、等待子进程全部运行完毕后再退出主进程。输出结果如下:正是各进程保存在不同GPU上的张量的广播求和(all_reduce)的结果。我们实际上可以根据上面的分布式基础写一个分布式训练,但由于不知道pytorch如何实现GPU间模型梯度的求和,即官方教程中所谓的ring_reduce(没找到相关API),时间原因,就不再去搜索相关方法了。这里仅记录pytorh内部的分布式模型训练,即利用DDP模块实现。Pytorch版本1.12.1。以上代码包含模型在多GPU上读取权重、进行分布式训练、保存权重等过程。细节注释如下:1、初始化进程组,由于使用GPU通信,后端应该写为NCCL。不过经过实验,即使错写为gloo,DDP内部也会自动使用NCCL作为通信模块。2、由于后面使用DDP包裹模型进行训练,其内部会自动将所有rank的模型权重同步为rank 0的权重,因此我们只需在rank 0上读取模型权重即可。这是基于Pytorch版本1.12.1,低级版本似乎没有这个特性,需要在不同rank分别导入权重,则load需要传入map_location,如下面注释的两行代码所示。3、这里创建model的优化器,而不是创建用ddp包裹后的ddp_model的优化器,是为了兼容单GPU训练,读取优化器权重更方便。4、将优化器权重读取至该进程占用的GPU。如果没有map_location参数,load会将权重读取到原本保存它时的设备。5、优化器获取权重。经过实验,即使权重不在优化器所在的GPU,权重也会迁移过去而不会报错。当然load直接读取到相应GPU会减少数据传输。6、DDP包裹模型,为模型复制一个副本到相应GPU中。所有rank的模型副本会与rank 0保持一致。注意,DDP并不复制模型优化器的副本,因此各进程的优化器需要我们在初始化时保持一致。权重要么不读取,要么都读取。7、这里开始模型的训练。数据需转移到相应的GPU设备。8、在backward中,所有进程的模型计算梯度后,会进行免费云主机域名平均(不是相加)。也就是说,DDP在backward函数添加了hook,所有进程的模型梯度的ring_reduce将在这里执行。这个可以通过给各进程模型分别输入不同的数据进行验证,backward后这些模型有相同的梯度,且验算的确是所有进程梯度的平均。此外,还可以验证backward函数会阻断(block)各进程使用梯度,只有当所有进程都完成backward之后,各进程才能读取和使用梯度。这保证了所有进程在梯度上的一致性。9、各进程优化器使用梯度更新其模型副本权重。由于初始化时各进程模型、优化器权重一致,每次反向传播梯度也保持一致,则所有进程的模型在整个训练过程中都能保持一致。10、由于所有进程权重保持一致,我们只需通过一个进程保存即可。11、定义rank 0的IP和端口,使用mp.spawn,只需在主进程中定义即可,无需分别在子进程中定义。12、创建子进程,传入:子进程调用的函数(该函数第一个参数必须是rank)、子进程函数的参数(除了rank参数外)、子进程数、是否等待所有子进程创建完毕再开始执行。以上就是关于“怎么使用Pytorch进行多卡训练”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注百云主机行业资讯频道。

相关推荐: 怎么用matlab代码解决单目标优化问题

这篇“怎么用matlab代码解决单目标优化问题”文章的知识点大部分人都不太理解,所以小编给大家总结了以免费云主机域名下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么用matlab代码解决单目标优化问…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 02/10 21:53
下一篇 02/10 21:53

相关推荐