如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机。
如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。
上图就是一个最简单的激活函数,当∑i=0N>0\sum_{i=0}^N>0∑i=0N>0时节点才会激活。
但是这个激活函数有一个问题是不可导,在∑i=0N=0\sum_{i=0}^N=0∑i=0N=0处是处于不连续的情况下,因此不可以求导,因此这个函数无法直接使用梯度下降的方式来优化,当时使用了启发式搜索的方式来求解单层感知机最优解的情况。
为了解决单层激活函数阶梯不可导的情况,科学家提出了sigmoid函数(Logistic函数):
f(x)=σ(x)=11+e−x
f(x) =\sigma(x)= \frac{1}{1+e^{-x}}
f(x)=σ(x)=1+e−x1
当x→∞x \to \inftyx→∞时,f(x)→1f(x) \to 1f(x)→1,当x→−∞x \to -\inftyx→−∞时,f(x)→0f(x) \to 0f(x)→0。而且可以看出倒数是连续可导的。
实际上sigmoid激活函数使用的非常多,最主要的是因为它连续光滑,而且值压缩在(0,1)的范围内,我们很多时候都需要(0,1)这样一个范围,比如概率和RGB。
但是sigmoid存在一个致命的权限,当f(x)→∞f(x) \to \inftyf(x)→∞和f(x)→−∞f(x) \to -\inftyf(x)→−∞时,导数都为0,因此当值处在这部分区间时,参数θ′=θ−∇=θ−0=θ\theta'=\theta-\nabla=\theta-0=\thetaθ′=θ−∇=θ−0=θ长时间得不到更新,出现梯度离线现象。
sigmoid在PyTorch实现
import torch
z = torch.linspace(-100,100,10)
# tensor([-100.0000, -77.7778, -55.5556, -33.3333, -11.1111, 11.1111,
# 33.3333, 55.5556, 77.7778, 100.0000])
torch.sigmoid(z)
# tensor([0.0000e+00, 1.6655e-34, 7.4564e-25, 3.3382e-15, 1.4945e-05, 9.9999e-01,
# 1.0000e+00, 1.0000e+00, 1.0000e+00, 1.0000e+00])
或者使用from torch.nn import functional as F
的方式:
from torch.nn import functional as F
F.sigmoid(z)
# tensor([0.0000e+00, 1.6655e-34, 7.4564e-25, 3.3382e-15, 1.4945e-05, 9.9999e-01,
# 1.0000e+00, 1.0000e+00, 1.0000e+00, 1.0000e+00])
可以看出当z=−100z=-100z=−100的时候,sigmoid的值已经是非常小的了,接近于0了;当z=100z=100z=100的时候,sigmoid的值也接近于0了
Tanh激活函数
Tanh激活函数在RNN中用的比较多。
f(x)=tanh(x)=ex−e−x(ex+e−x)=2sigmoid(2x)−1
f(x) = tanh(x)=\frac{e^x-e^{-x}}{(e^x+e^{-x})}=2sigmoid(2x)-1
f(x)=tanh(x)=(ex+e−x)ex−e−x=2sigmoid(2x)−1
所以Tanh的取值范围是(-1,1)。
z = torch.linspace(-1,1,10)
torch.tanh(z)
#tensor([-0.7616, -0.6514, -0.5047, -0.3215, -0.1107, 0.1107, 0.3215, 0.5047,
# 0.6514, 0.7616])
Rectified Linear Unit (ReLU)激活函数
非常简单,但是可以说是现在深度学习的奠基石的激活函数。现在深度学习使用最多的激活函数就是ReLU激活函数。
f(x)={0for x<0x,x≥0
f(x)=\begin{cases} 0 & \text {for x<0} \\ x, & \text{x}\geq 0 \end{cases}
f(x)={0x,for x<0x≥0
ReLU函数的导数:
f′(x)={0for x<01,x≥0
f'(x)=\begin{cases} 0 & \text {for x<0} \\ 1, & \text{x}\geq 0 \end{cases}
f′(x)={01,for x<0x≥0
因为当x≥0x\ge0x≥0时,梯度是1,因此在向后传播时,梯度计算起来非常方便,不会放大也不会缩小,不会出现梯度离散和梯度爆炸的情况。
z = torch.linspace(-1,1,10)
torch.relu(z)
# tensor([ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 11.1111, 33.3333,
# 55.5556, 77.7778, 100.0000])
F.relu(z)
# tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1111, 0.3333, 0.5556, 0.7778,
# 1.0000])
一般在做research的时候,通常都是用ReLU激活函数。