文 | 立交桥跳水冠军@知乎
本文已获作者授权,禁止二次转载

鱼与熊掌不可兼得,深度学习领域中的几个指标也相同。

主要的指标有如下四个:

(1)精度:自然精度是一个模型最根本的衡量指标,如果一个模型精度不高,再快,再绿色环保也无济于事。基本上所有刷榜的工作都是用其他所有指标换精度:比如用更深的网络就是用memory和computation换精度。然而到了实际应用中,尤其是部署侧,工程师越来越多的用一些方法适当的减少精度从而换取更小的内存占用或者运算时间 。

(2)内存:Out Of Memory Error恐怕是炼丹师最常见的情况了。内存(或者说可以高效访问的存储空间)的尺寸是有限的,如果网络训练需要的内存太大了,可能程序直接就报错了,即使不报错,也需要把内存中的数据做个取舍,一部分存到相对较慢的存储介质中(比如host memory)。

(3) 通信:随着网络规模越来越大,分布式训练已经是state-of-the-art的网络模型必不可少的部分(你见过谁用单卡在ImageNet训练ResNet50?),在大规模分布式系统,通信带宽比较低,相比于computation或者memory load/sotre,network communication会慢很多,如果可以降低通信量,那么整个网络的训练时间就会有大幅减少:这样研究员就不会借口调参,实际上把模型往服务器上一扔自己就跑出去浪了。(资本家狂喜)

(4)计算:虽然我们用的是计算机,但实际上恐怕只有很少的时间用于计算(computation)了,因为大多数时间都在等待数据的读取或者网络通信,不过即便如此,对于一些计算密集型的神经网络结构(比如BERT,几乎都是矩阵乘法),制约我们的往往是设备的计算能力(FLOPS),即每秒钟可以处理多少浮点计算。

计算能力是重要指标,即使多数情况用不满无疑同时将以上四点做好是我们追求的,但现实往往很残酷,需要我们做很多取舍。今天就在这儿介绍一些业内常见的trade-off。

(1)计算换内存

很多时候内存是最重要的:计算慢了我们多等一等就行了,内存爆了就彻底训练不了了。在神经网络训练中,内存的占用大头往往是activation,即神经网络每层的输出。我们在训练的时候需要把这些输出(activation)记录下来,因为我们反向传播的时候需要用这些activation计算梯度。

一个很直观的想法就是:我们干脆把一堆activation扔掉,到时候需要他们的时候再算一遍。这就是checkpoint机制的想法。虽然这个想法很简单,但是对于一个特定的神经网络,究竟扔掉/保留哪些activation一直没有定论,有兴趣的同学可以看一下我之前写的另一篇文章了解这个专题:

立交桥跳水冠军:DNN显存优化的终点?Checkmate论文总结:

https://zhuanlan.zhihu.com/p/299861314

(2)通信换内存

随着BERT,GPT的发展,研究员发现一件更尴尬的事情:内存不够了,但这次不仅仅是装不下activation,甚至光是参数(parame)和参数对应的optimizer都装不下了。那之前说的checkpoint就不管用了(人家只负责省activation)。

这时候有些读者会有想法:那如果我一张卡装不下,就两张卡来呗。恭喜你!你的想法和世界上最顶尖的程序员一样!这种做法可以被称为Model Parallel,即每个分布式节点存储不同的参数,feed一样的数据。目前Model Parallel有两种,粗略来说可以分成intra-layer拆分和inter-layer拆分:

▲intra-layer拆分,多见于NLP模型

▲inter-layer拆分,多见于CV模型

上面的例子可能比较抽象,我们来结合下面的两个具体工作说一下这两种model-parallel:

首先是intra-layer的拆分:

我们知道神经网络是一层一层的,每层可能是一个卷积,一个Pool,或者BN什么的。如果我们对一层进行了拆分,那么就是intra-layer的。下面这张图摘自英伟达的Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism(https://arxiv.org/abs/1909.08053),描述了如何把两个很大的矩阵乘法拆开到两个节点上来算

本来矩阵乘法是和  (忽略激活层什么的)。我们知道矩阵乘法有很好的性质:我们可以把矩阵乘法变成分块矩阵乘法。因此我们可以把上面的矩阵乘法变成

因此如果我们让第一个节点计算,第二个节点计算 ,最后再累加两个人的结果,不就好了?这样的好处就是第一个节点只需要存储A1和B1,第二个只需要A2和B2,相当于节省了一半的空间(和一个节点存储A和B相比)

inter-layer拆分:和intra-layer不同,我们只做网络层之间的切分。这种切分方式更符合直觉。上面的例子来自PipeDream(https://arxiv.org/abs/1806.03377、PipeDream: Fast and Efficient Pipeline Parallel DNN Training)。假设我们有5层的神经网络,有四个节点可以用,那我们可以让第一个节点算第一层,第二个节点算第二层,第三个节点算第三层,第四个节点算最后两层。这样我们很直接的就把网络拆开了,不管第1-5层的具体操作是卷积,BN还是什么,都可以这么搞。

但这么拆有一个问题,就是每台机器之间都存在数据依赖:当你发现第1,2,3节点在悠闲地打王者,第四节点在苦逼的干活,你就上去质问他们你们为啥在划水?他们表示很无辜:我在等第四节点把结果算完,然后把梯度传给我啊。

把每个节点工作的时间系统的记录下来,发现所有时刻都只有一个人在干活

很自然的想法就是,如果每个人都处理不同的batch不就好了?但这样做可能会引发精度的问题。有兴趣的读者可以去看PipeDream的论文。

(3)计算换通信

虽然刚才介绍了模型并行,但目前主流的还是数据并行,即每张卡分到同样的parameter,每次接收不同的input,算完之后每个人把自己的local gradient做一次同步,得到global gradient来更新本地的参数(如下图所示)

在这种情况下,我们的通信只发生在gradient allReduce的时刻(即图中最下面灰色的框)。虽然它只是训练过程的一部分,但因为随着分布式系统的增大,通信速度和计算、访存时间相比会越来越慢,因此这个allReduce操作逐渐成为了性能瓶颈。

为了打破这个瓶颈,有些研究员尝试压缩梯度:每次我们并不通信梯度本身,而是先把梯度做一个压缩,让他们的size变小,然后把压缩后的数据做一次传输,最后在本地解压缩这些数据,从而完成一次梯度的allReduce。其中做的比较好的就是TernGrad(TernGrad: Ternary Gradients to Reduce Communication in Distributed Deep Learning(https://arxiv.org/abs/1705.07878))。这个算法将原来一个N*float32这么大的梯度tensor压缩成了N*3这么大的tensor,再加一些可以忽略不计的meta-data。即用(-1,0,1)来表示原本float32的数值。

(4)显存换计算

这方面的例子我没想到太多,就想到诸如用3个3*3的卷积代替一个7*7的卷积:感受野不变,计算量减少,但是原本一个activation变成了三个,显存变大了。

(5)精度换计算/内存/通信

这种方法很“流氓”:深度学习模型最重要的就是精度,如果为了计算、内存和通信放弃了精度就很没道理。

不过得益于神经网络的超强鲁棒性,很多看似大胆的做法可以在显著降低计算,内存或通信的情况下只掉一点点精度。

这里简单介绍几种常见的做法:

  • 量化/用低精度计算:显而易见,如果你用Float16代替Float32,那么运行速度,需要的内存,需要的带宽基本上都可以直接砍一半

  • 稀疏通信:精度换通信的一种做法:我们每次对梯度做all reduce的时候并不需要传所有梯度,只需要选择一部分(比如数值比较大)的梯度传输就好了

  • 神经网络的各种剪枝:比如把很小的weight直接删掉,毕竟对最终结果没啥影响

后台回复关键词【入群

加入卖萌屋NLP/IR/Rec与求职讨论群

后台回复关键词【顶会

获取ACL、CIKM等各大顶会论文集!

深度学习如何均衡精度、内存、计算和通信开销?相关推荐

  1. 浅淡深度学习的发机机——张量计算

    浅淡深度学习的发机机--张量计算 张量计算是个看似陌生,实际上很常用的事物,它包括图形渲染的透明度混合.图像处理的滤镜.数学计算中的矩阵乘法.卷积等等,是图形引擎.图像算法.机器学习以及深度学习的基础 ...

  2. Python - 深度学习系列13- 显卡与CPU计算对比

    说明 因为装3060Ti的时候踩了坑(雷?),所以不太清楚这张卡是不是如同之前想象的一样,所以这篇文章会进行一系列的实验和对比.可能对其他希望用显卡进行计算的人有所帮助. 1 本篇的代码可以在装好显卡 ...

  3. 深度学习的半精度浮点数的运用

    深度学习除了大量数据之外,还可能存在内存容量和总线带宽等瓶颈,其中内存容量,需要考虑将大量的权重和偏置等等参数数据放在内存里面:总线带宽的瓶颈,主要考虑流经CPU(或GPU)总线的数据超过某个限制.对 ...

  4. 深度学习(二)卷积计算

    写在前面:所有关于深度学习的基础知识均为鄙人的笔记分享,很多内容摘自大神们的博客或论文,因时间太长记不清了分别来自哪里.若有侵权,请联系鄙人邮箱min.wenfang@qq.com 目前,卷积的计算大 ...

  5. 所谓的inference场景与深度学习终端加速器以及边缘计算和雾计算

    AI包括两部分: 1.广义的训练(含采集,特征工程以及狭义的训练和验证) 2.inference场景(讲人话就是加载模型进行测试). 上面两个场景在初学者眼里都是一台电脑上跑着玩玩. 因为深度学习模型 ...

  6. 深度学习笔记(一)—— 计算梯度[Compute Gradient]

      这是深度学习笔记第一篇,完整的笔记目录可以点击这里查看.      有两种方法来计算梯度:一种是计算速度慢,近似的,但很简单的方法(数值梯度),另一种是计算速度快,精确的,但更容易出错的方法,需要 ...

  7. 图神经网络应用——基于深度学习的图相似度计算(以SIMGNN为例的保姆级讲解)

    为啥想写这篇文章呢..因为之前提到的图神经网络应用篇鸽了一年多了,把自己的研究方向做一个总结,并向其他同样研究方向的朋友做一个报告,如有错误,敬请指出.而且,这个研究方向人太少了,万望能借此引起更多人 ...

  8. TensorFlow:实战Google深度学习框架(一)计算、数据、运行模型

    第3章 TensorFlow入门 3.1 TensorFlow计算模型--计算图 3.1.1 计算图的概念 3.1.2 计算图的使用 3.2 TensorFlow数据模型--张量 3.2.1 张量的概 ...

  9. 使用thop库对yolo等深度学习模型的FLOPS进行计算

    据说yolov5原来的FLOPS计算脚本有bug,因此这个大神推荐使用thop库进行计算,代码如下: input = torch.randn(1, 3, 416, 416) flops, params ...

最新文章

  1. DM368开发 -- 制作ubifs文件系统
  2. C#.NET学习笔记 -类,接口,对象
  3. Redis主从持久化测试
  4. 给程序员的10条建议,句句经典
  5. RPC与Restful比较
  6. 数论只会 for 循环 (数学+分块+记忆化)
  7. Git基本理论、项目搭建、文件操作以及分支介绍
  8. poj2752Seek the Name, Seek the Fame【kmp next数组应用】
  9. 将参数传递给Bash函数
  10. 2020 最烂密码 TOP 200 大曝光,霸榜的仍旧是 123456!
  11. ios开发证书reset原理分析以及解决方案
  12. matlab电气教程下载,Matlab 2020b介绍及下载安装步骤
  13. 微信小程序二维码生成
  14. 失败产品手册:一款影音娱乐平台的败局
  15. tomcat 日志拆分
  16. APS与ERP及MES的关系和接口
  17. Dubbo 常见的负载均衡(Load Balance)算法,一起学习一下吧~
  18. 使用JAXB实现JAVA对象和XML字符串的互相转换
  19. JAR包中的MANIFEST.MF文件详解以及编写规范
  20. 【web全景】web全景实现方案(转载)

热门文章

  1. ABP框架详解(八)动态ApiController的生成和访问机制
  2. IOS学习:常用第三方库(GDataXMLNode:xml解析库)
  3. [转载]WebBrowser知识
  4. Linux kernel中常见的宏整理
  5. ViceDinoSpecCtrlDlg.cpp
  6. bootstrap 居中 表格中_使用Twitter Bootstrap在表格单元格中垂直居中
  7. unittest里discover用法_unittest框架核心要素及应用
  8. linux sqlserver 管理工具,Linux连接SqlServer的图形化工具SQuirrel
  9. python如何读取字典的关键字_python提取字典key列表的方法
  10. 程序员面试金典 - 面试题 16.14. 最佳直线(哈希map+set)