【转载】深入理解深度学习中的激活函数

1. 什么是激活函数?

       生物神经网络是人工神经网络的起源。然而,人工神经网络(ANNs)的工作机制与大脑的工作机制并不是十分的相似。不过在我们了解为什么把激活函数应用在人工神经网络中之前,了解一下激活函数与生物神经网络的关联依然是十分有用的。一个典型神经元的物理结构由细胞体、向其他神经元发送信息的轴突以及从其他神经元接受信号或信息的树突组成。      

                                                        

       图中,红色的圆圈表示两个神经元连接的区域。神经元通过树突从其他神经元中接受信号。树突的信号强度称为突触权值,用于与传入信号相乘。树突传出的信号在细胞体中累积,如果最后的信号强度超过了某个阈值,神经元就会允许轴突中的信息继续传递。否则,信号就会被阻止而得不到进一步的传播。激活函数决定了信号是否能够被通过。这个例子仅仅是个只有阈值这一个参数的简单的阶跃函数。现在,当我们学习了一些新东西(或者忘掉一些东西)时,阈值以及一些神经元的突触权重会发生改变。这在神经元中创造了新的连接从而使得大脑能学习到新的东西。让我们在人工神经元的基础上来再次理解相同的概念。

                                              

       图中所示的例子是一个输入为x_{1}\rightarrow x_{n}的神经元,输入对应的权重分布为w_{1}\rightarrow w_{n},偏置为b,激活函数f作用于输入的权重之和上面。(x_{1},x_{2}...x_{n})是与权重(w_{1},w_{2}...w_{n})相乘的信号向量,接着是累加(例如:总和+偏置b)。最后,激活函数f作用于这个累加的总和。请注意,权重(w_{1},w_{2}...w_{n})和偏置b把输入信号转换为线性的。而另一方面,激活函数把信号转换为非线性的,而这种非线性使得我们能够学习到输入与输出之间任意复杂的变换关系。

      这些年来,人们使用了各种各样的激活函数,但是寻找一个合适的激活函数使神经网络学习得更好更快依然是一个非常活跃的研究领域。

2. 网络是怎么学习的?

        理解神经网络学习的基本概念是关键。假设网络原本应该得到的输出为y。网络产生的输出为y{}'。应该得到的输出与实际得到的输出之间的差值(y{}'-y)被转换为损失函数(J(\theta ))的度量。当神经网络的错误很多时,该损失很大,而当损失很小时则网络的错误也很少。整个训练过程就是在训练集上寻找使损失函数最小的权值和偏置

                                               

        在图中,损失函数的形状像一个碗。在训练过程中的任何一点,损失函数关于权值的偏导数只是在碗的当前位置上的斜率。可见通过向偏导数预测出的方向移动,我们可以到达碗的底部,从而最小化了损失函数。这个使用函数的偏导数来迭代找到局部最小值的方法称为梯度下降法。在人工神经网络中,权值通过称为反向传播的方法来更新。损失函数关于权值的偏导数用于更新权值。在某种意义上来说,误差是在网络上用导数来反向传播的。这是用迭代的方式来完成的,在许多轮迭代之后,损失达到最小值,并且损失函数的导数变为0。

3. 激活函数的类型

     线性激活函数:形式为f(x)=x的简单的线性函数。基本上,输入不经过任何修正就传递给输出。  

                                          

非线性激活函数:这些函数用于分离非线性可分的数据,并且是最常使用的激活函数。一个非线性等式决定了从输入到输出的映射。不同类型的非线性激活函数分别有sigmod, tanh, relu, lrelu, prelu, swish等等。本文接下来会详细的讨论这些激活函数           

4. 在一个人工神经网络中,我们为什么需要非线性激活函数?

      神经网络用于实现复杂的函数,而非线性激活函数能够使神经网络逼近任意复杂的函数。如果没有激活函数引入的非线性,多层神经网络就相当于单层的神经网络。让我们看一个简单的例子来理解为什么没有非线性,神经网络甚至不可能逼近像XOR和XNOR门这样简单的函数。

     在下图中,我们用图表表示了XOR门。我们的数据集中有两个类,分别用交叉和圆圈来表示。当两个特征x_{1}x_{2}相同时,类的标签为红色交叉,否则就是蓝色圆圈。当输入为(0,0)与(1,1)时红色交叉的输出为0,输入为(0,1)和(1,0)时的蓝色圆圈的输出为1。

                                

        通过上图我们可以看到数据点都是非线性可分的。也就是说,我们无法画出一条笔直的直线来分开蓝色圆圈和红色交叉。因此,我们才需要非线性的决策边界来将它们分开。如果没有非线性,神经网络就不能逼近XOR门。激活函数对控制神经网络的输出范围也起着至关重要的作用。神经元的输出\sum_{i}^{n} (w_{i}x_{i}+b)可以是非常大的值。而这个输出,若我们不经修改就输入到下一层神经元中,有可能演变成一个非常大的数从而使得计算过程非常难以处理。而激活函数的任务之一就是将神经元的输出映射到某个范围内(例如:0到1之间)。接下来,我们准备去了解一下不同类型的激活函数。

非线性激活函数

5.1 Sigmoid激活函数

       Sigmoid也被称为逻辑激活函数(Logistic Activation Function)。它将一个实数值压缩到0至1的范围内。当我们的最终目标是预测概率时,它可以被应用到输出层。它使很大的负数向0转变,很大的正数向1转变。在数学上表示为:

                                                                       

下图为sigmoid函数以及它的导数图像。

                

Sigmoid激活函数的三个主要缺点是:

1、梯度消失:sigmoid函数在0和1附近是平坦的。也就是说,sigmoid的梯度在0和1附近为0。在通过sigmoid函数网络反向传播时,当神经元的输出近似于0和1时它的梯度接近于0。这些神经元被称为饱和神经元。因此,这些神经元的权值无法更新。不仅如此,与这些神经元相连接的神经元的权值也更新得非常缓慢。这个问题也被称为梯度消失。所以,想象如果有一个大型网络包含有许多处于饱和动态的sigmoid激活函数的神经元,那么网络将会无法进行反向传播。

2、不是零均值:sigmoid的输出不是零均值的。

3、计算量太大:指数函数与其它非线性激活函数相比计算量太大了。下一个要讨论的是解决了sigmoid中零均值问题的非线性激活函数。

Sigmoid 和 Softmax 区别:

  sigmoid将一个real value映射到(0,1)的区间,用来做二分类。而 softmax 把一个 k 维的real value向量(a1,a2,a3,a4….)映射成一个(b1,b2,b3,b4….)其中 bi 是一个 0~1 的常数,输出神经元之和为 1.0,所以相当于概率值,然后可以根据 bi 的概率大小来进行多分类的任务。二分类问题时 sigmoid 和 softmax 是一样的,求的都是 cross entropy loss,而 softmax 可以用于多分类问题多个logistic回归通过叠加也同样可以实现多分类的效果,但是 softmax回归进行的多分类,类与类之间是互斥的,即一个输入只能被归为一类;多个logistic回归进行多分类,输出的类别并不是互斥的,即″苹果”这个词语既属于”水果”类也属于”3C”类别。

5.2 Tanh激活函数

                                           

     Tanh也被称为双曲正切激活函数。类似sigmoid,tanh也是把一个实数值压缩。与sigmoid不同的是,tanh在-1到1的输出范围内是零均值的。你可以把tanh函数看做是两个sigmoid加在一起。

       

       在实际运用中,tanh比sigmoid更好。这主要是因为Sigmoid函数在输入处于[-1,1]之间时,函数值变化敏感,一旦接近或者超出区间就失去敏感性,处于饱和状态,影响神经网络预测的精度值。而tanh的输出和输入能够保持非线性单调上升和下降关系,符合BP网络的梯度求解,容错性好,有界,渐进于0、1,符合人脑神经饱和的规律,与 sigmoid 的区别是,tanh 是 0 均值的,因此实际应用中 tanh 会比 sigmoid 更好。

      Tanh唯一的缺点是:tanh函数也存在着梯度消失的问题,因此在饱和时会导致梯度消失。为了解决梯度消失问题,让我们讨论另一个被称为线性整流函数(ReLU)的非线性激活函数,它比我们之前讨论的两个激活函数都更好,并且也是在今天应用最为广泛的激活函数。

5.3 线性整流函数(ReLU)

    

        激活函数的导数如图所示,ReLU激活函数从底部进行了半矫正(half-rectified)。在数学上,它可以由这个简单的表达式表达:

                                                      f(z)=max(0,z)

          这意味着,当输入z<0时,输出为0。当输入z>0时,输出就是输入z的值。这个激活函数能够使网络更快的收敛。没有饱和意味着至少在正数范围内,能够对梯度消失有抵抗能力,所以神经元至少在一半的输入范围内不会反向传播,全部都是0的结果。ReLU在计算上非常有效率,因为它是使用简单的阈值实现的。

ReLU如何引入非线性

用形式化的语言来说,所谓非线性,就是一阶导数不为常数ReLu的定义是max(0, x),因此,ReLU的导数为:

显然,ReLU的导数不是常数,所以ReLU非线性的。Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。

  1、ReLu虽然在大于0的区间是线性的,在小于等于0的部分也是线性的,但是它整体不是线性的,因为不是一条直线,所以Relu函数是非线性函数。也就是说,线性和非线性都是就函数的整体而言的。用术语来说,线性、非线性是就函数的整个定义域而言的。这就意味着无论我们堆多少层网络,如果这些层都使用线性激活函数,那这些层最终等效于一层!那这样的模型的表达能力就很限了。多个线性操作的组合也是一个线性操作,没有非线性激活,就相当于只有一个超平面去划分空间。

     ReLu是非线性的,效果类似于划分和折叠空间,组合多个(线性操作 + ReLu)就可以任意的划分空间

                                             
    2、对于浅层的机器学习,比如经典的三层神经网络,用它作为激活函数的话,那表现出来的性质肯定是线性的。但是在深度学习里,少则几十,多则上千的隐藏层,虽然,单独的隐藏层是线性的,但是很多的隐藏层表现出来的就是非线性的。举个简单的例子,一条曲线无限分段,每段就趋向直线,反过来,很多这样的直线就可以拟合曲线。类似,大规模的神经网络,包含很多这样的线性基本组件,自然也可以拟合复杂的非线性情况。Relu通过构造很多的线形空间(类似于折叠的方式),逼近非线性方程。

但是Relu神经元有几个缺点:

  • 因为其将所有的输入负数变为0,在训练中可能很脆弱,很容易导致神经元失活,使其不会在任何数据点上再次激活。简单地说,ReLu可能导致神经元死亡。
  • 对于ReLu中(x<0)的激活,此时梯度为0,因此在下降过程中权重不会被调整。这意味着进入这种状态的神经元将停止对错误/输入的变化做出反应(仅仅因为梯度为0,没有任何变化)。这就是所谓的dying ReLu problem.

平时使用的时候RELU的缺点并不是特别明显,只有在学习率设置不恰当(较大)的时候,会加快神经网络中神经元的“死亡”。

    为了解决relu激活函数在x<0时的梯度消失问题, 我们提出了被称为泄漏relu(Leaky Relu)的激活函数,这个激活函数试图解决ReLU激活函数”Dead ReLU”的问题。让我们详细了解一下leaky relu。

5.4泄漏ReLU激活函数(leaky relu)

     Leaky ReLU激活函数是一个想要缓解relu消亡问题的尝试。它的函数表达式如下:

                                                          

                                        

    Leaky relu的思想就是当x<0时,会有个很小0.1的正斜率\alpha,这是一个超参数,可调。这个函数多少消除了relu的消亡问题,但是它的结果并不一致。虽然它具有relu激活函数的所有特征,例如:计算效率高、收敛速度快、在正区域不饱和等。它的思想可以进一步的扩展。如用一个常数项代替乘以x,从而使我们能够将这个常数项乘以一个能够使leaky relu更好工作的超参数。这个leaky relu的拓展被称为parametric relu(参数relu)。

5.5 PReLU带参数激活函数(Parametric ReLU)

                                 

PRelu的函数为:

                                                                              

         其中\alpha为超参数。PRelu的思想是引进任意超参数\alpha,而这个\alpha可以通过反向传播学习(注意PRelu与leaky relu的区别,前者是学习得到,后者是我们认为设定)。这赋予了神经元在负区域内选择最好斜率的能力,因此,他们可以变成单纯的ReLU激活函数或者Leaky ReLU激活函数。如果\alpha=0,那么 PReLU 退化为ReLU;如果 \alpha是一个很小的固定值(如\alpha=0.01),则 PReLU 退化为 Leaky ReLU(LReLU)。 

(1) PReLU只增加了极少量的参数,也就意味着网络的计算量以及过拟合的危险性都只增加了一点点。特别的,当不同channels使用相同的ai时,参数就更少了。

(2) BP更新ai时,采用的是带动量的更新方式:
                                                          
       上式的两个系数分别是动量和学习率。需要特别注意的是更新ai时不施加权重衰减(L2正则化),因为这会把ai很大程度上push到0。事实上,即使不加正则化,试验中ai也很少有超过1的。

(3) 整个论文,ai被初始化为0.25。

       总之,一般使用ReLU效果更好,但是你可以通过实验使用Leaky ReLU或者Parametric ReLU来观察它们是否能对你的问题给出最好的结果。

5.6.ELU

     ELU函数是针对ReLU函数的一个改进型,相比于ReLU函数,在输入为负数的情况下,是有一定的输出的,而且这部分输出还具有一定的抗干扰能力。这样可以消除ReLU死掉的问题,不过还是有梯度饱和和指数运算的问题。

                         

     其中α是一个可调整的参数,它控制着ELU负值部分在何时饱和。 右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。ELU的输出均值接近于零,所以收敛速度更快 
                           tensorflow中:tf.nn.elu(features, name=None)

5.7、SELU

                                         

    经过该激活函数后使得样本分布自动归一化到0均值和单位方差(自归一化,保证训练过程中梯度不会爆炸或消失,效果比Batch Normalization 要好) 
     其实就是ELU乘了个lambda,关键在于这个lambda是大于1的。以前relu,prelu,elu这些激活函数,都是在负半轴坡度平缓,这样在activation的方差过大的时候可以让它减小,防止了梯度爆炸,但是正半轴坡度简单的设成了1。而selu的正半轴大于1,在方差过小的的时候可以让它增大,同时防止了梯度消失。这样激活函数就有一个不动点,网络深了以后每一层的输出都是均值为0方差为1。

tensorflow中:tf.nn.selu(features, name=None)

  1.  
    def selu(x):
  2.  
    with ops.name_scope(‘elu’) as scope:
  3.  
    alpha = 1.6732632423543772848170429916717
  4.  
    scale = 1.0507009873554804934193349852946
  5.  
    return scale*tf.where(x>=0.0, x, alpha*tf.nn.elu(x))

                            

5.8 SWISH激活函数

                                      

          Swish也被称为self-gated(自门控)激活函数,最近由谷歌研究人员发布。它的数学表达式为:

                                                                          

        一般来说,swish激活函数的表现比relu更好。从图中我们可以观察到swish激活函数在x轴的负区域内末端的图像形状与relu激活函数是不同的,这是因为swich激活函数即使输入的值在增加,它的输出也可以减少。大部分的激活函数都是单调的,即他们的输出值在输入增加的时候是不会减少的。但Swish在0点具有单边有界性,平滑且不单调。

发表评论

邮箱地址不会被公开。 必填项已用*标注