高效分布式深度学习训练方案(一):Horovod分布式框架

(一)分布式训练基础知识:

深度学习训练中的“反向传播”:通过神经网络得到预测结果,把预测结果跟标注Label进行比对,发现误差;然后得到神经网络里每个神经元权重导数;接着通过算法得到每个神经元导数,再通过“梯度下降”原则,更新神经元的权重以得到更好的神经元网络,周而复始迭代训练,使得误差减少,最终得到能够对训练数据集得到符合误差指标的结果的一组权重参数,即训练好的一个网络模型。所谓的“梯度下降”原则,即神经网络可以看成一个非常复杂的函数,包含数百万个参数。这些参数代表的是一个问题的数学解答。实质上,训练神经网络=最小化一个损失函数。“梯度下降”原则可以帮助我们在多次迭代“反向传播”后找到那个最小化的损失函数。

目前,神经网络推理能力随着规模、复杂度增加,从计算能力角度来说出现了新问题:很多时候大规模神经网络很难在单个/单点计算单元里面运行(单卡GPU显存受限),这会导致计算很慢(降低batchsize,增加epoch),以至无法高效运行大规模数据。当隐藏网络层数越多时(总网络层级增多时),这一问题将更加严重,运算会越复杂。

人们提出两种深度学习的基本方法以解决这个问题:

(1)模型并行(model parallel)。即把复杂的神经网络拆分,分布在计算单元或者GPU里面进行学习,让每个GPU同步进行计算。这个方法通常用在模型比较复杂的情况下。

(2)数据并行(data parallel)。即让每个机器里都有一个完整模型,然后把数据切分成n块,把n块分发给每个计算单元,每个计算单元独自计算出自己的梯度。同时每个计算单元的梯度会进行平均、同步,同步后的梯度可以在每个节点独立去让它修正模型,整个过程结束后每个节点会得到同样的模型。这个方法可以让能够处理的数据量增加,变成了原来的n倍。这种方法更常用且易懂。实现“数据并行”的两种常见方法如下:

(2.1)Parameter Server

在计算单元以外加设新的服务器,即“参数服务器”。每次训练的时候每个计算单元把梯度发送给参数服务器,服务器把他们进行汇总计算平均值,把平均值返回到每个计算单元,这样每个计算单元就同步了。参数服务器的做法理论容错性比较强,因为每个节点相互之间没有牵制,互相没有关联,它只是需要跟参数服务器本身进行通信,就可以运作了。缺点是有额外的网络开销,扩展效率会受到影响。

(2.2)AllReduce(Ring-AllReduce)

把每个计算单元构建成一个环,要做梯度平均的时候每个计算单元先把自己梯度切分成N块,然后发送到相邻下一个模块。现在有N个节点,那么N-1次发送后就能实现所有节点掌握所有其他节点的数据。AllReduce优点非常明显,性能非常好,如果在大规模分布式训练时候资源利用率相当高,网络占用是最优的。它的缺点是在工程上的缺点,容错性较差。

使用 Ring-Allreduce 算法进行分布式训练基本过程如下:

  1. 每个设备根据各自的训练数据分别进行梯度的计算,得到梯度
  2. 将每个设备上的梯度向量切分成长度大致相等的 N 个分片(其中分片数 N 与设备数量相等)
  3. ScatterReduce 阶段:通过 N-1 轮梯度传输和梯度相加,在每个设备上的梯度向量都有一小部分为所有设备中该分片梯度之和
  4. AllGather 阶段:通过 N-1 轮梯度传输,将上个阶段计算出的每个梯度向量分片之和广播到其他设备
  5. 在每个设备上合并分片梯度,并根据梯度更新每个设备上的模型

在深度学习训练过程中,计算梯度采用 BP 算法,其特点是后面层的梯度先被计算,而前面层的梯度慢于前面层,Ring-allreduce 架构可以充分利用这个特点,在前面层梯度计算的同时进行后面层梯度的传递,从而进一步减少训练时间。

(二)Horovod分布式框架:

Horovod 是一套支持 TensorFlow, Keras, PyTorch, and Apache MXNet 的分布式训练框架,由 Uber 构建并开源,Horovod 的主要主要有两个优点:

  1. 采用 Ring-Allreduce 算法,提高分布式设备的效率;
  2. 代码改动少,能够简化分布式深度学习项目的启动与运行。

(1)实现流程(把单机训练代码跟Horovod结合变成多机训练代码,即分布式训练代码)

(1.1)在代码里引入Horovod库,做初始化;

(1.2)定义优化器的时候,优化器重要参数是学习率;

(1.3)用一个对象把原来优化器包起来,这就是抽象了Horovod需要进行的梯度平均的逻辑,会比较容易使用;

(1.4)让Horovod把每次训练的初始状态广播到每个节点,这样保证每个节点从同一个地方开始;

(1.5)训练的长度,由于把训练分布在N个机器上了,所以可以极大减小,除以N就可以了。

(2)参考代码(基于Tensorflow实现Horovod的官方例程):


horovod/tensorflow.rst at master · horovod/horovod · GitHubicon-default.png?t=M4ADhttps://github.com/horovod/horovod/blob/master/docs/tensorflow.rst

import tensorflow as tf
import horovod.tensorflow as hvd# 初始化 Horovod,启动相关线程和 MPI 线程。
hvd.init()# Pin GPU to be used to process local rank (one GPU per process)
config = tf.ConfigProto()
# 为不同的进程分配不同的 GPU
config.gpu_options.visible_device_list = str(hvd.local_rank())# Build model...
loss = ...
# 根据 Worker 的数量增加学习率的大小
opt = tf.train.AdagradOptimizer(0.01 * hvd.size())# Add Horovod Distributed Optimizer
# 把常规 TensorFlow Optimizer 通过 Horovod 包起来,进而使用 ring-allreduce 来得到平均梯度。
opt = hvd.DistributedOptimizer(opt)# Add hook to broadcast variables from rank 0 to all other processes during initialization.
# 将模型的参数从第一个设备传向其他设备,以保证初始化模型参数的一致性。
hooks = [hvd.BroadcastGlobalVariablesHook(0)]# Make training operation
train_op = opt.minimize(loss)# Save checkpoints only on worker 0 to prevent other workers from corrupting them.
checkpoint_dir = '/tmp/train_logs' if hvd.rank() == 0 else None# The MonitoredTrainingSession takes care of session initialization,
# restoring from a checkpoint, saving to a checkpoint, and closing when done
# or an error occurs.
with tf.train.MonitoredTrainingSession(checkpoint_dir=checkpoint_dir,config=config,hooks=hooks) as mon_sess:while not mon_sess.should_stop():# Perform synchronous training.mon_sess.run(train_op)

用户可以使用 horovodrun 命令在多个服务器中运行:

(2.1)在一个拥有 4 个 GPUs 的机器上:

horovodrun -np 4 -H localhost:4 python train.py

(2.2)在每台拥有 4 个 GPU 的 4 个机器上:

horovodrun -np 16 -H server1:4,server2:4,server3:4,server4:4 python train.py


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部