原文:https://blog.ailemon.me/2018/04/09/deep-learning-the-ways-to-solve-underfitting/
我最近做深度学习在连续中文语音识别方向的应用的时候,根据一些论文和网上一些公开代码和模型结构,设计了一个神经网络的模型。但是在训练的时候,就首先遇到了很让人头疼的欠拟合问题。神经网络欠拟合的特征是,训练了很长时间,但是在训练集上,loss值仍然很大甚至与初始值没有太大区别,而且精确度也很低,几乎接近于0,在测试集上亦如此。且先不管模型结构配置的优劣,就欠拟合问题来说,需要从如下方面来着手。
就我目前所遇到的来说,神经网络的欠拟合大致分为两种情况,一种是神经网络的拟合能力不足,一种是网络配置的问题。先说说欠拟合的特征吧,即如何判断当前是不是欠拟合,以及到底是上述我说的哪一种情况。
我们先用两张图来说明一下吧:
如果我们训练神经网络的时候,在训练集上得到了这样的loss:
以及这样的Accurancy:
那么,欠拟合基本上是没得跑了。因为,一开始就有一个较大的loss,以及几乎为0的精度,然后不论怎么训练,比如训练了一整天,loss迟迟不下降,精度迟迟不上升,即神经网络总是不能很好地拟合训练数据,那么这就是欠拟合了。
判断神经网络的拟合能力是否足够
这时候,有一个小技巧,那就是,让神经网络在每次训练时,只迭代同样的数据,甚至每一个batch里面也是完全相同一模一样的数据,再来看看loss值和accurancy值的变化。如果发现,这时候,神经网络的Loss开始下降,accurancy也开始上升了,并且在训练了一段时间后神经网络能够正确地计算出所训练样本经过神经网络的输出值了,那么这种情况属于神经网络拟合能力不足。对于大量的数据样本,神经网络无法去拟合全部数据,只能拟合大量样本的整体特征,或者少数样本的具体特征。此时,需要做的很简单,只需要增加深度,也就是增加神经网络的层数就可以了。也可以增加神经网络的宽度,将每一层的神经单元数量增加,但是同等情况下,效果明显不如增加层数,而且要想达到较好的效果,需要增加的神经元数远超过增加一层增加的神经元数。深度深比宽度宽的模型更优这一点,是大家普遍认同的。
那么如果loss和accurancy仍然如此呢?不论怎么增加神经网络的层数,用哪怕只有一条数据去训练拟合,神经网络就是“岿然不动”,loss的值“屹立不倒”,那么这就与神经网络的拟合能力无关了,就要考虑一下其他的因素了。这也是我遇到的那个问题的根源所在。
寻找最优的权重初始化方案
首先要说的就是权重的初始化方案。神经网络在训练之前,我们需要给其赋予一个初值,但是如何选择这个初始值,这是个问题。神经网络有大概如下的几种初始化方案:
- 全零初始化 Zeros
- 全1初始化 Ones
- 初始化为固定值value Constant
- 随机正态分布初始化 RandomNormal
- 随机均匀分布初始化 RandomUniform
- 截尾高斯分布初始化 TruncatedNormal
- VarianceScaling
- 用随机正交矩阵初始化Orthogonal
- 使用单位矩阵初始化 Identiy
- LeCun均匀分布初始化方法 lecun_uniform
- LeCun正态分布初始化方法 lecun_normal
- Glorot正态分布初始化方法 glorot_normal
- Glorot均匀分布初始化 glorot_uniform
- He正态分布初始化 he_normal
- LeCun均匀分布初始化 he_uniform
具体的初始化方案的原理本文不再赘述,这么多初始化方案,其实按照大类来分,主要就三种:均匀分布、正太分布和相同固定值。
全0的初始化,一般只会被用于逻辑斯蒂回归之类的这种二分类问题上,最多是浅层的神经网络上。全为1或者某个其他相同的值的方案则很少见。因为这种初始化方案,会使得网络处于对称状态,导致的结果就是,每个神经元都具有相同的输出,然后在反向传播计算梯度时,会得到一个同一个梯度值,并且进行着同样的参数更新,这是我们不希望看到的。
在我用的基于tensorflow的神经网络框架keras中,神经网络默认初始化全部被初始化为了glorot_uniform,也就是一种均值为0,以0为中心的对称区间均匀分布的随机数。在我的模型上,这种接近于0的均匀分布会导致什么问题呢?那就是梯度的消失,使得训练时的loss难以收敛,就出现了上面那两张图的情况。这种初始化方案,训练速度不仅仅慢,而且结果也不好。可以考虑一些其他的方案试一试,比如he_normal和xavier normal等。所以,初始化就跟黑科技一样,用对了,超参数都不用调,没用对,跑出来的结果跟模型有bug一样,不忍直视。
使用适当的激活函数
不仅仅是初始化,在神经网络的激活函数方面的选取,也不是随意的。比如,卷积神经网络中,卷积层的输出,需要使用的激活函数一般为ReLu,循环神经网络中的循环层使用的激活函数一般为tanh,或者ReLu,全连接层一般也是多用ReLu来激活,只有在神经网络的输出层,使用全连接层来分类的情况下,才会使用softmax这种激活函数。而在各种机器学习入门教程里面最常讲到的sigmoid函数,想都不要想它,它已经不适用于深度学习了,哪怕是作为其改进版的softmax函数,也仅仅是在输出层才使用。
选择合适的优化器和学习速率
神经网络训练的优化器也是需要考虑的一大因素。神经网络的优化器其实有很多种,每种都有其不同的特点,我们最耳熟能详的就是梯度下降法,对应着有批量梯度下降,随机梯度下降。这种优化方法其实不赖,尤其是在神经网络即将最终收敛的时候,使用一个合适的学习速率使得其最终下降到尽可能低的点上。但是随机梯度下降有着明显的缺点,尤其是跟Momentum、Adagrad、Adadelta等其他优化器对比之后。
而且,这里我最推荐Adadelta,在一开始的效果很显著,只有在最终收敛之前可能不如SGD,可以选择这时候再换成SGD就行了。而且Adadelta不需要太多关注学习率,因为其训练的原理已经不怎么跟学习率有关了。
不过,如果是SGD的话,在排除了以上情况之后,当神经网络仍欠拟合或者loss不再下降时,可以将学习率调小一些试试。
一般来说,经过以上的组合拳式的优化调参,欠拟合问题基本上都能够得到解决,哪怕最后仍然准确率不高,但肯定是拟合了。欠拟合问题解决之后,接下来要解决的就是过拟合问题了。