文章转载自:https://zhuanlan.zhihu.com/p/71882757

1.什么是激活函数?

所谓激活函数(Activation Function),就是在人工神经网络的神经元上运行的函数,负责将神经元的输入映射到输出端。

激活函数对于人工神经网络模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用。它们将非线性特性引入到我们的网络中。如图,在神经元中,输入(inputs )通过加权,求和后,还被作用在一个函数上,这个函数就是激活函数。

2.为什么要用激活函数?

如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机(Perceptron)。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。

如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。

3.激活函数为什么是非线性的?

如果使用线性激活函数,那么输入跟输出之间的关系为线性的,无论神经网络有多少层都是线性组合。

使用非线性激活函数是为了增加神经网络模型的非线性因素,以便使网络更加强大,增加它的能力,使它可以学习复杂的事物,复杂的表单数据,以及表示输入输出之间非线性的复杂的任意函数映射。

输出层可能会使用线性激活函数,但在隐含层都使用非线性激活函数

4.常用的激活函数:sigmoid,Tanh,ReLU,Leaky ReLU,PReLU,ELU,Maxout

(1) sigmoid函数

sigmoid函数又称 Logistic函数,用于隐层神经元输出,取值范围为(0,1),可以用来做二分类。

sigmoid函数表达式: \sigma (x) = \frac{1}{1+e^{-x}}

它的导数为:

sigmoid函数的几何形状是一条S型曲线,图像如下:

优点:

  1. Sigmoid函数的输出在(0,1)之间,输出范围有限,优化稳定,可以用作输出层。
  2. 连续函数,便于求导。

缺点:

    1. sigmoid函数在变量取绝对值非常大的正值或负值时会出现饱和现象,意味着函数会变得很平,并且对输入的微小改变会变得不敏感。在反向传播时,当梯度接近于0,权重基本不会更新,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。

    2. sigmoid函数的输出不是0均值的,会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响。

    3. 计算复杂度高,因为sigmoid函数是指数形式。

(2) Tanh函数

Tanh函数也称为双曲正切函数,取值范围为[-1,1]。

Tanh函数定义如下:

它的导数为:

函数图像如下:

Tanh函数是 sigmoid 的变形:

Tanh函数是 0 均值的,因此实际应用中 Tanh 会比 sigmoid 更好。但是仍然存在梯度饱和exp计算的问题。

(3) ReLU函数

整流线性单元(Rectified linear unit,ReLU)是现代神经网络中最常用的激活函数,大多数前馈神经网络默认使用的激活函数。

ReLU函数定义如下:f(x)=max(0,x)

函数图像如下:

优点:

  1. 使用ReLU的SGD算法的收敛速度比 sigmoid 和 tanh 快。

    2. 在x>0区域上,不会出现梯度饱和、梯度消失的问题。

    3. 计算复杂度低,不需要进行指数运算,只要一个阈值就可以得到激活值。

缺点:

  1. ReLU的输出不是0均值的。

    2. Dead ReLU Problem(神经元坏死现象):ReLU在负数区域被kill的现象叫做dead relu。ReLU在训练的时很“脆弱”。在x<0时,梯度为0。这个神经元及之后的神经元梯度永远为0,不再对任何数据有所响应,导致相应参数永远不会被更新。

产生这种现象的两个原因:参数初始化问题;learning rate太高导致在训练过程中参数更新太大。

解决方法:采用Xavier初始化方法,以及避免将learning rate设置太大或使用adagrad等自动调节learning rate的算法。

(4) Leaky ReLU函数

渗漏整流线性单元(Leaky ReLU),为了解决dead ReLU现象。用一个类似0.01的小值来初始化神经元,从而使得ReLU在负数区域更偏向于激活而不是死掉。这里的斜率都是确定的。

(5) PReLU

参数整流线性单元(Parametric Rectified linear unit,PReLU),用来解决ReLU带来的神经元坏死的问题。

公式: f(x) = max(ax,x) 。 其中 a 不是固定的,是通过反向传播学习出来的。

(6) ELU

指数线性单元(ELU):具有relu的优势,没有Dead ReLU问题,输出均值接近0,实际上PReLU和Leaky ReLU都有这一优点。有负数饱和区域,从而对噪声有一些鲁棒性。可以看做是介于ReLU和Leaky ReLU之间的一个函数。当然,这个函数也需要计算exp,从而计算量上更大一些。

(7) Maxout

论文链接:https://arxiv.org/pdf/1302.4389.pdf

推荐一篇博客,讲的很好:https://blog.csdn.net/hjimce/article/details/50414467

Maxout隐藏层每个神经元的计算公式如下:

maxout的思路也不难,本身我们的激活函数就可以作为一个激活层来当做层使用,而maxout相对来说更灵活,直接将激活层的部分替换为了一个新的隐层结构如上图,简单粗暴点说就是用一个隐层来作为激活函数,而这个隐层的神经元个数就是maxout的K参数可以人工指定。

这一层的输出就是这样的,其中maxout层的权重w和b也是要进行权重初始化的,只不过在输出上使用了max来去激活值最大的输出从而使得 这一整个隐层变成了一个分段线性函数。

1、maxout激活函数并不是一个固定的函数,不像Sigmod、Relu、Tanh等函数,是一个固定的函数方程

2、它是一个可学习的激活函数,因为我们W参数是学习变化的。

3、它是一个分段线性函数:

然而任何一个凸函数,都可以由线性分段函数进行逼近近似。其实我们可以把以前所学到的激活函数:relu、abs激活函数,看成是分成两段的线性函数,如下示意图所示:

maxout的拟合能力是非常强的,它可以拟合任意的的凸函数。最直观的解释就是任意的凸函数都可以由分段线性函数以任意精度拟合(学过高等数学应该能明白),而maxout又是取k个隐隐含层节点的最大值,这些”隐隐含层"节点也是线性的,所以在不同的取值范围下,最大值也可以看做是分段线性的(分段的个数与k值有关)

Maxout是通过分段线性函数来拟合所有可能的凸函数来作为激活函数的,但是由于线性函数是可学习,所以实际上是可以学出来的激活函数。具体操作是对所有线性取最大,也就是把若干直线的交点作为分段的边界,然后每一段取最大。

优点:

  1. Maxout的拟合能力非常强,可以拟合任意的凸函数。

     2. Maxout具有ReLU的所有优点,线性、不饱和性。

     3. 不会出现神经元坏死的现象。

缺点:增加了参数量。显然计算量太大。常规的激活函数就只是单纯的函数而已。而maxout还需要反向传播去更新它自身的权重系数。

 

Dead ReLU Problem:

设激活函数为relu的层的权重为W,则W的权重更新公式为:

W = W - lr \frac{\partial L}{\partial W}

假设学习率lr很大,梯度更新量也很大,此时W会发生非常大的更新,举个例子:

原始W=[1.5,2.5,1.4,5.5]

lr=10,

梯度更新量对应为[15,25,14,55]

则更新之后W全部为很大的负数了。。。。

假设上一层的输出(也就是带有relu激活函数的层的输入)大都为正,这将会导致当前层的所有未激活函数输出均为负数,那么进入relu之后就被直接置0了,导致输出永远为0,梯度更新量永远为0,该层的权重系数不再发生变化。

解决方案:

    1、leaky relu,elu甚至是random relu的使用是的输入是负数的时候激活函数也有输出;

    2、避免使用太大的学习率

    3、权重初始化策略不要初始化出太大的W

3. 应用中如何选择合适的激活函数?

这个问题目前没有确定的方法,凭一些经验吧。
(1)深度学习往往需要大量时间来处理大量数据,模型的收敛速度是尤为重要的。所以,总体上来讲,训练深度学习网络尽量使用zero-centered数据 (可以经过数据预处理实现) 和zero-centered输出。所以要尽量选择输出具有zero-centered特点的激活函数以加快模型的收敛速度。
(2)如果使用 ReLU,那么一定要小心设置 learning rate,而且要注意不要让网络出现很多 “dead” 神经元,如果这个问题不好解决,那么可以试试 Leaky ReLU、PReLU 或者 Maxout.
(3)最好不要用 sigmoid,你可以试试 tanh,不过可以预期它的效果会比不上 ReLU 和 Maxout.

4.参考资料

    文章的一部分内容还参考自:

  【1】https://zhuanlan.zhihu.com/p/92412922

  【2】https://blog.csdn.net/tyhj_sf/article/details/79932893

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐