常见激活函数使用
Logistic函数或Logistic曲线是一种常见的S形函数,它是皮埃尔·弗朗索瓦·韦吕勒在1844或1845年在研究它与人口增长的关系时命名的。广义Logistic曲线可以模仿一些情况人口增长(P)的S形曲线。起初阶段大致是指数增长;然后随着开始变得饱和,增加变慢;最后,达到成熟时增加停止。
Sigmoid函数,即f(x)=1/(1+e-x)。神经元的非线性作用函数。(-x是幂数)
sigmoid函数是一个良好的阈值函数, 连续,光滑 严格单调 关于(0,0.5)中心对称 对阈值函数 _ 1, x > \delta f(x)= / \ - 0, x < -\delta 的良好近似 其导数f'(x)=f(x)*[1-f(x)],可以节约计算时间 f(x) = 1/[1+e^(-x)].图形如上。 如果x = a*r.其中a为倾斜系数,当a足够小,这个图形可以无限制接近你这个阈值函数
首先我们看下sigmoid和relu的曲线;
结论就是sigmoid的导数只有在0附近的时候有比较好的激活性,在正负饱和区的梯度都接近于0,所以这会造成梯度弥散,而relu函数在大于0的部分梯度为常数,所以不会产生梯度弥散现象。第二,relu函数在负半区的导数为0 ,所以一旦神经元激活值进入负半区,那么梯度就会为0,也就是说这个神经元不会经历训练,即所谓的稀疏性。第三,relu函数的导数计算更快,程序实现就是一个if-else语句,而sigmoid函数要进行浮点四则运算。综上,relu是一个非常优秀的激活函数。。。
人工神经网络的学习算法-BP算法
神经网络的学习是基于一组样本进行的,它包括输入和输出(这里用期望输出表示),输入和输出有多少个分量就有多少个输入和输出神经元与之对应。最初神经网络的权值(Weight)和阈值(Threshold)是任意给定的,学习就是逐渐调整权值和阈值使得网络的实际输出和期望输出一致。
我们假设样本有P个,输入层有N个神经元,隐含层有K个神经元,输出层有M个神经元。Xj为输入层神经元j的输入,Hj为隐含层神经元j的输出,Fj为输出层神经元j的实际输出,Rj为输出层神经元j的期望输出,前一层的输出即为后一层的输入。Whji是输入层神经元i与隐含层神经元j之间的连接权值,Thj是隐含神经元j的阈值,Woji是隐含层神经元i与输出层神经元j之间的连接权值,Toj是输出神经元j的阈值。神经元的非线性作用函数是Sigmoid函数,即f(x)=1/(1+e-x)。
深度学习中的激活函数导引
激活函数的定义与作用
在人工神经网络中,神经元节点的激活函数定义了对神经元输出的映射,简单来说,神经元的输出(例如,全连接网络中就是输入向量与权重向量的内积再加上偏置项)经过激活函数处理后再作为输出。加拿大蒙特利尔大学的Bengio教授在 ICML 2016 的文章[1]中给出了激活函数的定义:激活函数是映射 h:R→R,且几乎处处可导。
神经网络中激活函数的主要作用是提供网络的非线性建模能力,如不特别说明,激活函数一般而言是非线性函数。假设一个示例神经网络中仅包含线性卷积和全连接运算,那么该网络仅能够表达线性映射,即便增加网络的深度也依旧还是线性映射,难以有效建模实际环境中非线性分布的数据。加入(非线性)激活函数之后,深度神经网络才具备了分层的非线性映射学习能力。因此,激活函数是深度神经网络中不可或缺的部分。
激活函数的历史发展与近期进展
从定义来看,几乎所有的连续可导函数都可以用作激活函数。但目前常见的多是分段线性和具有指数形状的非线性函数。下文将依次对它们进行总结。
Sigmoid
Sigmoid 是使用范围最广的一类激活函数,具有指数函数形状 。正式定义为:
可见,sigmoid 在定义域内处处可导,且两侧导数逐渐趋近于0,即:
Bengio 教授等[1]将具有这类性质的激活函数定义为软饱和激活函数。与极限的定义类似,饱和也分为左饱和与右饱和:
左饱和:右饱和:
与软饱和相对的是硬饱和激活函数,即:f'(x)=0,当 |x| > c,其中 c 为常数。
同理,硬饱和也分为左饱和和右饱和。常见的 ReLU 就是一类左侧硬饱和激活函数。
Sigmoid 的软饱和性,使得深度神经网络在二三十年里一直难以有效的训练,是阻碍神经网络发展的重要原因。具体来说,由于在后向传递过程中,sigmoid向下传导的梯度包含了一个f'(x) 因子(sigmoid关于输入的导数),因此一旦输入落入饱和区,f'(x) 就会变得接近于0,导致了向底层传递的梯度也变得非常小。此时,网络参数很难得到有效训练。这种现象被称为梯度消失。一般来说, sigmoid 网络在 5 层之内就会产生梯度消失现象[2]。梯度消失问题至今仍然存在,但被新的优化方法有效缓解了,例如DBN中的分层预训练,Batch Normalization的逐层归一化,Xavier和MSRA权重初始化等代表性技术。
Sigmoid 的饱和性虽然会导致梯度消失,但也有其有利的一面。例如它在物理意义上最为接近生物神经元。 (0, 1) 的输出还可以被表示作概率,或用于输入的归一化,代表性的如Sigmoid交叉熵损失函数
tanh可见,tanh(x)=2sigmoid(2x)-1,也具有软饱和性。Xavier在文献[2]中分析了sigmoid与tanh的饱和现象及特点,具体见原论文。此外,文献 [3] 中提到tanh 网络的收敛速度要比sigmoid快。因为 tanh 的输出均值比 sigmoid 更接近 0,SGD会更接近 natural gradient[4](一种二次优化技术),从而降低所需的迭代次数。
ReLU
虽然2006年Hinton教授提出通过分层无监督预训练解决深层网络训练困难的问题,但是深度网络的直接监督式训练的最终突破,最主要的原因是采用了新型激活函数ReLU[5, 6]。与传统的sigmoid激活函数相比,ReLU能够有效缓解梯度消失问题,从而直接以监督的方式训练深度神经网络,无需依赖无监督的逐层预训练,这也是2012年深度卷积神经网络在ILSVRC竞赛中取得里程碑式突破的重要原因之一。
ReLU的 正式定义为:可见,ReLU 在x<0 时硬饱和。由于 x>0时导数为 1,所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。但随着训练的推进,部分输入会落入硬饱和区,导致对应权重无法更新。这种现象被称为“神经元死亡”。
ReLU还经常被“诟病”的一个问题是输出具有偏移现象[7],即输出均值恒大于零。偏移现象和 神经元死亡会共同影响网络的收敛性。本文作者公开在arxiv的文章[8]中的实验表明,如果不采用Batch Normalization,即使用 MSRA 初始化30层以上的ReLU网络,最终也难以收敛。相对的,PReLU和ELU网络都能顺利收敛,这两种改进的激活函数将在后面介绍。实验所用代码见GitHub - Coldmooon/Code-for-MPELU: Code for Improving Deep Neural Network with Multiple Parametric Exponential Linear Units 。
ReLU另外一个性质是提供神经网络的稀疏表达能力,在Bengio教授的Deep Sparse Rectifier Neural Network[6]一文中被认为是ReLU带来网络性能提升的原因之一。但后来的研究发现稀疏性并非性能提升的必要条件,文献 RReLU [9]也指明了这一点。
PReLU[10]、ELU[7]等激活函数不具备这种稀疏性,但都能够提升网络性能。本文作者在文章[8]中给出了一些实验比较结果。首先,在cifar10上采用NIN网络,实验结果为 PReLU > ELU > ReLU,稀疏性并没有带来性能提升。其次,在 ImageNet上采用类似于[11] 中model E的15 层网络,实验结果则是ReLU最好。为了验证是否是稀疏性的影响,以 LReLU [12]为例进一步做了四次实验,负半轴的斜率分别为1,0.5,0.25, 0.1,需要特别说明的是,当负半轴斜率为1时,LReLU退化为线性函数,因此性能损失最大。实验结果展现了斜率大小与网络性能的一致性。综合上述实验可知,ReLU的稀疏性与网络性能之间并不存在绝对正负比关系。
PReLU
PReLU [10]是ReLU 和 LReLU的改进版本,具有非饱和性:与LReLU相比,PReLU中的负半轴斜率a可学习而非固定。原文献建议初始化a为0.25,不采用正则。个人认为,是否采用正则应当视具体的数据库和网络,通常情况下使用正则能够带来性能提升。
虽然PReLU 引入了额外的参数,但基本不需要担心过拟合。例如,在上述cifar10+NIN实验中, PReLU比ReLU和ELU多引入了参数,但也展现了更优秀的性能。所以实验中若发现网络性能不好,建议从其他角度寻找原因。
与ReLU相比,PReLU收敛速度更快。因为PReLU的输出更接近0均值,使得SGD更接近natural gradient。证明过程参见原文[10]。
此外,作者在ResNet 中采用ReLU,而没有采用新的PReLU。这里给出个人浅见,不一定正确,仅供参考。首先,在上述LReLU实验中,负半轴斜率对性能的影响表现出一致性。对PReLU采用正则将激活值推向0也能够带来性能提升。这或许表明,小尺度或稀疏激活值对深度网络的影响更大。其次,ResNet中包含单位变换和残差两个分支。残差分支用于学习对单位变换的扰动。如果单位变换是最优解,那么残差分支的扰动应该越小越好。这种假设下,小尺度或稀疏激活值对深度网络的影响更大。此时,ReLU或许是比PReLU更好的选择。
RReLU
数学形式与PReLU类似,但RReLU[9]是一种非确定性激活函数,其参数是随机的。这种随机性类似于一种噪声,能够在一定程度上起到正则效果。作者在cifar10/100上观察到了性能提升。
Maxout
Maxout[13]是ReLU的推广,其发生饱和是一个零测集事件(measure zero event)。正式定义为:
Maxout网络能够近似任意连续函数,且当w2,b2,…,wn,bn为0时,退化为ReLU。 其实,Maxout的思想在视觉领域存在已久。例如,在HOG特征里有这么一个过程:计算三个通道的梯度强度,然后在每一个像素位置上,仅取三个通道中梯度强度最大的数值,最终形成一个通道。这其实就是Maxout的一种特例。
Maxout能够缓解梯度消失,同时又规避了ReLU神经元死亡的缺点,但增加了参数和计算量。
ELU
ELU[7]融合了sigmoid和ReLU,具有左侧软饱性。其正式定义为:
右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。ELU的输出均值接近于零,所以收敛速度更快。经本文作者实验,ELU的收敛性质的确优于ReLU和PReLU。在cifar10上,ELU 网络的loss 降低速度更快;在 ImageNet上,不加 Batch Normalization 30 层以上的 ReLU 网络会无法收敛,PReLU网络在MSRA的Fan-in (caffe )初始化下会发散,而 ELU 网络在Fan-in/Fan-out下都能收敛 。实验代码见GitHub - Coldmooon/Code-for-MPELU: Code for Improving Deep Neural Network with Multiple Parametric Exponential Linear Units。
论文的另一个重要贡献是分析了Bias shift 现象与激活值的关系,证明了降低Bias shift 等价于把激活值的均值推向0。
Noisy Activation Functions
engio教授在ICML 2016 提出了一种激活策略[1],可用于多种软饱和激活函数,例如 sigmoid和 tanh。
当激活函数发生饱和时, 网络参数还能够在两种动力下继续更新:正则项梯度和噪声梯度。引入适当的噪声能够扩大SGD的参数搜索范围,从而有机会跳出饱和区。在激活函数中引入噪声的更早工作可追溯到[5],但文献[5]的工作并不考虑噪声引入的时间和大小。本篇的特点在于,只在饱和区才引入噪声,且噪声量与饱和程度相关——原式与泰勒展开式一次项之差 δ。算法1中g表示sigmoid,用于归一化 δ。注意,ReLU的 δ恒为0,无法直接加噪声,所以作者把噪声加在了输入上。
CReLU
CReLU [14]是Wenling Shang 发表在 ICML 2016的工作,本篇同样提出了一种激活策略:其中,[] 表示 ReLU(其他亦可)。
作者在观察第一层滤波器(filter)时发现,滤波器相位具有成对现象(pair-grouping phenomenon)。这一发现揭示了网络的底层学到了一些冗余滤波器来提取输入的正负相位信息的可能性。因此可以考虑采用适当的操作移除这些冗余滤波器。对此,作者提出了CReLU,将激活函数的输入额外做一次取反,等价于将输入相位旋转180°。这种策略可以看作在网络中加入相位的先验。实验在cifar10上观察到能以更少的参数获得性能提升。使用CReLU时,要有意识的将滤波器数量减半,否则, 网络参数变为2倍。
MPELU
MPELU[8]是我们组的工作,将分段线性与ELU统一到了一种形式下。在NIN+CIFAR10,本文作者发现ELU与LReLU性能一致,而与PReLU差距较大。经过分析,ELU泰勒展开的一次项就是LReLU。当在ELU前加入BN让输入集中在0均值附近, 则ELU与LReLU之差——泰勒展开高次项会变小,粗略估计,约55.57%的激活值误差小于0.01。因此,受PReLU启发,令α可学习能够提高性能。此外,引入参数β能够进一步控制ELU的函数形状。正式定义为:α 和 β可以使用正则。α, β 固定为1时,MPELU 退化为 ELU; β 固定为很小的值时,MPELU 近似为 PReLU;当α=0,MPELU 等价于 ReLU。
MPELU 的优势在于同时具备 ReLU、PReLU和 ELU的优点。首先,MPELU具备ELU的收敛性质,能够在无 Batch Normalization 的情况下让几十层网络收敛。其次,作为一般化形式, MPELU较三者的推广能力更强。简言之,MPELU = max(ReLU, PReLU, ELU)。当前对ELU网络普遍采用的初始化方法是 MSRA。这在实际中是可行的,只是不具备理论解释性。我们的工作利用泰勒公式和MSRA的推导过程,为ELU网络初始化提供了理论解释。此外,Dmytro 提出了 LSUV[15],理论上可以用于 ELU/MPELU 的初始化。但在30/52层ELU网络上,发现 LSUV 会导致ELU网络在几次迭代之内发散,网络文件见GitHub - Coldmooon/Code-for-MPELU: Code for Improving Deep Neural Network with Multiple Parametric Exponential Linear Units。
总结
深度学习的快速发展,催生了形式各异的激活函数。面对琳琅满目的成果,如何做出选择目前尚未有统一定论,仍需依靠实验指导。一般来说,在分类问题上建议首先尝试 ReLU,其次ELU,这是两类不引入额外参数的激活函数。然后可考虑使用具备学习能力的PReLU和本文作者提出的MPELU,并使用正则化技术,例如应该考虑在网络中增加Batch Normalization层。
本文围绕深度卷积神经网络结构,对十余种激活函数进行了总结,相关代码可在作者的github主页上下载:GitHub - Coldmooon/Code-for-MPELU: Code for Improving Deep Neural Network with Multiple Parametric Exponential Linear Units。
为什么引入激活函数?
如果不用激励函数(其实相当于激励函数是f(x) = x),在这种情况下你每一层输出都是上层输入的线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron)了。
正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络就有意义了(不再是输入的线性组合,可以逼近任意函数)。最早的想法是sigmoid函数或者tanh函数,输出有界,很容易充当下一层输入(以及一些人的生物解释balabala)。激活函数的作用是为了增加神经网络模型的非线性。否则你想想,没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。所以你没有非线性结构的话,根本就算不上什么神经网络。
为什么引入Relu呢?
第一,采用sigmoid等函数,算激活函数时(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。
第二,对于深层网络,sigmoid函数反向传播时,很容易就会出现梯度消失的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失,从而无法完成深层网络的训练。
第三,Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生(以及一些人的生物解释balabala)。
当然现在也有一些对relu的改进,比如prelu,random relu等,在不同的数据集上会有一些训练速度上或者准确率上的改进,具体的大家可以找相关的paper看。
多加一句,现在主流的做法,会多做一步batch normalization,尽可能保证每一层网络的输入具有相同的分布[1]。而最新的paper[2],他们在加入bypass connection之后,发现改变batch normalization的位置会有更好的效果。大家有兴趣可以看下。
深度学习的基本原理是基于人工神经网络,信号从一个神经元进入,经过非线性的activation function,传入到下一层神经元;再经过该层神经元的activate,继续往下传递,如此循环往复,直到输出层。正是由于这些非线性函数的反复叠加,才使得神经网络有足够的capacity来抓取复杂的pattern,在各个领域取得state-of-the-art的结果。显而易见,activation function在深度学习中举足轻重,也是很活跃的研究领域之一。目前来讲,选择怎样的activation function不在于它能否模拟真正的神经元,而在于能否便于优化整个深度神经网络。下面我们简单聊一下各类函数的特点以及为什么现在优先推荐ReLU函数。
Sigmoid函数
Sigmoid函数是深度学习领域开始时使用频率最高的activation function。它是便于求导的平滑函数,其导数为,这是优点。然而,Sigmoid有三大缺点:
-
容易出现gradient vanishing
-
函数输出并不是zero-centered
-
幂运算相对来讲比较耗时
Gradient Vanishing
优化神经网络的方法是Back Propagation,即导数的后向传递:先计算输出层对应的loss,然后将loss以导数的形式不断向上一层网络传递,修正相应的参数,达到降低loss的目的。 Sigmoid函数在深度网络中常常会导致导数逐渐变为0,使得参数无法被更新,神经网络无法被优化。原因在于两点:(1) 在上图中容易看出,当中较大或较小时,导数接近0,而后向传递的数学依据是微积分求导的链式法则,当前层的导数需要之前各层导数的乘积,几个小数的相乘,结果会很接近0 (2) Sigmoid导数的最大值是0.25,这意味着导数在每一层至少会被压缩为原来的1/4,通过两层后被变为1/16,…,通过10层后为1/1048576。请注意这里是“至少”,导数达到最大值这种情况还是很少见的。
输出不是zero-centered
Sigmoid函数的输出值恒大于0,这会导致模型训练的收敛速度变慢。举例来讲,对,如果所有均为正数或负数,那么其对的导数总是正数或负数,这会导致如下图红色箭头所示的阶梯式更新,这显然并非一个好的优化路径。深度学习往往需要大量时间来处理大量数据,模型的收敛速度是尤为重要的。所以,总体上来讲,训练深度学习网络尽量使用zero-centered数据 (可以经过数据预处理实现) 和zero-centered输出。
幂运算相对耗时
相对于前两项,这其实并不是一个大问题,我们目前是具备相应计算能力的,但面对深度学习中庞大的计算量,最好是能省则省 :-)。之后我们会看到,在ReLU函数中,需要做的仅仅是一个thresholding,相对于幂运算来讲会快很多。
tanh函数
tanh读作Hyperbolic Tangent,如上图所示,它解决了zero-centered的输出问题,然而,gradient vanishing的问题和幂运算的问题仍然存在。
ReLU函数
ReLU函数其实就是一个取最大值函数,注意这并不是全区间可导的,但是我们可以取sub-gradient,如上图所示。ReLU虽然简单,但却是近几年的重要成果,有以下几大优点:
-
解决了gradient vanishing问题 (在正区间)
-
计算速度非常快,只需要判断输入是否大于0
-
收敛速度远快于sigmoid和tanh
ReLU也有几个需要特别注意的问题:
-
ReLU的输出不是zero-centered
-
Dead ReLU Problem,指的是某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。有两个主要原因可能导致这种情况产生: (1) 非常不幸的参数初始化,这种情况比较少见 (2) learning rate太高导致在训练过程中参数更新太大,不幸使网络进入这种状态。解决方法是可以采用Xavier初始化方法,以及避免将learning rate设置太大或使用adagrad等自动调节learning rate的算法。
尽管存在这两个问题,ReLU目前仍是最常用的activation function,在搭建人工神经网络的时候推荐优先尝试!
Leaky ReLU函数
人们为了解决Dead ReLU Problem,提出了将ReLU的前半段设为而非0。另外一种直观的想法是基于参数的方法,即Parametric ReLU:,其中可由back propagation学出来。理论上来讲,Leaky ReLU有ReLU的所有优点,外加不会有Dead ReLU问题,但是在实际操作当中,并没有完全证明Leaky ReLU总是好于ReLU。
ELU (Exponential Linear Units) 函数
ELU也是为解决ReLU存在的问题而提出,显然,ELU有ReLU的基本所有优点,以及:
-
不会有Deal ReLU问题
-
输出的均值接近0,zero-centered
它的一个小问题在于计算量稍大。类似于Leaky ReLU,理论上虽然好于ReLU,但在实际使用中目前并没有好的证据ELU总是优于ReLU。
小结
建议使用ReLU函数,但是要注意初始化和learning rate的设置;可以尝试使用Leaky ReLU或ELU函数;不建议使用tanh,尤其是sigmoid函数。