目录
最近在做NLP相关任务的时候,训练神经网络模型的过程中,遇到过模型过拟合的情况,到底怎么解决过拟合,来提高模型的性能,不是特别的清晰。以前学习机器学习的时候,也讲到了模型的过拟合,对里面怎么来解决过拟合的方法也有一些模糊的印象,例如正则化、增加数据和集成学习等,对于原理和具体的步骤等细节也记不清楚了。因此很有必要对这一专题做一个总结,加深印象和记忆。当然网上也是有一大堆的相关博客,我写这篇博客的目的不求做出创新,只求做到全面,对自己以后跳槽有利。
一、过拟合的表现以及判定
1、模型过拟合的表现
过拟合(over-fitting),机器学习模型或者是深度学习模型在训练样本中表现得过于优越,导致在验证数据集以及测试数据集中表现不佳。也就是泛化误差比较大,泛化能力差。从方差和偏差的角度来说,过拟合也就是训练集上高方差,低偏差。为了更加生动形象的表示,我们看一些经典的图:
对比这几个图,发现图一的拟合并没有把大体的规律给拟合出来,这个就是欠拟合。图三则是拟合的太细致了,用的拟合函数太复杂了,在这些数据集上的效果很好,但是换到另外的一个数据集效果肯定可预见的不好。只有图二是最好的,把数据的规律拟合出来了,同时在更换数据集后,效果也不会很差。
仔细想想图片三中的模型,拟合函数肯定是一个高次函数,其参数个数肯定肯定比图二的要多,可以说图三的拟合函数比图二的要大,模型更加复杂。这也是过拟合的一个判断经验,模型是否太复杂。另外,针对图三,我们把一些高次变量对应的参数值变小,也就相当于把模型变简单了。这个角度上看,可以减小参数值,也就是一般模型过拟合,参数值整体比较大。从模型复杂性来讲,可以是:
1、模型的参数个数;2、模型的参数值的大小。个数越多,参数值越大,模型就越复杂。
2、模型过拟合的判定
针对模型过拟合这个问题,有没有什么方法来判定模型是否过拟合呢?其实一般都是依靠模型在训练集和验证集上的表现有一个大体的判断就行了。如果要有一个具体的方法,可以参考机器学中,学习曲线来判断模型是否过拟合。如下图:
也就是看训练集合验证集随着样本数量的增加,他们之间的差值变化。如果训练集和测试集的准确率都很低,那么说明模型欠拟合;如果训练集的准确率接近于1而验证集还相差甚远,说明模型典型的过拟合。当然具体的差多少这个没有明确的定义,个人看法是,如果训练集95%+,而验证集才60-80直接感觉都是有点过拟合的。
二、过拟合的原因
1、数据量太小
这个是很容易产生过拟合的一个原因。设想,我们有一组数据很好的吻合3次函数的规律,现在我们局部的拿出了很小一部分数据,用机器学习或者深度学习拟合出来的模型很大的可能性就是一个线性函数,在把这个线性函数用在测试集上,效果可想而知肯定很差了。
2、训练集和验证集分布不一致
训练集训练出一个适合训练集那样分布的数据集,当你把模型运用到一个不一样分布的数据集上,效果肯定大打折扣。这个是显而易见的。
3、模型复杂度太大
在选择模型算法的时候,首先就选定了一个复杂度很高的模型,然后数据的规律是很简单的,复杂的模型反而就不适用了。
4、数据质量很差
数据还有很多噪声,模型在学习的时候,肯定也会把噪声规律学习到,从而减小了具有一般性的规律。这个时候模型用来预测肯定效果也不好。
5、过度训练
这个是同第4个是相联系的,只要训练时间足够长,那么模型肯定就会吧一些噪声隐含的规律学习到,这个时候降低模型的性能是显而易见的。
三、过拟合的解决方案
针对过拟合的原因我们可以有针对性的来使用一些方法和技巧来减少过拟合。
1、模型层面
这里主要是减小模型的复杂度,主要是从模型包含的参数个数和参数值。
a、正则化
这里包含L1和L2范数,具体的区别去看相关的理论去了解,这里一般使用L1范数,使得模型拟合的参数大部分都为0,这样就可以说从参数值和参数个数的角度减少了模型的复杂度,从而降低了过拟合。
b、权值共享
这个方法常用于深度学习中,一般在网络中,某些层可能会使用同样的参数,那么这样就在参数个数上减小了——模型复杂度也随之降低
c、dropout
这个方法也很常见,在神经网络中以一定的概率使得神经元不工作。这种方法的本质上是没一个step中,使用的模型都是不一样的,并且模型参数在一定程度上也是减少了。
torch.nn.Dropout(0.5)
在pytorch中,这里的0.5的意思就是神经元不保留的概率,这个与tf框架不同。
d、Batch Normalization
这个批归一化处理层,是一个作用非常大的。我自己在写网络中也尝试在使用这个BN层,其作用是:使得每一层的数据分布不变,做归一化处理,加快了模型的收敛速度,避免梯度消失、提高准确率。反正就是优点很多!
e、权值衰减
权值衰减——weight_decay,简单的理解就是乘在正则项的前面的系数,目的是为了使得权值衰减到很小的值,接近如0。一般在深度学习好中,pytorch的提供的优化器都可以设置的:
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9,weight_decay=1e-5)
2、数据层面
a、保证数据集分布一致性
在切分数据集的时候要保证分布一致性。可以使用sklearn包中,model_selection相关train_text_split来实现数据集切割后分布的一致性。
b、增加数据集的规模
最好的是人工标注高质量的数据,但是成本非常高;可以采用一定的数据增强策略,来实现数据集的扩充。注意的是这里可能会引入一定的噪声,噪声也会影响模型的性能的,要注意利弊的取舍。另外CV和NLP的数据增强是不一样的,NLP数据增强更难。
3、训练层面
这个训练就要看经验了,模型需要到达什么样的一个基线标准。然后参考这个标准对模型实施early-stopping。神经网络的训练过程中我们会初始化一组较小的权值参数,随着模型的训练,这些权值也变得越来越大了。为了减小过拟合的影响,就有可能需要早停止了。我本人没有使用过early-stopping,一般都是设置10个epoch然后看效果来考虑时候增加epochs的次数。
4、其他
集成学习——也就是一个均值的思想,通过集成的思想来减弱过拟合的影响。
参考文章: