DNN可以使用的损失函数和激活函数不少。这些损失函数和激活函数如何选择呢?下面我们就对DNN损失函数和激活函数的选择做一个总结。
1. 梯度消失与梯度爆炸
我们设第一层卷积的参数为
(
W
1
,
b
1
)
(W_1, b_1)
( W 1 , b 1 ) 第二层卷积的参数是,
(
W
2
,
b
2
)
(W_2, b_2)
( W 2 , b 2 ) 依次类推。又设激活函数为
f
f
f ,每一层卷积在经过激活函数前的值为 ,经
a
i
a_i
a i 过激活函数后的值为
f
i
f_i
f i 。
按照上面的表示,在CNN中,输入
x
x
x ,第一层的输出就是
f
1
=
(
W
1
x
+
b
1
)
f_1 = (W_1 x+ b_1)
f 1 = ( W 1 x + b 1 ) ,第二层的输出就是
f
2
=
f
(
W
2
f
(
W
1
x
+
b
1
)
+
b
2
)
f_2 = f(W_2f(W_1x + b_1)+b_2)
f 2 = f ( W 2 f ( W 1 x + b 1 ) + b 2 ) ,第三层的输出就是
f
3
=
f
(
W
3
f
(
W
2
f
(
W
1
x
+
b
1
)
+
b
2
)
+
b
3
)
f_3 = f(W_3f(W_2f(W_1x+b_1)+b_2)+b_3)
f 3 = f ( W 3 f ( W 2 f ( W 1 x + b 1 ) + b 2 ) + b 3 ) 。设最终损失为
C
C
C ,我们来尝试从第三层开始,用BP算法推导一下损失对参数
W
1
W_1
W 1 的偏导数,看看会发生什么。
为了简洁起见,略过求导过程,最后的结果为:
∂
C
∂
W
1
=
∂
C
∂
a
3
∂
a
3
∂
z
3
W
3
∂
a
2
∂
z
2
W
2
∂
a
1
∂
z
1
∂
z
1
∂
W
1
\frac{\partial C}{\partial W_1} = \frac{\partial C}{\partial a_3}\frac{\partial a_3}{\partial z_3}W_3\frac{\partial a_2}{\partial z_2}W_2\frac{\partial a_1}{\partial z_1}\frac{\partial z_1}{\partial W_1}
∂ W 1 ∂ C = ∂ a 3 ∂ C ∂ z 3 ∂ a 3 W 3 ∂ z 2 ∂ a 2 W 2 ∂ z 1 ∂ a 1 ∂ W 1 ∂ z 1
我们常常说原始神经网络的梯度消失问题,这里的
∂
a
3
∂
z
3
\frac{\partial a_3}{\partial z_3}
∂ z 3 ∂ a 3 、
∂
a
2
∂
z
2
\frac{\partial a_2}{\partial z_2}
∂ z 2 ∂ a 2 就是梯度消失的“罪魁祸首”。
例如sigmoid的函数,它的导数的取值范围是(0, 0.25],也就是说对于导数中的每一个元素,我们都有
0
<
∂
a
3
∂
z
3
≤
0.25
0 < \frac{\partial a_3}{\partial z_3} \le 0.25
0 < ∂ z 3 ∂ a 3 ≤ 0 . 2 5 ,
0
<
∂
a
2
∂
z
2
≤
0.25
0 <\frac{\partial a_2}{\partial z_2} \le 0.25
0 < ∂ z 2 ∂ a 2 ≤ 0 . 2 5 ,小于1的数乘在一起,必然是越乘越小的。这才仅仅是3层,如果10层的话, 根据
0.2
5
10
≈
0.000000954
0.25^{10} \approx 0.000000954
0 . 2 5 1 0 ≈ 0 . 0 0 0 0 0 0 9 5 4 ,第10层的误差相对第一层卷积的参数
W
1
W_1
W 1 的梯度将是一个非常小的值,这就是所谓的“梯度消失”。
ReLU函数的改进就是它使得
∂
a
3
∂
z
3
∈
{
0
,
1
}
\frac{\partial{a_3}}{\partial{z_3}}\in\{0,1\}
∂ z 3 ∂ a 3 ∈ { 0 , 1 } ,
∂
a
2
∂
z
2
∈
{
0
,
1
}
\frac{\partial{a_2}}{\partial{z_2}}\in\{0,1\}
∂ z 2 ∂ a 2 ∈ { 0 , 1 } ,
∂
a
1
∂
z
1
∈
{
0
,
1
}
\frac{\partial{a_1}}{\partial{z_1}}\in\{0,1\}
∂ z 1 ∂ a 1 ∈ { 0 , 1 } ,这样的话只要一条路径上的导数都是1,无论神经网络是多少层,这一部分的乘积都始终为1,因此深层的梯度也可以传递到浅层中。
当然,当Relu = 0引发的 “dead neuron"是另外一个问题,事实上,使用Relu激活函数引发的"dead neuron” 统计平均占据总神经元数量的 40%,为此而引入Relu函数的各种变形,如Leaky Relu等;
Note:
上面只讨论了
∂
a
3
∂
z
3
\frac{\partial a_3}{\partial z_3}
∂ z 3 ∂ a 3 对梯度消失/爆炸的影响,而梯度下降过程中另外一项
∂
C
∂
a
3
\frac{\partial C}{\partial a_3}
∂ a 3 ∂ C 则是由损失函数决定,由此,即
∂
a
i
∂
z
i
\frac{\partial a_i}{\partial z_i}
∂ z i ∂ a i 由激活函数决定,而
∂
L
∂
a
i
\frac{\partial L}{\partial a_i}
∂ a i ∂ L 则是由损失函数决定。
对于loss function梯度下降过程更新参数的过程,损失函数、激活函数及权重
W
i
W_i
W i 的组合构成了总的梯度下降过程(对CNN,其各channel的
W
i
W_i
W i 不同,而RNN则共享同一
W
i
W_i
W i ,这个也是为什么RNN更容易出现梯度消失/爆炸的原因,下文再详细叙述),三者均是造成梯度下降\爆炸的原因(但往往激活函数对梯度消失爆炸的影响更大),这也是为什么将sigmoid函数换成了Relu函数后依旧存在梯度消失\爆炸问题,因为换成了Relu函数只是解决了梯度消失\爆炸的一个方面,此外
W
i
W_i
W i 的连乘也是一大重要因素(这也是引入batch norm的原因:batchnorm就是通过对每一层的输出规范为均值和方差一致的方法,消除了
W
i
W_i
W i 带来的放大缩小的影响,进而解决梯度消失和爆炸的问题,或者可以理解为BN将输出从饱和区拉倒了非饱和区!详细可参考详解机器学习中的梯度消失、爆炸原因及其解决方法 )
扫描二维码关注公众号,回复:
3760577 查看本文章
从上述公式推导中不难看出,其中
W
i
W_i
W i 的连乘项是不可避免的,能控制的只有
∂
C
∂
a
i
\frac{\partial C}{\partial a_i}
∂ a i ∂ C 以及
∂
a
i
∂
z
i
\frac{\partial a_i}{\partial z_i}
∂ z i ∂ a i ,前者由损失函数决定,后者由激活函数决定。
结合参数更新公式:
w
i
=
w
i
−
α
∂
L
∂
w
i
w_i = w_i - \alpha \frac{\partial L}{\partial w_i}
w i = w i − α ∂ w i ∂ L
b
i
=
b
i
−
α
∂
L
∂
b
i
b_i = b_i - \alpha \frac{\partial L}{\partial b_i}
b i = b i − α ∂ b i ∂ L
我们希望的理想情况是损失函数在误差大
y
−
f
(
X
)
y - f(X)
y − f ( X ) 大的地方具有较大梯度,在误差小的地方具有较小梯度。
2. 均方差损失函数 + Sigmoid激活函数的问题
在讲反向传播算法时,我们用均方差损失函数和Sigmoid激活函数做了实例,首先我们就来看看均方差+Sigmoid的组合有什么问题。
对于均方差损失函数,公式如下:
L
=
1
2
n
∑
x
∣
∣
y
(
x
)
−
a
L
(
x
)
∣
∣
2
L = \frac{1}{2n} \sum_x ||y(x) - a^L(x)||^2
L = 2 n 1 x ∑ ∣ ∣ y ( x ) − a L ( x ) ∣ ∣ 2 其中,C表示代价,x表示样本,y表示实际值,a表示输出值,n表示样本的总数。为简单起见,同样一个样本为例进行说明,此时二次代价函数为:
L
=
(
y
−
a
)
2
2
L = \frac{(y-a)^2}{2}
L = 2 ( y − a ) 2
目前训练ANN最有效的算法是反向传播算法。简而言之,训练ANN就是通过反向传播代价,以减少代价为导向,调整参数。参数主要有:神经元之间的连接权重w,以及每个神经元本身的偏置b。调参的方式是采用梯度下降算法(Gradient descent),沿着梯度方向调整参数大小。w和b的梯度推导如下:
∂
C
∂
w
=
(
a
−
y
)
σ
′
(
z
)
x
\frac{\partial C}{\partial w} = (a - y) \sigma \prime(z) x
∂ w ∂ C = ( a − y ) σ ′ ( z ) x
∂
C
∂
b
=
(
a
−
y
)
σ
′
(
z
)
\frac{\partial C}{\partial b} = (a - y) \sigma \prime(z)
∂ b ∂ C = ( a − y ) σ ′ ( z )
其中,
z
z
z 表示神经元的输入,
σ
\sigma
σ 表示激活函数。从以上公式可以看出,w和b的梯度跟激活函数的梯度成正比,激活函数的梯度越大,w和b的大小调整得越快,训练收敛得就越快。
而神经网络若采用的激活函数为Sigmoid函数,Sigmoid激活函数的表达式为:
σ
(
z
)
=
1
1
+
e
−
z
\sigma(z) = \frac{1}{1+e^{-z}}
σ ( z ) = 1 + e − z 1
Sigmoid 的函数图像如下:
从图上可以看出,对于Sigmoid,当
z
z
z 的取值越来越大后,函数曲线变得越来越平缓,意味着此时的导数
σ
′
(
z
)
\sigma\prime(z)
σ ′ ( z ) 也越来越小。同样的,当
z
z
z 的取值越来越小时,也有这个问题。仅仅在
z
z
z 取值为0附近时,导数
σ
′
(
z
)
\sigma\prime(z)
σ ′ ( z ) 的取值较大。
即sigmoid只有在0附近取值时具有较大梯度,当
z
z
z 取值较大或者较小时候,
σ
′
(
z
)
\sigma\prime(z)
σ ′ ( z ) (对应上文的
∂
a
∂
z
\frac{\partial a}{\partial z}
∂ z ∂ a )值都很小,
上文中得到,运用梯度下降反向传播进行参数更新时,每一层向前递推都要乘以
σ
′
(
z
)
\sigma\prime(z)
σ ′ ( z ) ,得到梯度变化值。Sigmoid的这个曲线意味着在大多数时候,我们的梯度变化值很小,导致我们的
W
,
b
W, b
W , b 更新到极值的速度较慢,也就是我们的算法收敛速度较慢。
2.1 实例论述
以一个神经元的二类分类训练为例,进行两次实验(ANN常用的激活函数为sigmoid函数,该实验也采用该函数):输入一个相同的样本数据x=1.0(该样本对应的实际分类y=0);两次实验各自随机初始化参数,从而在各自的第一次前向传播后得到不同的输出值,形成不同的代价(误差):
在实验1中,随机初始化参数,使得第一次输出值为0.82(该样本对应的实际值为0);经过300次迭代训练后,输出值由0.82降到0.09,逼近实际值。而在实验2中,第一次输出值为0.98,同样经过300迭代训练,输出值只降到了0.20。
从两次实验的代价曲线中可以看出:实验1的代价随着训练次数增加而快速降低,但实验2的代价在一开始下降得非常缓慢;直观上看,初始的误差越大,收敛得越缓慢(实际原因是当初试误差大的时候,梯度更新幅度主要由激活函数导数项决定,虽然初试误差大,但由于激活函数导数项相差很大,造成直观上看初试误差大,反倒收敛的慢的表面现象)。
其实,误差大导致训练缓慢的原因在于使用了二次代价函数。
主要是由于当使用二次代价损失函数时候,进行后向传播过程中引入了激活函数导数项,证明如下: : 二次代价函数的公式为:
L
=
1
2
n
∑
x
∣
∣
y
(
x
)
−
a
L
(
x
)
∣
∣
2
L = \frac{1}{2n} \sum_x ||y(x) - a^L(x)||^2
L = 2 n 1 x ∑ ∣ ∣ y ( x ) − a L ( x ) ∣ ∣ 2 其中,C表示代价,x表示样本,y表示实际值,a表示输出值,n表示样本的总数。为简单起见,同样一个样本为例进行说明,此时二次代价函数为:
L
=
(
y
−
a
)
2
2
L = \frac{(y-a)^2}{2}
L = 2 ( y − a ) 2 目前训练ANN最有效的算法是反向传播算法。简而言之,训练ANN就是通过反向传播代价,以减少代价为导向,调整参数。参数主要有:神经元之间的连接权重w,以及每个神经元本身的偏置b。调参的方式是采用梯度下降算法(Gradient descent),沿着梯度方向调整参数大小。w和b的梯度推导如下:
∂
C
∂
w
=
(
a
−
y
)
σ
′
(
z
)
x
\frac{\partial C}{\partial w} = (a - y) \sigma \prime(z) x
∂ w ∂ C = ( a − y ) σ ′ ( z ) x
∂
C
∂
b
=
(
a
−
y
)
σ
′
(
z
)
\frac{\partial C}{\partial b} = (a - y) \sigma \prime(z)
∂ b ∂ C = ( a − y ) σ ′ ( z )
其中,z表示神经元的输入,
σ
\sigma
σ 表示激活函数。从以上公式可以看出,w 和 b 的梯度跟激活函数的梯度成正比,激活函数的梯度越大,w 和 b 的大小调整得越快,训练收敛得就越快。
而当神经网络常用的激活函数为sigmoid函数,该函数的曲线如下所示:
如图所示,实验2的初始输出值(0.98)对应的梯度明显小于实验1的输出值(0.82),因此实验2的参数梯度下降得比实验1慢。这就是初始的代价(误差)越大,导致训练越慢的原因。
本质是二次损失函数引入激活函数梯度项,而sigmoid激活函数梯度自带“消失”属性,即
s
i
g
m
a
′
(
z
)
≤
0.25
sigma \prime (z) \le 0.25
s i g m a ′ ( z ) ≤ 0 . 2 5 ,当神经网络层数比较深时,其多次乘积造成梯度回传到前层的时候更新量近似为0,导致前层神经网络难以得到训练
与我们的期望不符,即:不能像人一样,错误越大,改正的幅度越大,从而学习得越快。
事实上,sigmoid函数作为激活函数的实际应用并不多,况且,能用sigmoid的地方均可用tanh函数替代,况且tanh函数还自带聚合效果(关于原点对称),目前可能用到地方也就是二分类,作为输出函数,即将0,1分类换算成概率输出。
3. 使用交叉熵损失函数+Sigmoid激活函数改进DNN算法收敛速度
上一节我们讲到Sigmoid的函数特性导致反向传播算法收敛速度慢的问题,那么如何改进呢?换掉Sigmoid?这当然是一种选择。另一种常见的选择是用交叉熵损失函数来代替均方差损失函数。
我们来看看每个样本的交叉熵损失函数的形式:
C
=
−
1
n
∑
x
[
y
l
n
a
+
(
1
−
y
)
l
n
(
1
−
a
)
]
C = -\frac{1}{n}\sum_x[y lna + (1-y)ln(1-a)]
C = − n 1 x ∑ [ y l n a + ( 1 − y ) l n ( 1 − a ) ] 其中,x表示样本,n表示样本的总数。那么,重新计算参数w的梯度:
∂
C
∂
w
j
=
−
1
n
∑
x
(
y
σ
(
z
)
−
(
1
−
y
)
1
−
σ
(
z
)
)
∂
σ
∂
w
j
=
−
1
n
∑
x
(
y
σ
(
z
)
−
(
1
−
y
)
1
−
σ
(
z
)
)
σ
′
(
z
)
x
j
=
1
n
∑
x
σ
′
(
z
)
x
j
σ
(
z
)
(
1
−
σ
(
z
)
)
(
σ
(
z
)
−
y
)
=
1
n
∑
x
x
j
(
σ
(
z
)
−
y
)
\frac{\partial C}{\partial w_j} = -\frac{1}{n} \sum_x (\frac{y}{\sigma(z)} - \frac{(1-y)}{1 - \sigma(z)})\frac{\partial \sigma}{\partial w_j} \\ = -\frac{1}{n} \sum_x (\frac{y}{\sigma(z)} - \frac{(1-y)}{1 - \sigma(z)})\sigma \prime (z) x_j \\ = \frac{1}{n} \sum_x \frac{\sigma \prime (z) x_j}{\sigma(z)(1- \sigma(z))}(\sigma(z) - y) \\ = \frac{1}{n}\sum_{x} x_j (\sigma(z) - y)
∂ w j ∂ C = − n 1 x ∑ ( σ ( z ) y − 1 − σ ( z ) ( 1 − y ) ) ∂ w j ∂ σ = − n 1 x ∑ ( σ ( z ) y − 1 − σ ( z ) ( 1 − y ) ) σ ′ ( z ) x j = n 1 x ∑ σ ( z ) ( 1 − σ ( z ) ) σ ′ ( z ) x j ( σ ( z ) − y ) = n 1 x ∑ x j ( σ ( z ) − y ) 其中(具体证明见附录):
σ
′
(
z
)
=
σ
(
z
)
(
1
−
σ
(
z
)
)
\sigma \prime (z) = \sigma(z)(1- \sigma(z))
σ ′ ( z ) = σ ( z ) ( 1 − σ ( z ) )
因此,w的梯度公式中原来的
σ
′
(
z
)
\sigma \prime (z)
σ ′ ( z ) 被消掉了;另外,该梯度公式中的
σ
(
z
)
−
y
\sigma(z) - y
σ ( z ) − y 表示输出值与实际值之间的误差。所以,当误差越大,梯度就越大,参数w调整得越快,训练速度也就越快。同理可得,b的梯度为:
∂
C
∂
b
=
1
n
∑
x
(
σ
(
z
)
−
y
)
\frac{\partial C}{\partial b} = \frac{1}{n} \sum_x (\sigma (z) - y)
∂ b ∂ C = n 1 x ∑ ( σ ( z ) − y ) 实际情况证明,交叉熵代价函数带来的训练效果往往比二次代价函数要好。
4. 使用对数似然损失函数和softmax激活函数进行DNN分类输出
在前面我们讲的所有DNN相关知识中,我们都假设输出是连续可导的值。但是如果是分类问题,那么输出是一个个的类别,那我们怎么用DNN来解决这个问题呢?
比如假设我们有一个三个类别的分类问题,这样我们的DNN输出层应该有三个神经元,假设第一个神经元对应类别一,第二个对应类别二,第三个对应类别三,这样我们期望的输出应该是(1,0,0),(0,1,0) 和 (0,0,1)这三种。即样本真实类别对应的神经元输出应该无限接近或者等于1,而非该样本真实输出对应的神经元的输出应该无限接近或者等于0。或者说,我们希望输出层的神经元对应的输出是若干个概率值,这若干个概率值即我们DNN模型对于输入值对于各类别的输出预测,同时为满足概率模型,这若干个概率值之和应该等于1。
DNN分类模型要求是输出层神经元输出的值在0到1之间,同时所有输出值之和为1。很明显,现有的普通DNN是无法满足这个要求的。但是我们只需要对现有的全连接DNN稍作改良,即可用于解决分类问题。在现有的DNN模型中,我们可以将输出层第
i
i
i 个神经元的激活函数定义为如下形式:
a
i
L
=
e
z
i
L
∑
j
=
1
n
L
e
z
J
L
a_i^L = \frac{e^{z_i^L}}{\sum_{j=1}^{n_L}e^{z_J^L}}
a i L = ∑ j = 1 n L e z J L e z i L 其中,
n
L
n_L
n L 是输出层第
L
L
L 层的神经元个数,或者说我们的分类问题的类别数。
很容易看出,所有的
a
i
L
a_i^L
a i L 都是在(0,1) 之间的数字,而
∑
j
=
1
n
L
e
z
J
L
\sum_{j=1}^{n_L}e^{z_J^L}
∑ j = 1 n L e z J L 作为归一化因子保证了所有的
a
i
L
a_i^L
a i L 之和为1。
这个方法很简洁漂亮,仅仅只需要将输出层的激活函数从Sigmoid之类的函数转变为上式的激活函数即可。上式这个激活函数就是我们的softmax激活函数。它在分类问题中有广泛的应用。将DNN用于分类问题,在输出层用softmax激活函数也是最常见的了。
下面这个例子清晰的描述了softmax激活函数在前向传播算法时的使用。假设我们的输出层为三个神经元,而未激活的输出为3,1和-3,我们求出各自的指数表达式为:20,2.7和0.05,我们的归一化因子即为22.75,这样我们就求出了三个类别的概率输出分布为0.88,0.12和0。
从上面可以看出,将softmax用于前向传播算法是也很简单的。那么在反向传播算法时还简单吗?反向传播的梯度好计算吗?答案是Yes!
对于用于分类的softmax激活函数,对应的损失函数一般都是用对数似然函数,即:
J
(
W
,
b
,
a
L
,
y
)
=
−
∑
k
y
k
l
n
a
k
L
J(W,b,a^L,y) = - \sum\limits_ky_klna_k^L
J ( W , b , a L , y ) = − k ∑ y k l n a k L
其中
y
k
y_k
y k 的取值为0或者1,如果某一训练样本的输出为第
i
i
i 类。则
y
i
=
1
y_i = 1
y i = 1 , 其余的
j
≠
i
j\ne i
j ̸ = i 都有
y
j
=
0
y_j=0
y j = 0 。由于每个样本只属于一个类别,所以这个对数似然函数可以简化为:
J
(
W
,
b
,
a
L
,
y
)
=
−
l
n
a
i
L
J(W,b,a^L,y) = -ln a_i^L
J ( W , b , a L , y ) = − l n a i L
其中
i
i
i 即为训练样本真实的类别序号。
可见损失函数只和真实类别对应的输出有关,这样假设真实类别是第
i
i
i 类,则其他不属于第
i
i
i 类序号对应的神经元的梯度导数直接为0。对于真实类别第
i
i
i 类,他对应的第
j
j
j 个
w
w
w 链接
w
i
j
L
w_{ij}^L
w i j L 对应的梯度计算为:
∂
J
(
W
,
b
,
a
L
,
y
)
∂
w
i
j
L
=
∂
J
(
W
,
b
,
a
L
,
y
)
∂
a
i
L
∂
a
i
L
∂
z
i
L
∂
z
i
L
∂
w
i
j
L
=
−
1
a
i
L
(
e
z
i
L
)
∑
j
=
1
n
L
e
z
j
L
−
e
z
i
L
e
z
i
L
(
∑
j
=
1
n
L
e
z
j
L
)
2
a
j
L
−
1
=
−
1
a
i
L
(
e
z
i
L
∑
j
=
1
n
L
e
z
j
L
−
e
z
i
L
∑
j
=
1
n
L
e
z
j
L
e
z
i
L
∑
j
=
1
n
L
e
z
j
L
)
a
j
L
−
1
=
−
1
a
i
L
a
i
L
(
1
−
a
i
L
)
a
j
L
−
1
=
(
a
i
L
−
1
)
a
j
L
−
1
\frac{\partial J(W,b,a^L,y)}{\partial w_{ij}^L} = \frac{\partial J(W,b,a^L,y)}{\partial a_i^L}\frac{\partial a_i^L}{\partial z_i^L}\frac{\partial z_i^L}{\partial w_{ij}^L} \\ = -\frac{1}{a_i^L}\frac{(e^{z_i^L})\sum\limits_{j=1}^{n_L}e^{z_j^L}-e^{z_i^L}e^{z_i^L}}{(\sum\limits_{j=1}^{n_L}e^{z_j^L)^2}} a_j^{L-1} \\ = -\frac{1}{a_i^L} (\frac{e^{z_i^L}}{\sum\limits_{j=1}^{n_L}e^{z_j^L}}-\frac{e^{z_i^L}}{\sum\limits_{j=1}^{n_L}e^{z_j^L}}\frac{e^{z_i^L}}{\sum\limits_{j=1}^{n_L}e^{z_j^L}}) a_j^{L-1} \\ = -\frac{1}{a_i^L} a_i^L(1- a_i^L) a_j^{L-1} \\ = (a_i^L -1) a_j^{L-1}
∂ w i j L ∂ J ( W , b , a L , y ) = ∂ a i L ∂ J ( W , b , a L , y ) ∂ z i L ∂ a i L ∂ w i j L ∂ z i L = − a i L 1 ( j = 1 ∑ n L e z j L ) 2 ( e z i L ) j = 1 ∑ n L e z j L − e z i L e z i L a j L − 1 = − a i L 1 ( j = 1 ∑ n L e z j L e z i L − j = 1 ∑ n L e z j L e z i L j = 1 ∑ n L e z j L e z i L ) a j L − 1 = − a i L 1 a i L ( 1 − a i L ) a j L − 1 = ( a i L − 1 ) a j L − 1
同样的可以得到
b
i
L
b_i^L
b i L 的梯度表达式为:
∂
J
(
W
,
b
,
a
L
,
y
)
∂
b
i
L
=
a
i
L
−
1
\frac{\partial J(W,b,a^L,y)}{\partial b_i^L} = a_i^L -1
∂ b i L ∂ J ( W , b , a L , y ) = a i L − 1 可见,梯度计算也很简洁,也没有第一节说的训练速度慢的问题。举个例子,假如我们对于第2类的训练样本,通过前向算法计算的未激活输出为(1,5,3),则我们得到softmax激活后的概率输出为:(0.015,0.866,0.117)。由于我们的类别是第二类,则参数b的反向传播的梯度应该为:(0.015,0.866-1,0.117)。是不是很简单呢?
当softmax输出层的反向传播计算完以后,后面的普通DNN层的反向传播计算和之前讲的普通DNN没有区别。
5. DNN其他激活函数
除了上面提到了激活函数,DNN常用的激活函数还有:
1) tanh:这个是sigmoid的变种,表达式为:
t
a
n
h
(
z
)
=
e
z
−
e
−
z
e
z
+
e
−
z
tanh(z) = \frac{e^z-e^{-z}}{e^z+e^{-z}}
t a n h ( z ) = e z + e − z e z − e − z
tanh激活函数和sigmoid激活函数的关系为:
t
a
n
h
(
z
)
=
2
s
i
g
m
o
i
d
(
2
z
)
−
1
tanh(z) = 2sigmoid(2z)-1
t a n h ( z ) = 2 s i g m o i d ( 2 z ) − 1
tanh和sigmoid对比主要的特点是它的输出落在了[-1,1],这样输出可以进行标准化。同时tanh的曲线在较大时变得平坦的幅度没有sigmoid那么大,这样求梯度变化值有一些优势。当然,要说tanh一定比sigmoid好倒不一定,还是要具体问题具体分析。
2) softplus:这个其实就是sigmoid函数的原函数,表达式为:
s
o
f
t
p
l
u
s
(
z
)
=
l
o
g
(
1
+
e
z
)
softplus(z) = log(1+e^z)
s o f t p l u s ( z ) = l o g ( 1 + e z )
它的导数就是sigmoid函数。softplus的函数图像和ReLU有些类似。它出现的比ReLU早,可以视为ReLU的鼻祖。
3)PReLU:从名字就可以看出它是ReLU的变种,特点是如果未激活值小于0,不是简单粗暴的直接变为0,而是进行一定幅度的缩小。如下图。当然,由于ReLU的成功,有很多的跟风者,有其他各种变种ReLU,这里就不多提了。
6. DNN损失函数和激活函数小结
上面我们对DNN损失函数和激活函数做了详细的讨论,重要的点有:1)如果使用sigmoid激活函数,则交叉熵损失函数一般肯定比均方差损失函数好。2)如果是DNN用于分类,则一般在输出层使用softmax激活函数和对数似然损失函数。3)ReLU激活函数对梯度消失问题有一定程度的解决,尤其是在CNN模型中。
参考资料
[1] Neural Networks and Deep Learning by By Michael Nielsen [2] Deep Learning, book by Ian Goodfellow, Yoshua Bengio, and Aaron Courville [3] 深度神经网络(DNN)损失函数和激活函数的选择 [4] 交叉熵代价函数(作用及公式推导)