Distributed Training & Tensorflow

由于场景&数据量的需要,所以之前在组内做了关于分布式训练的分享,这次在博客上发下

以下分别是PPT,以及对应的简单总结

Distributed Training & Tensorflow.ppt

模型并行(Model Parallelism)

切分模型,每一部分运行在不同的设备上
模型并行很复杂,并且依赖于神经网络的结构。
例如全连接网络,这种方式通常不会获得太多的收益

直观的拆分方式是一层一层的拆,分散到不同的设备上,但是上层受下层限制,前一层没训练完时,后面的只能等着,没起到并行的作用

所以,接下来我们考虑下垂直拆分,这种方法能稍微好点,但是上层需要全部下层的输出,也就意味着有大量的跨设备通信。大规模的学习,一般是跨机器的,此时这样就会更慢了。
不过,有一些网络结构,比如cnn,有一些layer只有部分连接到了更底层的layer,这样可以简单并高效的跨设备

此外,一些RNN模型是由记忆单元组成的,一个cell在t时的输出,被当做t+1时的输入。如果你水平拆分,第一步只有一个device激活,第二步有两个,随着数据传到output层所有设备都同时激活起来。这里仍然有大量的跨设备通信,但是每个单元复杂度是相同的,所以并行的运行多单元的收益是要多过通信的损耗

简单说,模型并行的方式可以加速某些类型的NN,但不是全部,它需要特别的调优,比如保证通信最多的设备是在同一个机器上

数据并行(Data Parallelism)

将模型复制在每一个设备上,然后同时接收不同的mini batch数据训练,然后聚合梯度更新模型。

这里存在两种更新方式 synchronous updates and asynchronous updates.

synchronous updates

同步更新等待所有梯度计算完毕,才去进行平均。如果有一个设备比其他都要慢,那么其他的device都要等待它,通常梯度计算完毕后,会立即复制到所有device上,此时可能会占满服务器带宽

优化:为了减少每一步的等待时间,可以忽略来自最慢副本的梯度,一般百分之十左右,比如可以运行20份,只等待18份。参数更新后,18个副本可以立刻开始训练,不需要等剩下的两个。这被叫做18replicas plus 2 spare replicas

asynchronous updates

当一个副本计算完梯度后,立即用它更新模型参数,不做聚合。副本之间是相互独立的,不需互相等待,这种方式可以每分钟运行更多次

尽管参数仍然需要在每一步复制到每个设备上,但是不同设备是在不同时间,所以降低了带宽被占满的风险

异步数据并行是非常有吸引力的选项,它简单,没有同步延迟,更好的利用带宽。实践中效果好的让人惊讶.实际上,当一个副本基于参数计算梯度时,参数将被其他device更新(平均N个设备的话,会被更新N-1次),这里没法保证被计算的梯度是指向正向的方向.当梯度严重过期时,被称为stale gradients,这会减慢收敛,引入噪声和抖动(学习曲线暂时震荡),甚至会发散

如何减少stale gradient影响:

  1. 减少学习率
  2. Drop stale gradients or scale them down
  3. 调整mini-batch尺寸
  4. 在最开始的几个epoch只用一个replica(warmup phase),stale gradient影响

https://arxiv.org/pdf/1604.00981v2.pdf 谷歌大脑团队2016年论文显示,data parallelism with synchronous using a few spare replicas是最高效的,不止收敛快,而且会产出更好的模型。目前这块还是很活跃的领域,所以不要直接排除掉异步更新

带宽饱和

不管是同步还是异步,数据并行都要在每一步训练开始前和参数服务器通信模型参数。这就意味着会有一个点,增加额外的gpu不再提高性能,因为时间都花费在数据移入移出GPU RAM。此时,更多的GPUs只会增加带宽饱和,使训练变慢

模型越大越密,需要越多的参数和梯度去更新,Saturation也越严重。
小模型不太严重(并行训练收益也小)。对于大而稀疏的模型,其梯度多为0,所以通信比较高效。Jeff Debean(nitiator and lead of the Google Brain project ) 称,对于dense model,50 GPUs 可以加速25-40倍。
对于sparser model 500GPUs可以加速300倍。
具体测试:

Neural Machine Translation: 6x speedup on 8 GPUs
Inception/ImageNet: 32x speedup on 50 GPUs
RankBrain: 300x speedup on 500 GPUs

减少带宽饱和的方法

  1. 尽量将GPUs分布在尽量少的机器上,避免不要的网络损耗
  2. 将参数分散在几个参数服务器上
  3. 减少模型参数的浮点精度,32bits to 16bits,这会减少一半的数据转移,又不会对收敛速度和模型效果有什么影响
    (甚至可以降低到8-bit,这在移动设备上部署和运行模型很有用,详细可以看Pete Warden的文章 https://petewarden.com/2016/05/03/how-to-quantize-neural-networks-with-tensorflow/)

文章主要参考Hands-on Machine Learning with Scikit-Learn and TensorFlow此书,推荐有空闲的人看看,有助于梳理知识脉络