3.5 验证曲线:绘制分数以评估模型¶
每一个估计器都有其优势和劣势。它的泛化误差可以分解为偏差,方差和噪声。估计器的偏差是不同训练集的平均误差。估计器的方差表示对不同训练集,模型的敏感度。噪声是数据的特质。
在下图中,可以看见一个函数和函数中的一些噪声数据。使用三种不同的估计器来拟合函数:带有自由度为1,4和15的二项式特征的线性回归。第一个估计器最多只能提供一个样本与真实函数间不好的拟合,因为该函数太过简单,第二个估计器估计的很好,最后一个估计器估计训练数据很好,但是不能拟合真实的函数,例如对各种训练数据敏感(高方差)。
偏差和方差是估计器固有的特质,我们经常选择学习算法和超参数,以使偏差和方差都尽可能小(参见Bias-variance dilemma)。另一个减少模型方差的方法是使用更多的训练数据。但是,如果真实函数太过复杂才能估计出一个低方差的估计器,你只能收集更多训练集数据。
在一个简单的一维问题中,可以很容易看见估计器是否收到偏差或者方差的影响。但是,在高维空间中,模型很难被可视化。因为这个原因,使用工具来描述就很有用。
例子:
3.5.1 验证曲线
需要一个评分函数(详见 度量和评分:量化预测的质量)以验证一个模型,例如分类器的准确率。选择一个估计器的多个超参数的好方法当然是网格搜索或者相类似的方法(详见 调整估计器的超参数)通过选择超参数以使在验证集或者多个验证集的分数最大化。需要注意的是:如果基于验证分数优化超参数,验证分数是有偏的并且不是好的泛化估计。为了得到一个好的泛化估计,可以通过计算在另一个测试集上的分数。
但是,有时绘制在训练集上单个参数的影响曲线也是有意义的,并且验证分数可以找到对于某些超参数,估计器是过拟合还是欠拟合。
validation_score函数可以应用在该例子中:
>>> import numpy as np
>>> from sklearn.model_selection import validation_curve
>>> from sklearn.datasets import load_iris
>>> from sklearn.linear_model import Ridge
>>> np.random.seed(0)
>>> X, y = load_iris(return_X_y=True)
>>> indices = np.arange(y.shape[0])
>>> np.random.shuffle(indices)
>>> X, y = X[indices], y[indices]
>>> train_scores, valid_scores = validation_curve(Ridge(), X, y, "alpha",
... np.logspace(-7, 3, 3),
... cv=5)
>>> train_scores
array([[0.93..., 0.94..., 0.92..., 0.91..., 0.92...],
[0.93..., 0.94..., 0.92..., 0.91..., 0.92...],
[0.51..., 0.52..., 0.49..., 0.47..., 0.49...]])
>>> valid_scores
array([[0.90..., 0.84..., 0.94..., 0.96..., 0.93...],
[0.90..., 0.84..., 0.94..., 0.96..., 0.93...],
[0.46..., 0.25..., 0.50..., 0.49..., 0.52...]])
如果训练分数和验证分数都很低,估计器就是欠拟合。如果训练集的分数很高,并且验证集的分数很低,估计器就是过拟合。除此以外,估计器的效果就很好。一个较低的训练分数和一个较高的验证分数通常是不可能的。所有三个例子中可以在下图中发现,通过指定不同的支持向量机(SVM)的参数在数字集上的结果。
3.5.2 学习曲线
一个学习曲线表现的是在不同的训练样本个数下估计器的验证集和训练集得分。它是一个用于发现增加训练集数据可以获得多大收益和是否估计器会遭受更多的方差和偏差。考虑下面的例子中,绘制了朴素贝叶斯分类器和支持向量机的学习曲线。
对于朴素贝叶斯,验证分数和训练分数都向某一个分数收敛,随着训练集大小的增加,分数下降的很低。因次,并不会从较大的数据集中获益很多。
与之相对比,小数据量的数据,支持向量机的训练分数比验证分数高很多。添加更多的数据给训练样本很可能会提高模型的泛化能力。
通过使用函数learning_curve绘制学习曲线所需的值(样本被用的个数,训练集的平均分数和验证集的平均分数):
>>> from sklearn.model_selection import learning_curve
>>> from sklearn.svm import SVC
>>> train_sizes, train_scores, valid_scores = learning_curve(
... SVC(kernel='linear'), X, y, train_sizes=[50, 80, 110], cv=5)
>>> train_sizes
array([ 50, 80, 110])
>>> train_scores
array([[0.98..., 0.98 , 0.98..., 0.98..., 0.98...],
[0.98..., 1. , 0.98..., 0.98..., 0.98...],
[0.98..., 1. , 0.98..., 0.98..., 0.99...]])
>>> valid_scores
array([[1. , 0.93..., 1. , 1. , 0.96...],
[1. , 0.96..., 1. , 1. , 0.96...],
[1. , 0.96..., 1. , 1. , 0.96...]])