激活函数(Activation Function)
什么是激活函数?
人工神经网络的激活函数就是模仿生物神经网络。在生物神经网络中,信息的传递是通过神经元的树突和轴突的结合,(在前膜的内侧有致密突起和网格形成的囊泡栏栅,其空隙处正好容纳一个突触小泡,它可能有引导突触小泡与前膜接触的作用,促进突触小泡内递质的释放。当突触前神经元传来的冲动到达突触小体时,小泡内的递质即从前膜释放出来,进入突触间隙,并作用于突触后膜;如果这种作用足够大时,即可引起突触后神经元发生兴奋或抑制反应),黄字部分是来自某百科的解释,高中也学过,读不懂也没有关系,下面来点大白话的。如下图红色的圈圈,树突会产生一些物质,不断断积累,当到达一定程度的时候,信息就会传递给轴突,差不多就是这个过程,当然严谨的来说,信息传递的过程十分的复杂,就如黄字部分说的那样。
对于人工神经网络而言,是想用机器来尽可能的模仿生物神经元信息传递的过程。人工神经网络的定义通常是 的形式,通过多层神经元的叠加,来拟合复杂的函数。考虑一个问题,如果只是简单的通过累加的形式进行处理的话,那么神经网络能实现的功能是十分有限的。简单的例子,如下图所示,下面的数据是线性不可分的,是一个异或问题,只是通过 是无法实现的。这时就需要加入非线性元素来改变,可以使得神经网络输出非线性决策边界。特别是多层神经网络,加入非线性元素,可以使得神经网络任意逼近任何非线性函数。
另一方面,激活函数还可以起到将数据特征映射到新的特征空间的作用,这样可以更加有利于数据的训练,加速模型的收敛。
激活函数
常用的激活函数Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU,还有一些他们的变体以及其他的函数。这篇文章列举了26种激活函数,这里是原文,可能需要翻墙,小伙伴可以点击去看一下。
step function
import numpy as np
import matplotlib.pyplot as plt
def step_func(x):
# if x > 0:
# return 1
# else:
# return 0
return np.array(x > 0, dtype=np.int)
x = np.arange(-3,3,0.01)
y = step_func(x)
plt.xlabel("Input")
plt.ylabel("Output")
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()
Sigmoid
Sigmoid函数是传统神经网络中最常用的激活函数,虽然现在已经不常用,但当年还是十分受欢迎的。Sigmoid函数也叫Logistic 函数,值域在0到1之间。
Sigmoid函数的表达式及其求导:
import numpy as np
# sigmoid函数
def sigmoid(x):
y = 1/(1+np.exp(-x))
return y
# sigmoid函数求导
def d_sigmoid(x):
dx = sigmoid(x)*(1-sigmoid(x))
return dx
sigmoid激活函数
sigmoid函数导数
当x的值趋近负无穷的时候,y趋近于0;x趋近于正无穷的时候,y趋近于1;在 区间内,梯度变化比较明显,即x发生很小的变化,y变化的也比较明显。
sigmoid的优缺点:
优点:
- Sigmoid函数的输出映射在(0,1)之间,单调连续,输出范围有限,优化稳定
- 求导容易
缺点:
- 幂运算,计算成本高
- 容易出现梯度弥散(反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练)
- 不是以0为中心,为导致收敛速度下降(具体解释可以参考这里)
tanh
双曲正切函数,tanh函数输出以0为中心,区间为
,tanh可以想象成两个sigmoid函数放在一起,性能要高于sigmoid函数
tanh函数的变形:
求导:
def tanh(x):
# y = np.tanh(x)
y = 2*sigmoid(2*x)-1
return y
def d_tanh(x):
dx = 1- tanh(x)*tanh(x)
return dx
tanh激活函数
tanh激活函数求导
tanh函数的优缺点:
优点:只要是解决了sigmoid关于zero-centered的输出问题。导数范围变大,在 之间,而sigmoid在 之间,梯度消失问题有所缓解。
缺点:
- 幂运算,计算成本高
- 梯度消失问题
ReLU
Relu(Rectified Linear Unit)-修正线性单元函数,函数的表达式:
即 ReLU=max(0, x)
def relu(x):
# 1
# y = np.maximum(0, x)
# 2
# y = np.where(x>0, x, 0)
# 3
y = (x + np.abs(x))/2
return y
ReLU激活函数
从图上可以看出,当 的时候,ReLu有饱和问题,当 的时候,则存在硬饱和(当x<0时,导数恒等于0;软饱和,导数趋近于0)。所以当 的时候不会存在梯度消失的问题。为了解决左硬饱和问题,提出了Leaky ReLU**、**PReLU解决方法。Leaky ReLU的函数公式为: ,其中 是一个很小的值,这样做可以使得负轴的信息不会全部丢失,解决ReLU神经元死亡问题
def leaky_relu(x, leak):
y = np.maximum(leak*x, x)
return y
Leaky ReLU:
PReLU和Leaky ReLU的表达式相同,
,区别在于这里的
不是固定的,而是一个可以学习的参数。
ReLU激活函数的优缺点
优点:
- 梯度不饱和,收敛速度快;
- 相对于sigmoid/tanh激活函数,极大的改善了梯度消失的问题;
- 不需要进行指数运算,因此运算速度快、复杂度低
缺点:
- 对参数初始化和学习率非常敏感,存在神经元死亡;
- ReLU的输出均值也大于0,偏移现象和 神经元死亡会共同影响网络的收敛性;
ReLU激活函数为什么比sigmoid和tanh好?
- 相比Sigmoid和tanh,ReLU摒弃了复杂的计算(这里指的是幂运算),提高了运算速度;
- 对于深层的网络而言,Sigmoid和tanh函数反向传播的过程中,和容易出现梯度消失的问题;
- ReLU会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
ReLU激活函数为什么能解决梯度消失问题?
上面的内容图片公式点 这里。假如对 b1进行反向传播,求梯度,公式如上,
表示激活函数,假如我们选择的是sigmoid函数,可以从上文中得知,sigmoid最大的梯度是0.25,所以在反向传播的过程中,梯度的下降呈指数下降,所以很容易造成梯度消失,使得前层网络的权值无法更新或者更新的很慢。选用ReLu函数,当
时,激活函数的梯度恒等于1,所以就可以大大解决了梯度消失的问题。
ReLU激活函数导致神经元“死亡”问题
设 经过 激活以后为a(其中的 表示ReLU),定义损失函数为
一个简单的前馈网络传递的过程可以见到的表示如下:
即
反向传播的话就是根据链式求导法则,要更新
,所以对
进行求导:
设学习率
(learning rate)为定值,权重
的更新取决于
,
越大,则更新的越多, 即:
就上式而言,如果
的值设置的太大,权重就会更新的很多,这么一种情况,由于权重更新的太多,可能对于任意训练数据
,输出结果
都小于零,此时经过 ReLU 后,
。这样的话会出现什么情况,根据上文我们说的ReLU函数求导,当
的时候,梯度为0;当
的时候梯度为1, 所以
,即
, 可以看出此后
就会停止更新,所以就会好造成该部分的神经元 “死亡”。