如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机。
如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。
上图就是一个最简单的激活函数,当
∑
i
=
0
N
>
0
\sum_{i=0}^N>0
∑i=0N>0时节点才会激活。
但是这个激活函数有一个问题是不可导,在
∑
i
=
0
N
=
0
\sum_{i=0}^N=0
∑i=0N=0处是处于不连续的情况下,因此不可以求导,因此这个函数无法直接使用梯度下降的方式来优化,当时使用了启发式搜索的方式来求解单层感知机最优解的情况。
为了解决单层激活函数阶梯不可导的情况,科学家提出了sigmoid函数(Logistic函数):
f
(
x
)
=
σ
(
x
)
=
1
1
+
e
−
x
f(x) =\sigma(x)= \frac{1}{1+e^{-x}}
f(x)=σ(x)=1+e−x1
当
x
→
∞
x \to \infty
x→∞时,
f
(
x
)
→
1
f(x) \to 1
f(x)→1,当
x
→
−
∞
x \to -\infty
x→−∞时,
f
(
x
)
→
0
f(x) \to 0
f(x)→0。而且可以看出倒数是连续可导的。
实际上sigmoid激活函数使用的非常多,最主要的是因为它连续光滑,而且值压缩在(0,1)的范围内,我们很多时候都需要(0,1)这样一个范围,比如概率和RGB。
但是sigmoid存在一个致命的权限,当 f ( x ) → ∞ f(x) \to \infty f(x)→∞和 f ( x ) → − ∞ f(x) \to -\infty f(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 = − 100 z=-100 z=−100的时候,sigmoid的值已经是非常小的了,接近于0了;当 z = 100 z=100 z=100的时候,sigmoid的值也接近于0了
Tanh激活函数
Tanh激活函数在RNN中用的比较多。
f
(
x
)
=
t
a
n
h
(
x
)
=
e
x
−
e
−
x
(
e
x
+
e
−
x
)
=
2
s
i
g
m
o
i
d
(
2
x
)
−
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
)
=
{
0
for x<0
x
,
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
)
=
{
0
for x<0
1
,
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
≥
0
x\ge0
x≥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激活函数。