常用激活函数

什么是激活函数(what)

https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701224541.png Nerual Network中的每个神经元节点接受上一层神经元的输出值作为本神经元的输入值,并将输入值传递给下一层,输入层神经元节点会将输入属性值直接传递给下一层(隐层或输出层)。 在多层神经网络中,上层节点的输出和下层节点的输入之间具有一个函数关系,这个函数称为激活函数(又称激励函数, active function)

激活函数的用途(why)

加入激活函数,深度神经网络才能具备分层的非线性映射学习能力。因此激活函数是神经网络不可或缺的一部分

如果不用激励函数(其实相当于激励函数是f(x) = x),在这种情况下你每一层节点的输入都是上层输出的线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron)了,那么网络的逼近能力就相当有限。 正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络表达能力就更加强大(不再是输入的线性组合,而是几乎可以逼近任意函数)。

激活函数需要具备的性质

1.非线性

即导数不是常数。保证多层网络不退化成单层线性网络。这也是激活函数的意义所在。

2.可微性(定义域内 存在导函数):

可微性保证了在优化中梯度的可计算性。传统的激活函数如sigmoid等满足处处可微。对于分段线性函数比如ReLU,只满足几乎处处可微(即仅在有限个点处不可微)。对于SGD算法来说,由于几乎不可能收敛到梯度接近零的位置,有限的不可微点对于优化结果不会有很大影响。

3.单调性(monotonic)

即导数符号不变。当激活函数是单调的时候,单层网络能够保证是凸函数。 更通俗的理解:函数形式是当我输入一个数值,通过计算得到一个数值,里面没有超级参数,也就是说我们在保证函数分布不发生改变的情况下,对神经元的数值带来一个非线性的映射。但是需要保证单调的原因是不改变分布的规律发生大的改变。当不是单调的时候我们可能会有很多个局部最优,不能保证是一个凸函数,对后面我们做梯度下降时带来巨大影响。

4.计算简单

激活函数在神经网络前向的计算次数与神经元的个数成正比,因此简单的非线性函数自然更适合用作激活函数。这也是ReLU之流比其它使用Exp等操作的激活函数更受欢迎的其中一个原因。

5.非饱和性(saturation)

饱和指的是在某些区间梯度接近于零(即梯度消失),使得参数无法继续更新的问题。最经典的例子是Sigmoid,它的导数在x为比较大的正值和比较小的负值时都会接近于0。更极端的例子是阶跃函数,由于它在几乎所有位置的梯度都为0,因此处处饱和,无法作为激活函数。ReLU在x>0时导数恒为1,因此对于再大的正值也不会饱和。但同时对于x<0,其梯度恒为0,这时候它也会出现饱和的现象。Leaky ReLU[3]和PReLU的提出正是为了解决这一问题。

6.输出范围有限

有限的输出范围使得网络对于一些比较大的输入也会比较稳定,这也是为什么早期的激活函数都以此类函数为主,如Sigmoid、Tanh。但这导致了前面提到的梯度消失问题,而且强行让每一层的输出限制到固定范围会限制其表达能力。因此现在这类函数仅用于某些需要特定输出范围的场合,比如概率输出(此时loss函数中的log操作能够抵消其梯度消失的影响)、LSTM里的gate函数。

7.接近恒等变换(identity)

f(x)≈x,即约等于x。这样的好处是使得输出的幅值不会随着深度的增加而发生显著的增加,从而使网络更为稳定,同时梯度也能够更容易地回传。这个与非线性是有点矛盾的,因此激活函数基本只是部分满足这个条件。

8.参数少

大部分激活函数都是没有参数的。像PReLU带单个参数会略微增加网络的大小。还有一个例外是Maxout,尽管本身没有参数,但在同样输出通道数下k路Maxout需要的输入通道数是其它函数的k倍,这意味着神经元数目也需要变为k倍;但如果不考虑维持输出通道数的情况下,该激活函数又能将参数个数减少为原来的k倍。

9.归一化(normalization)

对应的激活函数是SELU,主要思想是使样本分布自动归一化到零均值、单位方差的分布,从而稳定训练。归一化的思想也被用于网络结构的设计,比如Batch Normalization。

有哪些激活函数(which)

激活函数的发展经历了Sigmoid -> Tanh -> ReLU -> Leaky ReLU -> Maxout这样的过程,还有一个特殊的激活函数Softmax,因为它只会被用在网络中的最后一层,用来进行最后的分类和归一化。 如果按照饱和与否作为区分条件,表示如下图: https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701225445.png

1.Sigmoid函数

最常用的非线性的激活函数

数学形式如下: $$ f(z) = \frac{1}{1 + e^{-z}} $$

导数是: $$ f^{’}(z) = f(z)(1-f(z)) $$

图像如下: https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701230219.png

特点: 它能够把输入的连续实值变换为0和1之间的输出,特别的,如果是非常大的负数,那么输出就是0;如果是非常大的正数,输出就是1 平滑、易求导。可以将函数映射到(0, 1)之间,通常二分类算法会在最后套一层sigmoid函数。

缺点: sigmoid函数曾经被使用的很多,不过近年来,用它的人越来越少了。主要是因为它固有的一些缺点: 缺点1:在深度神经网络中梯度反向传递时导致梯度爆炸和梯度消失,其中梯度爆炸发生的概率非常小,而梯度消失发生的概率比较大。首先来看Sigmoid函数的导数,如下图所示 https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701230352.png 如果我们初始化神经网络的权值为 [ 0 , 1 ] [0,1][0,1] 之间的随机值,由反向传播算法的数学推导可知,梯度从后向前传播时,每传递一层梯度值都会减小为原来的0.25倍,如果神经网络隐层特别多,那么梯度在穿过多层后将变得非常小接近于0,即出现梯度消失现象;当网络权值初始化为 ( 1 , + ∞ ) (1,+∞)(1,+∞) 区间内的值,则会出现梯度爆炸情况。

缺点2:Sigmoid函数的输出不是关于原点对称的。因为如果输入神经元的数据总是正数,那么关于w的梯度在反向传播的过程中,将会要么全部是正数,要么全部是负数,这将会导致梯度下降权重更新时出现z字型的下降。

缺点3:其解析式中含有幂运算,计算机求解时相对来讲比较耗时。对于规模比较大的深度网络,这会较大地增加训练时间。

用Python实现:

1
2
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

2.tanh函数

数学式为: $$ \tanh(x) = \frac{e_x - e^{-x}}{e_x + e^{-x}} = 2f(2x) - 1 $$

导数为: $$ \tan h^{’}(x) = 4f(2x)(1-f(2x)) $$

tanh函数及其导数的几何图像如下图: https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701231613.png

优点: 它解决了Sigmoid函数的不是zero-centered输出问题

  • 输出是0均值
  • 梯度取值范围在(0, 1)之间

缺点: 同sigmoid一样,然而,梯度消失(gradient vanishing)的问题和幂运算的问题仍然存在

3.Relu函数

数学式子: $$ f(x) = max(0,x) $$

Relu函数及其导数的图像如下图所示: https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701231939.png

ReLU是一种后来才出现的激活函数。从图中 可以看到,当x<0时,出现硬饱和,当x>0时,不存在饱和问题。因此,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。然而,随着训练的推进,部分输入会落入硬饱和区,导致对应权重无法更新,这种现象被称为“神经元死亡”。与sigmoid类似,ReLU的输出均值也大于0。偏移现象和神经元死亡会共同影响网络的收敛性。

产生激活函数大面积为被激活的原因是: 1、初始化的参数设置时,使Relu没有被激活。 2、学习率太高,神经元在一定范围内波动,可能会发生数据多样性的丢失,这种情况下神经元不会被激活,数据多样性的丢失不可逆。

ReLU函数其实就是一个取最大值函数,注意这并不是全区间可导的,但是我们可以取sub-gradient,如上图所示。ReLU虽然简单,但却是近几年的重要成果,有以下几大优点:

  1. 解决了gradient vanishing问题 (在正区间)
  2. 计算速度非常快,只需要判断输入是否大于0
  3. 收敛速度远快于sigmoid和tanh

ReLU也有几个需要特别注意的问题:

  1. ReLU的输出不是zero-centered
  2. Dead ReLU Problem,指的是某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。有两个主要原因可能导致这种情况产生: (1) 非常不幸的参数初始化,这种情况比较少见 (2) learning rate太高导致在训练过程中参数更新太大,不幸使网络进入这种状态。解决方法是可以采用Xavier初始化方法,以及避免将learning rate设置太大或使用adagrad等自动调节learning rate的算法。

尽管存在这两个问题,ReLU目前仍是最常用的activation function,在搭建人工神经网络的时候推荐优先尝试!

Python代码:

1
2
def relu(x):
    return np.maximum(0, x)

4.Leaky ReLU函数(PReLU)

https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701232741.png

PRelu函数: 形式同leaky relu,区别在于PRelu的$\alpha$不是超参,而是学习出来的。

5.ELU(Exponential Linear Units)函数

函数表达式: $$ f(x) = \begin{cases} x, & if x>0 \ \alpha(e^x-1), & 其他情况 \ \end{cases} $$

函数及其导数的图像如下图所示: https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701233043.png

ELU也是为解决ReLU存在的问题而提出, 优点:

  • 收敛速度快
  • 不会有Deal ReLU问题
  • 输出的均值接近0,zero-centered
  • 左侧软饱合能够让ELU对输入变化或噪声更鲁棒

它的一个小问题在于计算量稍大(包含指数)。类似于Leaky ReLU,理论上虽然好于ReLU,但在实际使用中目前并没有好的证据ELU总是优于ReLU。

6.softPlus

https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701233234.png https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701233251.png

7.大一统:Maxout函数

$$ f(x) = max(\omega_1^Tx+b_1, \omega_2^Tx+b_2, …,\omega_n^Tx+b_n,) $$ 使用maxout的默认先验:样本集是凸集可分的

https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701233953.png

优点: Maxout具有ReLU的优点,如计算简单,不会 saturation饱和,同时又没有ReLU的一些缺点,如容易死掉。

缺点: 每个神经元的参数double,这就导致整体参数的数量激增,明显增加了计算量,使得应用maxout的层的参数个数成k倍增加,原本只需要1组就可以,采用maxout之后就需要k倍了。

https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701233659.png

8.softmax

$$ softmax(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{n}{e^{z_i}}} $$

  • 值域0-1之间
  • 通常用于多分类的输出,所有分类的输出之和为1 指数函数曲线呈现递增趋势,最重要的是斜率逐渐增大,也就是说在轴上一个很小的变化,可以导致y轴上很大的变化。经过使用指数形式的Softmax函数能够将差距大的数值距离拉的更大。但是这样也有可能会产生溢出。
  • 使用Softmax函数作为输出层激活函数时,常使用交叉熵作损失函数。Softmax函数计算过程中,容易因为输出节点的输出值较大产生溢出现象,在计算交叉熵时也可能会产生溢出。为了计算的稳定性,TensorFlow提供了一个统一的接口,将Softmax与交叉熵损失函数同时实现,同时也处理了数值不稳定的异常,使用TensorFlow深度学习框架的时候,推荐使用这个统一的接口,避免分开使用Softmax函数与交叉熵损失函数。 https://geoer666-1257264766.cos.ap-beijing.myqcloud.com/20220701234423.png

Python实现softmax:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# softmax函数(用于多元分类问题)
def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y


# softmax函数改良版
def softmaxV2(a):
    c = np.max(a)
    exp_a = np.exp(a - c)  # 溢出对策
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

9.Dice

https://zhuanlan.zhihu.com/p/455474500

如何选择合适的激活函数(how)

传统的激活函数选择的建议:

  1. 优先relu
  2. 再考虑prelu、leaky relu、maxout
  3. 再考虑tanh
  4. 除非用作二分类的最后一层,其他一般不建议使用sigmoid

这个问题目前没有确定的方法,凭一些经验吧:

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

参考

https://blog.csdn.net/qq_18310041/article/details/91042085 https://blog.csdn.net/tyhj_sf/article/details/79932893

0%