Horovod源码剖析(一)
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更适合数据量比较大的的情形,这样可以使用更多的