zoukankan      html  css  js  c++  java
  • 神经网络中的激活函数具体是什么?为什么ReLu要好过于tanh和sigmoid function?(转)

    为什么引入激活函数?

    如果不用激励函数(其实相当于激励函数是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也有几个需要特别注意的问题:

    1. ReLU的输出不是zero-centered

    2. 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函数。

    更多关于激活函数的知识

    via : https://www.zhihu.com/question/29021768

  • 相关阅读:
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    asp.net session对象的持久化
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    小白也能看懂的约瑟夫环问题
  • 原文地址:https://www.cnblogs.com/shixisheng/p/9562336.html
Copyright © 2011-2022 走看看