Horovod源码剖析:
核心模块 – operations
horovod/common/operations.h
首先了解一下mpi常见的通信操作
MPI-Scatter:scatter与broadcast类似都是一对多的通信,将一段array 的不同部分发送给所有的进程
MPI-Boardcast:与scatter进行区分,broadcast是将0号进程将相同的信息发送给所有的进程;
MPI-Gather:MPI_Gather和scatter刚好相反,他的作用是从所有的进程中将每个进程的数据集中到根进程中, 同样根据进程的编号对array元素排序
4. MPI-Allgather:当数据分布在所有的进程中时,MPI_Allgather将所有的数据聚合到每个进程中。 定义的操作: 5. MPI-Reduce:所有节点上的值进行规约(求和) 6. MPI-Allreduce:reduce之后在进行boardcast,
horovod 定义的通信算子
Allreduce:Allgather:Broadcast:Join:
horovod 的实现
分成三大模块:(1)适配层(2)统一层(3)算子实现层
适配层
python如何调用c++的动态链接库:适配不同的深度学习框架
统一层:不同框架都需要调用的统一算法,在horovod/common下
horovod 使用步骤:
初始化horovod, hvd.init将一个GPU与一个进程worker绑定 config.gpu_options.visible_device_list = str(hvd.local_rank())根据GPU数量放大学习率 opt = tf.train.AdgradOptimizer(lr*hvd.size())使用hvd.DistributedOptimizer封装原有的optimizer opt = hvd.DistributedOptimizer(opt)
每一个worker的梯度仍有原来的的optimzer计算,只是梯度同步有hvd.DistributedOptimizer计算
分布式训练策略
数据并行:
同步模式:要求各个设备的计算能力要均衡,而且要求集群的通信也要均衡,防止一个拖油瓶拖慢整个集群的训练 异步模式:会出现梯度失效的问题,同样是因为各个设备的运算与通信速度不同,导致某些worker的参数比较新,默写worker的参数比较旧
模型并行:主要是针对大模型,如google的神经翻译系统或者模型本身存在一些可以并行的单元,那么也可以利用模型并行来提升训练速度,比如goolenet的inception模块
分布式通信算法
PS:allreduce操作,随着节点的增多,通信的时延也会提升。Ring-allreduce:希望减少allreduce产生的单节点带宽受限的问题。在有N块卡的集群中,每张卡都将数据分成mini-batch/N份,不同的卡计算不同的数据块索引。一张卡的最大带宽就是一个节点的数据量,不会随着节点的增多而产生时延的升高。
Data Transferred=2(N−1)*K/N,数据传输量不随着GPU节点的增多而提高。一次迭代的耗时为为K/V_bottleneck,与总数据量和最慢的worker相关。 GPU节点的增多而提高。一次迭代的耗时为为K/V_bottleneck,与总数据量和最慢的worker相关。相对于PS模型,Ring-allreduce更适合数据量比较大的的情形,这样可以使用更多的