前面我们对卷积在输入数据、卷积核的维度上进行了扩展,但输入数据、卷积核都是单个,如果从图形的角度来说都是灰色的,没有考虑彩色图片情况。在实际应用中,输入数据往往是多通道的,如彩色图片就3通道,即R、G、B通道。对于3通道的情况如何卷积呢?3通道图片的卷积运算与单通道图片的卷积运算基本一致,对于3通道的RGB图片,其对应的滤波器算子同样也是3通道的。例如一个图片是6 x 6 x 3,分别表示图片的高度(height)、宽度(weight)和通道(channel)。过程是将每个单通道(R,G,B)与对应的filter进行卷积运算求和,然后再将3通道的和相加,得到输出图片的一个像素值。具体过程如图6-11所示。
Dropout是Srivastava等人在2014年的一篇论文中,提出的一种针对神经网络模型的正则化方法 Dropout: A Simple Way to Prevent Neural Networks from Overfitting。
Dropout在训练模型中是如何实现的呢?Dropout的做法是在训练过程中按一定比例(比例参数可设置)随机忽略或屏蔽一些神经元。这些神经元被随机“抛弃”,也就是说它们在正向传播过程中对于下游神经元的贡献效果暂时消失了,反向传播时该神经元也不会有任何权重的更新。所以,通过传播过程,dropout将产生和L2范数相同的收缩权重的效果。
随着神经网络模型的不断学习,神经元的权值会与整个网络的上下文相匹配。神经元的权重针对某些特征进行调优,会产生一些特殊化。周围的神经元则会依赖于这种特殊化,如果过于特殊化,模型会因为对训练数据过拟合而变得脆弱不堪。神经元在训练过程中的这种依赖于上下文的现象被称为复杂的协同适应(complex co-adaptations)。
加入了Dropout以后,输入的特征都是有可能会被随机清除的,所以该神经元不会再特别依赖于任何一个输入特征,也就是说不会给任何一个输入设置太大的权重。网络模型对神经元特定的权重不那么敏感。这反过来又提升了模型的泛化能力,不容易对训练数据过拟合。
Dropout训练的集成包括所有从基础网络除去非输出单元形成子网络,如图5-7所示。
图5-15动量算法示意图
由图5-15,可知动量算法每下降一步都是由前面下降方向的一个累积和当前点的梯度方向组合而成。含动量的随机梯度下降法,其算法伪代码:
假设 batch_size=10, m=1000
初始化参数向量θ、学习率为λ、动量参数α、初始速度v
while 停止准则未满足 do
Repeat {
forj = 1, 11, 21, .., 991 {
更新梯度: g ̂←1/(batch_size) 〖 〖∑ 〗┬(i=j)〗┴(j+batch_size) ∇_θL(〖f(x〗^((i) ),θ),y^((i) ))
计算速度:v←αv-λg ̂
更新参数:θ←θ+v
}
}
end while
既然每一步都要将两个梯度方向(历史梯度、当前梯度)做一个合并再下降,那为什么不先按照历史梯度往前走那么一小步,按照前面一小步位置的“超前梯度”来做梯度合并呢?如此一来,可以先往前走一步,在靠前一点的位置(如图5-16中的C点)看到梯度,然后按照那个位置再来修正这一步的梯度方向,如下图5-16所示。这就得到动量算法的一种改进算法,称为Nesterov accelerated gradient 简称 NAG 算法。这种预更新方法能防止大幅振荡,不会错过最小值,并对参数更新更加敏感。
图5-16 NAG下降法示意图
NAG下降法的算法伪代码:
假设 batch_size=10, m=1000
初始化参数向量θ、学习率λ、动量参数α、初始速度v
while 停止准则未满足 do
更新超前点:θ ̃ ← θ+αv
Repeat {
forj = 1, 11, 21, .., 991 {
更新梯度(在超前点): g ̂←1/(batch_size) 〖 〖∑ 〗┬(i=j)〗┴(j+batch_size) ∇_θ ̃ L(〖f(x〗^((i) ),θ ̃),y^((i) ))
计算速度:v←αv-λg ̂
更新参数:θ←θ+v
}
}
end while
NAG动量法和经典动量法的差别就在B点和C点梯度的不同。动量法,更多关注梯度下降方法的优化,如果能从方向和学习率同时优化,效果或许更理想。事实也确实如此,而且这些优化在深度学习中显得尤为重要。接下来我们介绍几种自适应优化算法,这些算法同时从梯度方向及学习率进行优化,效果非常好。
5.6.3 AdaGrad算法
传统梯度下降算法对学习率这个超参数非常敏感,难以驾驭;对参数空间的某些方向也没有很好的方法。这些不足在深度学习中,因高维空间、多层神经网络等因素,常会出现平坦、鞍点、悬崖等问题,因此,传统梯度下降法在深度学习中显得力不从心。还好现在已有很多解决这些问题的有效方法。上节介绍的动量算法在一定程度缓解对参数空间某些方向的问题,但需要新增一个参数,而且对学习率的控制还不很理想。为了更好驾驭这个超参数,人们想出来多种自适应优化算法,使用自适应优化算法,学习率不再是一个固定不变值,它会根据不同情况自动调整来适用情况。这些算法使深度学习向前迈出一大步!这节我们将介绍几种自适应优化算法。
AdaGrad算法是通过参数来调整合适的学习率λ,能独立地自动调整模型参数的学习率,对稀疏参数进行大幅更新和对频繁参数进行小幅更新。因此,Adagrad方法非常适合处理稀疏数据。AdaGrad算法在某些深度学习模型上效果不错。但还有些不足,可能因其累积梯度平方导致学习率过早或过量的减少所致。
AdaGrad算法伪代码:
假设 batch_size=10, m=1000
初始化参数向量θ、学习率λ
小参数δ,一般取一个较小值(如〖10〗^(-7)),该参数避免分母为0
初始化梯度累积变量 r=0
while 停止准则未满足 do
Repeat {
forj = 1, 11, 21, .., 991 {
更新梯度: g ̂←1/(batch_size) 〖 〖∑ 〗┬(i=j)〗┴(j+batch_size) ∇_θL(〖f(x〗^((i) ),θ),y^((i) ))
累积平方梯度:r←r+g ̂⊙g ̂ #⊙表示逐元运算
计算速度:△θ← -λ/(δ+√r)⊙g ̂
更新参数:θ←θ+△θ
}
}
end while
由上面算法的伪代码可知:
(1)随着迭代时间越长,累积梯度r越大,从而学习速率λ/(δ+√r)随着时间就减小,在接近 目标值时,不会因为学习速率过大而越过极值点。
(2)不同参数之间学习速率不同,因此,与前面固定学习速率相比,不容易在鞍点卡住。
(3)如果梯度累积参数r比较小,则学习速率会比较大,所以参数迭代的步长就会比较大。 相反,如果梯度累积参数比较大,则学习速率会比较小,所以迭代的步长会比较小。
5.6.4 RMSProp算法
RMSProp算法修改AdaGrad,为的是在非凸背景下效果更好。针对梯度平方和累计越来越大的问题,RMSProp指数加权的移动平均代替梯度平方和。RMSProp为使用移动平均,引入了一个新的超参数ρ,用来控制移动平均的长度范围。
RMSProp算法伪代码:
假设 batch_size=10, m=1000
初始化参数向量θ、学习率λ、衰减速率ρ
小参数δ,一般取一个较小值(如〖10〗^(-7)),该参数避免分母为0
初始化梯度累积变量 r=0
while 停止准则未满足 do
Repeat {
forj = 1, 11, 21, .., 991 {
更新梯度: g ̂←1/(batch_size) 〖 〖∑ 〗┬(i=j)〗┴(j+batch_size) ∇_θL(〖f(x〗^((i) ),θ),y^((i) ))
累积平方梯度:r←ρr+(1-ρ)g ̂⊙g ̂
计算参数更新:△θ← -λ/(δ+√r)⊙g ̂
更新参数:θ←θ+△θ
}
}
end while
RMSProp算法在实践中已被证明是一种有效且实用的深度神经网络优化算法,在深度学习中得到广泛应用。
5.6.5 Adam算法
Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
Adam是另一种学习速率自适应的深度神经网络方法,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习速率。Adam算法伪代码如下:
假设 batch_size=10, m=1000
初始化参数向量θ、学习率λ
矩估计的指数衰减速率ρ_1 和ρ_2在区间[0,1)内。
小参数δ,一般取一个较小值(如〖10〗^(-7)),该参数避免分母为0
初始化一阶和二阶矩变量 s=0,r=0
初始化时间步 t=0
while 停止准则未满足 do
Repeat {
forj = 1, 11, 21, .., 991 {
更新梯度: g ̂←1/(batch_size) 〖 〖∑ 〗┬(i=j)〗┴(j+batch_size) ∇_θL(〖f(x〗^((i) ),θ),y^((i) ))
t←t+1
更新有偏一阶矩估计:s ←ρ_1 s +(1-ρ_1)g ̂
更新有偏二阶矩估计:r← ρ_2 r +(1-ρ_2)g ̂⊙g ̂
修正一阶矩偏差:s ̂=s/(1-ρ_1^t )
修正二阶矩偏差:r ̂=r/(1-ρ_2^t )
累积平方梯度:r←ρr+(1-ρ)g ̂⊙g ̂
计算参数更新:△θ=-λ s ̂/(δ+√(r ̂ ))
更新参数:θ←θ+△θ
}
}
end while
前文介绍了深度学习的正则化方法,它是深度学习核心之一;优化算法也是深度学习的核心之一。优化算法很多,如随机梯度下降法、自适应优化算法等,那么具体使用时该如何选择呢?
RMSprop,Adadelta和Adam被认为是自适应优化算法,因为它们会自动更新学习率。而使用SGD时,必须手动选择学习率和动量参数,通常会随着时间的推移而降低学习率。
有时可以考虑综合使用这些优化算法,如采用先用Adam,然后用SGD优化方法,这个想法,实际上由于在训练的早期阶段SGD对参数调整和初始化非常敏感。因此,我们可以通过先使用Adam优化算法进行训练,这将大大节省训练时间,且不必担心初始化和参数调整,一旦用Adam训练获得较好的参数后,我们可以切换到SGD +动量优化,以达到最佳性能。采用这种方法有时能达到很好效果,如图5-17所示,迭代次数超过150后,用SGD效果好于Adam。
图5-17 迭代次数与测试误差间的对应关系
5.7GPU加速
深度学习涉及很多向量或多矩阵运算,如矩阵相乘、矩阵相加、矩阵-向量乘法等。深层模型的算法,如BP,Auto-Encoder,CNN等,都可以写成矩阵运算的形式,无须写成循环运算。然而,在单核CPU上执行时,矩阵运算会被展开成循环的形式,本质上还是串行执行。GPU(Graphic Process Units,图形处理器)的众核体系结构包含几千个流处理器,可将矩阵运算并行化执行,大幅缩短计算时间。随着NVIDIA、AMD等公司不断推进其GPU的大规模并行架构,面向通用计算的GPU已成为加速可并行应用程序的重要手段。得益于GPU众核(many-core)体系结构,程序在GPU系统上的运行速度相较于单核CPU往往提升几十倍乃至上千倍。
目前,GPU已经发展到了较为成熟的阶段。利用GPU来训练深度神经网络,可以充分发挥其数以千计计算核心的能力,在使用海量训练数据的场景下,所耗费的时间大幅缩短,占用的服务器也更少。如果对适当的深度神经网络进行合理优化,一块GPU卡相当于数十甚至上百台CPU服务器的计算能力,因此GPU已经成为业界在深度学习模型训练方面的首选解决方案。
如何使用GPU?现在很多深度学习工具都支持GPU运算,使用时只要简单配置即可。Pytorch支持GPU,可以通过to(device)函数来将数据从内存中转移到GPU显存,如果有多个GPU还可以定位到哪个或哪些GPU。Pytorch一般把GPU作用于张量(Tensor)或模型(包括torch.nn下面的一些网络模型以及自己创建的模型)等数据结构上。