如果神经网络没有激活函数。在这种情况下,每个神经元只会利用权重和偏差对输入信息进行线性变换。虽然线性变换使神经网络更简单,但这个网络的功能会变得更弱,并无法从数据中学习复杂的模式。
没有激活函数的神经网络本质上只是一个线性回归模型。因此,我们对神经元的输入进行非线性变换,而神经网络中的这种非线性是由激活函数引入的。
sigmoid函数(sigmoid function)或logistic函数(logistic function)称为logistic函数的原因为该函数是两类别逻辑回归模型的预测表达式
1、Sigmoid函数
Sigmoid函数饱和使梯度消失。当神经元的激活在接近0或1处时会饱和,在这些区域梯度几乎为0,这就会导致梯度消失,几乎就有没有信号通过神经元传回上一层。Sigmoid函数的输出不是零中心的。因为如果输入神经元的数据总是正数,那么关于w的梯度在反向传播的过程中,将会要么全部是正数,要么全部是负数,这将会导致梯度下降权重更新时出现z字型的下降。
Sigmoid(logistic)是一个“挤压”函数,将输入挤压到(0,1)区间内。过大或者过小值会被压缩成接近1或者0的输出,此点类似神经元的生物特性,一部分输入被抑制(对应输出接近0),一部分输入被激活(对应输出接近1)。所以logistic函数相当于一个软性门,可以控制输入神经元的输出信息量,类似于特征选择。
非零中心化,非零中心化输出给下一个神经元,会导致偏置偏移,引起梯度下降收敛速度变慢
logistic函数输出是(0,1)区间,可以将其看做概率分布。可以和统计学习模型结合
公式:
2、Tanh
tanh函数的其他性质都与sigmoid函数相同。与sigmoid相似,唯一不同的是它是关于原点对称的。tanh函数在所有点上都是连续可微的,与sigmoid函数相比,tanh函数的梯度更陡。你可能会好奇,我们如何决定选择哪个激活函数。通常tanh比sigmoid函数更受欢迎,因为它是以0为中心的,而且梯度不受一定方向的限制。
(1)跟logistic函数相比,值域是(-1,1),是零中心化的,这是优点
(2)计算复杂度要高于logistic函数,训练和推理速度会更慢
Tanh解决了Sigmoid的输出是不是零中心的问题,但仍然存在饱和问题。 为了防止饱和,现在主流的做法会在激活函数前多做一步batch normalization,尽可能保证每一层网络的输入具有均值较小的、零中心的分布。
公式:
3、ArcTan
视觉上类似于双曲正切(Tanh)函数,ArcTan 激活函数更加平坦,这让它比其他双曲线更加清晰。在默认情况下,其输出范围在-π/2 和π/2 之间。其导数趋向于零的速度也更慢,这意味着学习的效率更高。但这也意味着,导数的计算比 Tanh 更加昂贵
4、softsign
Softsign 是 Tanh 激活函数的另一个替代选择。就像 Tanh 一样,Softsign 是反对称、去中心、可微分,并返回-1 和 1 之间的值。其更平坦的曲线与更慢的下降导数表明它可以更高效地学习。另一方面,导数的计算比 Tanh 更麻烦。
公式:
5、 ISRU(反平方根函数)
公式:
6、以上s型激活函数导函数对比:
从图中可以看到sigmoid导数小,最大的地方导数才0.25,这样在BP的时候,往后乘一乘梯度就没有嘞,也就是深层网络经常会遇到的Gradient Vanishing(梯度消失)。另外sigmoid的输出在0-1之间,而神经网络更好的输入的值是关于原点对称的。
tanh作为sigmoid的改进版,一定程度上减轻了以上的两个问题,tanh在原点附近接近 f ( x ) = x f(x)=x f(x)=x,关于原点对称且在原店附近导数接近1。就是希望能够做到原点附近的数据不被压缩能够很好的传递到下一层,而周边的数据被一定程度的压缩来引入非线性。
其他不常用的激活函数如反正切
a
r
c
t
a
n
arctan
arctan,
s
o
f
t
s
i
g
n
softsign
softsign,以及Inverse Square Root Unit(ISRU)同样减轻了以上问题。从导数的函数图中可以看到
a
r
c
t
a
n
arctan
arctan的导数较大,估计对学习速度应该会有帮助。sigmoid和tanh被广泛使用据说是因为导数比较好求。
代码实现:
#激活函数
import numpy as np
import matplotlib.pyplot as plt
import math
def plot_mul():
x = np.arange(-8, 8, 0.2)
y = sigmoid(x)
y1 = tanh(x)
y2 = arctan(x)
y3 = softsign(x)
y4 = ISRU(x,2)
plt.plot(x, y, label='sigmoid')
plt.plot(x, y1, label='tanh')
plt.plot(x, y2, label='arctan')
plt.plot(x, y3, label='softsign')
plt.plot(x, y4, label='ISRU')
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
plt.title('Activation')
plt.legend()
plt.show()
def sigmoid(x):
y = 1/(1+np.exp(-x))
return y
def tanh(x):
y =( np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
return y
def arctan(x):
y = np.arctan(x)
return y
def softsign(x):
y = x/(1+abs(x))
return y
def ISRU(x,a):
y = x/np.sqrt(1+a*x*x) #设α = 2
return y
plot_mul()
#导函数
import numpy as np
import matplotlib.pyplot as plt
import math
def plot_mul():
x = np.arange(-8, 8, 0.2)
y = dy_sigmoid(x)
y1 = dy_tanh(x)
y2 = dy_arctan(x)
y3 = dy_softsign(x)
y4 = dy_ISRU(x,2)
plt.plot(x, y, label='sigmoid')
plt.plot(x, y1, label='tanh')
plt.plot(x, y2, label='arctan')
plt.plot(x, y3, label='softsign')
plt.plot(x, y4, label='ISRU')
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axhline(y=1, ls=":", c="grey") # 添加水平直线
plt.axhline(y=0.25, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
plt.title('Gradients')
plt.legend()
plt.show()
def dy_sigmoid(x):
y = 1/(1+np.exp(-x))
dy = y*(1-y)
return dy
def dy_tanh(x):
y = 1/(1+np.exp(-x))
dy = 4*y*(1-y)
return dy
def dy_arctan(x):
y = 1/(x *x +1)
return y
def dy_softsign(x):
y = 1/((abs(x) +1)*(abs(x) +1))
return y
def dy_ISRU(x,a):
y = (1/np.sqrt(1+a*x*x))*(1/np.sqrt(1+a*x*x))*(1/np.sqrt(1+a*x*x)) #设α = 2
return y
plot_mul()
7、Relu
相较于sigmoid和tanh函数,ReLU对于随机梯度下降的收敛有巨大的加速作用;sigmoid和tanh在求导时含有指数运算,而ReLU求导几乎不存在任何计算量。对比sigmoid类函数主要变化是:
(1)单侧抑制;
(2)相对宽阔的兴奋边界;
(3)稀疏激活性。
存在问题:ReLU单元比较脆弱并且可能“死掉”,而且是不可逆的,因此导致了数据多样化的丢失。通过合理设置学习率,会降低神经元“死掉”的概率
公式:
8、Leaky ReLU
Leaky ReLU函数只是ReLU函数的一个改进版本。对于ReLU函数,当x<0时,梯度为0,这将使该区域的神经元失活。
定义Leaky ReLU是为了解决这个问题。不把x的负值定义为0,而是定义为x的一个极小的线性分量。
公式:
9、Parameterised ReLU(PRelu)
这是ReLU的另一个变体,旨在解决轴的左半部分的梯度为零的问题。顾名思义,参数化ReLU引入了一个新参数作为函数负部分的斜率。下面是如何修改ReLU函数以体现包含的斜率参数
公式:
当
α
α
α的值固定为0.01时,函数就变成了Leaky ReLU函数。然而,对于Parameterised ReLU函数, ‘
α
α
α’也是一个可训练的参数。该网络还学习了’
α
α
α '的值,以便更快、更优地收敛。这个函数的导数和Leaky ReLu函数是一样的,除了0.01的值会被替换成
α
α
α的值。
当使用Leaky ReLU函数仍然不能解决未激活神经元问题,相关信息无法成功传递到下一层时,就可以使用Parameterised ReLU函数。
10、Exponential Linear Unit (ELU)
指数化线性单元(简称ELU)也是线性整流单元(ReLU)的一种变体,它改变了函数负部分的斜率。与Leaky Relu和Parametric Relu函数不同,ELU使用了Log曲线来代替直线定义函数的负数部分。
公式:
11、SELU
12、softplus
公式:
13、以上relu及其变体激活函数及其导函数对比:
上面说的Sigmoid函数都或多或少都存在梯度消失的问题,这使得深层的网络难以训练。后来出现的ReLU(Rectified Linear Unit)基本解决了这个问题,它保证了至少在x>0的时候导数是不会减少的。这样在BP的过程中梯度就不会莫名消失。只不过ReLU有个dead neuron的问题,从函数图上可以看到,负半轴的值为0导数也为0,也就是forward pass和backward pass都不能传递任何信息。在使用ReLU的时候不要用太大的learning rate,否则很容易造成一堆dead neuron。
后来出现了Leaky ReLU(LReLU)解决了dead neuron的问题,而且使得输出数据分布在0的两侧,一定程度上对学习有帮助。后来有人做了一些改进如Parametric ReLU (PReLU)以及Randomized ReLU (RReLU)。在PReLU里,下面公式里的α是变量,可通过BP学习,Randomized ReLU则是在训练时随机在一定范围内选择α,而在测试中则是使用均值。如训练时α在 [0.1,0.3] 范围内随机选择,则在测试时α的值即为
0.2
0.2
0.2
代码实现:
#激活函数
import numpy as np
import matplotlib.pyplot as plt
import math
def plot_mul():
x = np.arange(-8, 8, 0.002)
y = relu(x)
y1 = lrelu(x)
y2 = prelu(0.2,x)
y3 = softplus(x)
y4 = elu(x,1)
y5 = selu(x, 1.16326,1.0507)
plt.figure()
plt.title('Activation')
a1=plt.subplot(231)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a1.plot(x, y,'b', label='relu')
a1.legend()
a2=plt.subplot(232)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a2.plot(x, y1, 'y',label='lrelu')
a2.legend()
a3=plt.subplot(233)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a3.plot(x, y2, 'g',label='prelu')
a3.legend()
a4=plt.subplot(234)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a4.plot(x, y3,'r', label='softplus')
a4.legend()
a5=plt.subplot(235)
plt.axhline(y=0, ls=":", c='grey')
plt.axvline(x=0, ls=":", c='grey')
a5.plot(x, y4,'m', label='elu')
a5.legend()
a6=plt.subplot(236)
plt.axhline(y=0, ls=":", c='grey')
plt.axvline(x=0, ls=":", c='grey')
a6.plot(x, y5,'c', label='selu')
a6.legend()
plt.suptitle('Activation')
plt.show()
def relu(x):
y = np.where(x<0, 0, x)
return y
def lrelu(x):
y = np.where(x < 0, 0.01*x, x)
return y
def prelu(a,x):
y = np.where(x<0, a*x, x)# a 取 0.2时
return y
def softplus(x):
y = np.log(1+np.exp(x))
return y
def elu(x,a): # a 取0.2时
y = np.where(x<0, a * (np.exp(x) - 1), x)
return y
def selu(x,a,s):
y = np.where(x<0, s*a * (np.exp(x) - 1), s*x)
return y
plot_mul()
#导函数
import numpy as np
import matplotlib.pyplot as plt
import math
def plot_mul():
x = np.arange(-8, 8, 0.002)
y = dy_relu(x)
y1 = dy_lrelu(x)
y2 = dy_prelu(0.2,x)
y3 = dy_softplus(x)
y4 = dy_elu(x,1)
y5 = dy_selu(x, 1.16326,1.0507)
plt.figure()
plt.title('Activation')
a1=plt.subplot(231)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a1.plot(x, y,'b', label='relu')
a1.legend(loc=7)
a2=plt.subplot(232)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a2.plot(x, y1, 'y',label='lrelu')
a2.legend(loc=7)
a3=plt.subplot(233)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a3.plot(x, y2, 'g',label='prelu')
a3.legend(loc=7)
a4=plt.subplot(234)
plt.axhline(y=0, ls=":", c="grey") # 添加水平直线
plt.axvline(x=0, ls=":", c="grey") # 添加垂直直线
a4.plot(x, y3,'r', label='softplus')
a4.legend(loc=7)
a5=plt.subplot(235)
plt.axhline(y=0, ls=":", c='grey')
plt.axvline(x=0, ls=":", c='grey')
a5.plot(x, y4,'m', label='elu')
a5.legend(loc=7)
a6=plt.subplot(236)
plt.axhline(y=0, ls=":", c='grey')
plt.axvline(x=0, ls=":", c='grey')
a6.plot(x, y5,'c', label='selu')
a6.legend(loc=7)
plt.suptitle('Gradients')
plt.show()
def dy_relu(x):
y = np.where(x < 0, 0, 1)
return y
def dy_lrelu(x):
y = np.where(x < 0, 0.01, 1)
return y
def dy_prelu(a,x):
y = np.where(x<0, a, 1)# a 取 0.2时
return y
def dy_softplus(x):
y = np.exp(x)/(1+np.exp(x))
return y
def dy_elu(x,a): # a 取0.2时
y = np.where(x<0, a * np.exp(x), 1)
return y
def dy_selu(x,a,s):
y = np.where(x<0, s*a * np.exp(x), s)
return y
plot_mul()
14、Bent-identity
Bent Identity 是介于 Identity 与 ReLU之间的一种折衷选择。它允许非线性行为,尽管其非零导数有效提升了学习并克服了与 ReLU 相关的静默神经元的问题。由于其导数可在 1的任意一侧返回值,因此它可能容易受到梯度爆炸和消失的影响
公式:
15、Tanhshrink
公式:
16、Swish
Swish是一个不太为人所知的激活函数,它是由谷歌的研究人员发现的。Swish的计算效率与ReLU相当,并且在层次更深的模型上显示出比ReLU更好的性能。swish的值从负无穷到正无穷。
公式:
函数曲线是平滑且在所有点上都是可微的。这在模型优化过程中是有帮助的,也是Swish被认为优于Relu的原因之一。关于这个函数的一个独特的特征是,swich函数不是单调的。这意味着即使输入值增加,函数值也可能减少。
更多参考:https://www.cnblogs.com/makefile/p/activation-function.html
17、Mish
ReLU有一些已知的弱点,但是通常它执行起来很轻,并且在计算上很轻。Mish具有较强的理论渊源,在测试中,就训练稳定性和准确性而言,Mish的平均性能优于ReLU。
复杂度只稍微增加了一点(V100 GPU和Mish,相对于ReLU,每epoch增加大约1秒),考虑到训练稳定性的提高和最终精度的提高,稍微增加一点时间似乎是值得的。
更多参考:https://blog.csdn.net/u011984148/article/details/101444274
其他激活函数可见参考博客
参考博客:
http://www.360doc.com/content/19/0621/20/46986705_844015608.shtml
http://www.360doc.com/content/20/0306/11/13810766_897183699.shtml
https://www.cnblogs.com/CJT-blog/p/10421822.html
https://blog.csdn.net/weixin_34161083/article/details/88860528
https://www.jianshu.com/p/36c377941b26?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation