神经网络神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数 Activation Function。
为什么要使用激活函数?
激活函数的作用
神经网络中激活函数的主要作用是提供网络的非线性建模能力,如不特别说明,激活函数一般而言是非线性函数。假设一个示例神经网络中仅包含线性卷积和全连接运算,那么该网络仅能够表达线性映射,即便增加网络的深度也依旧还是线性映射,难以有效建模实际环境中非线性分布的数据。加入(非线性)激活函数之后,深度神经网络才具备了分层的非线性映射学习能力。
在神经网络中,激活函数不是真的去激活什么,而是用激活函数给神经网络加入一些非线性因素,使得网络可以更好地解决较为复杂的问题。比如有些问题是线性可分的,而现实场景中更多问题不是线性可分的,若不使用激活函数则难以拟合非线性问题,测试时会有低准确率。所以激活函数主要是非线性的,如sigmoid、tanh、relu。sigmoid函数通常用于二分类,但要防止梯度消失,故适合浅层神经网络且需要配备较小的初始化权重,tanh函数具有中心对称性,适合于有对称性的二分类。注意:不能在隐藏层里用线性激活函数,唯一可以使用线性激活函数的通常就是输出层。
在深度学习中,relu是使用最多的激活函数,简单又避免了梯度消失。
非线性处理单元。激活函数使神经网络具有非线性。它决定感知机是否激发。
Sigmoid函数是一个在生物学中常见的S型函数,也称为S型生长曲线。 在信息科学中,由于其单增以及反函数单增等性质,Sigmoid函数常被用作神经网络的阈值函数,将变量映射到0,1之间,因此有时被称为“挤压函数”。
Sigmoid函数定义:
f ( x ) = 1 1 + e − x f(x)=\frac{1}{1+e^{-x}} f(x)=1+e−x1
Sigmoid函数图像:
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(z):
return 1/(1+np.exp(-z))
nums=np.arange(-10,10,step=1)
fig,ax=plt.subplots(figsize=(12,8))
ax.plot(nums,sigmoid(nums),'r')
plt.title('sigmoid函数曲线图')
plt.show()
Sigmoid函数求导:
f ′ ( x ) = ( 1 1 + e − x ) ′ = e − x ( 1 + e − x ) 2 = f ( x ) ( 1 − f ( x ) ) f^{'}(x)=(\frac{1}{1+e^{-x}})^{'}=\frac{e^{-x}}{(1+e^{-x})^2}=f(x)(1-f(x)) f′(x)=(1+e−x1)′=(1+e−x)2e−x=f(x)(1−f(x))
优点:
是能够控制数值的幅度,在深层网络中可以保持数据幅度不会出现大的变化
主要用于二分类。
因可解释为神经元的饱和激发率(firing rate) ,历史上比较流行。
缺点:
饱和神经元 会“kill” 梯度(引起梯度消失):指离中心点较远的x处的导数接近于0,停止反向传播的学习过程。
Sigmoid输出不是零中心的,这样在求权重w的梯度时,梯度总是正或负的。
. exp() 运算导致计算较复杂。
注:软饱和和硬饱和
sigmoid 在定义域内处处可导,且两侧导数逐渐趋近于0。Bengio 教授等[1]将具有这类性质的激活函数定义为软饱和激活函数。与极限的定义类似,饱和也分为左饱和(左边的导数逐渐趋于0)与右饱和(右边的导数逐渐趋于0)。
与软饱和相对的是硬饱和激活函数,即:f’(x)=0,当 |x| > c,其中 c 为常数。同理,硬饱和也分为左饱和和右饱和。常见的 ReLU 就是一类左侧硬饱和激活函数。
Tanh函数是sigmoid函数的一种变体,又称双曲正切激活函数。
Tanh函数定义:
t a n h ( x ) = e x p ( x ) − e x p ( − x ) e x p ( x ) + e x p ( − x ) tanh(x)=\frac{exp(x)-exp(-x)}{exp(x)+exp(-x)} tanh(x)=exp(x)+exp(−x)exp(x)−exp(−x)
t a n h ( x ) = 2 s i g m o i d ( 2 x ) − 1 tanh(x)=2sigmoid(2x)−1 tanh(x)=2sigmoid(2x)−1
Tanh函数及其导数图像:
python实现之
import numpy as np
import matplotlib.pyplot as plt
def tanh(x):
return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)
x = np.linspace(-10, 10)
y = tanh(x)
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, label="Tanh", color="red")
plt.legend()
plt.show()
t a n h ′ ( x ) = [ e x p ( x ) − e x p ( − x ) e x p ( x ) + e x p ( − x ) ] ′ = 1 − t a n h ( x ) 2 tanh'(x)=[\frac{exp(x)-exp(-x)}{exp(x)+exp(-x)}]^{'}=1-tanh(x)^2 tanh′(x)=[exp(x)+exp(−x)exp(x)−exp(−x)]′=1−tanh(x)2
python实现之图像:
import numpy as np
import matplotlib.pyplot as plt
def dtanh(x):
return 1-pow((np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x)),2)
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)
x = np.linspace(-10, 10)
y = dtanh(x)
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, label="dTanh(x)/dx", color="red")
plt.legend()
plt.show()
tanh在特征相差明显时的效果会很好,在循环过程中会不断扩大特征效果。
输出零中心。
tanh相比sigmoid收敛更快
与 sigmoid 的区别是,tanh(x)的梯度消失问题比sigmoid要轻;tanh 是 0 均值的,因此实际应用中 tanh 会比 sigmoid 更好。一般优化算法里,大部分会选择tanh函数(但在二分类问题中,选用sigmoid函数使输出值介于0和1之间)。因为 tanh 的输出均值比 sigmoid 更接近 0,SGD会更接近 natural gradient[4](一种二次优化技术),从而降低所需的迭代次数。
缺点:
hard-tanh、hard-logistic
Logistic函数和Tanh函数都是Sigmoid函数,具有饱和性,但是计算开销较大。因为这两个函数都是在中间(0附近)近似线性,两端饱和。因此,这两个函数可以分别通过分段函数hard-tanh、hard-logistic来近似。
硬双曲正切函数(hard tanh),和 tanh 以及整流线性单元类似,但是不同于后者,它是有界的。
定义:
h a r d − t a n h ( x ) = m a x ( m i n ( x , 1 ) , − 1 ) hard-tanh(x)=max(min(x,1),-1) hard−tanh(x)=max(min(x,1),−1)
也可以写为:
h a r d − l o g i s t i c ( x ) = m a x ( m i n ( 0.25 x + 0.5 , 1 ) , 0 ) hard-logistic(x)=max(min(0.25x+0.5,1),0) hard−logistic(x)=max(min(0.25x+0.5,1),0)
ReLU(Rectified Linear Unit),又称修正线性单元ReLU,是一种分段函数。通常指代以斜坡函数及其变种为代表的非线性函数,是目前深度神经网络中最常用的激活函数。
ReLU函数定义:
R e L U ( x ) = m a x ( 0 , x ) ReLU(x)=max(0,x) ReLU(x)=max(0,x)
ReLU函数求导:
R e L U ′ ( x ) = { 0 x<0 1 x ≥ 0 ReLU'(x)= \begin{cases} 0& \text{x<0}\\ 1& \text{x$\geq$ 0} \end{cases} ReLU′(x)={01x<0x≥ 0
通常在 x = 0 x=0 x=0处,取其导数为1。因为 x = 0 x=0 x=0的概率很小很小。
【python代码】
import numpy as np
import matplotlib.pyplot as plt
def ReLU(x):
return np.maximum(0,x)
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)
x = np.linspace(-10, 10)
y = ReLU(x)
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,10])
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data', 0))
ax.set_yticks([0, 10])
plt.plot(x, y, label="ReLU(x)", color="red")
plt.legend()
plt.show()
为了克服梯度弥散,2006年Hinton提出ReLU,不再依赖于无监督学习逐层进行预训练,直接通过监督学习来训练网络,并在2012年的竞赛中ILSVRC中取得里程碑式突破。
优点:
缺点:
偏移现象和 神经元死亡会共同影响网络的收敛性。
在实际应用中,为了避免上述情况,有几种ReLU的变种也会被广泛使用。
ReLU的变种
带泄露的ReLU(Leaky ReLU)在输入x<0时,保持一个很小的梯度 γ \gamma γ。这样当神经元非激活时也能有一个非零的梯度可以更新参数,避免不能被激活。
Leaky ReLU函数定义:
L e a k y R e L U ( x ) = { x x>0 γ x x<0 = m a x ( 0 , x ) + m i n ( 0 , x ) LeakyReLU(x)=\begin{cases} x& \text{x>0}\\ \gamma x& \text{x<0} \end{cases}=max(0,x)+min(0,x) LeakyReLU(x)={xγxx>0x<0=max(0,x)+min(0,x)
其中, γ \gamma γ是一个很小的常数,比如0.01,当 γ < 1 \gamma<1 γ<1时,
L e a k y R e L U ( x ) = m a x ( x , γ x ) LeakyReLU(x)=max(x,\gamma x) LeakyReLU(x)=max(x,γx)
通常来说, L e a k y R e L U ( x ) = m a x ( x , 0.01 x ) LeakyReLU(x)=max(x,0.01 x) LeakyReLU(x)=max(x,0.01x)
带参数的ReLU(PReLU),就是在ReLU函数基础上引入一个可学习的参数 α \alpha α,不同的神经元可以有不同的参数,对于第i个神经元,其 PReLU定义为:
P R e L U i ( x ) = { x x>0 α i x x<=0 = m a x ( 0 , x ) + α i m i n ( 0 , x ) PReLU_i(x)=\begin{cases} x& \text{x>0}\\ \alpha_i x& \text{x<=0} \end{cases}=max(0,x)+\alpha_imin(0,x) PReLUi(x)={xαixx>0x<=0=max(0,x)+αimin(0,x)
其中 α i \alpha_i αi为斜率。因此,PReLU为非饱和函数,如果 α i = 0 \alpha_i=0 αi=0,就退化为ReLU;如果 α i = 0.01 \alpha_i=0.01 αi=0.01,就为 Leaky ReLU函数。PReLU可以允许不同神经元具有不同的参数,也可以一组神经元共享一个参数。
优点:
ReLU家庭像
Leaky ReLU α是固定的;PReLU的α不是固定的,通过训练得到;RReLU的α是从一个高斯分布中随机产生,并且在测试时为固定值,与Noisy ReLU类似(但是区间正好相反)。
ELU(指数线性单元)是一个近似的零中心化的非线性函数。
ELU函数定义:
f ( x ) = { x x>0 α ( e x p ( x ) − 1 ) x<=0 = m a x ( 0 , x ) + m i n ( 0 , α ( e x p ( x ) − 1 ) ) f(x)=\begin{cases} x& \text{x>0}\\ \alpha(exp(x)-1)& \text{x<=0} \end{cases}=max(0,x)+min(0,\alpha(exp(x)-1)) f(x)={xα(exp(x)−1)x>0x<=0=max(0,x)+min(0,α(exp(x)−1))
ELU函数求导:
f ( x ) = { 1 x>0 f ( x ) + α x<=0 f(x)=\begin{cases} 1& \text{x>0}\\ f(x)+\alpha& \text{x<=0} \end{cases} f(x)={1f(x)+αx>0x<=0
其中, α > = 0 \alpha>=0 α>=0是一个超参数,决定 x < = 0 x<=0 x<=0的饱和曲线,并调整输出均值在0附近。
ELU被证实有较高的噪声鲁棒性,同时能够使得使得神经元
的平均激活均值趋近为 0,同时对噪声更具有鲁棒性。由于需要计算指数,计算量较大。
优点:
未完待续
参考资料:
深度学习
选择激活函数的经验法则:
1、如果输出是0、1值(二分类问题),则输出层选择sigmoid函数,然后其他的所有神经单元都选择Relu函数。
2、sigmoid函数:除了输出层是一个二分类问题,基本不会用它。
3、tanh函数:tanh是非常优秀的,几乎适合所有场合。
4、ReLU激活函数:最常用的默认函数,如果不确定用哪个激活函数,就使用ReLu或者Leaky ReLU。
5、如果ReLU效果欠佳,尝试Leaky ReLU或Maxout等变种。
6、在浅层神经网络中,如不超过4层的,可选择使用多种激励函数,没有太大的影响。
如果不确定哪个激活函数效果更好,可以都试试,然后在验证集上进行评价。