0 激活函数的作用
神经网络的某一层的输入和权重进行内积操作后,需要经过激活函数进行变换处理。
一般神经网络中用到的激活函数都是非线性函数,这是因为多个连续的线性变换仍然是输入的线性变换。使用非线性激活函数可以增强模型的拟合能力,经过多个非线性激活函数对输入的连续处理后,可以拟合任意复杂的变换过程,完成图像分类、检测等任务。
pytorch总体实现:https://pytorch.org/docs/stable/nn.functional.html#non-linear-activation-functions
1 饱和激活函数
饱和激活函数是指在网络训练过程中,因为输入处于激活函数的饱和位置而有可能造成梯度弥散的激活函数,饱和激活函数包括了sigmoid、tanh和hard-sigmoid。
1.1 sigmoid
f
(
x
)
=
1
1
+
e
−
x
f(x) = \frac{1}{1 + e^{-x}}
f(x)=1+e−x1
f
′
(
x
)
=
e
−
x
(
1
+
e
−
x
)
2
=
f
(
x
)
(
1
−
f
(
x
)
)
f^{'}(x) = \frac{e^{-x}}{(1+e^{-x})^2} = f(x)(1-f(x))
f′(x)=(1+e−x)2e−x=f(x)(1−f(x))
sigmoid的缺点:
- sigmoid函数在输入x的绝对值较大时,梯度接近于0,因此会造成训练过程中的梯度弥散,阻碍网络的训练过程;
- sigmoid并不是以0为中心点的;
- sigmoid的计算过程设计到了指数运算,计算量大。
基于该原因,目前的神经网络训练过程中已经很少大范围的使用sigmoid作为激活函数。
实现:torch.nn.functional.sigmoid(input)
1.2 hard-sigmoid
分段光滑的sigmoid函数,具体形式为:
f
(
x
)
=
{
0
,
x
≤
−
3
x
6
+
1
2
,
−
3
<
x
<
3
1
,
x
≥
3
f(x) = \begin{cases} 0, &&& x \leq -3 \\ \frac{x}{6}+\frac{1}{2},&&& -3 < x < 3 \\ 1,&&& x \geq 3\end{cases}
f(x)=⎩⎪⎨⎪⎧0,6x+21,1,x≤−3−3<x<3x≥3
下图为sigmoid(红色曲线)和hard-sigmoid(蓝色折线)的对比:
实现:torch.nn.functional.hardsigmoid(input, inplace=False)
1.3 tanh
f
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
f(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}
f(x)=ex+e−xex−e−x
f
′
(
x
)
=
1
−
f
2
(
x
)
f^{'}(x) = 1 - f^2(x)
f′(x)=1−f2(x)
tanh相比于sigmoid:
- 共同缺点:
1.同样在输入x绝对值很大的情况下存在梯度弥散现象;
2.包含指数运算,计算量较大。 - 优点:
1.以0为中心点;
2.中心位置的梯度比sigmoid大,收敛更快;
实现:torch.nn.functional.tanh(input)
1.4 hard-tanh
分段光滑的tanh函数,具体形式为:
f
(
x
)
=
{
−
1
,
x
≤
−
1
x
,
−
1
<
x
<
1
1
,
x
≥
1
f(x) = \begin{cases} -1, &&& x \leq -1 \\ x,&&& -1 < x < 1 \\ 1,&&& x \geq 1\end{cases}
f(x)=⎩⎪⎨⎪⎧−1,x,1,x≤−1−1<x<1x≥1
下图为tanh(红色曲线)和hard-tanh(蓝色折线)的对比:
实现:torch.nn.functional.hardtanh(input, inplace=False)
2 非饱和的激活函数
上面的两个激活函数sigmoid和tanh都会在数据绝对值较大时产生梯度弥散现象,针对此问题,12年出现了Relu激活函数,并在后续陆续出现了各种变体,逐步解决了网络训练过程中的梯度弥散现象。这些非饱和的激活函数包括了ReLU、Leaky ReLU、PReLU、RReLU、ELU、Swish、Hard-Swish和Mish等。
2.1 ReLU
f
(
x
)
=
{
x
,
x
≥
0
0
,
o
t
h
e
r
w
i
s
e
f(x) = \begin{cases} x, &&& x \geq 0 \\ 0, &&& otherwise \end{cases}
f(x)={x,0,x≥0otherwise
f
′
(
x
)
=
{
1
,
x
≥
0
0
,
o
t
h
e
r
w
i
s
e
f^{'}(x) = \begin{cases} 1, &&& x \geq 0 \\ 0, &&& otherwise \end{cases}
f′(x)={1,0,x≥0otherwise
ReLU优点:
- 输入值经过ReLU函数后,负值部分变为0,使得输入变得稀疏,有助于提升计算效率;
- ReLU在正值部分梯度为1,不会造成梯度弥散,并且计算梯度的计算量小;
ReLU的缺点:
- 负值部分强制变为0,会使得训练中部分神经元变为“dead”状态不参与模型训练;
- 不以0为中心。
2.2 softplus
f
(
x
)
=
l
n
(
1
+
e
x
)
f(x) = ln(1 + e^x)
f(x)=ln(1+ex)
f
′
(
x
)
=
1
1
+
e
−
x
f^{'}(x) = \frac{1}{1 + e^{-x}}
f′(x)=1+e−x1
softplus是平滑版本的ReLU,并且在输入小于0时也是有梯度信息的,但由于梯度计算时有指数运算,计算量大且效果并比不ReLU好,因此其目前也未大范围的应用。
实现:torch.nn.functional.softplus(input, beta=1, threshold=20)
2.3 LeakyReLU
针对ReLU在输入为负时梯度为0的问题,后续又出现了Leaky ReLU。
f
(
x
)
=
{
x
,
x
≥
0
α
x
,
o
t
h
e
r
w
i
s
e
f(x) = \begin{cases} x, &&& x \geq 0 \\ \alpha x, &&& otherwise \end{cases}
f(x)={x,αx,x≥0otherwise
f
′
(
x
)
=
{
1
,
x
≥
0
α
,
o
t
h
e
r
w
i
s
e
f^{'}(x) = \begin{cases} 1, &&& x \geq 0 \\ \alpha, &&& otherwise \end{cases}
f′(x)={1,α,x≥0otherwise
α
\alpha
α是一个很小的正数(PyTorch中默认为0.01),在输入为负时,保证神经元不会完全“dead”。
实现:torch.nn.functional.leaky_relu(input, negative_slope=0.01, inplace=False)
2.4 pReLU
pReLU是leaky ReLU的变体,pReLU不再是指定一个固定的 α \alpha α值,而是通过网络的训练过程学习得到。
f
(
x
)
=
{
x
,
x
≥
0
α
x
,
o
t
h
e
r
w
i
s
e
f(x) = \begin{cases} x, &&& x \geq 0 \\ \alpha x, &&& otherwise \end{cases}
f(x)={x,αx,x≥0otherwise
f
′
(
x
)
=
{
1
,
x
≥
0
α
,
o
t
h
e
r
w
i
s
e
f^{'}(x) = \begin{cases} 1, &&& x \geq 0 \\ \alpha, &&& otherwise \end{cases}
f′(x)={1,α,x≥0otherwise
实现:
#weight就是可学习的参数
torch.nn.functional.prelu(input, weight)
2.5 rReLU
rReLU都是leaky ReLU的变体,rRelu则是认为 α \alpha α值符合均匀分布。
实现:
torch.nn.functional.rrelu(input, lower=1./8, upper=1./3, training=False, inplace=False)
pytorch中默认 α \alpha α符合 u ( l o w e r , u p p e r ) u(lower,upper) u(lower,upper), l o w e r lower lower默认值是0.25, u p p e r upper upper默认值是0.3333。
2.6 ReLU6
f
(
x
)
=
{
6
,
x
≥
6
x
,
0
<
x
<
6
0
,
x
≤
0
f(x) = \begin{cases} 6, &&& x \geq 6 \\ x, &&& 0 < x < 6 \\ 0,&&& x \leq 0\end{cases}
f(x)=⎩⎪⎨⎪⎧6,x,0,x≥60<x<6x≤0
所谓的relu6就是relu的变体,设置正值部分的最大输出为6。这样做的好处是为了满足移动端部署的需求,因为移动端通常使用Float16或者Int8等较低精度的模型,如果不对激活函数的输出进行限制的话,激活值可能会存在溢出,造成精度损失。
2.7 ELU
f
(
x
)
=
{
x
,
x
≥
0
α
(
e
x
−
1
)
,
o
t
h
e
r
w
i
s
e
f(x) = \begin{cases} x, &&& x \geq 0 \\ \alpha (e^x - 1), &&& otherwise \end{cases}
f(x)={x,α(ex−1),x≥0otherwise
f
′
(
x
)
=
{
1
,
x
≥
0
f
(
x
)
+
α
,
o
t
h
e
r
w
i
s
e
f^{'}(x) = \begin{cases} 1, &&& x \geq 0 \\ f(x) + \alpha, &&& otherwise \end{cases}
f′(x)={1,f(x)+α,x≥0otherwise
优点:
- 融合了sigmoid和ReLU,左侧具有软饱和性,右侧无饱和性;
- 右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。
缺点:
包含指数运算,计算量大。
实现:
torch.nn.functional.elu(input, alpha=1.0, inplace=False)
PyTorch的实现中, α \alpha α默认为1.0;
2.8 SELU
f
(
x
)
=
λ
{
x
,
x
≥
0
α
(
e
x
−
1
)
,
o
t
h
e
r
w
i
s
e
f(x) = \lambda \begin{cases} x, &&& x \geq 0 \\ \alpha (e^x - 1), &&& otherwise \end{cases}
f(x)=λ{x,α(ex−1),x≥0otherwise
f
′
(
x
)
=
λ
{
1
,
x
≥
0
f
(
x
)
+
α
,
o
t
h
e
r
w
i
s
e
f^{'}(x) =\lambda \begin{cases} 1, &&& x \geq 0 \\ f(x) + \alpha, &&& otherwise \end{cases}
f′(x)=λ{1,f(x)+α,x≥0otherwise
SELU就是给ELU乘上一个系数 λ \lambda λ,在作者的理论推导中, α \alpha α=1.6732632423543772848170429916717, λ \lambda λ=1.0507009873554804934193349852946。
在Self-Normalizing Neural Networks中,作者提到,SELU可以使得输入在经过一定层数之后变为固定的分布。
参考:https://www.bilibili.com/video/av16583463
2.9 swish
f
(
x
)
=
x
∗
s
i
g
m
o
i
d
(
β
x
)
f(x) = x * sigmoid(\beta x)
f(x)=x∗sigmoid(βx)
f
′
(
x
)
=
s
i
g
m
o
i
d
(
β
x
)
+
x
∗
s
i
g
m
o
i
d
(
β
x
)
∗
(
1
−
s
i
g
m
o
i
d
(
β
x
)
)
∗
β
f^{'}(x) = sigmoid(\beta x) + x * sigmoid(\beta x) * (1 - sigmoid(\beta x)) * \beta
f′(x)=sigmoid(βx)+x∗sigmoid(βx)∗(1−sigmoid(βx))∗β
β
\beta
β是个常数或者可以训练的参数。其具有无上界有下界、平滑、非单调的特性。其在模型效果上优于ReLU。
β \beta β接近0时,swish约为 1 2 x \frac{1}{2}x 21x; β \beta β趋于无穷时, f ( x ) = 2 ∗ m a x ( 0 , x ) f(x)=2 * max(0, x) f(x)=2∗max(0,x),所以Swish函数可以看作是介于线性函数与ReLU之间的平滑函数。
swish效果比relu好,但其梯度运算的计算量大。
2.10 hard-swish
f
(
x
)
=
{
x
,
x
≥
3
x
×
x
+
3
6
,
−
3
<
x
<
3
0
,
x
≤
−
3
f(x) = \begin{cases} x, &&& x \geq 3 \\ x \times \frac{x+3}{6}, &&& -3 < x < 3 \\ 0,&&& x \leq -3\end{cases}
f(x)=⎩⎪⎨⎪⎧x,x×6x+3,0,x≥3−3<x<3x≤−3
作者提到,虽然这种Swish非线性提高了精度,但是在嵌入式环境中,计算sigmoid函数代价要大得多。
因此作者使用hard-Swish和hard-Sigmoid替换了ReLU6和SE-block中的Sigmoid函数,但是只是在网络的后半段才将ReLU6替换为h-Swish,因为作者发现Swish函数只有在更深的网络层使用才能体现其优势。
首先是肯定了Swish的重要性,然后指出在量化模式下,Sigmoid函数比ReLU6的计算代价大的多,所以才有了这个ReLU6版本的h-Swish。
2.11 mish
f
(
x
)
=
x
∗
tanh
(
ln
(
1
+
e
x
)
)
f(x) = x * \tanh(\ln(1 + e^x))
f(x)=x∗tanh(ln(1+ex))
f
′
(
x
)
=
tanh
(
ln
(
1
+
e
x
)
)
+
x
∗
(
1
−
tanh
2
(
ln
(
1
+
e
x
)
)
)
∗
s
i
g
m
o
i
d
(
x
)
f^{'}(x) = \tanh(\ln(1 + e^x)) + x * (1 - \tanh^2(\ln(1+e^x)))*sigmoid(x)
f′(x)=tanh(ln(1+ex))+x∗(1−tanh2(ln(1+ex)))∗sigmoid(x)
Mish 函数保证在曲线上几乎所有点上的平滑度;
随着层深的增加,ReLU 精度迅速下降,其次是 Swish, 而 Mish 能更好地保持准确性。
参考:
https://zhuanlan.zhihu.com/p/158922374
https://blog.csdn.net/jsk_learner/article/details/102822001#Mish_161
https://zhuanlan.zhihu.com/p/98863801