一、引入激活函数的目的
图1:带一层隐藏层的神经网络
先看一个只含一层隐藏层的神经网络,如图1所示。输入为 n 条样本X,隐藏层H的权重和偏置分别为W_h,b_o,输出层O的权重和偏置分别为W_o,b_o。输出层的计算为:
H=XW_h+b_h (1)
O=HW_o+b_o (2)
将(1),(2)联合起来可得:
O=(XW_h+b_h)W_o+b_o=XW_hW_o+b_hW_o+b_o
从(3)可以看出,虽然加入了隐藏层,但是还是等效于单层的神经网络:权重为 W_hW_o ,偏置为 b_hW_o+b_o
上述的根本原因在于全连接只是对数据做仿射变换*,而多个仿射变换的叠加仍然是一个放射变换。因此有必要引入非线性变换,这个非线性变换就是今天要讲的激活函数。
*仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。简单来说,“仿射变换”就是:“线性变换”+“平移”
二、激活函数之性质
-
**非线性:**即导数不是常数。保证多层网络不退化成单层线性网络。这也是激活函数的意义所在。
-
**可微性:**保证了在优化中梯度的可计算性。虽然 ReLU 存在有限个点处不可微,但处处 subgradient,可以替代梯度。
-
**计算简单:**激活函数复杂就会降低计算速度,因此 RELU 要比 Exp 等操作的激活函数更受欢迎。
-
非饱和性(saturation):饱和指的是在某些区间梯度接近于零(即梯度消失),使得参数无法继续更新的问题。最经典的例子是 Sigmoid,它的导数在 x 为比较大的正值和比较小的负值时都会接近于 0。RELU 对于 x<0,其梯度恒为 0,这时候它也会出现饱和的现象。Leaky ReLU 和 PReLU 的提出正是为了解决这一问题。
-
单调性(monotonic):即导数符号不变。当激活函数是单调的时候,单层网络能够保证是凸函数。但是激活函数如 mish 等并不满足单调的条件,因此单调性并不是硬性条件,因为神经网络本来就是非凸的。
-
参数少:大部分激活函数都是没有参数的。像 PReLU 带单个参数会略微增加网络的大小。还有一个例外是 Maxout,尽管本身没有参数,但在同样输出通道数下 k 路 Maxout 需要的输入通道数是其它函数的 k 倍,这意味着神经元数目也需要变为 k 倍。
三、sigmoid激活函数带来的梯度消失和爆炸问题
为了解决前面的问题,后来提出了sigmoid激活函数。sigmoid函数可以将元素的值变换到0和1之间,定义如下:
图2:sigmoid激活函数
def Sigmoid(x):
return 1. / (1 + np.exp(-x))
#或
tf.nn.sigmoid(x,name=None)
sigmoid激活函数的导数为:
优点:
- 梯度平滑,求导容易
- Sigmoid函数的输出映射在(0,1)之间,单调连续,输出范围有限,优化稳定,可以用作输出层
缺点:
- 激活函数计算量大(在正向传播和反向传播中都包含幂运算和除法);
- 梯度消失:输入值较大或较小(图像两侧)时,sigmoid导数则接近于零,因此在反向传播时,这个局部梯度会与整个代价函数关于该单元输出的梯度相乘,结果也会接近为 0 ,无法实现更新参数的目的;
- Sigmoid 的输出不是 0 为中心(zero-centered)。因为如果输入都是正数的话