深度学习(二):DNN损失函数和激活函数的选择

在《深度学习(一):DNN前向传播算法和反向传播算法》中,我们对DNN的前向反向传播算法的使用做了总结。里面使用的损失函数是均方差,而激活函数是sigmoid。实际上DNN可以使用的损失函数和激活函数不少。这些损失函数和激活函数如何选择呢?下面我们就对DNN损失函数和激活函数的选择做一个总结。

一、 均方差损失函数+sigmoid激活函数的问题

在讲反向传播算法时,我们用均方差损失函数和Sigmoid激活函数做了实例,首先我们就来看看均方差+Sigmoid的组合有什么问题。

首先我们回顾下Sigmoid激活函数的表达式为:
σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1+e^{-z}} σ(z)=1+ez1

σ ( z ) \sigma(z) σ(z)的函数图像如下:

CSDN图标

从图上可以看出,对于Sigmoid,当 z z z的取值越来越大后,函数曲线变得越来越平缓,意味着此时的导数 σ ′ ( z ) \sigma ^{'}(z) σ(z)也越来越小。同样的,当 z z z的取值越来越小时,也有这个问题。仅仅在 z z z取值为0附近时,导数 σ ′ ( z ) \sigma ^{'}(z) σ(z)的取值较大。

上篇讲的均方差+Sigmoid的反向传播算法中,每一层向前递推都要乘以 σ ′ ( z ) \sigma ^{'}(z) σ(z),得到梯度变化值。Sigmoid的这个曲线意味着在大多数时候,我们的梯度变化值很小,导致我们的 W , b W,b W,b更新到极值的速度较慢,也就是我们的算法收敛速度较慢。那么有什么办法可以改进呢?

二、 使用交叉熵损失函数+Sigmoid激活函数改进DNN算法收敛速度

上一章我们讲到Sigmoid的函数特性导致反向传播算法收敛速度慢的问题,那么如何改进呢?换掉Sigmoid?这当然是一种选择。另一种常见的选择是用交叉熵损失函数来代替均方差损失函数

我们来看看每个样本的交叉熵损失函数的形式:
J ( W , b , a , y ) = − y ∙ l n a − ( 1 − y ) ∙ l n ( 1 − a ) J(W,b,a,y) = -y \bullet lna- (1-y) \bullet ln(1 -a) J(W,b,a,y)=ylna(1y)ln(1a)

其中, ∙ \bullet 为向量内积。

使用了交叉熵损失函数,就能解决Sigmoid函数导数变化大多数时候反向传播算法慢的问题吗?我们来看看当使用交叉熵时,我们输出层 δ L \delta^L δL的梯度情况。
δ L = ∂ J ( W , b , a L , y ) ∂ z L = − y 1 a L ( a L ) ( 1 − a L ) + ( 1 − y ) 1 1 − a L ( a L ) ( 1 − a L ) = − y ( 1 − a L ) + ( 1 − y ) a L = a L − y \begin{aligned} \delta^L & = \frac{\partial J(W,b,a^L,y)}{\partial z^L} \\& = -y\frac{1}{a^L}(a^L)(1-a^L) + (1-y) \frac{1}{1-a^L}(a^L)(1-a^L) \\& = -y(1-a^L) + (1-y)a^L \\& = a^L-y \end{aligned} δL=zLJ(W,b,aL,y)=yaL1(aL)(1aL)+(1y)1aL1(aL)(1aL)=y(1aL)+(1y)aL=aLy

可见此时我们的 δ L \delta^L δL梯度表达式里面已经没有了 σ ′ ( z ) \sigma ^{'}(z) σ(z)。作为一个特例,回顾一下我们在上篇均方差损失函数时在 δ L \delta^L δL的梯度,
δ L = ∂ J ( W , b , x , y ) ∂ z L = ( a L − y ) ⊙ σ ′ ( z ) \delta^L =\frac{\partial J(W,b,x,y)}{\partial z^L} = (a^L-y) \odot \sigma^{'}(z) δL=zLJ(W,b,x,y)=(aLy)σ(z)

对比两者在第 L L L层的 δ L \delta^L δL梯度表达式,就可以看出,使用交叉熵,得到的 δ L \delta^L δL梯度表达式没有了 σ ′ ( z ) \sigma ^{'}(z) σ(z),梯度为预测值和真实值的差距,这样求得的 W L , b L W^L,b^L WL,bL的梯度也不包含 σ ′ ( z ) \sigma ^{'}(z) σ(z),因此避免了反向传播的 L L L层收敛速度慢的问题。

通常情况下,如果我们使用了sigmoid激活函数,交叉熵损失函数肯定比均方差损失函数好用。

注:

根据BP算法:
δ l = δ l + 1 ∂ z l + 1 ∂ z l = ( W l + 1 ) T δ l + 1 ⊙ σ ′ ( z l ) \delta^{l} = \delta^{l+1}\frac{\partial z^{l+1}}{\partial z^{l}} = (W^{l+1})^T\delta^{l+1}\odot \sigma^{'}(z^l) δl=δl+1zlzl+1=(Wl+1)Tδl+1σ(zl)
在后面一层层反向传播时,还是需要计算后面每层的激活函数的导数 σ ′ ( z l ) \sigma^{'}(z^l) σ(zl)。那么其实交叉熵损失函数只能在输出层 L L L不需要激活函数的导数,在后面的层层反向传播计算时还是需要的。所以说这个方法可以一定程度减小梯度消失的问题,并不能完全消除。

三、使用对数似然损失函数和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 (1) a_i^L = \frac{e^{z_i^L}}{\sum\limits_{j=1}^{n_L}e^{z_j^L}} \qquad \text{(1)} aiL=j=1nLezjLeziL(1)

其中, n L n_L nL是输出层第 L L L层的神经元个数,或者说我们的分类问题的类别数。

很容易看出,所有的 a i L a_i^L aiL都是在(0,1) 之间的数字,而 ∑ j = 1 n L e z j L \sum\limits_{j=1}^{n_L}e^{z_j^L} j=1nLezjL作为归一化因子保证了所有的 a i L a_i^L aiL之和为1。

这个方法很简洁漂亮,仅仅只需要将输出层的激活函数从Sigmoid之类的函数转变为上式的激活函数即可。上式这个激活函数就是我们的softmax激活函数。它在多分类问题中有广泛的应用。

下面这个例子清晰的描述了softmax激活函数在前向传播算法时的使用。假设我们的输出层为三个神经元,而未激活的输出为3,1和-3,我们求出各自的指数表达式为:20,2.7和0.05,我们的归一化因子即为22.75,这样我们就求出了三个类别的概率输出分布为0.88,0.12和0。

CSDN图标

从上面可以看出,将softmax用于前向传播算法是很简单的。那么在反向传播算法时还简单吗?反向传播的梯度好计算吗?答案是Yes!

对于用于分类的softmax激活函数,对应的损失函数一般都是用对数似然函数,即:
J ( W , b , a L , y ) = − ∑ k y k l n a k L (2) J(W,b,a^L,y) = - \sum\limits_ky_klna_k^L\qquad \text{(2)} J(W,b,aL,y)=kyklnakL(2)

其中 y k y_k yk的取值为0或者1,如果某一训练样本的输出为第 i i i类。则 y i = 1 y_i=1 yi=1,其余的 j ≠ i j≠i j̸=i都有 y j = 0 y_j=0 yj=0。由于每个样本只属于一个类别,所以这个对数似然函数可以简化为:
J ( W , b , a L , y ) = − l n a i L (3) J(W,b,a^L,y) = -lna_i^L\qquad \text{(3)} J(W,b,aL,y)=lnaiL(3)

其中 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 wijL对应的梯度计算为:

∂ 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 (4) = − 1 a i L a i L ( 1 − a i L ) a j L − 1 = ( a i L − 1 ) a j L − 1 \begin{aligned} \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} \text{(4)}\\& = -\frac{1}{a_i^L} a_i^L(1- a_i^L) a_j^{L-1} \\& = (a_i^L -1) a_j^{L-1} \end{aligned} wijLJ(W,b,aL,y)=aiLJ(W,b,aL,y)ziLaiLwijLziL=aiL1(j=1nLezjL)2(eziL)j=1nLezjLeziLeziLajL1=aiL1(j=1nLezjLeziLj=1nLezjLeziLj=1nLezjLeziL)ajL1(4)=aiL1aiL(1aiL)ajL1=(aiL1)ajL1

这里我补充一句:最后一层的激活函数是softmax函数,因此才会有公式(4)的形式,也就是说,只对于做softmax激活的神经元的前面的权重 W W W才会有公式(4)的梯度形式。 再往前的神经元,可以是其它的激活函数。

同样的可以得到 b i L b_i^L biL的梯度表达式为:
∂ J ( W , b , a L , y ) ∂ b i L = a i L − 1 (5) \frac{\partial J(W,b,a^L,y)}{\partial b_i^L} = a_i^L -1\qquad \text{(5)} biLJ(W,b,aL,y)=aiL1(5)

当然,输出层 δ i L \delta_i^L δiL梯度为:
δ i L = ∂ J ( W , b , a L , y ) ∂ z i L = ∂ J ( W , b , a L , y ) ∂ b i L = a i L − 1 (6) \delta_i^L = \frac{\partial J(W,b,a^L,y)}{\partial z_i^L} = \frac{\partial J(W,b,a^L,y)}{\partial b_i^L} =a_i^L -1\qquad \text{(6)} δiL=ziLJ(W,b,aL,y)=biLJ(W,b,aL,y)=aiL1(6)

其中 i i i为样本真实类别。可见,梯度计算也很简洁,也没有第一节说的训练速度慢的问题。

观察公式(6),得出一个结论:对于真实类别位置,反向传播的梯度是要-1,而不是真实类别的位置,反向传播的梯度为0

举个例子,假如我们对于第2类的训练样本,通过前向算法计算的未激活输出为(1,5,3),则我们得到softmax激活后的概率输出为:(0.015,0.866,0.117)。由于我们的类别是第二类,则反向传播的梯度应该为:(0.015,0.866-1,0.117)。

当softmax输出层的反向传播计算完以后,后面的普通DNN层的反向传播计算和之前讲的普通DNN没有区别。

四、梯度爆炸梯度消失与ReLU激活函数

学习DNN,大家一定听说过梯度爆炸梯度消失两个词。尤其是梯度消失,是限制DNN与深度学习的一个关键障碍,目前也没有完全攻克。

什么是梯度爆炸和梯度消失呢?从理论上说都可以写一篇论文出来。不过简单理解,就是在反向传播的算法过程中,由于我们使用了是矩阵求导的链式法则,有一大串连乘,如果连乘的数字在每层都是小于1的,则梯度越往前乘越小,导致梯度消失,而如果连乘的数字在每层都是大于1的,则梯度越往前乘越大,导致梯度爆炸

比如我们之前讲到了 δ \delta δ的计算,可以表示为:
δ l = ∂ J ( W , b , x , y ) ∂ z l = ∂ J ( W , b , x , y ) ∂ z L ∂ z L ∂ z L − 1 ∂ z L − 1 ∂ z L − 2 . . . ∂ z l + 1 ∂ z l \delta^l =\frac{\partial J(W,b,x,y)}{\partial z^l} = \frac{\partial J(W,b,x,y)}{\partial z^L}\frac{\partial z^L}{\partial z^{L-1}}\frac{\partial z^{L-1}}{\partial z^{L-2}}...\frac{\partial z^{l+1}}{\partial z^{l}} δl=zlJ(W,b,x,y)=zLJ(W,b,x,y)zL1zLzL2zL1...zlzl+1

如果不巧我们的样本导致每一层 ∂ z l + 1 ∂ z l \frac{\partial z^{l+1}}{\partial z^{l}} zlzl+1的都小于1,则随着反向传播算法的进行,我们的 δ l \delta^l δl会随着层数越来越小,甚至接近越0,导致梯度几乎消失,进而导致前面的隐藏层的 W , b W,b W,b参数随着迭代的进行,几乎没有大的改变,更谈不上收敛了。这个问题目前没有完美的解决办法。

而对于梯度爆炸,则一般可以通过调整我们DNN模型中的初始化参数得以解决

对于无法完美解决的梯度消失问题,目前有很多研究,一个可能部分解决梯度消失问题的办法是使用ReLURectified Linear Unit)激活函数,ReLU在卷积神经网络CNN中得到了广泛的应用,在CNN中梯度消失似乎不再是问题。那么它是什么样子呢?其实很简单,比我们前面提到的所有激活函数都简单,表达式为:
σ ( z ) = m a x ( 0 , z ) \sigma(z) = max(0,z) σ(z)=max(0,z)

也就是说大于等于0则不变,小于0则激活后为0。就这么一玩意就可以解决梯度消失?至少部分是的。具体的原因现在其实也没有从理论上得以证明。

关于梯度消失和爆炸。稍后我会整理出一个更详细的文档。

五、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}} tanh(z)=ez+ezezez

tanh激活函数和sigmoid激活函数的关系为:
t a n h ( z ) = 2 s i g m o i d ( 2 z ) − 1 tanh(z) = 2sigmoid(2z)-1 tanh(z)=2sigmoid(2z)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) softplus(z)=log(1+ez)

它的导数就是sigmoid函数。softplus的函数图像和ReLU有些类似。它出现的比ReLU早,可以视为ReLU的鼻祖。

CSDN图标

3)PReLU:从名字就可以看出它是ReLU的变种,特点是如果未激活值小于0,不是简单粗暴的直接变为0,而是进行一定幅度的缩小。如下图。当然,由于ReLU的成功,有很多的跟风者,有其他各种变种ReLU,这里就不多提了。

CSDN图标

六、DNN损失函数和激活函数小结

上面我们对DNN损失函数和激活函数做了详细的讨论,重要的点有:

1)如果使用sigmoid激活函数,则交叉熵损失函数一般肯定比均方差损失函数好。

2)如果是DNN用于分类,则一般在输出层使用softmax激活函数和对数似然损失函数。

3)ReLU激活函数对梯度消失问题有一定程度的解决,尤其是在CNN模型中。

参考文献:

深度神经网络(DNN)损失函数和激活函数的选择
转自上文。

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值