Pytorch中的分布式神经网络训练 pytorch 训练神经网络
itomcoil 2024-12-15 13:58 54 浏览
随着深度学习的多项进步,复杂的网络(例如大型transformer 网络,更广更深的Resnet等)已经发展起来,从而需要了更大的内存空间。 经常,在训练这些网络时,深度学习从业人员需要使用多个GPU来有效地训练它们。 在本文中,我将向您介绍如何使用PyTorch在GPU集群上设置分布式神经网络训练。
通常,分布式训练会在有一下两种情况。
1. 在GPU之间拆分模型:如果模型太大而无法容纳在单个GPU的内存中,则需要在不同GPU之间拆分模型的各个部分。
1. 跨GPU进行批量拆分数据。当mini-batch太大而无法容纳在单个GPU的内存中时,您需要将mini-batch拆分到不同的GPU上。
跨GPU的模型拆分
跨GPU拆分模型非常简单,不需要太多代码更改。 在设置网络本身时,可以将模型的某些部分移至特定的GPU。 之后,在通过网络转发数据时,数据也需要移动到相应的GPU。 下面是执行相同操作的PyTorch代码段。
from torch import nn
class Network(nn.Module):
def __init__(self, split_gpus=False):
super().__init__()
self.module1 = ...
self.module2 = ...
self.split_gpus = split_gpus
if split_gpus: #considering only two gpus
self.module1.cuda(0)
self.module2.cuda(1)
def forward(self, x):
if self.split_gpus:
x = x.cuda(0)
x = self.module1(x)
if self.split_gpus:
x = x.cuda(1)
x = self.module2(x)
return x
跨GPU的数据拆分
有3种在GPU之间拆分批处理的方法。
· 积累梯度
· 使用nn.DataParallel
· 使用nn.DistributedDataParallel
积累梯度
在GPU之间拆分批次的最简单方法是累积梯度。 假设我们要训练的批处理大小为256,但是一个GPU内存只能容纳32个批处理大小。 我们可以执行8(= 256/32)个梯度下降迭代而无需执行优化步骤,并继续通过loss.backward()步骤添加计算出的梯度。 一旦我们累积了256个数据点的梯度,就执行优化步骤,即调用optimizer.step()。 以下是用于实现累积渐变的PyTorch代码段。
TARGET_BATCH_SIZE, BATCH_FIT_IN_MEMORY = 256, 32
accumulation_steps = int(TARGET_BATCH_SIZE / BATCH_FIT_IN_MEMORY)
network.zero_grad() # Reset gradients tensors
for i, (imgs, labels) in enumerate(dataloader):
preds = network(imgs) # Forward pass
loss = loss_function(preds, labels) # Compute loss function
loss = loss / accumulation_steps # Normalize our loss (if averaged)
loss.backward() # Backward pass
if (i+1) % accumulation_steps == 0: # Wait for several backward steps
optim.step() # Perform an optimizer step
network.zero_grad() # Reset gradients tensors
优点: 不需要多个GPU即可进行大批量训练。 即使使用单个GPU,此方法也可以进行大批量训练。
缺点: 比在多个GPU上并行训练要花费更多的时间。
使用nn.DataParallel
如果您可以访问多个GPU,则将不同的批处理拆分分配给不同的GPU,在不同的GPU上进行梯度计算,然后累积梯度以执行梯度下降是很有意义的。
多GPU下的forward和backward
基本上,给定的输入通过在批处理维度中分块在GPU之间进行分配。 在前向传递中,模型在每个设备上复制,每个副本处理批次的一部分。 在向后传递过程中,将每个副本的梯度求和以生成最终的梯度,并将其应用于主gpu(上图中的GPU-1)以更新模型权重。 在下一次迭代中,主GPU上的更新模型将再次复制到每个GPU设备上。
在PyTorch中,只需要一行就可以使用nn.DataParallel进行分布式训练。 该模型只需要包装在nn.DataParallel中。
model = torch.nn.DataParallel(model)
...
...
loss = ...
loss.backward()
优点:并行化多个GPU上的NN训练,因此与累积梯度相比,它减少了训练时间。因为代码更改很少,所以适合快速原型制作。
缺点:nn.DataParallel使用单进程多线程方法在不同的GPU上训练相同的模型。 它将主进程保留在一个GPU上,并在其他GPU上运行不同的线程。 由于python中的线程存在GIL(全局解释器锁定)问题,因此这限制了完全并行的分布式训练设置。
使用DistributedDataParallel
与nn.DataParallel不同,DistributedDataParallel在GPU上生成单独的进程进行多重处理,并利用GPU之间通信实现的完全并行性。但是,设置DistributedDataParallel管道比nn.DataParallel更复杂,需要执行以下步骤(但不一定按此顺序)。
将模型包装在torch.nn.Parallel.DistributedDataParallel中。
设置数据加载器以使用distributedSampler在所有GPU之间高效地分配样本。 Pytorch为此提供了torch.utils.data.Distributed.DistributedSampler。设置分布式后端以管理GPU的同步。 torch.distributed.initprocessgroup(backend ='nccl')。
pytorch提供了用于分布式通讯后端(nccl,gloo,mpi,tcp)。根据经验,一般情况下使用nccl可以通过GPU进行分布式训练,而使用gloo可以通过CPU进行分布式训练。在此处了解有关它们的更多信息https://pytorch.org/tutorials/intermediate/dist_tuto.html#advanced-topics
在每个GPU上启动单独的进程。同样使用torch.distributed.launch实用程序功能。假设我们在群集节点上有4个GPU,我们希望在这些GPU上用于设置分布式培训。可以使用以下shell命令来执行此操作。
python -m torch.distributed.launch --nproc_per_node=4
--nnodes=1 --node_rank=0
--master_port=1234 train.py <OTHER TRAINING ARGS>
在设置启动脚本时,我们必须在将运行主进程并用于与其他GPU通信的节点上提供一个空闲端口(在这种情况下为1234)。
以下是涵盖所有步骤的完整PyTorch要点。
import argparse
import torch
from torch.utils.data.distributed import DistributedSampler
from torch.utils.data import DataLoader
#prase the local_rank argument from command line for the current process
parser = argparse.ArgumentParser()
parser.add_argument("--local_rank", default=0, type=int)
args = parser.parse_args()
#setup the distributed backend for managing the distributed training
torch.distributed.init_process_group('nccl')
#Setup the distributed sampler to split the dataset to each GPU.
dist_sampler = DistributedSampler(dataset)
dataloader = DataLoader(dataset, sampler=dist_sampler)
#set the cuda device to a GPU allocated to current process .
device = torch.device('cuda', args.local_rank)
model = model.to(device)
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank],
output_device=args.local_rank)
#Start training the model normally.
for inputs, labels in dataloader:
inputs = inputs.to(device)
labels = labels.to(device)
preds = model(inputs)
loss = loss_fn(preds, labels)
loss.backward()
optimizer.step()
请注意,上述实用程序调用是针对GPU集群上的单个节点的。 此外,如果要使用多节点设置,则必须在选择启动实用程序时选择一个节点作为主节点,并提供master_addr参数,如下所示。 假设我们有2个节点,每个节点有4个GPU,第一个IP地址为" 192.168.1.1"的节点是主节点。 我们必须分别在每个节点上启动启动脚本,如下所示。
在第一个节点上运行
python -m torch.distributed.launch --nproc_per_node=4
--nnodes=1 --node_rank=0
--master_addr="192.168.1.1" --master_port=1234 train.py <OTHER TRAINING ARGS>
在第二个节点上,运行
python -m torch.distributed.launch --nproc_per_node=4
--nnodes=1 --node_rank=1
--master_addr="192.168.1.1" --master_port=1234 train.py <OTHER TRAINING ARGS>
其他实用程序功能:
在评估模型或生成日志时,需要从所有GPU收集当前批次统计信息,例如损失,准确率等,并将它们在一台机器上进行整理以进行日志记录。 PyTorch提供了以下方法,用于在所有GPU之间同步变量。
1. torch.distributed.gather(inputtensor,collectlist,dst):从所有设备收集指定的inputtensor并将它们放置在collectlist中的dst设备上。
1. torch.distributed.allgather(tensorlist,inputtensor):从所有设备收集指定的inputtensor并将其放置在所有设备上的tensor_list变量中。
1. torch.distributed.reduce(inputtensor,dst,reduceop = ReduceOp.SUM):收集所有设备的input_tensor并使用指定的reduce操作(例如求和,均值等)进行缩减。最终结果放置在dst设备上。
1. torch.distributed.allreduce(inputtensor,reduce_op = ReduceOp.SUM):与reduce操作相同,但最终结果被复制到所有设备。
有关参数和方法的更多详细信息,请阅读torch.distributed软件包。 https://pytorch.org/docs/stable/distributed.html
例如,以下代码从所有GPU提取损失值,并将其减少到主设备(cuda:0)。
#In continuation with distributedDataParallel.py abovedef get_reduced_loss(loss, dest_device):
loss_tensor = loss.clone()
torch.distributed.reduce(loss_tensor, dst=dest_device)
return loss_tensorif args.local_rank==0:
loss_tensor = get_reduced_loss(loss.detach(), 0)
print(f'Current batch Loss = {loss_tensor.item()}'
优点:相同的代码设置可用于单个GPU,而无需任何代码更改。 单个GPU设置仅需要具有适当设置的启动脚本。
缺点: BatchNorm之类的层在其计算中使用了整个批次统计信息,因此无法仅使用一部分批次在每个GPU上独立进行操作。 PyTorch提供SyncBatchNorm作为BatchNorm的替换/包装模块,该模块使用跨GPU划分的整个批次计算批次统计信息。 请参阅下面的示例代码以了解SyncBatchNorm的用法。
network = .... #some network with BatchNorm layers in itsync_bn_network = nn.SyncBatchNorm.convert_sync_batchnorm(network)
ddp_network = nn.parallel.DistributedDataParallel(
sync_bn_network,
device_ids=[args.local_rank], output_device=args.local_rank)
总结
· 要在GPU之间拆分模型,请将模型拆分为submodules,然后将每个submodule推送到单独的GPU。
· 要在GPU上拆分批次,请使用累积梯度nn.DataParallel或nn.DistributedDataParallel。
· 为了快速进行原型制作,可以首选nn.DataParallel。
· 为了训练大型模型并利用跨多个GPU的完全并行训练,应使用nn.DistributedDataParallel。
· 在使用nn.DistributedDataParallel时,用nn.SyncBatchNorm替换或包装nn.BatchNorm层。
作者:Nilesh Vijayrania
deephub翻译组
相关推荐
- 《Queendom》宣布冠军!女团MAMAMOO四人激动落泪
-
网易娱乐11月1日报道据台湾媒体报道,南韩女团竞争回归的生死斗《Queendom》昨(10/31)晚播出大决赛,并以直播方式进行,6组女团、女歌手皆演唱新歌,并加总前三轮的赛前赛、音源成绩与直播现场投...
- 正确复制、重写别人的代码,不算抄袭
-
我最近在一篇文章提到,工程师应该怎样避免使用大量的库、包以及其他依赖关系。我建议的另一种方案是,如果你没有达到重用第三方代码的阈值时,那么你就可以自己编写代码。在本文中,我将讨论一个在重用和从头开始编...
- HTML DOM tr 对象_html event对象
-
tr对象tr对象代表了HTML表格的行。HTML文档中出现一个<tr>标签,就会创建一个tr对象。tr对象集合W3C:W3C标签。集合描述W3Ccells返回...
- JS 打造动态表格_js如何动态改变表格内容
-
后台列表页最常见的需求:点击表头排序+一键全选。本文用原生js代码实现零依赖方案,涵盖DOM查询、排序算法、事件代理三大核心技能。效果速览一、核心思路事件入口:为每个<th>绑...
- 连肝7个晚上,总结了66条计算机网络的知识点
-
作者|哪吒来源|程序员小灰(ID:chengxuyuanxiaohui)计算机网络知识是面试常考的内容,在实际工作中经常涉及。最近,我总结了66条计算机网络相关的知识点。1、比较http0....
- Vue 中 强制组件重新渲染的正确方法
-
作者:MichaelThiessen译者:前端小智来源:hackernoon有时候,依赖Vue响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据。或者,我们可能只想抛开当前的...
- 为什么100个前端只有1人能说清?浏览器重排/重绘深度解析
-
面试现场的"致命拷问""你的项目里做过哪些性能优化?能具体讲讲重排和重绘的区别吗?"作为面试官,我在秋招季连续面试过100多位前端候选人,这句提问几乎成了必考题。但令...
- HTML DOM 介绍_dom4j html
-
HTMLDOM(文档对象模型)是一种基于文档的编程接口,它是HTML和XML文档的编程接口。它可以让开发人员通过JavaScript或其他脚本语言来访问和操作HTML和XML文档...
- JavaScript 事件——“事件流和事件处理程序”的注意要点
-
事件流事件流描述的是从页面中接收事件的顺序。IE的事件流是事件冒泡流,而NetscapeCommunicator的事件流是事件捕获流。事件冒泡即事件开始时由最具体的元素接收,然后逐级向上传播到较为不...
- 探秘 Web 水印技术_水印制作网页
-
作者:fransli,腾讯PCG前端开发工程师Web水印技术在信息安全和版权保护等领域有着广泛的应用,对防止信息泄露或知识产品被侵犯有重要意义。水印根据可见性可分为可见水印和不可见水印(盲水印)...
- 国外顶流网红为流量拍摄性侵女学生?仅被封杀三月,回归仍爆火
-
曾经的油管之王,顶流网红DavidDobrik复出了。一切似乎都跟他因和成员灌酒性侵女学生被骂到退网之前一样:住在950万美元的豪宅,开着20万美元的阿斯顿马丁,每条视频都有数百万观看...人们仿佛...
- JavaScript 内存泄漏排查方法_js内存泄漏及解决方法
-
一、概述本文主要介绍了如何通过Devtools的Memory内存工具排查JavaScript内存泄漏问题。先介绍了一些相关概念,说明了Memory内存工具的使用方式,然后介绍了堆快照的...
- 外贸独立站,网站优化的具体内容_外贸独立站,网站优化的具体内容有哪些
-
Wordpress网站优化,是通过优化代码、数据库、缓存、CSS/JS等内容,提升网站加载速度、交互性和稳定性。网站加载速度,是Google搜索引擎的第一权重,也是SEO优化的前提。1.优化渲染阻塞。...
- 这8个CSS工具可以提升编程速度_css用什么编译器
-
下面为大家推荐的这8个CSS工具,有提供函数的,有提供类的,有提取代码的,还有收集CSS的统计数据的……请花费两分钟的时间看完这篇文章,或许你会找到意外的惊喜,并且为你的编程之路打开了一扇新的大门。1...
- vue的理解-vue源码 历史 简介 核心特性 和jquery区别 和 react对比
-
一、从历史说起Web是WorldWideWeb的简称,中文译为万维网我们可以将它规划成如下的几个时代来进行理解石器时代文明时代工业革命时代百花齐放时代石器时代石器时代指的就是我们的静态网页,可以欣...
- 一周热门
- 最近发表
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)