众所周知神经网络单元是由线性单元和非线性单元组成的,而非线性单元就是咱们今天要介绍的--激活函数,不一样的激活函数得出的结果也是不一样的。他们也各有各的优缺点,虽然激活函数有本身的发展历史,不断的优化,可是如何在众多激活函数中作出选择依然要看咱们所实现深度学习实验的效果。算法
这篇博客会分为上下两篇,上篇介绍一些经常使用的激活函数(Sigmoid、tanh、ReLU、LeakyReLU、maxout)。下篇介绍一下不经常使用的激活函数(PRelu、ELU、SELU)。网络
sigmoid激活函数将输入映射到(0,1)之间,他的数学函数为:ide
$$\sigma (z)=\frac{1}{1+e^{-z}}$$函数
历史上sigmoid很是经常使用,可是因为他的两个缺点,实际不多用了,如今看到sigmoid激活函数,都是在新手教程中作一些简单的实验。学习
它可以把输入的连续实值变换为0和1之间的输出,特别的,若是是很是大的负数,那么输出就是0;若是是很是大的正数,输出就是1.测试
一、梯度消失字体
咱们先看sigmoid激活函数的导数图像,优化
从图中咱们能够看出,当$\sigma (x)$中的$x$较大或者较小时,导数接近0,然后向传递的数学依据是微积分求导的链式法则,当前层的导数须要以前各层导数的乘积,几个小数的相乘,结果会很接近0。Sigmoid导数的最大值是0.25,这意味着导数在每一层至少会被压缩为原来的1/4,经过两层后被变为1/16,…,经过10层后为1/1048576。这种状况就是梯度消失。梯度一旦消失,参数不能沿着loss下降的方向优化,spa
二、不是原点中心对称.net
经过Sigmoid函数咱们能够知道,Sigmoid的输出值恒大于0,输出不是0均值(既zero-centerde),这会致使后一层的神经元将获得上一层输出的非均值的输入。
举例来说$\sigma (\sum_i w_ix_i+b)$,若是$x_i$恒大于0,那么对其$w_i$的导数老是正数或老是负数,向传播的过程当中w要么都往正方向更新,要么都往负方向更新,致使有一种捆绑的效果,使得收敛缓慢。且可能致使陷入局部最小值。固然了,若是按batch去训练,那么那个batch可能获得不一样的信号,因此这个问题仍是能够缓解一下的。
三、运算量大:
解析式中含有幂运算,计算机求解时相对来说比较耗时。对于规模比较大的深度网络,这会较大地增长训练时间。
def sigmoid(x): return 1.0 / (1.0 + np.exp(-x))
tanh函数它的输出是zero-centered的,可是它一样存在梯度消失和幂指数问题。数学函数为:
$$f(z)=tanh(z)=\frac{e^{z}-e^{-z}}{e^z}+e^{-z}$$
def tanh(x): return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
tanh函数相比于Sigmoid函数每每更具备优越性,这主要是由于Sigmoid函数在输入处于[-1,1]之间时,函数值变化敏感,一旦接近或者超出区间就失去敏感性,处于饱和状态。
这才是一个目前主流论文中很是经常使用的激活函数,它的数学公式为:
$$f(x)=max(0,x)$$
def relu(x): return np.where(x<0,0,x)
死掉:一个很是大的梯度通过一个 ReLU 神经元,更新过参数以后,这个神经元不再会被任何数据激活相应的权重永远不会更新。有两种缘由致使这种状况:一、很是不幸的初始化。二、学习率设置的过高致使在训练过程当中参数更新太大,解决方法是使用Xavier初始化方法,合理设置学习率,会下降这种状况的发生几率。或使用Adam等自动调节学习率的算法。
补充:ReLU相比sigmoid和tanh的一个缺点是没有对上界设限,在实际使用中,能够设置一个上限,如ReLU6经验函数: f(x)=min(6,max(0,x))
LeakyReLU也有人称为PReLU,可是仍是不太同样的,LeakyReLU中的斜率a是自定义的,pReLU中的a是经过训练学习获得的,LeakyReLU是为了解决“ReLU死亡”问题的尝试
$$f(x)=\left\{\begin{matrix}
x&&x>0\\
0.01x&&其余
\end{matrix}\right.$$
ReLU 中当 x<0 时,函数值为 0 。而 Leaky ReLU 则是给出一个很小的负数梯度值,好比 0.01 。
有些研究者的论文指出这个激活函数表现很不错,可是其效果并非很稳定。
def prelu(x,a): return np.where(x<0,a*x,x)
虽然Leaky ReLU修复了ReLU的神经元死亡问题,可是在实际的使用并无彻底证实Leaky ReLU彻底优于ReLU。
在RReLU中,负值的斜率$a_i$在训练中是随机的,$a_i$是可学习的,若是$a_i=0$,那么 PReLU 退化为ReLU;若是$a_i$是一个很小的固定值(如$a_i=0.01$),则 PReLU 退化为 Leaky ReLU。
$a_i$在以后的测试中就变成了固定的了。RReLU的亮点在于,在训练环节中,$a_i$是从一个均匀的分布$U(I,u)$中随机抽取的数值。形式上来讲,咱们能获得如下数学表达式:
$$f(x)=\left\{\begin{matrix}
x&&x>0\\
a_ix&&x\leqslant 0
\end{matrix}\right.$$
其中$$a_i\sim U(x,y),区间(x,y)上的均匀分布;x,y\in [0,1]$$
(1)PReLU只增长了极少许的参数,也就意味着网络的计算量以及过拟合的危险性都只增长了一点点。特别的,当不一样channels使用相同的$a$时,参数就更少了。
(2)BP更新$a$时,采用的是带动量的更新方式,以下:
$$\Delta a_i=\mu \Delta a_i+\epsilon \frac{\partial \varepsilon }{\partial a_i}$$
ELU也是为了解决ReLU存在的问题而提出的,ELU有ReLU的基本全部优势,以及不会有Dead ReLU问题,和输出的均值接近0(zero-certered),它的一个小问题在于计算量稍大。相似于Leaky ReLU,理论上虽然好于ReLU,但在实际使用中目前并无好的证据ELU老是优于ReLU。
$$f(x)=\left\{\begin{matrix}
x&&x>0\\
\alpha (e^x-1)&&x\leq 0
\end{matrix}\right.$$
$$f'(x)=\left\{\begin{matrix}
1&&x>0\\
f(x)+a&&x\leq 0
\end{matrix}\right.$$
def elu(x, a): return np.where(x < 0, a*(np.exp(x)-1), a*x)
其中$\alpha$是一个可调整的参数,它控制着ELU负值部分在什么时候饱和。右侧线性部分使得ELU可以缓解梯度消失,而左侧软饱可以让ELU对输入变化或噪声更鲁棒。ELU的输出均值接近于零,因此收敛速度更快
$$SELU(x)=\lambda \left\{\begin{matrix}
x&&x>0\\
\alpha e^x-\alpha &&x\leq 0
\end{matrix}\right.$$
通过该激活函数后使得样本分布自动归一化到0均值和单位方差(自归一化,保证训练过程当中梯度不会爆炸或消失,效果比Batch Normalization 要好)
其实就是ELU乘了个$\alpha$,关键在于这个$\alpha$是大于1的。之前relu,prelu,elu这些激活函数,都是在负半轴坡度平缓,这样在激活函数的方差过大的时候可让它减少,防止了梯度爆炸,可是正半轴坡度简单的设成了1。而selu的正半轴大于1,在方差太小的的时候可让它增大,同时防止了梯度消失。这样激活函数就有一个不动点,网络深了之后每一层的输出都是均值为0方差为1。
def selu(x): alpha = 1.6732632423543772848170429916717 scale = 1.0507009873554804934193349852946 return scale*np.where(x>=0.0, x, alpha*(np.exp(x)-1))
其中超参 α
和 λ
的值是 证实获得 的(而非训练学习获得):
α
= 1.6732632423543772848170429916717λ
= 1.0507009873554804934193349852946
即:
αλ
)softmax用于多分类神经网络输出,若是某一个$a_i$打过其余z,那这个映射的份量就逼近1,其余就逼近0,主要应用于“分类”。
$$SOFTMAX:a_i=\sigma_i(z)=\frac{e^{z_i}}{\sum_{j=1}^{m}e^{z_j}},z_i=w_ix+b$$
做用:把神经元中线性部分输出的得分值(score),转换为几率值。softmax输出的是(归一化)几率,
含有softmax激活函数的网络层有这样一个性质:$\sum_{i=1}^{j}\sigma _i(z)=1$,能够解释为每一个节点的输出值小于等于1。softmax激励函数一般在神经网络的最后一层做为分类器的输出,输出值(几率)最大的即为分类结果。
$$猫:\begin{pmatrix}0.05\\ 0.05\\ 0.7\\ 0.2\end{pmatrix} 狗:\begin{pmatrix}0.8\\ 0.06\\ 0.01\\ 0.04\end{pmatrix}$$
这个问题目前没有肯定的方法,凭一些经验吧。
1)深度学习每每须要大量时间来处理大量数据,模型的收敛速度是尤其重要的。因此,整体上来说,训练深度学习网络尽可能使用zero-centered数据 (能够通过数据预处理实现) 和zero-centered输出。因此要尽可能选择输出具备zero-centered特色的激活函数以加快模型的收敛速度。
2)若是使用 ReLU,那么必定要当心设置 learning rate,并且要注意不要让网络出现不少 “dead” 神经元,若是这个问题很差解决,那么能够试试 Leaky ReLU、PReLU。
3)最好不要用 sigmoid,你能够试试 tanh,不过能够预期它的效果会比不上 ReLU 和 Maxout.
最后来一张全家照
import math import matplotlib.pyplot as plt import numpy as np import matplotlib as mpl plt.rcParams['font.sans-serif']=['SimHei'] # 指定默认字体 plt.rcParams['axes.unicode_minus']=False # 用来正常显示符号 def sigmoid(x): return 1.0 / (1.0 + np.exp(-x)) def tanh(x): return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x)) def relu(x): return np.where(x<0,0,x) def prelu(x,a): return np.where(x<0,a*x,x) def elu(x, a): return np.where(x < 0, a*(np.exp(x)-1), a*x) def selu(x): alpha = 1.6732632423543772848170429916717 scale = 1.0507009873554804934193349852946 return scale*np.where(x>=0.0, x, alpha*(np.exp(x)-1)) fig = plt.figure(figsize=(6,4)) ax = fig.add_subplot(111) x = np.linspace(-10, 10) y_sigmoid = sigmoid(x) y_tanh = tanh(x) y_relu = relu(x) y_LeakyReLU = prelu(x, 0.05) y_elu = elu(x, 0.25) y_selu = selu(x) plt.xlim(-11,11) plt.ylim(-1.1,1.1) ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.xaxis.set_ticks_position('bottom') ax.spines['bottom'].set_position(('data',0)) ax.set_xticks([-10,-5,0,5,10]) ax.yaxis.set_ticks_position('left') ax.spines['left'].set_position(('data',0)) ax.set_yticks([-1,-0.5,0.5,1]) plt.plot(x,y_sigmoid,label="Sigmoid",color = "blue") # 蓝色 plt.plot(2*x,y_tanh,label="tanh", color = "red") # 红色 plt.plot(2*x,y_relu,label="relu", color = "c") # 青色 plt.plot(2*x,y_LeakyReLU, '-.', label="LeakyReLU", color = "Violet") # 紫色 plt.plot(2*x,y_elu, ":", label="elu", color = "green") # 绿色 plt.plot(2*x,y_selu, "--", label="selu", color = "k") # 黑色 plt.legend() plt.show()
SELU论文地址:【Self-Normalizing Neural Networks】.
StevenSun2014的CSDN博客:经常使用激活函数总结