编者按:DRDO研究人员Ayoosh Kathuria深入讨论了激活函数如何隐式地改变传入网络层的数据分布,进而影响网络的优化过程。
这是优化系列的第三篇,我们想要通过这一系列文章全面回顾深度学习中的优化技术。到目前为止,我们已经讨论了:
用于对抗局部极小值、鞍点的mini batch梯度下降(点击阅读)。
动量、RMSProp、Adam(点击阅读)等方法在原始梯度下降的基础上加强了哪些方面,以应对病态曲率问题。
不同于之前的机器学习方法,神经网络并不依赖关于输入数据的任何概率学或统计学假定。然而,为了确保神经网络学习良好,最重要的因素之一是传入神经网络层的数据需要具有特定的性质。
数据分布应该是零中心化(zero centered)的,也就是说,分布的均值应该在零附近。不具有这一性质的数据可能导致梯度消失和训练抖动。
分布最好是正态的,否则可能导致网络过拟合输入空间的某个区域。
在训练过程中,不同batch和不同网络层的激活分布,应该保持一定程度上的一致。如果不具备这一性质,那么我们说分布出现了内部协方差偏移(Internal Covariate shift),这可能拖慢训练进程。
这篇文章将讨论如何使用激活函数应对前两个问题。文末将给出一些选择激活函数的建议。
梯度消失问题有丰富的文档,随着神经网络越来越深,这一问题越来越得到重视。下面我们将解释梯度为什么会消失。让我们想象一个最简单的神经网络,一组线性堆叠的神经元。
实际上,上面的网络很容易扩展成深度密集连接架构。只需将网络中的每个神经元替换成一个使用sigmoid激活函数的全连接层。
sigmoid函数的图像是这样的。
看下sigmoid函数的斜率,我们会发现它在两端趋向于零。sigmoid函数梯度图像可以印证这一点。
求sigmoid激活层输出在其权重上的导数时,我们可以看到,sigmoid函数的梯度是表达式中的一个因子,该梯度的取值范围为0到1.
上式中的第二项就是sigmoid的导数,值域为0到1.
回到我们的例子,让我们求下神经元A的梯度。应用链式法则,我们得到:
上面的表达式中的每项都可以进一步分解为梯度的乘积,其中一项为sigmoid函数的梯度。例如:
现在,假设A之前不止3个神经元,而是有50个神经元。在实践中,这完全是可能的,实际应用中的网络很容易到50层。
那么A的梯度表达式中就包含50项sigmoid梯度的乘积,每项的取值范围为0到1,这也许会将A的梯度推向零。
让我们做一个简单的试验。随机取样50个0到1之间的数,然后将它们相乘。
import random
from functools import reduce
li = [random.uniform(0,1) for x in range(50)
print(reduce(lambda x,y: x*y, li))
你可以自己试验一下。我试了很多次,从来没能得到一个数量级大于10-18的数。如果这个值是神经元A的梯度表达式中的一个因子,那么梯度几乎就等于零。这意味着,在较深的架构中,较深的神经元基本不学习,即使学习,和较浅的网络层中的神经元相比,学习的速率极低。
这个现象就是梯度消失问题,较深的神经元中的梯度变为零,或者说,消失了。这就导致神经网络中较深的层学习极为缓慢,或者,在最糟的情况下,根本不学习。
饱和神经元会导致梯度消失问题进一步恶化。假设,传入带sigmoid激活的神经元的激活前数值ωTx + b非常高或非常低。那么,由于sigmoid在两端处的梯度几乎是0,任何梯度更新基本上都无法导致权重ω和偏置b发生变化,神经元的权重变动需要很多步才会发生。也就是说,即使梯度原本不低,由于饱和神经元的存在,最终梯度仍会趋向于零。
在普通深度网络设定下,ReLU激活函数的引入是缓解梯度消失问题的首个尝试(LSTM的引入也是为了应对这一问题,不过它的应用场景是循环模型)。
当x > 0时,ReLU的梯度为1,x < 0时,ReLU的梯度为0. 这带来了一些好处。ReLU函数梯度乘积并不收敛于0,因为ReLU的梯度要么是0,要么是1. 当梯度值为1时,梯度原封不动地反向传播。当梯度值为0时,从这点往后不会进行反向传播。
sigmoid函数是双边饱和的,也就是说,正向和负向都趋向于零。ReLU则提供单边饱和。
准确地说,ReLU的左半部分不叫饱和,饱和的情况下,函数值变动极小,而ReLU的左半部分根本不变。但两者的作用是类似的。你也许会问,单边饱和带来了什么好处?
我们可以把深度网络中的神经元看成开关,这些开关专门负责检测特定特征。这些特征常常被称为概念。高层网络中的神经元也许最终会专门检测眼睛、轮胎之类的高层特征,而低层网络中的神经元最终专门检测曲线、边缘之类的低层特征。
当这样的概念存在于神经网络的输入时,我们想要激活相应的神经元,而激活的数量级则可以测量概念的程度。例如,如果神经元检测到了边缘,它的数量级也许表示边缘的锐利程度。
然而,神经元的负值在这里就没什么意义了。用负值编码不存在的概念的程度感觉怪怪的。
以检测边缘的神经元为例,相比激活值为5的神经元,激活值为10的神经元可能检测到了更锐利的边缘。但是区分激活值-5和-10的神经元就没什么意义了,因为负值表示根本不存在边缘。因此,统一用零表示概念不存在是很方便的。ReLU的单边饱和正符合这一点。
单边饱和提高了神经元对噪声的鲁棒性。为什么?假设神经元的值是无界的,也就是在两个方向上都不饱和。具有程度不同的概念的输入产生神经元正值输出的不同。由于我们想要用数量级指示信号的强度,这很好。
然而,背景噪声、神经元不擅长检测的概念(例如,包含弧线的区域传入检测线条的神经元),会生成不同的神经元负值输出。这类不同可能给其他神经元带去大量无关、无用信息。这也可能导致单元间的相关性。例如,检测线条的神经元也许和检测弧线的神经元负相关。
而在神经元单边饱和(负向)的场景下,噪声等造成的不同,也就是之前的负值输出数量级的不同,被激活函数的饱和元素挤压为零,从而防止噪声产生无关信号。
ReLU函数在算力上也有优势。基于ReLU的网络训练起来比较快,因为计算ReLU激活的梯度不怎么需要算力,而sigmoid梯度计算就需要指数运算。
ReLU归零激活前的负值,这就隐式地给网络引入了稀疏性,同样节省了算力。
ReLU也有缺陷。虽然稀疏性在算力上有优势,但过多的稀疏性实际上会阻碍学习。激活前神经元通常也包含偏置项,如果偏置项是一个过小的负数,使得ωTx + b < 0,那么ReLU激活在反向传播中的梯度就是0,使负的激活前神经元无法更新。
如果学习到的权重和偏置使整个输入域上的激活前数值都是负数,那么神经元就无法学习,引起类似sigmoid的饱和现象。这称为死亡ReLU问题。
不管输入是什么,ReLU只输出非负激活。这可能是一个劣势。
对基于ReLU的神经网络而言,网络层ln的权重ωn的激活为
因此,对损失函数L而言:
上式中的I是一个指示函数,传入的ReLU值为正数时输出1,否则输出0. 由于ReLU只输出非负值,ωn中的每项权重的梯度更新正负都一样。
这有什么问题?问题在于,由于所有神经元的梯度更新的符号都一样,网络层ln中的所有权重在一次更新中,要么全部增加,要么全部减少。然而,理想情况的梯度权重更新也许是某些权重增加,另一些权重减少。ReLU下,这做不到。
假设,根据理想的权重更新,有些权重需要减少。然而,如果梯度更新是正值,这些权重可能在当前迭代中变为过大的正值。下一次迭代,梯度可能会变成较小的负值以补偿这些增加的权重,这也许会导致最终跳过需要少量负值或正值变动才能取到的权重。
这可能导致搜寻最小值时出现之字模式,拖慢训练速度。
为了克服死亡ReLU问题,人们提出了Leaky ReLU。Leaky ReLU和普通ReLU几乎完全一样,除了x < 0时有一个很小的斜率。
在实践中,这个很小的斜率α通常取0.01.
Leaky ReLU的优势在于反向传播可以更新产生负的激活前值的权重,因为Leaky ReLU激活函数的负值区间的梯度是αex。YOLO(点击阅读)目标检测算法就用了Leaky ReLU。
因为负的激活前值会生成负值而不是0,Leaky ReLU没有ReLU中的权重只在一个方向上更新的问题。
α该取多大,人们做了很多试验。有一种称为随机Leaky ReLU的方法,负值区间的斜率从均值为0、标准差为1的均匀分布中随机抽取。
随机ReLU的论文主张,随机ReLU能得到比Leaky ReLU更好的结果,训练起来也更快,并通过经验方法得出,如果限定只使用单一的α值,那么1/5.5要比通常选择的0.01效果要好。
随机Leaky ReLU奏效的原因是负值区间斜率的随机选择给负的激活前值梯度带来了随机性。在优化算法中引入的随机性,或者说噪声,有助于摆脱局部极小值和鞍点(在本系列的第一篇文章中,我们深入讨论了这一主题)。
后来人们又进一步提出,α可以看作一个参数,在网络的训练过程中学习。采用这一方法的激活函数称为参数化ReLU。
神经元饱和看起来是一件很糟的事情,但ReLU中的单边饱和未必不好。尽管前面提到的一些ReLU变体抑制了死亡ReLU问题,但却丧失了单边饱和的益处。
基于上面的讨论,看起来一个完美的激活函数应该同时具备以下两个性质:
产生零中心化分布,以加速训练过程。
具有单边饱和,以导向更好的收敛。
Leaky ReLU和PReLU(参数化ReLU)满足第一个条件,不满足第二个条件。而原始的ReLU满足第二个条件,不满足第一个条件。
同时满足两个条件的一个激活函数是指数线性单元(ELU)。
x > 0部分,ELU的梯度是1,x < 0部分的梯度则是α × ex。ELU激活函数的负值区域趋向于-α. α是一个超参数,通常取1.
首先尝试ReLU激活。尽管我们上面列出了ReLU的一些问题,但很多人使用ReLU取得了很好的结果。根据奥卡姆剃刀原则,先尝试更简单的方案比较好。相比ReLU的有力挑战者,ReLU的算力负担最轻。如果你的项目需要从头开始编程,那么ReLU的实现也特别简单。
如果ReLU的效果不好,我会接着尝试Leaky ReLU或ELU。我发现能够产生零中心化激活的函数一般要比不能做到这点的函数效果好得多。ELU看起来很有吸引力,但是由于负的激活前值会触发大量指数运算,基于ELU的网络训练和推理都很缓慢。如果算力资源对你而言不成问题,或者网络不是特别巨大,选择ELU,否则,选择Leaky ReLU。LReLU和ELU都增加了一个需要调整的超参数。
如果算力资源很充沛,时间很充裕,你可以将上述激活函数的表现与PReLU和随机ReLU做下对比。如果出现了过拟合,那么随机ReLU可能会有用。参数化ReLU加入了需要学习的一组参数,所以,只在具备大量训练数据的情况下才考虑选用参数化ReLU。
这篇文章讨论了传入什么样的数据分布,有利于神经网络层恰当地学习。激活函数隐式地归一化这些分布,而一种称为批归一化(Batch Normalization)的技术明确地进行了这一操作。批归一化是近年来深度学习领域的主要突破之一。不过,我们要到本系列的下一篇文章才会讨论这一技术,目前而言,你可以亲自尝试下在自己的网络上使用不同的激活函数有什么效果!试验愉快!
梯度爆炸问题:https://machinelearningmastery.com/exploding-gradients-in-neural-networks/
ReLU优势的深入讨论:https://stats.stackexchange.com/q/176794
Reddit上关于ReLU为何仍在使用的讨论:https://www.reddit.com/comments/73rtd7/
ELU论文:https://arxiv.org/abs/1511.07289
原文地址:https://blog.paperspace.com/vanishing-gradients-activation-function/