中国电影,分布式练习从入门到抛弃,安静

技能前沿

作者:张智

编辑整理:萝卜兔


在操练神经网络模型时,咱们更偏心大规划的数据集和杂乱的网络结构。更大规划的数据集和更杂乱的网络结构能够让咱们的模型表征才能更强,但一起也对核算的时刻和空间提出了应战。


序文


假如咱们只用单机单GPU来跑,则会呈现一卡有难(运转时刻长、显存缺乏等),十卡围观的状况。许多神经网络库都供给了分布式操练的API,可是假如不了解内涵机理,咱们依然很难得到满足的功率和作用。


首要需求清晰的是神经网络模型的操练进程中,哪些部分能够并行,哪些部分不能并行。

关于一个 Batch 中的第 个样例xi 来说,经过 forward 求得 lossi 再经过 backward 求得梯度 Gi 的进程是彼此独立的,不相同例能够一起进行核算。可是,当一切样例的 backward 完结后,咱们需求求再运用  更新操练参数,这个进程依靠一切样例的核算成果 Gi ,不能并行。


Parameter Server


咱们能够仿照 MapReduce 的思路,将上述能够并行的部分作为 Mapper,不能并行中国电影,分布式操练从入门到扔掉,安静的部分作为 Reducer。Parameter Server 包括一个参数服务器(其实纷歧定是服务器,能够是 GPU0),和几个作业服务器(其实纷歧定是服务器,能够是 GPU1、GPU2、GPU3)。下图中左边为参数 GPU(GPU 0),用于存储参数和数据;三浦折叠右侧三个为作业 GPU(GPU1、GPU2、GPU3),用于前馈和反应核算。


模型的操练能够分红 5 步:

1. 在操练前将数据和初始化参数加言汐霍念晟载进 GPU0 中,假如无法一次加载进来也能够分片加载;

2. 参数服务器 GPU0 将一个 Batch 的数据切成 3 份,交给作业服务器 GPU1、GPU2、伤风能够吃鸡蛋吗GPU3(Map);

3. 参数服务器 GPU0 将模型(参数)仿制 3 8090新视觉份,交给作业服务器 GPU1、GPU2、GPU3(Map);

4. 作业服务器 GPU1、GPU2、GPU3 运用数据和模型求得 loss 和梯度;

5. 将梯度求均匀,在参数服务器 GPU0 更新参关迟数(Reducer),并回到第二步(由于 GPU 中已经有数据了,所以不需求再进行第一步);


跟着几个作业服务器 GPU 的增多,咱们所需求的操练时刻会越来越短。在 Pytnoneblrorch 中,咱们只需求这样调用 API,谙组词就能够完结 PS 并行:

import torchimport torch.nn as nn
# Define dataset...train_dataset = ...train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=...)
# 留意这儿模型地点的 GPU 应与 device_ids[0] 和 output_device 共同model = ...model_dp = nn.DataParallel(model.cuda(), device_ids=[0, 1, 2], output_device=0)
# 留意要经过 module 获取模型实例optimizer = optim.SGD(model_dp.module.parameters复合维生素b())
for epoch in range(100): for batch_idx, (data, target) in enumerate(train_loader): optimize宣震新浪博客r.zero_grad() output = model(data) loss = F.nll_loss(output, target) loss.backward() optimizer.step() if batch_idx % args.log_interval == 0: print('Train Epoch: {} [{}/{}]\tLoss: {}'.format( epoch, batch_idx * len(data), len(train_dataset), loss.item()))

左右滑动看悉数代码


可是,GPU 之间带宽有限中国电影,分布式操练从入门到扔掉,安静、数据传输很耗时。常常跑图 10 分钟,打怪 30 秒。Mapper 和 Reducer 都需求占用很多带宽。假如是 15M/s 的网络传输速度,GPU0 Mapper 完 1G 的数据和模型就要一分多钟。因而,咱们需求一种机中国电影,分布式操练从入门到扔掉,安静制涣散 GPU0 的传输压力。


Ring Allreduc中国电影,分布式操练从入门到扔掉,安静e


怎么涣散网络传输的压力,一直是高性能核算研讨的首要课题之一。超算的集群经历能够为我供给学习。Ring Allreduce 是高性能核算中比较常用的技能,它能够协助咱们在不运用中心节点的前提下完结 Map 和 Reduce。

Ring Allreduce 包括 5 个相等的作业 GPU(GPU1、GPU2、GPU3、GPU4、GPU5),第 i 个 GPU 能够从第 i-1 个 GPU 接纳数据,能够向第 i+1 个光大 GPU 发送数据,构成一个环(所以叫 ring)。


操练前将数据切片放入 GPU1、GPU2、GPU3、GPU4、GPU5 中,将初始化参数仿制到 GPU1、GPU2、GPU3、GPU4、GPU5 中。


GPU山城小岳岳 核算模型的梯度 Gi 时,依据模型的层数 j 将 Gi 分红 j 份。咱们求也便是在求每一个  。关于一个 5 层的网络,咱们能够写成这样:

i=1 开端,GPUi  向 GPUi+1  发送第 i 层的梯度 Gii  , GPUi+1  在收到梯度后求和(所以叫 all reduce,每一个 GPU 都是一个 reducer)。

从 i=2 开端,GPUi  向 GPUi+1   发送第 i 层的梯度 Gii , GPUi+1 在收到梯度后求和。

从  i=3 开端,GPUi 向 GPUi+1   发送第 i 层的梯度Gii   中国电影,分布式操练从入门到扔掉,安静,GPUi+1  在收到梯度后求和。

从 &xxxx日本nbsp;i=4  开端,GPUi 向 GPUi+1 发送第 i 层的梯度Gii ,GPUi+1 在收到梯度后求和。

上述 reduce 进程完结了求和,咱们还需求将求和的成果同步到一切 GPU 中。从 i=1 开端,GPU向 GPUi+1 发送第 i 层的梯度 Gii ,  在收到梯度后运用收到的梯度掩盖当时梯度(更换了一种 reduce 操作)。

从  i=2 开端打麻将技巧十句口诀,GPUi 向 GPUi+1 &宫小柒nbsp;发送第 i 层的梯度 Gii , GPUi+1 在收到梯度后运用收到的梯度掩盖当时梯度。

从  i=3  开端,GPUi  向 GPUi+1  发送第 i 层的梯度 Gii  , GPUi+1 在收到梯度后运用收到的梯度掩盖当时梯度。

从 i=4 开端,GPUi  向 GPUi+1  发送第 i 层的梯度 Gii ,GPUi+1  在总裁的挂名老婆收到梯度后运用收到的梯度掩盖当时梯度。

最终只需求再除以 N(N 为 GPU 个数)得到的成果便是  了。运用 Ring Allreduce ,咱们依然能够像 Parameter Server 相同在 2 x(N-1)中国电影,分布式操练从入门到扔掉,安静次传输后完结参数的更新进程。


可是,每次传输不再是针对中心节点,而是涣散在各节点的两两之间,大大减小了对带宽的压力,完结了带宽并行。


在 Pytorch 中,咱们只需求这样调用 Horovod API 就能够完结 Ring Allreduce:

import torchimport horovod.torch as hvd
# 初始化hvd.init()
# 设置可用 GPUtorch.cuda.set_device(hvd.local_ra中国电影,分布式操练从入门到扔掉,安静nk())
# Define dataset...train_dataset = ...
# 数据切片分给几个 GPUtrain_sampler = torch.utils.data.distributed.DistributedSampler( train_dataset, num_replicas=hvd.size(), rank=hvd.rank())
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=..., sampler=train_sampler)
model = ...model.cuda()
# 初始化参数仿制给几个 GPUhvd.broadcast_parameters(model谷歌结构.state_dict(), root_rank=0)optimizer = optim.SGD(model.parameters())
# 每个 GPU 完结前馈和反应、all reduce 完结核算均匀梯度、更新参数optimizer = hvd.DistributedOptimizer(optimizer, named爱情的滋味_parameters=model.named_parameters())
for epoch in range(100):for batch_idx, (data, target) in enumerate(train_loader): optimizer.zero_grad(美人动态图片) output = model(data) loss = F.nll_loss(output, target) loss.backward() optimizer.step() if batch_idx % args.log_interval == 0:不要啊师傅 print('Train Epoch: {} [{}/{}]\tLoss: {}'.format( epoch, batch_idx * len(data), len(train_sampler), loss.item()))

左右滑动看悉数代码


在实践的使用进程中,咱们或许还需求对 Embedding 或是 BatchNormalize 的并行进行定制化处理,不然会对模型的梯度更新有所影响,从而导致 train 和 val 的精确之间有较大的 gap。笔者正在完结这部分的开发,咱们将在下一篇文章具体介绍。


作者简介

感谢来自深圳大学的研讨生张智童鞋,为咱们共享精彩的内容。


<<  向左滑动,增加@Eva 入群>>