目录
机器学习中欠拟合(underfitting)和过拟合(overfitting)是一个很常见的问题,在训练模型的过程中,我们通常希望达到以下两个目的:
(1). 训练的损失值尽可能地小。
(2). 训练的损失值与测试的损失值之间的差距尽可能地小。
当第一个目的没有达到时,则说明模型没有训练出很好的效果,模型对于判别数据的模式或特征的能力不强,则认为它是欠拟合的。
当第一个目的达到,第二个没有达到时,说明模型训练出了很好的效果,但是在测试集(或者说在未见过的数据集上)进行测试的损失值比较大,则说明模型在新的数据上的表现很差,此时可认为模型过度拟合训练的数据,而对于未参与训练的数据不具备很好的判别或拟合能力,这种情况下,模型是过拟合的。换句话说,就是模型的泛化(generalization)能力比较差。
欠拟合的特点:训练的损失值很大,且测试的损失值也很大。
过拟合的特点:训练的损失值足够小,而测试的损失值很大。
对于一个足够复杂度或足够参数量的模型或神经网络来说,随着训练的进行,会经历一个“欠拟合-适度拟合-过拟合”的过程。
对于一个复杂度不够的模型或参数量太少的神经网络来说,只有欠拟合。
以下通过python代码模型对欠拟合、拟合和过拟合进行一个简单的示例。
- %matplotlib inline # 在Jupyternote中运行的话,这条语句使得所生成的图直接嵌入在notebook中
- import matplotlib.pyplot as plt
- import numpy as np
-
- n_dots = 20
- x = np.linspace(0, 1, n_dots) # [0, 1] 之间创建 20 个点
- y = x**3 + 0.2*np.random.rand(n_dots) - 0.1; # 立方函数,加上一点随机噪声,再加上一个固定offset
-
- plt.figure(figsize=(18, 8), dpi=200)
- titles = ['Under Fitting', 'Fitting', 'Over Fitting', 'completely remembered']
- models = [None, None, None, None]
- for index, order in enumerate([1, 3, 10, 19]):
- plt.subplot(2, 2, index + 1)
- p = np.poly1d(np.polyfit(x, y, order))
- # 画出拟合出来的多项式所表达的曲线以及原始的点
- t = np.linspace(0, 1, 200)
- plt.plot(x, y, 'ro', t, p(t), '-', t, t**3, 'r--')
- models[index] = p
- plt.title(titles[index], fontsize=20)
以上代码中,首先生成一个立方函数序列,然后加上一点随机噪声,以及一个固定的offset。
然后,分别调用numpy中的polyfit函数,分别使用4种不同的阶数参数进行拟合。
最后将不同拟合结果绘图表示。
运行结果如下图所示:
由于我们已知原曲线是3阶函数(虽然加了一些噪声),所以用1阶线性拟合肯定是不够的,如左上角所示这是一个欠拟合的例子。
用3阶函数拟合有噪声的3阶数据序列恰好,如右上图所示,虽然由于有噪声的影响,拟合曲线没有经过所有的点,但是目测也可以看出拟合曲线基本上跟随了原序列的变化趋势。如果将数据生成中的噪声项去掉的话,比如说修改如下:
y = x**3 - 0.1 # + 0.2*np.random.rand(n_dots) - 0.1; #np.random.randn();
则会得到3阶拟合时的曲线如下所示(确实是完美的拟合):
左下和右下两个图是典型的过拟合的情况,其中右下角的图由于参数个数等于数据点数,拟合曲线精确地穿过了原数据序列的每一个数据点,通俗一点说就是,拟合模型记住了每一个数据点!
以上代码段还把拟合模型记录下来了,以下将这4个模型的参数打印出来看看从参数情况能不能看出一些过拟合和欠拟合的迹象来。
- for m in models:
- print('model coeffs: {0}'.format(m.coeffs))
-
- for m in models:
- print('model coeffs sum-of-square: {0}'.format(np.sum(np.square(m.coeffs))))
运行结果如下:
model coeffs: [ 0.90656683 -0.20994301] model coeffs: [ 0.34108161 1.09642447 -0.50202762 0.02950332] model coeffs: [ 8.62992809e+03 -4.27289357e+04 8.97569104e+04 -1.04142252e+05 7.27915680e+04 -3.13417328e+04 8.13859457e+03 -1.18078139e+03 7.90123217e+01 -1.30475103e+00 -7.67698480e-03] model coeffs: [-9.07254662e+09 7.08851859e+10 -2.44712640e+11 4.81111711e+11 -5.61668160e+11 3.14872097e+11 1.29251544e+11 -4.52528856e+11 4.89830880e+11 -3.35854989e+11 1.63641501e+11 -5.87384091e+10 1.56700458e+10 -3.08602201e+09 4.39544611e+08 -4.36052791e+07 2.82236748e+06 -1.05386082e+05 1.68389858e+03 -7.81964424e-03] model coeffs sum-of-square: 0.8659394920428187 model coeffs sum-of-square: 1.571385471713174 model coeffs sum-of-square: 27150703071.498024 model coeffs sum-of-square: 1.3157805372193948e+24
如上所示,关于欠拟合,从模型参数是看不出什么来的,但是从模型参数可以看出过拟合的迹象!过拟合体现在参数上就是会出现(绝对)值很大的参数。越大的参数意味着越复杂的模型,对于一个简单的数据(for example, generated from simple underlying function)用一个复杂的模型去拟合当然会产生过拟合的现象!
以上将所有参数的平方和打印出来,可以看出后面两种过拟合情况的参数平方和非常大! 事实上机器学习中常用的L1正则化和L2正则化就是通过对很大的参数(将参数绝对值和或者参数平方和计入损失函数中)进行惩罚来规避或者缓和过拟合效果的。
产生欠拟合的主要原因有两个:
1. 模型的容量或复杂度不够,对神经网络来说是参数量不够或网络太简单,没有很好的特征提取能力。通常为了避免模型过拟合,会添加正则化,当正则化惩罚太过,会导致模型的特征提取能力不足,反而导致欠拟合。
2. 训练数据量太少或训练迭代次数太少,导致模型没有学到足够多的特征。
相应地,解决欠拟合问题有以下方法:
a. 换个更复杂的模型,对神经网络来说,换个特征提取能力强或参数量更大的网络。
b. 减少正则化的惩罚力度。
c. 增加迭代次数或想办法弄到足够的训练数据或想办法从少量数据上学到足够的特征。如适度增大epoch,数据增强,预训练,迁移学习,小样本学习,无监督学习等。
过拟合产生的原因有以下几个:
1. 模型太复杂,对神经网络来说,参数太多或特征提取能力太强,模型学到(甚至是记住)了一些偶然的非本质的特征。
2. 数据分布太单一,例如训练用的所有鸟类都在笼子里,模型很容易把笼子当成识别鸟的特征。
3. 数据噪声太大或干扰信息太多,如人脸检测,训练图像的分辨率都是几百乘几百,而人脸只占了几十到几百个像素,此时背景太大,背景信息都属于干扰信息或噪声。
4. 训练迭代次数太多,对数据反复地训练也会让模型学到偶然的特征。
相应地,过拟合解决方法有以下几个:
a. 换一个复杂度低一点的模型或正则化,对神经网络来说,使用参数量少一点的网络
b. 使用或加大正则化强度
c. 使用不同分布的数据来训练。如数据增强,预训练等。
d. 使用图像裁剪等方法对图像进行预处理。
e. 及时地停止训练。如何判断什么时候该停止训练?使用K折交叉验证,若训练损失还在减少,而验证损失开始增加,则说明开始出现过拟合。
如上所述,如果我们的模型不够复杂容易导致欠拟合。而模型过于复杂又会导致虽然训练的结果很好但是没有泛化能力,不能在新数据上正确工作。在模型复杂性与泛化能力之间存在一个折中,下图所示为从欠拟合到拟合到过拟合分别在训练集和测试集上的性能的变化趋势,据此可以简明地判断折中点位于何处。
图 模型复杂度与训练准确度、测试准确度之间的权衡