[分布式训练] 单机多卡的正确打开方式:Horovod

转自:https://fyubang.com/2019/07/26/distributed-training4/

讲完了单机多卡的分布式训练的理论、TensorFlow和PyTorch分别的实现后,今天瓦砾讲一个强大的第三方插件:Horovod。

Horovod是Uber开源的跨平台的分布式训练工具,名字来自于俄国传统民间舞蹈,舞者手牵手围成一个圈跳舞,与Horovod设备之间的通信模式很像,有以下几个特点:

  1. 兼容TensorFlow、Keras和PyTorch机器学习框架。
  2. 使用Ring-AllReduce算法,对比Parameter Server算法,有着无需等待,负载均衡的优点。
  3. 实现简单,五分钟包教包会。(划重点)

Uber官方在git上给了很详细的例子: https://github.com/horovod/horovod/tree/master/examples,所以这里只简单讲一下大概的使用方法:

TensorFlow

以TF的Custom Training Loop API为例:

import tensorflow as tf
import horovod.tensorflow as hvd# 1. 初始化horovod
hvd.init()
# 2. 给当前进程分配对应的gpu,local_rank()返回的是当前是第几个进程
config = tf.ConfigProto()
config.gpu_options.visible_device_list = str(hvd.local_rank())
# 3. Scale学习率,封装优化器
opt = tf.train.AdagradOptimizer(0.01 * hvd.size())
opt = hvd.DistributedOptimizer(opt)
# 4. 定义初始化的时候广播参数的hook,这个是为了在一开始的时候同步各个gpu之间的参数
hooks = [hvd.BroadcastGlobalVariablesHook(0)]
# 搭建model,定义loss
loss = ...
train_op = opt.minimize(loss)
# 5. 只保存一份ckpt就行
checkpoint_dir = '/tmp/train_logs' if hvd.rank() == 0 else None
# 7. 用MonitoredTrainingSession实现初始化,读写ckpt
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)

具体的代码看tensorflow_mnist.py:https://github.com/horovod/horovod/blob/master/examples/tensorflow_mnist.py

单机双卡训练输入以下命令:

CUDA_VISIBLE_DEVICES=6,7 horovodrun -np 2 -H localhost:2 python tensorflow_mnist.py

这里 -np指的是进程的数量。

执行之后可以看到如下的结果,因为多线程,每个step都打印了两遍。

[1,0]<stderr>:INFO:tensorflow:loss = 0.13126025, step = 300 (0.191 sec)
[1,1]<stderr>:INFO:tensorflow:loss = 0.01396352, step = 310 (0.177 sec)
[1,0]<stderr>:INFO:tensorflow:loss = 0.063738815, step = 310 (0.182 sec)
[1,1]<stderr>:INFO:tensorflow:loss = 0.044452004, step = 320 (0.215 sec)
[1,0]<stderr>:INFO:tensorflow:loss = 0.028987963, step = 320 (0.212 sec)
[1,0]<stderr>:INFO:tensorflow:loss = 0.09094897, step = 330 (0.206 sec)
[1,1]<stderr>:INFO:tensorflow:loss = 0.11366991, step = 330 (0.210 sec)
[1,0]<stderr>:INFO:tensorflow:loss = 0.08559138, step = 340 (0.200 sec)
[1,1]<stderr>:INFO:tensorflow:loss = 0.037002128, step = 340 (0.201 sec)
[1,0]<stderr>:INFO:tensorflow:loss = 0.15422738, step = 350 (0.181 sec)
[1,1]<stderr>:INFO:tensorflow:loss = 0.06424393, step = 350 (0.179 sec)

PyTorch

Torch下也是类似的套路,但是由于PyTorch本身单机多卡训练已经够简单了,API也稳定,所以笔者一般做的时候就是直接用Torch自己的DPDDP了。

import torch
import horovod.torch as hvd# 1. 初始化horovod
hvd.init()
# 2. 给当前进程分配对应的gpu,local_rank()返回的是当前是第几个进程
torch.cuda.set_device(hvd.local_rank())
# Define dataset...
train_dataset = ...
# 3. 用DistributedSampler给各个worker分数据
train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset, num_replicas=hvd.size(), rank=hvd.rank())
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=..., sampler=train_sampler)
# Build model...
model = ...
model.cuda()
# 4. 封装优化器
optimizer = optim.SGD(model.parameters())
optimizer = hvd.DistributedOptimizer(optimizer, named_parameters=model.named_parameters())
# 5. 初始化的时候广播参数,这个是为了在一开始的时候同步各个gpu之间的参数
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
# 训练
for epoch in range(100):for batch_idx, (data, target) in enumerate(train_loader):optimizer.zero_grad()output = model(data)loss = F.nll_loss(output, target)loss.backward()optimizer.step()if batch_idx % args.log_interval == 0:print('Train Epoch: {} [{}/{}]\tLoss: {}'.format(epoch, batch_idx * len(data), len(train_sampler), loss.item()))

速度

瓦砾还没有来得及做一个全面的Horovod、tf.distribute和 Torch的单机多卡训练速度的横向对比,不过大家可以参考这两篇:

  1. Horovod: fast and easy distributed deep learning in TensorFlow
  2. Goodbye Horovod, Hello CollectiveAllReduce

总体而言,用了All-Reduce算法的API,速度应该都差不多,如果你是土豪,拥有NVLINK(卡间通信极快)的话,那忘了我说的这几篇“废话”吧朋友。Orz。

总结

终于结束了单机多卡系列的最后一章,由于博客本身的限制,给的例子整体还是比较简单,以入门为主,大家具体使用的时候肯定还是会遇到一些坑,这里瓦砾把踩过的一些坑和解决办法列举在这,以避免大家以后重复踩坑:

  • tf.contrib.distributed.MirroredStrategy 需要optimizer支持merge_call(bert实现的optimizer是直接修改apply_gradient的,所以会报错),这个时候就需要正确地修改optimizer里的_apply_dense、_apply_sparse(参考Issue 23986 和 JayYip)。或者用horovod,就可以避免这个问题。
  • Effective batch size,不同的多卡工具对输入的batch size的操作不一样,要确定最后进模型的effective batch size才有意义。一般来说,多进程的batch size指的是每张卡的batch size。
  • Learning rate scale,学习率要根据effective batch size调整。
  • All-Reduce由于是多进程的,数据流各自独立,为了防止同一个step多gpu的batch重叠,最好的的办法是在每个进程里根据local_rank设置shard的数据,保证各个gpu采样的数据不重叠。
  • 为了使用horovod,新建docker container时,要加—privileged,否则会疯狂报warning,虽然没影响,但是看着难受。
  • Pytorch的DP多卡要注意最后一个batch的batch size不能小于gpu的数量,否则会报错,最保险的做法是drop_last,扔掉最后的batch。
  • 并不是所有情况下All-Reduce都比PS好,比如当卡间通信用的是NVLink的时候,在gpu数量不多的情况下,数据传输的时间不是瓶颈,All-Reduce的提升就几乎没有了。
  • DP和DDP有一个区别在于BatchNorm。
  • DDP封装model后不能再改动model。
  • 待补充。。。

Reference

  1. Horovod的官方给的一些例子。
  2. Uber:如何用Horovod实现bert的单机多卡训练
  3. Goodbye Horovod, Hello CollectiveAllReduce
  4. Horovod: fast and easy distributed deep learning in TensorFlow

[分布式训练] 单机多卡的正确打开方式:Horovod相关推荐

  1. [分布式训练] 单机多卡的正确打开方式:PyTorch

    [分布式训练] 单机多卡的正确打开方式:PyTorch 转自:https://fyubang.com/2019/07/23/distributed-training3/ PyTorch的数据并行相对于 ...

  2. [分布式训练] 单机多卡的正确打开方式:理论基础

    [分布式训练] 单机多卡的正确打开方式:理论基础 转自:https://fyubang.com/2019/07/08/distributed-training/ 瓦砾由于最近bert-large用的比 ...

  3. pytorch单机多卡的正确打开方式 以及可能会遇到的问题和相应的解决方法

    pytorch 单机多卡的正确打开方式 pytorch 使用单机多卡,大体上有两种方式 简单方便的 torch.nn.DataParallel(很 low,但是真的很简单很友好) 使用 torch.d ...

  4. pytorch GPU分布式训练 单机单卡、单机多卡

    可以用"watch -n 0.1 nvidia-smi"来查看gpu状态,我用的是3块12G的GPU进行实验 本实验将使用一个简单的瞎写的网络进行,网络训练一个分类任务,当然这个不 ...

  5. Redis分布式锁的正确打开方式

    Redis分布式锁的正确打开方式 为什么需要分布式锁 分布式锁原理 单机锁和分布式锁的联系与区别 Redis分布式锁的演进史 第一版 SETNX 如何避免死锁 解决锁被别人释放 锁过期时间不好评估 R ...

  6. 拜托!这才是分布式系统CAP的正确打开方式!

    "纸面"上的CAP 相信很多同学都听过CAP这个理论,为了避免我们认知不同,我们先来统一下知识起点. CAP理论在1999年一经提出就成为了分布式系统领域的顶级教义.并表明分布式服 ...

  7. 为什么说vivo S7才是5G轻薄旗舰的正确打开方式

    8月3日,vivo发布了最新的5G旗舰机型S7.S7 170g的整机重量和7.39mm的机身厚度,瞬间让其成为年轻用户追捧的热点. 一.厚重的5G手机 众所周知,5G手机由于在信号处理的技术要求上比4 ...

  8. 通过机器学习识别“迪士尼在逃公主”,程序员宠女的正确打开方式!

    到了庆祝的时候了!我们刚刚送走了圣诞老人.现在正等待新年的钟声敲响.所以我想到建立一个很酷的东西(至少我的七岁小公主会觉得)同时学一点机器学习.所以我们要做一个什么? 我借用的我女儿所有迪士尼公主人偶 ...

  9. 企业搭建私域流量的正确打开方式

    做私域流量并不是@下新好友就解决了问题,如果这样也别期待私域流量发挥太大的价值.有人说,要给私域流量提供价值.提供价值没有错,错在你给所有用户提供了大家不想要的价值,而且私域流量中有一些用户再提供价值 ...

最新文章

  1. c语言中程序偏离,C语言中的指针加减偏移量
  2. P3119 [USACO15JAN]草鉴定Grass Cownoisseur
  3. (NO.00004)iOS实现打砖块游戏(八):游戏中小球与砖块的碰撞
  4. [转]第一章 Windows Shell是什么 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987862】...
  5. [SOCI2005]最大子矩阵(DP) + [JXOI2018]守卫(DP) + [CQOI2016]手机号码(数位DP)[各种DP专练]
  6. 详细整理分层开发步骤!
  7. 阿里巴巴旗下平台口碑推出无人收银技术,改造便利店市场;重庆法院运用 AI 探索“智能判案”...
  8. python句柄无效_subprocess.Popen 运行windows命令出现“句柄无效”报错的解决方法
  9. pythongui界面复选框数值选择并求和_如何使用Python从图像中分离复选框按钮和复......
  10. python做插件应用_Python插件机制实现详解
  11. gperf linux 安装_Gperftools安装
  12. 三星电子总算找到“备胎”:从比利时采购半导体材料
  13. 网络知识和交换机的基本配置知识培训
  14. JVM设置最大最小内存参数
  15. python split 正则_Python 正则表达式:split
  16. 数据结构 2-3-3 循环链表
  17. 【011】Excel宏编程相关封装模块(新建文件、关闭文件、新增/删除工作薄)_004_#VBA
  18. WOW技术---2, 骨骼动画
  19. JavaScript中查找关键词
  20. ClickHouse MergeTree表引擎和建表语句

热门文章

  1. VS Code Git 日常操作
  2. PLSQL 查询结果只显示年月日不显示时分秒的解决方法
  3. MyBatis-Plus_实体作为条件构造器构造方法的参数
  4. VBA MultiPage 循环多页控件
  5. java配置jndi连接数_JavaWeb:Tomcat下配置数据源(JNDI)连接数据库 | 学步园
  6. python student类_9.Python类和对象
  7. vstar为什么登录不了_一手的闲鱼号,为什么现在闲鱼号一号难求
  8. php keep user login,php5.4安装dedecms登录后台空白解决办法(session_register函数已废弃)...
  9. 中小学计算机教学大纲,中小学信息技术教材教法教学大纲
  10. eclipse java的jvm匹配_eclipse设置jvm