🐡 多GPU计算
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
 
 
 
 
 
12.5. 多GPU训练 - 动手学深度学习 2.0.0-beta0 documentation
到目前为止,我们讨论了如何在CPU和GPU上高效地训练模型,同时在 中展示了深度学习框架如何在CPU和GPU之间自动地并行化计算和通信,还在 中展示了如何使用命令列出计算机上所有可用的GPU。 但是我们没有讨论如何真正实现深度学习训练的并行化。 是否一种方法,以某种方式分割数据到多个设备上,并使其能够正常工作呢? 本节将详细介绍如何从零开始并行地训练网络, 这里需要运用小批量随机梯度下降算法(详见 )。 后面我还讲介绍如何使用高级API并行训练网络(请参阅 )。 我们从一个简单的计算机视觉问题和一个稍稍过时的网络开始。 这个网络有多个卷积层和汇聚层,最后可能有几个全连接的层,看起来非常类似于LeNet [LeCun et al., 1998]或AlexNet[Krizhevsky et al., 2012] 。 假设我们有多个GPU(如果是桌面服务器则有\(2\)个,AWS g4dn.12xlarge上有\(4\)个,p3.16xlarge上有\(8\)个,p2.16xlarge上有\(16\)个)。 我们希望以一种方式对训练进行拆分,为实现良好的加速比,还能同时受益于简单且可重复的设计选择。 毕竟,多个GPU同时增加了内存和计算能力。 简而言之,对于需要分类的小批量训练数据,我们有以下选择。 第一种方法,在多个GPU之间拆分网络。 也就是说,每个GPU将流入特定层的数据作为输入,跨多个后续层对数据进行处理,然后将数据发送到下一个GPU。 与单个GPU所能处理的数据相比,我们可以用更大的网络处理数据。 此外,每个GPU占用的 显存 (memory footprint)可以得到很好的控制,虽然它只是整个网络显存的一小部分。 然而,GPU的接口之间需要的密集同步可能是很难办的,特别是层之间计算的工作负载不能正确匹配的时候, 还有层之间的接口需要大量的数据传输的时候(例如:激活值和梯度,数据量可能会超出GPU总线的带宽)。 此外,计算密集型操作的顺序对于拆分来说也是非常重要的,这方面的最好研究可参见 [Mirhoseini et al., 2017] ,其本质仍然是一个困难的问题,目前还不清楚研究是否能在特定问题上实现良好的线性缩放。 综上所述,除非存框架或操作系统本身支持将多个GPU连接在一起,否则不建议这种方法。 第二种方法,拆分层内的工作。 例如,将问题分散到\(4\)个GPU,每个GPU生成\(16\)个通道的数据,而不是在单个GPU上计算\(64\)个通道。 对于全连接的层,同样可以拆分输出单元的数量。 描述了这种设计,其策略用于处理显存非常小(当时为2GB)的GPU。 当通道或单元的数量不太小时,使计算性能有良好的提升。 此外,由于可用的显存呈线性扩展,多个GPU能够处理不断变大的网络。 然而,我们需要大量的同步或 屏障操作 (barrier operation),因为每一层都依赖于所有其他层的结果。 此外,需要传输的数据量也可能比跨GPU拆分层时还要大。 因此,基于带宽的成本和复杂性,我们同样不推荐这种方法。 最后一种方法,跨多个GPU对数据进行拆分。 这种方式下,所有GPU尽管有不同的观测结果,但是执行着相同类型的工作。 在完成每个小批量数据的训练之后,梯度在GPU上聚合。 这种方法最简单,并可以应用于任何情况,同步只需要在每个小批量数据处理之后进行。 也就是说,当其他梯度参数仍在计算时,完成计算的梯度参数就可以开始交换。 而且,GPU的数量越多,小批量包含的数据量就越大,从而就能提高训练效率。 但是,添加更多的GPU并不能让我们训练更大的模型。 中比较了多个GPU上不同的并行方式。 总体而言,只要GPU的显存足够大,数据并行是最方便的。 有关分布式训练分区的详细描述,请参见 [Li et al., 2014] 。 在深度学习的早期,GPU的显存曾经是一个棘手的问题,然而如今除了非常特殊的情况,这个问题已经解决。 下面我们将重点讨论数据并行性。 假设一台机器有\(k\)个GPU。 给定需要训练的模型,虽然每个GPU上的参数值都是相同且同步的,但是每个GPU都将独立地维护一组完整的模型参数。 例如, 演示了在\(k=2\)时基于数据并行方法训练模型。 ...
 
🐡 自动并行
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
深度学习框架会在后端自动构建计算图。利用计算图,系统可以了解所有依赖关系,并且可以选择性地并行执行多个不相互依赖的任务以提高速度。
通常情况下单个操作符将使用所有CPU或单个GPU上的所有计算资源。例如,即使在一台机器上有多个CPU处理器,dot 操作符也将使用所有CPU上的所有核心(和线程)。这样的行为同样适用于单个GPU。因此,并行化对于单设备计算机来说并不是很有用,而并行化对于多个设备就很重要了。虽然并行化通常应用在多个GPU之间,但增加本地CPU以后还将提高少许性能。例如,Hadjis.Zhang.Mitliagkas.ea.2016则把结合GPU和CPU的训练应用到计算机视觉模型中。借助自动并行化框架的便利性,可以依靠几行Python代码实现相同的目标。更广泛地考虑,这里对自动并行计算的讨论主要集中在使用CPU和GPU的并行计算上,以及计算和通信的并行化内容。
 

基于GPU的并行计算

从定义一个具有参考性的用于测试的工作负载开始:下面的run函数将执行次“矩阵-矩阵”乘法时需要使用的数据分配到两个变量(x_gpu1x_gpu2)中,这两个变量分别位于不同设备上。
通过在测量之前预热设备(对设备执行一次传递)来确保缓存的作用不影响最终的结果。
torch.cuda.synchronize()函数将会等待一个CUDA设备上的所有流中的所有核心的计算完成。函数接受一个device参数,代表是哪个设备需要同步。如果device参数是None(默认值),它将使用current_device()找出的当前设备。
如果删除两个任务之间的synchronize语句,系统就可以在两个设备上自动实现并行计算。
🐡 硬件
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
 
很好地理解算法和模型才可以捕获统计方面的问题,构建出具有出色性能的系统。同时,至少对底层硬件有一定的了解也是必不可少的。一个好的设计可以很容易地在性能上造就数量级的差异,这也是后续产生的能够训练网络(例如,训练时间为 周)和无法训练网络(训练时间为 个月,导致错过截止期)之间的差异。
                                                                           每个程序员都应该知道的延迟数字
每个程序员都应该知道的延迟数字
 

计算机

大多数深度学习研究者和实践者都可以使用一台具有相当数量的内存、计算资源、某种形式的加速器(如一个或者多个GPU)的计算机,计算机由以下关键部件组成:
  • 一个处理器(CPU),它除了能够运行操作系统和许多其他功能之外,还能够执行给它的程序,通常由个或更多个核心组成。
  • 内存(随机访问存储,RAM)用于存储和检索计算结果,如权重向量和激活参数,以及训练数据。
🐳 图像卷积
type
status
date
slug
summary
tags
category
icon
password
Property
 
 

DNN局限性

DNN十分适合处理表格数据,其中行对应样本,列对应特征。 对于表格数据,我们寻找的模式可能涉及特征之间的交互,但是不能预先假设任何与特征交互相关的先验结构。 此时,DNN可能是最好的选择,然而对于高维感知数据,这种缺少结构的网络可能会变得不实用。
假设有一个足够充分的照片数据集,数据集中是拥有标注的照片,每张照片具有百万级像素,这意味着网络的每次输入都有一百万个维度。 即使将隐藏层维度降低到1000,这个全连接层也将有 个参数。 想要训练这个模型将不可实现,因为需要有大量的GPU、分布式优化训练的经验和超乎常人的耐心。此外,拟合如此多的参数还需要收集大量的数据。 然而,如今人类和机器都能很好地区分猫和狗:这是因为图像中本就拥有丰富的结构,而这些结构可以被人类和机器学习模型使用。 卷积神经网络是机器学习利用自然图像中一些已知结构的创造性方法。
 

空间不变性

想象一下,假设你想从一张图片中找到某个物体。 合理的假设是:无论哪种方法找到这个物体,都应该和物体的位置无关。 理想情况下,系统应该能够利用常识:猪通常不在天上飞,飞机通常不在水里游泳。 但是,如果一只猪出现在图片顶部,我们还是应该认出它。
                                                       沃尔多游戏示例图
沃尔多游戏示例图
可以从儿童游戏”沃尔多在哪里”中得到灵感: 在这个游戏中包含了许多充斥着活动的混乱场景,而沃尔多通常潜伏在一些不太可能的位置,我们的目标就是找出他。 尽管沃尔多的装扮很有特点,但是在眼花缭乱的场景中找到他也如大海捞针。 然而沃尔多的样子并不取决于他潜藏的地方,因此可以使用一个“沃尔多检测器”扫描图像。 该检测器将图像分割成多个区域,并为每个区域包含沃尔多的可能性打分。 卷积神经网络正是将空间不变性(spatial invariance)的这一概念系统化,从而基于这个模型使用较少的参数来学习有用的表示。
 
将上述想法总结一下,从而帮助我们设计适合于计算机视觉的神经网络架构:
🐳 填充和步幅
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
有时,在应用了连续的卷积之后,最终得到的输出远小于输入大小。这是由于卷积核的宽度和高度通常大于所导致的。比如,一个 像素的图像,经过的卷积后,将减少到像素。如此一来,原始图像的边界丢失了许多有用信息。而填充是解决此问题最有效的方法。 有时,我们可能希望大幅降低图像的宽度和高度,例如原始的输入分辨率十分冗余,步幅则可以在这类情况下提供帮助。

填充

由于通常使用小卷积核,对于任何单个卷积可能只会丢失几个像素。 但随着应用许多连续卷积层,累积丢失的像素数就多了。 解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填0)。
例如,在图中将 输入填充到,那么输出就增加为
notion image
如果添加行填充(大约一半在顶部,一半在底部)和列填充(左侧大约一半,右侧一半),则输出形状将为:
输出的高度和宽度将分别增加
 
在许多情况下,我们需要设置 ,使输入和输出具有相同的高度和宽度,这样可以在构建网络时更容易地预测每个图层的输出形状。
🐳 多输入多输出
type
status
date
slug
summary
tags
category
icon
password
Property
 

多输入通道

当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。假设输入的通道数为,那么卷积核的输入通道数也需要为
如果卷积核的窗口形状是
  • ,可以把卷积核看作形状为的二维张量
  • ,卷积核的每个输入通道将包含形状为的二维张量。由于输入和卷积核都有个通道,我们可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算,再对通道求和(将的结果相加)得到二维张量。
 
  • 输入
  • 输出
notion image
🐳 多维卷积
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
一般来说,一维卷积用于文本数据,二维卷积用于图像数据,对宽度和高度都进行卷积,三维卷积用于视频及3D图像处理领域(检测动作及人物行为),对立方体的三个面进行卷积 。

一维卷积Conv1d

一维卷积实质是对一个词向量做卷积,如下所示:
notion image
  • 图中的输入的数据维度为8,过滤器的维度为5。卷积后输出的数据维度为8−5+1=4
  • 如果过滤器数量仍为1,输入数据的channel数量变为16,则输入数据维度为8×16
  • 一维卷积常用于序列模型,自然语言处理领域。
 
torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
Pytorch中nn.Conv1d卷积运算要求输入源是3维,输入源的三个维度分别是:第一个维度代表每个序列的个数即样本数,第二个维度代表每一个序列的通道数,第三个维度代表这个词向量序列,如下所示:
🐳 转置卷积
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
我们一般可以通过卷积操作来实现高维特征到低维特征的转换.比如在一维卷积中,一个5 维的输入特征,经过一个大小为3 的卷积核,其输出为3 维特征。如果设置步长大于1,可以进一步降低输出特征的维数。但在一些任务中,我们需要将低维特征映射到高维特征,并且依然希望通过卷积操作来实现。
我们将低维特征映射到高维特征的卷积操作称为转置卷积,也称为反卷积。在卷积网络中,卷积层的前向计算和反向传播也是一种转置关系。
notion image
notion image
 

基本操作

🐳 空洞卷积
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
对于一个卷积层,如果希望增加输出单元的感受野,一般可以通过三种方式实现:
  • 增加卷积核的大小
  • 增加层数,比如两层3 × 3 的卷积可以近似一层5 × 5 卷积的效果
  • 在卷积之前进行汇聚操作.前两种方式会增加参数数量,而第三种方式会丢失一些信息
空洞卷积(Atrous Convolution)是一种不增加参数数量,同时增加输出单元感受野的一种方法,也称为膨胀卷积(Dilated Convolution)。空洞卷积通过给卷积核插入“空洞”来变相地增加其大小。如果在卷积核的每两个元素之间插入𝐷 − 1 个空洞,卷积核的有效大小为 其中𝐷称为膨胀率(Dilation Rate),当𝐷 = 1 时卷积核为普通的卷积核
                                   膨胀率𝐷 = 2
膨胀率𝐷 = 2
                                     膨胀率𝐷 = 3
膨胀率𝐷 = 3
🐳 池化层
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
处理图像时,我们希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。而机器学习任务通常会跟全局图像的问题有关(例如,“图像是否包含一只猫呢?”),所以最后一层的神经元应该对整个输入的全局敏感。通过逐渐聚合信息,生成越来越粗糙的映射,最终实现学习全局表示的目标,同时将卷积图层的所有优势保留在中间层。
此外,当检测较底层的特征时(例如边缘),我们通常希望这些特征保持某种程度上的平移不变性。例如,如果我们拍摄黑白之间轮廓清晰的图像X,并将整个图像向右移动一个像素,即Z[i, j] = X[i, j + 1],则新图像Z的输出可能大不相同。而在现实中,随着拍摄角度的移动,任何物体几乎不可能发生在同一像素上。即使用三脚架拍摄一个静止的物体,由于快门的移动而引起的相机振动,可能会使所有物体左右移动一个像素(除了高端相机配备了特殊功能来解决这个问题)。
 
池化(pooling)层,它具有双重目的:
  • 降低卷积层对位置的敏感性
    • 卷积层对位置敏感,比如之前的边缘检测,一个像素移位可能导致边缘的输出。但在实际的图像里,我们感兴趣的物体,不会总出现在固定位置:即使连续拍摄同一个物体也既有可能出现像素位置上的偏移。这会导致同一个边缘对应的输出可能出现在卷积输出Y中的不同位置,进而对后面的模式识别造成不便。
      假设使用池化层,例如:最大值池化,最大值的话就是把上一层几个特征的最大值输入给下一层,也就是说上一层几个特征位置在哪里都没关系,都会给到下一层的同样的位置,属于特征位置上的合并,所以实现了平移不变性
  • 降低对空间降采样表示的敏感性
 

最大池化层和平均池化层

🐳 CNN前向传播算法
type
status
date
slug
summary
tags
category
icon
password
Property
 

CNN的基本结构

一个常见的CNN例子如下图:
notion image
图中是一个图形识别的CNN模型。可以看出最左边的船的图像就是我们的输入层,计算机理解为输入若干个矩阵,这点和DNN基本相同。
接着是卷积层(Convolution Layer),这个是CNN特有的,卷积层的激活函数使用的是ReLU。在卷积层后面是池化层(Pooling layer),这个也是CNN特有的。需要注意的是,池化层没有激活函数
卷积层+池化层的组合可以在隐藏层出现很多次,上图中出现两次。而实际上这个次数是根据模型的需要而来的。当然也可以灵活使用使用卷积层+卷积层,或者卷积层+卷积层+池化层的组合,这些在构建模型的时候没有限制。但是最常见的CNN都是若干卷积层+池化层的组合,如上图中的CNN结构。
在若干卷积层+池化层后面是全连接层(Fully Connected Layer, 简称FC),全连接层其实就是DNN结构,只是输出层使用了Softmax激活函数来做图像识别的分类。
从上面CNN的模型描述可以看出,CNN相对于DNN,比较特殊的也就是是卷积层和池化层。
 
用一个彩色的汽车样本的图像识别直观的看下CNN的结构。图中的CONV即为卷积层,POOL即为池化层,而FC即为DNN全连接层,包括了最后的用Softmax激活函数的输出层。
notion image
🐳 CNN反向传播算法
type
status
date
slug
summary
tags
category
icon
password
Property
 

回顾DNN的反向传播算法

在DNN中,首先计算出输出层的 :
利用数学归纳法,用 的值一步步的向前求出第层的
有了 的表达式,从而求出的梯度:
有了 梯度表达式,就可以用梯度下降法来优化 求出最终的所有的值
现在我们想把同样的思想用到CNN中,很明显,CNN有些不同的地方,不能直接去套用DNN的反向传播算法的公式
 

CNN的反向传播算法思想