本篇文章主要是对深度学习中运用多GPU进行训练的一些基本的知识点进行的一个梳理

文章中的内容都是经过认真地分析,并且尽量做到有所考证

抛砖引玉,希望可以给大家有更多的启发,并能有所收获

介绍

大多数时候,梯度下降算法的训练需要较大的Batch Size才能获得良好性能。而当我们选择比较大型的网络时候,由于GPU资源有限,我们往往要减少样本数据的Batch Size。

当GPU无法存储足够的训练样本时,我们该如何在更大的batch size上进行训练?

面对这个问题,事实上我们有几种工具、技巧可以选择,它们也是下文中将介绍的内容。

在这篇文章中,我们将探讨:

  • 多GPU训练和单GPU训练有什么区别
  • 如何最充分地使用多GPU机器
  • 如何进行多机多卡训练?

更多关于多机多卡的分布式训练的详细架构理解和实践请参考我的下一篇文章:

Zhang Bin:深度学习分布式训练相关介绍 - Part 2 详解分布式训练架构PS-Worker与Horovod​zhuanlan.zhihu.com

本文章介绍的内容在框架间是通用的,代码示例为:在不借助外部框架的情况下,将单GPU训练TensorFlow代码改为支持多GPU的训练代码

单GPU训练 vs 多GPU训练

单GPU训练 一般代码比较简单,并且能满足我们的基本需求,通常做法是设定变量CUDA_VISIBLE_DEVICES的值为某一块GPU来Mask我们机器上的GPU设备,虽然有时当我们忘了设定该变量时程序会自动占用所有的GPU资源,但如果没有相应的代码去分配掌控GPU资源的使用的话,程序还是只会利用到第一张卡的计算资源,其他的资源则仅是占用浪费状态。

多GPU训练 则可以从两个方面提升我们模型训练的上限:1. 超过单卡显存上限的模型大小, 2. 更大的Batch Size和更快训练速度。相应的,目前各大主流框架的多GPU训练一般存在两种模式:

  • 模型并行 :分布式系统中的不同GPU负责网络模型的不同部分,进而可以 构建超过单卡显存容量大小的模型 。比如,可以将神经网络的不同层分配到不同的GPU设备,或者将不同的参数变量分配到不同的GPU设备。
  • 数据并行 :不同的 GPU设备有同一模型的多个副本,将数据分片并分配到每个GPU上,然后将所有GPU的计算结果按照某种方式合并,进而可以增加训练数据的Batch Size

此外,从主机的数量的角度来讲还存在 单机多卡多机多卡(分布式)的区别:

  • 单机多卡:只需运行一份代码,由该代码分配该台机器上GPU资源的使用
  • 多机多卡:每台机器上都需要运行一份代码,机器之间需要互相通信传递梯度,并且模型参数的更新也存在同步训练模式和异步训练模式的区别

多GPU机器的充分利用

这一节,将详细探讨一下多GPU的机器该如何利用。

对于多GPU训练,一般我们需要用数据并行的模式比较多,通过增大 Batch Size 并辅以较高的 Learning Rate 可以加快模型的收敛速度。

由于我们模型是通过若干步梯度反向传播来迭代收敛模型的参数,而每一步的梯度由一个Batch内样本数据的损失情况(Loss)得到,因此当 Batch Size 比较大时, 可以减少 Batch样本的分布和整体样本的分布偏差太大的风险,进而使得每一步的梯度更新方向更加准确。

比如,单卡训练 InceptionResNet 网络最大Batch Size为100, 学习率为0.001。采用4张卡去训练时,可以设置Batch Size为400, 学习率为 0.002。在代码中对每一个Batch 400 切分成 4*100,然后给到不同GPU卡上的模型上去训练。

下面主要介绍一下单机多卡训练的细节及部分Tensorflow代码:

  • 数据切片
  • 模型构建
  • 梯度反传

1. 数据分片

对于多GPU训练,我们首先要做的就是对训练数据进行分片,对于单机多卡模型,其数据的分片可以在代码的内部进行, 而对于多机多卡模型,多机之间没有必要进行数据切分方面的通信,建议的做法是先在本地做好数据的分配,然后再由不同的机器读取不同的数据。

Tensorflow 单机多卡的数据切片代码如下,其中 training_iterator 为采用tf.data.Dataset 构建的训练数据pipeline, num_tower 为当前机器上可见的(CUDA_VISIBLE_DEVICES)GPU数量。

tower 指模型的副本。

mnist_input, mnist_label = training_iterator.get_next()
tower_inputs = tf.split(mnist_input, num_towers)
tower_labels = tf.split(mnist_label, num_towers)

通过 tf.split 函数,将输入的数据按照GPU的数量平分。

2. 构建模型

有了分好的数据,下一步骤则是需要将模型放置在我们所有的GPU上,并将切片好的数据传入。

Tensorflow可以指定将不同的任务分配到不同的设备上, GPU支持大量并行计算, 因此可以将模型的运算、梯度的计算分配到GPU上来,而变量的存储、梯度的更新则可以由CPU来执行,如下图所示

下面我们来看一下TensorFlow的相关代码

最外层的 for i in range(num_towers): 使我们需要构建模型 num_towers次。

下一层的 with tf.device(device_string % i): 代表着每次我们构建模型时,模型放置的设备名称, 如果为GPU机器device_string = '/gpu:%d' ,如果为CPU机器 device_string = '/cpu:%d'

再下一层 with (tf.variable_scope(("tower"), reuse=True if i > 0 else None)): 通过 scope 的reuse 使得我们再后续放置模型的时候,不同GPU之间的模型参数共享。

最后一层 with (slim.arg_scope([slim.model_variable, slim.variable], device="/cpu:0" if num_gpus != 1 else "/gpu:0")): 使得我们将构建模型中的变量放到CPU上来, 而运算则仍保留在该GPU上。

tower_gradients = []
tower_predictions = []
tower_label_losses = []
optimizer = tf.train.AdamOptimizer(learning_rate=1E-3)for i in range(num_towers):with tf.device(device_string % i):with (tf.variable_scope(("tower"), reuse=True if i > 0 else None)):with (slim.arg_scope([slim.model_variable, slim.variable],device="/cpu:0" if num_gpus != 1 else "/gpu:0")):x = tf.placeholder_with_default(tower_inputs[i], [None, 224,224,3], name="input")y_ = tf.placeholder_with_default(tower_labels[i], [None, 1001], name="label")# logits = tf.layers.dense(x, 10)logits = build_model(x)predictions = tf.nn.softmax(logits, name="predictions")loss = tf.losses.softmax_cross_entropy(onehot_labels=y_, logits=logits)grads = optimizer.compute_gradients(loss)tower_gradients.append(grads)tower_predictions.append(predictions)tower_label_losses.append(loss)

3. 梯度反传

上一步已经得到了各个tower上的梯度,下面则需要将这些梯度结合起来并进行反向传播以更新模型的参数。

梯度结合的代码可以参考下面的函数

def combine_gradients(tower_grads):"""Calculate the combined gradient for each shared variable across all towers.Note that this function provides a synchronization point across all towers.Args:tower_grads: List of lists of (gradient, variable) tuples. The outer listis over individual gradients. The inner list is over the gradientcalculation for each tower.Returns:List of pairs of (gradient, variable) where the gradient has been summedacross all towers."""filtered_grads = [[x for x in grad_list if x[0] is not None] for grad_list in tower_grads]final_grads = []for i in range(len(filtered_grads[0])):grads = [filtered_grads[t][i] for t in range(len(filtered_grads))]grad = tf.stack([x[0] for x in grads], 0)grad = tf.reduce_sum(grad, 0)final_grads.append((grad, filtered_grads[0][i][1],))return final_grads

通过 combine_gradients 来计算合并梯度,再通过 optimizer.apply_gradients 对得到的梯度进行反向传播

merged_gradients = combine_gradients(tower_gradients)
train_op = optimizer.apply_gradients(merged_gradients, global_step=global_step)

最后再通过 sess.run(train_op) 对执行。

多机多卡 分布式训练

多机多卡相比较于单机多卡,其使得模型训练的上限进一步突破。一般我们一台服务器只支持8张GPU卡,而采用分布式的多机多卡训练方式,可以将几十甚至几百台服务器调度起来一起训练一个模型。

但相比于单机多卡,多机多卡分布式训练方式的配置更复杂一些,不仅要保证多台机器之间是可以互相通信的,还需要配置不同机器之间的角色以及不同机器之间梯度传递。

  • TensorFlow 原生 PS架构
    在Parameter server架构(PS架构)中,集群中的节点被分为两类:parameter server和worker。其中parameter server存放模型的参数,而worker负责计算参数的梯度。在每个迭代过程,worker从parameter sever中获得参数,然后将计算的梯度返回给parameter server,parameter server聚合从worker传回的梯度,然后更新参数,并将新的参数广播给worker。
  • Uber 开发的Horovod架构 - 支持 TensorFlow, Keras, PyTorch, and Apache MXNet
    Horovod 是一套面向 TensorFlow 的分布式训练框架,由 Uber 构建并开源,目前已经运行于 Uber 的 Michelangelo 机器学习即服务平台上。Horovod 能够简化并加速分布式深度学习项目的启动与运行。
    Horovod架构采用全新的梯度同步和权值同步算法,叫做 ring-allreduce。此种算法各个节点之间只与相邻的两个节点通信,并不需要参数服务器。因此,所有节点都参与计算也参与存储。

关于分布式训练的更多介绍,请参考我的下一篇文章

Zhang Bin:深度学习分布式训练相关介绍 - Part 2 详解分布式训练架构PS-Worker与Horovod​zhuanlan.zhihu.com

相关参考链接

TensorFlow - Multi GPU Computation

分布式tensorflow(一)

TensorFlow分布式全套(原理,部署,实例)

distributeTensorflowExample

程序如何在两个gpu卡上并行运行_深度学习分布式训练相关介绍 - Part 1 多GPU训练...相关推荐

  1. 程序如何在两个gpu卡上并行运行_【他山之石】如何支撑上亿类别的人脸训练?显存均衡的模型并行(PyTorch实现)...

    18年的工作,一直没抽出时间整理出来,模型并行看似神秘,在网上搜索相关资料的时候大部也是以谈原理的居多,唯独少了有人拿出代码来捅破这层窗户纸.这里我放出一个PyTorch版本的Demo供大家参考交流. ...

  2. 【31】GPU(下):为什么深度学习需要使用GPU?

    [计算机组成原理]学习笔记--总目录 [31]GPU(下):为什么深度学习需要使用GPU? 引言 一.Shader 的诞生和可编程图形处理器[GPU发展历史] 1.可编程管线(Programable ...

  3. 31 | GPU(下):为什么深度学习需要使用 GPU?

    31 | GPU(下):为什么深度学习需要使用 GPU? 上一讲,我带你一起看了三维图形在计算机里的渲染过程.这个渲染过程,分成了顶点处理.图元处理. 栅格化.片段处理,以及最后的像素操作.这一连串的 ...

  4. 史上最全面的深度学习硬件指南

    转载自: http://it.sohu.com/20160204/n436921549.shtml 英语原文来自: http://timdettmers.com/2015/03/09/deep-lea ...

  5. 28款GitHub最流行的开源机器学习项目,推荐GitHub上10 个开源深度学习框架

    20 个顶尖的 Python 机器学习开源项目 机器学习 2015-06-08 22:44:30 发布 您的评价: 0.0 收藏 1收藏 我们在Github上的贡献者和提交者之中检查了用Python语 ...

  6. 深度学习对GPU要求高吗?为什么深度学习要用GPU?

    深度学习对GPU要求高吗?为什么深度学习要用GPU?实际上深度学习并不一定需要用GPU,只是如果不使用GPU,训练模型所耗的时间会变得巨长.GPU优势在于并行运算,而深度学习中的运算大部分都是矩阵运算 ...

  7. Uber发布史上最简单的深度学习框架Ludwig!

    昨日,Uber官网重磅宣布新开源深度学习框架Ludwig,不需要懂编程知识,让专家能用的更顺手,让非专业人士也可以玩转人工智能,堪称史上最简单的深度学习框架! Ludwig是一个建立在TensorFl ...

  8. 重磅!Uber发布史上最简单的深度学习框架Ludwig!不懂编程也能玩转人工智能

    点击我爱计算机视觉标星,更快获取CVML新技术 昨日,Uber官网重磅宣布新开源深度学习框架Ludwig,不需要懂编程知识,让专家能用的更顺手,让非专业人士也可以玩转人工智能,堪称史上最简单的深度学习 ...

  9. Uber发布史上最简单的深度学习框架Ludwig!不懂编程也能玩转人工智能

    昨日,Uber官网重磅宣布新开源深度学习框架Ludwig,不需要懂编程知识,让专家能用的更顺手,让非专业人士也可以玩转人工智能,堪称史上最简单的深度学习框架! image Ludwig是一个建立在Te ...

最新文章

  1. 检测到会话cookie中缺少HttpOnly属性
  2. MIP技术交流分享(3月9日)
  3. wordpresd免登录发布接口php_屏蔽修改wp-login.php登录入口确保WordPress网站后台安全...
  4. gnuplot读取mysql数据库_Tpcc-MySQL测试并使用gnuplot生成图表
  5. mysql 提示ssl问题
  6. 猿取向的规划设计模式 ——GoF《设计模式》阅读摘要(零)
  7. MySQL四种事务隔离级的说明
  8. thinkphp5--多文件入口设置
  9. You need to use a Theme.AppCompat theme (or descendant) with this activity
  10. 如何快速解决或避免EDI系统磁盘空间不足?
  11. 91位图和bigemap大地图的区别
  12. 20考研率辉计算机复试(散列)
  13. 怎样实现VLAN间通信,三种解决方案,一节课带你掌握
  14. 要做就做明链,这才是堂堂正正的优化之路,不要走捷径
  15. 未来计算机教师职业愿景展望,教师愿景与职业规划
  16. 服务器终端性能测试之MBW内存测试
  17. PMBOK第六版工具与技术:数据收集数据分析数据表现
  18. 51单片机8*8点阵,按键控制数字加拼音上下左右移动
  19. 手机端网页技术--使自己做的asp.net网页适应手机浏览
  20. 风靡全球25年的重磅IP,新作沦为脚本乐园

热门文章

  1. AUTOSAR从入门到精通100讲(二十三)-AUTOSAR通信篇—PduR模块
  2. 初中数学知识点总结_初中物理 | 最全知识点总结
  3. oracle 回滚空间查询,oracle回滚段和回滚表空间操作
  4. java 8是指什么_java中8个基本数据类型到底是指什么?是什么意思,有什么作用?我需要权威的回答,...
  5. ac算法 有什么用 Java_AC算法使用例子
  6. java 静态方法与实例方法的区别_静态方法与实例方法的区分
  7. 简单的css缩放动画,仿腾讯新闻的分享按钮和美团app底部的图标样式
  8. 4、Node.js REPL(交互式解释器)
  9. 将一个实体转换成 Url 参数的形式 ?a=ab=b
  10. 记HTML5 a 标签的一个小坑