Pytorch中的分布式神经网络训练

简介: Pytorch中的分布式神经网络训练

随着深度学习的多项进步,复杂的网络(例如大型transformer 网络,更广更深的Resnet等)已经发展起来,从而需要了更大的内存空间。经常,在训练这些网络时,深度学习从业人员需要使用多个GPU来有效地训练它们。在本文中,我将向您介绍如何使用PyTorch在GPU集群上设置分布式神经网络训练。


通常,分布式训练会在有一下两种情况。

  1. 在GPU之间拆分模型:如果模型太大而无法容纳在单个GPU的内存中,则需要在不同GPU之间拆分模型的各个部分。
  2. 跨GPU进行批量拆分数据。当mini-batch太大而无法容纳在单个GPU的内存中时,您需要将mini-batch拆分到不同的GPU上。

跨GPU的模型拆分

跨GPU拆分模型非常简单,不需要太多代码更改。在设置网络本身时,可以将模型的某些部分移至特定的GPU。之后,在通过网络转发数据时,数据也需要移动到相应的GPU。下面是执行相同操作的PyTorch代码段。

fromtorchimportnnclassNetwork(nn.Module):
def__init__(self, split_gpus=False):
super().__init__()
self.module1= ...
self.module2= ...
self.split_gpus=split_gpusifsplit_gpus:           #consideringonlytwogpusself.module1.cuda(0)
self.module2.cuda(1)
defforward(self, x):
ifself.split_gpus:
x=x.cuda(0)
x=self.module1(x)
ifself.split_gpus:
x=x.cuda(1)
x=self.module2(x)
returnx

跨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, 32accumulation_steps=int(TARGET_BATCH_SIZE/BATCH_FIT_IN_MEMORY)
network.zero_grad()                           #Resetgradientstensorsfori, (imgs, labels) inenumerate(dataloader):
preds=network(imgs)                     #Forwardpassloss=loss_function(preds, labels)       #Computelossfunctionloss=loss/accumulation_steps#Normalizeourloss (ifaveraged)
loss.backward()                           #Backwardpassif (i+1) %accumulation_steps==0:       #Waitforseveralbackwardstepsoptim.step()                           #Performanoptimizerstepnetwork.zero_grad()                   #Resetgradientstensors

优点:不需要多个GPU即可进行大批量训练。即使使用单个GPU,此方法也可以进行大批量训练。

缺点:比在多个GPU上并行训练要花费更多的时间。

使用nn.DataParallel

如果您可以访问多个GPU,则将不同的批处理拆分分配给不同的GPU,在不同的GPU上进行梯度计算,然后累积梯度以执行梯度下降是很有意义的。

640.png

多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.init_process_group(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-mtorch.distributed.launch--nproc_per_node=4--nnodes=1--node_rank=0--master_port=1234train.py<OTHERTRAININGARGS>

在设置启动脚本时,我们必须在将运行主进程并用于与其他GPU通信的节点上提供一个空闲端口(在这种情况下为1234)。

以下是涵盖所有步骤的完整PyTorch要点。

importargparseimporttorchfromtorch.utils.data.distributedimportDistributedSamplerfromtorch.utils.dataimportDataLoader#prasethelocal_rankargumentfromcommandlineforthecurrentprocessparser=argparse.ArgumentParser()
parser.add_argument("--local_rank", default=0, type=int)
args=parser.parse_args()
#setupthedistributedbackendformanagingthedistributedtrainingtorch.distributed.init_process_group('nccl')
#SetupthedistributedsamplertosplitthedatasettoeachGPU.
dist_sampler=DistributedSampler(dataset)
dataloader=DataLoader(dataset, sampler=dist_sampler)
#setthecudadevicetoaGPUallocatedtocurrentprocess .
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)
#Starttrainingthemodelnormally.
forinputs, labelsindataloader:
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-mtorch.distributed.launch--nproc_per_node=4--nnodes=1--node_rank=0--master_addr="192.168.1.1"--master_port=1234train.py<OTHERTRAINING

ARGS>

在第二个节点上,运行

python-mtorch.distributed.launch--nproc_per_node=4--nnodes=1--node_rank=1--master_addr="192.168.1.1"--master_port=1234train.py<OTHERTRAININ

G ARGS>

其他实用程序功能:

在评估模型或生成日志时,需要从所有GPU收集当前批次统计信息,例如损失,准确率等,并将它们在一台机器上进行整理以进行日志记录。PyTorch提供了以下方法,用于在所有GPU之间同步变量。

  1. torch.distributed.gather(input_tensor,collect_list,dst):从所有设备收集指定的input_tensor并将它们放置在collect_list中的dst设备上。
  2. torch.distributed.all_gather(tensor_list,input_tensor):从所有设备收集指定的input_tensor并将其放置在所有设备上的tensor_list变量中。
  3. torch.distributed.reduce(input_tensor,dst,reduce_op = ReduceOp.SUM):收集所有设备的input_tensor并使用指定的reduce操作(例如求和,均值等)进行缩减。最终结果放置在dst设备上。
  4. torch.distributed.all_reduce(input_tensor,reduce_op = ReduceOp.SUM):与reduce操作相同,但最终结果被复制到所有设备。

有关参数和方法的更多详细信息,请阅读torch.distributed软件包。https://pytorch.org/docs/stable/distributed.html

例如,以下代码从所有GPU提取损失值,并将其减少到主设备(cuda:0)。

#IncontinuationwithdistributedDataParallel.pyabovedefget_reduced_loss(loss, dest_device):
loss_tensor=loss.clone()
torch.distributed.reduce(loss_tensor, dst=dest_device)
returnloss_tensorifargs.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= .... #somenetworkwithBatchNormlayersinitsync_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之间拆分模型,请将模型拆分为sub_modules,然后将每个sub_module推送到单独的GPU。
  • 要在GPU上拆分批次,请使用累积梯度nn.DataParallel或nn.DistributedDataParallel。
  • 为了快速进行原型制作,可以首选nn.DataParallel。
  • 为了训练大型模型并利用跨多个GPU的完全并行训练,应使用nn.DistributedDataParallel。
  • 在使用nn.DistributedDataParallel时,用nn.SyncBatchNorm替换或包装nn.BatchNorm层。
相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
目录
相关文章
|
2月前
|
机器学习/深度学习 人工智能 算法
AI 基础知识从 0.6 到 0.7—— 彻底拆解深度神经网络训练的五大核心步骤
本文以一个经典的PyTorch手写数字识别代码示例为引子,深入剖析了简洁代码背后隐藏的深度神经网络(DNN)训练全过程。
534 56
|
6月前
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
219 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
ICLR 2025 | EDiT:一种基于 Local SGD 策略的大模型高效分布式训练方法
蚂蚁 AI Infra 团队在深度学习最核心之一的训练框架方向上持续投入与创新,实现了提升资源利用率、加速训练、提升训练稳定性等目标。我们提出的 EDiT 方法,即为其中一项工作。
|
1月前
|
机器学习/深度学习 算法 PyTorch
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
|
25天前
|
机器学习/深度学习 算法 PyTorch
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
|
5月前
|
机器学习/深度学习 PyTorch 算法框架/工具
基于Pytorch 在昇腾上实现GCN图神经网络
本文详细讲解了如何在昇腾平台上使用PyTorch实现图神经网络(GCN)对Cora数据集进行分类训练。内容涵盖GCN背景、模型特点、网络架构剖析及实战分析。GCN通过聚合邻居节点信息实现“卷积”操作,适用于非欧氏结构数据。文章以两层GCN模型为例,结合Cora数据集(2708篇科学出版物,1433个特征,7种类别),展示了从数据加载到模型训练的完整流程。实验在NPU上运行,设置200个epoch,最终测试准确率达0.8040,内存占用约167M。
基于Pytorch 在昇腾上实现GCN图神经网络
|
5月前
|
机器学习/深度学习 算法 PyTorch
Perforated Backpropagation:神经网络优化的创新技术及PyTorch使用指南
深度学习近年来在多个领域取得了显著进展,但其核心组件——人工神经元和反向传播算法自提出以来鲜有根本性突破。穿孔反向传播(Perforated Backpropagation)技术通过引入“树突”机制,模仿生物神经元的计算能力,实现了对传统神经元的增强。该技术利用基于协方差的损失函数训练树突节点,使其能够识别神经元分类中的异常模式,从而提升整体网络性能。实验表明,该方法不仅可提高模型精度(如BERT模型准确率提升3%-17%),还能实现高效模型压缩(参数减少44%而无性能损失)。这一革新为深度学习的基础构建模块带来了新的可能性,尤其适用于边缘设备和大规模模型优化场景。
182 16
Perforated Backpropagation:神经网络优化的创新技术及PyTorch使用指南
|
5月前
|
机器学习/深度学习 自然语言处理 PyTorch
基于Pytorch Gemotric在昇腾上实现GAT图神经网络
本实验基于昇腾平台,使用PyTorch实现图神经网络GAT(Graph Attention Networks)在Pubmed数据集上的分类任务。内容涵盖GAT网络的创新点分析、图注意力机制原理、多头注意力机制详解以及模型代码实战。实验通过两层GAT网络对Pubmed数据集进行训练,验证模型性能,并展示NPU上的内存使用情况。最终,模型在测试集上达到约36.60%的准确率。
|
5月前
|
机器学习/深度学习 算法 PyTorch
基于Pytorch Gemotric在昇腾上实现GraphSage图神经网络
本实验基于PyTorch Geometric,在昇腾平台上实现GraphSAGE图神经网络,使用CiteSeer数据集进行分类训练。内容涵盖GraphSAGE的创新点、算法原理、网络架构及实战分析。GraphSAGE通过采样和聚合节点邻居特征,支持归纳式学习,适用于未见节点的表征生成。实验包括模型搭建、训练与验证,并在NPU上运行,最终测试准确率达0.665。
|
7月前
|
并行计算 PyTorch 算法框架/工具
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
本文探讨了如何通过技术手段混合使用AMD与NVIDIA GPU集群以支持PyTorch分布式训练。面对CUDA与ROCm框架互操作性不足的问题,文章提出利用UCC和UCX等统一通信框架实现高效数据传输,并在异构Kubernetes集群中部署任务。通过解决轻度与强度异构环境下的挑战,如计算能力不平衡、内存容量差异及通信性能优化,文章展示了如何无需重构代码即可充分利用异构硬件资源。尽管存在RDMA验证不足、通信性能次优等局限性,但该方案为最大化GPU资源利用率、降低供应商锁定提供了可行路径。源代码已公开,供读者参考实践。
492 3
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践

热门文章

最新文章

推荐镜像

更多