Horovod之分布式训练的使用(tensorflow),注意事项以及加速优化


最近由于工作需要,重新研究了下horovod。


Horovod原理

详细可看原论文
总结:

  1. Horovod 使用ring-all-reduce分布式计算方式。
  2. 运行过程:
    • 前向 每个模型收到batch size大小的输入以及计算gradients;
    • 后向时,每个模型分别计算gradients;
    • 使用allreduce计算average gradients;
    • 使用gradient进行更新;
      不同的gpu使用不同的训练数据训练,再使用allredece做整合,相当于增加了batch size。

Horovod 安装

推荐使用满足cuda版本的各个软件的最新版本。

openmpi
nccl
horovod
tf/pytorch

Tensorflow例子

tf的session运行时可以添加hooks(钩子)和scanfold(脚手架)指定sess跑前,跑后要干些什么规定化操作。
常见的hook有:

  • LoggingTensorHook:自动个n步输出日志。
  • CheckpointSaverHook:自动保存文件以及自动从最新的ckpt恢复。
    简单的来说,hook就是将一些每一步训练前后常用操作写成一个端口,方便复用。

Horovod定义了两种初始化变量的方法

  1. 不适用hook
bcast = hvd.broadcast_global_variables(0)
  1. 使用hooks
hooks = [hvd.BroadcastGlobalVariablesHook(0)]

以下会分别做相关的介绍。

1. Session(不使用hooks)
import tensorflow as tf
import horovod.tensorflow as hvd


# Initialize Horovod
hvd.init()

# Pin GPU to be used to process local rank (one GPU per process)
config = tf.ConfigProto()
config.gpu_options.visible_device_list = str(hvd.local_rank())

# Build model...
loss = ...
opt = tf.train.AdagradOptimizer(0.01 * hvd.size())

# Add Horovod Distributed Optimizer
opt = hvd.DistributedOptimizer(opt)

# Add hook to broadcast variables from rank 0 to all other processes during
# initialization.
bcast = hvd.broadcast_global_variables(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

with tf.train.Session(config=config,hooks=hooks) as sess:
  bcast.run()
  while not sess.should_stop():
    # Perform synchronous training.
    sess.run(train_op)
2. MonitoredTrainingSession版本(使用hooks)

Session版本也可以使用hooks, 使用以下代码可以使用session调用hooks

call hooks.begin()
sess = tf.compat.v1.Session()
call hooks.after_create_session()
while not stop is requested:
  call hooks.before_run()
  try:
    results = sess.run(merged_fetches, feed_dict=merged_feeds)
  except (errors.OutOfRangeError, StopIteration):
    break
  call hooks.after_run()
call hooks.end()
sess.close()

直接使用MonitoredTrainingSession,自动调用hooks会比较方便。

import tensorflow as tf
import horovod.tensorflow as hvd


# Initialize Horovod
hvd.init()

# Pin GPU to be used to process local rank (one GPU per process)
config = tf.ConfigProto()
config.gpu_options.visible_device_list = str(hvd.local_rank())

# Build model...
loss = ...
opt = tf.train.AdagradOptimizer(0.01 * hvd.size())

# Add Horovod Distributed Optimizer
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)

运行

  1. train.py指定使用哪些gpu
os.environ["CUDA_VISIBLE_DEVICES"] = "2,3"
  1. 运行命令
horovodrun -np 2 python *.py

注意事项以及如何调试优化

请按步骤检查一下的问题。

  1. 保证安装环境正常
    Open MPI 3.1.3可能有问题,推荐使用 3.1.2或者升级到Open MPI 4.0.0.
    使用正确版本的tensorflow,好像tf1.13.1对于多进程处理可能有问题,个人暂时没碰到过。
  2. 正确的gpu cpu绑定参数
    运行horovodrun和miprun时使用正确的参数(理想情况下,控制gpu的进程应该和最近的cpu接口绑定,这个取决于服务器的主板以及cpu类型,推荐使用 -map-by socket.)
mpirun —map-by socket
  1. 每个gpu一个进程
    训练代码中加入
config.gpu_options.visible_device_list = str(hvd.local_rank())
# 4为gpu数量
mpirun -np 4
  1. 查看每个gpu使用的进程数目以及占用显存大小是否正常。

  2. GPU使用率低下,数据读取过慢
    保证使用tf.data多线程读取方式或者搞笑的数据预处理方式
    一般来说gpu使用率要达到80-95,10-25说明使用率过低

  3. 检查哪些问题使得gpu运行过慢
    使用horovod timeline 或者 nvprof 查看哪里有问题

    1. tf data pipeline过慢,查看官方代码https://github.com/tensorflow/models/tree/master/official/vision/image_classification
    2. GPU之间数据交换有问题,保证你正在使用 InfiniBand.使用NCCL_DEBUG=INFO查看数据交换细节进行检查
    mpirun -np 4 — map-by socket -x NCCL_DEBUG=INFO python something.py -{params} or use horovodrun which includes the –x binding.
    
  4. GPUs 通信
    查看gpu通讯保证gpu之间数据交换正常。
    用NCCL_DEBUG=INFO查看数据交换细节。
    下为正常运行的输出结果。

gpu001:1299562:1299573 [0] NCCL INFO Ring 00 : 0[0] -> 1[1] via P2P/IPC
gpu000:149460:149495 [0] NCCL INFO Ring 01 : 16 -> 0 [send] via NET/IB/0
gpu002:164181:164216 [0] NCCL INFO Ring 01 : 12 -> 8 [receive] via NET/IB/0
gpu002:164181:164216 [0] NCCL INFO Ring 01 : 4 -> 8 [receive] via NET/IB/0
  1. 其他通用的建议
    1. 使用较大的batchsize塞满gpu,但要考虑batchsize和收敛性的问题。
    2. 学习率使用正常的学习率 × \times ×gpu数目
    3. 使用最新版本的软件。
注意事项总结
  1. 正确的cpu gpu绑定方式
  2. 不浪费多余的gpu,一个gpu一个进程
  3. 使用高效的data pipeline
  4. 优化gpu使用率达到80%以上
  5. 使用最新版本的cuda相关软件,cuda以及nccl
  6. 确保不同的gpu通信正常

分布式训练总结

  1. tensorflow使用horovod能够有效的利用多gpu进行训练,而且随着gpu使用的越多,horovod提升的效果越明显。
  2. 个人觉得使用多gpu的初衷是需要试验较大的batch size或者网络模型比较大,找到最优的临界batch size需要做大量的实验。如果为了提升网络模型训练速度,不如多用用timeline+profile或者改进data pipeline。

参考资料

  1. https://github.com/horovod/horovod
  2. https://towardsdatascience.com/why-is-your-horovod-slower-than-the-usual-201b4b8574d5
  3. https://mc.ai/a-quick-guide-to-distributed-training-with-tensorflow-and-horovod-on-amazon-sagemaker/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值