深度神经网络(DNN)损失函数和激活函数的选择

    在深度神经网络(DNN)反向传播算法(BP)中,咱们对DNN的前向反向传播算法的使用作了总结。里面使用的损失函数是均方差,而激活函数是Sigmoid。实际上DNN可使用的损失函数和激活函数很多。这些损失函数和激活函数如何选择呢?下面咱们就对DNN损失函数和激活函数的选择作一个总结。html

1. 均方差损失函数+Sigmoid激活函数的问题

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

    首先咱们回顾下Sigmoid激活函数的表达式为:$$\sigma(z) = \frac{1}{1+e^{-z}}$$网络

    $\sigma(z)$的函数图像以下:函数

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

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

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

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

    咱们来看看二分类时每一个样本的交叉熵损失函数的形式:$$J(W,b,a,y) = - [y lna + (1-y) ln(1 -a)]$$htm

    这个形式其实很熟悉,在逻辑回归原理小结中其实咱们就用到了相似的形式,只是当时咱们是用最大似然估计推导出来的,而这个损失函数的学名叫交叉熵。blog

    使用了交叉熵损失函数,就能解决Sigmoid函数导数变化大多数时候反向传播算法慢的问题吗?咱们来看看当使用交叉熵时,咱们输出层$\delta^L$的梯度状况。get

$$ \begin{align} \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{align}$$

    可见此时咱们的$\delta^l$梯度表达式里面已经没有了$\sigma ^{'}(z)$,做为一个特例,回顾一下咱们上一节均方差损失函数时在$\delta^L$梯度,$$\frac{\partial J(W,b,x,y)}{\partial z^L} = (a^L-y) \odot \sigma^{'}(z)$$

    对比二者在第L层的$\delta^L$梯度表达式,就能够看出,使用交叉熵,获得的的$\delta^l$梯度表达式没有了$\sigma^{'}(z)$,梯度为预测值和真实值的差距,这样求得的$W^l,b^l$的地图也不包含$\sigma^{'}(z)$,所以避免了反向传播收敛速度慢的问题。

    一般状况下,若是咱们使用了sigmoid激活函数,交叉熵损失函数确定比均方差损失函数好用。

3. 使用对数似然损失函数和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个神经元的激活函数定义为以下形式:$$a_i^L = \frac{e^{z_i^L}}{\sum\limits_{j=1}^{n_L}e^{z_j^L}}$$

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

    很容易看出,全部的$a_i^L$都是在(0,1) 之间的数字,而$\sum\limits_{j=1}^{n_L}e^{z_j^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) = - \sum\limits_ky_klna_k^L$$

    其中$y_k$的取值为0或者1,若是某一训练样本的输出为第i类。则$y_i=1$,其他的$j \neq i$都有$y_j=0$。因为每一个样本只属于一个类别,因此这个对数似然函数能够简化为:$$J(W,b,a^L,y) = -lna_i^L$$

    其中$i$即为训练样本真实的类别序号。

    可见损失函数只和真实类别对应的输出有关,这样假设真实类别是第i类,则其余不属于第i类序号对应的神经元的梯度导数直接为0。对于真实类别第i类,他对应的第j个w连接$w_{ij}^L$对应的梯度计算为:$$ \begin{align} \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} \end{align}$$

    一样的能够获得$b_i^L$的梯度表达式为:$$\frac{\partial J(W,b,a^L,y)}{\partial b_i^L} = a_i^L -1$$

    可见,梯度计算也很简洁,也没有第一节说的训练速度慢的问题。举个例子,假如咱们对于第2类的训练样本,经过前向算法计算的未激活输出为(1,5,3),则咱们获得softmax激活后的几率输出为:(0.015,0.866,0.117)。因为咱们的类别是第二类,则反向传播的梯度应该为:(0.015,0.866-1,0.117)。是否是很简单呢?

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

4. 梯度爆炸梯度消失与ReLU激活函数

    学习DNN,你们必定据说过梯度爆炸和梯度消失两个词。尤为是梯度消失,是限制DNN与深度学习的一个关键障碍,目前也没有彻底攻克。

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

    好比咱们在前一篇反向传播算法里面讲到了$\delta$的计算,能够表示为:$$\delta^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}})^T\frac{\partial J(W,b,x,y)}{\partial z^L}$$

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

    而对于梯度爆炸,则通常能够经过调整咱们DNN模型中的初始化参数得以解决。

    对于没法完美解决的梯度消失问题,目前有不少研究,一个可能部分解决梯度消失问题的办法是使用ReLU(Rectified Linear Unit)激活函数,ReLU在卷积神经网络CNN中获得了普遍的应用,在CNN中梯度消失彷佛再也不是问题。那么它是什么样子呢?其实很简单,比咱们前面提到的全部激活函数都简单,表达式为:$$\sigma(z) = max(0,z)$$

    也就是说大于等于0则不变,小于0则激活后为0。就这么一玩意就能够解决梯度消失?至少部分是的。具体的缘由如今其实也没有从理论上得以证实。这里我也就很少说了。

5. DNN其余激活函数

    除了上面提到了激活函数,DNN经常使用的激活函数还有:

    1) tanh:这个是sigmoid的变种,表达式为:$$tanh(z) = \frac{e^z-e^{-z}}{e^z+e^{-z}}$$

    tanh激活函数和sigmoid激活函数的关系为:$$tanh(z) = 2sigmoid(2z)-1$$

    tanh和sigmoid对比主要的特色是它的输出落在了[-1,1],这样输出能够进行标准化。同时tanh的曲线在较大时变得平坦的幅度没有sigmoid那么大,这样求梯度变化值有一些优点。固然,要说tanh必定比sigmoid好倒不必定,仍是要具体问题具体分析。

    2) softplus:这个其实就是sigmoid函数的原函数,表达式为:$$softplus(z) = log(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模型中。

    下一篇咱们讨论下DNN模型的正则化问题。

 

(欢迎转载,转载请注明出处。欢迎沟通交流: liujianping-ok@163.com) 

参考资料:

1) Neural Networks and Deep Learning by By Michael Nielsen

2) Deep Learning, book by Ian Goodfellow, Yoshua Bengio, and Aaron Courville

3) UFLDL Tutorial