我们可以把和作为纵坐标,画出与训练数据集m的大小关系,这就是学习曲线。通过学习曲线,可以直观地观察到模型的准确性与训练数据集大小的关系。
如果数据集的大小为m,则通过下面的流程即可画出学习曲线:
- 把数据集分成训练数据集和交叉验证数据集。
- 取训练数据集的20%作为训练样本,训练出模型参数。
- 使用交叉验证数据集来计算训练出来的模型的准确性。
- 以训练数据集的准确性,交叉验证的准确性作为纵坐标,训练数据集个数作为横坐标,在坐标轴上画出上述步骤计算出来的模型准确性。
- 训练数据集增加10%,跳到步骤3继续执行,直到训练数据集大小为100%为止。
学习曲线要表达的内容是,当训练数据集增加时,模型对训练数据集拟合的准确性以及对交叉验证数据集预测的准确性的变化规律。
1.实例:画出学习曲线
通过一个例子来看看在scikit-learn里如何画出模型的学习曲线,从而判断模型的准确性及优化方向。
我们还使用之前举过的例子,生成一个在附近波动的点来作为训练样本,不过这次要多生成一些点,因为要考虑当训练样本数据增加的时候,模型的准确性是怎么变化的。
import numpy as np
n_dots = 200
X = np.linspace(0,1,n_dots)
y = np.sqrt(X)+0.2*np.random.rand(n_dots)-0.1
# 因为sklearn的接口里,需要用到 n_sample x n_feature 的矩阵
# 所以需要转化为 200 x 1 的矩阵
X = X.reshape(-1,1)
y = y.reshape(-1,1)
(1)需要构造一个多项式模型。在scikit-learn里,需要使用Pipeline来构造多项式模型,Pipeline的意思是流水线,即这个流水线里可以包含多个数据处理模型,前一个模型处理完,转到下一个模型处理。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
def polynomial_model(degree=1):
polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)
linear_regression = LinearRegression()
# 这是一个流水线,先增加多项式阶数,然后在用线性回归算法来拟合数据
pipeline = Pipeline([("polynomial_features",polynomial_features),
("linear_regression",linear_regression)])
return pipeline
polynomial_model()函数生成一个多项式模型,其中参数degree表示多项式的阶数,比如polynomial_model(3)将生成一个3阶多项式的模型。
在scikit-learn里面,我们不用自己去实现学习曲线算法,直接使用sklearn.model_selection.learning_curve()函数来画出学习曲线,它会自动把训练样本的数量按照预定的规则逐渐增加,然后画出不同训练样本数量的变化规则,比如train_sizes=np.linspace(0.1,1.0,5)表示把训练样本数量从0.1~1分成五等分,生成[0.1,0.325,0.55,0.775,1]的序列,从序列中取出训练样本数量百分比,逐个计算在当前训练样本数量情况下训练出来的模型准确性。
from sklearn.model_selection import learning_curve
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel("Training examples")
plt.ylabel("Score")
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o--', color="r",
label="Training score")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
label="Cross-validation score")
plt.legend(loc="best")
return plt
这个函数实现的功能就是画出模型的学习曲线。其中有个细节需要注意,当计算模型的准确性时,是随机从数据集中分配出训练样本和交叉验证样本,这样会导致数据分布不均匀。即同样训练样本数量的模型,由于随机分配,导致每次计算出来的准确性都不一样。为了解决这个问题,我们在计算模型准确性时,多次计算,并求准确性的平均值和方差。上述代码中plt.fill_between()函数会把模型准确性的平均值的上下方差的空间里用颜色填充。然后用plt.plot()函数画出模型准确性的平均值。上述函数画出了训练样本的准确性,也画出来交叉验证样本的准确性。
(2)使用polynomial_model()函数构造出3个模型,分别是一阶多项式、三姐多项式和十阶多项式,分别画出这3个模型的学习曲线。
# 为了让学习曲线更平滑,交叉验证数据集的得分计算 10 次,每次都重新选中 20% 的数据计算一遍
cv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)
titles = ['Learning Curves (Under Fitting)',
'Learning Curves',
'Learning Curves (Over Fitting)']
degrees = [1, 3, 10]
plt.figure(figsize=(18, 4))
for i in range(len(degrees)):
plt.subplot(1, 3, i + 1)
plot_learning_curve(polynomial_model(degrees[i]), titles[i], X, y, ylim=(0.75, 1.01), cv=cv)
plt.show()
最终得到的学习曲线如下图所示:
左图:一阶多项式,欠拟合;中图:三阶多项式,较好地拟合了数据集;右图:十阶多项式,过拟合。虚线:针对训练数据集计算出来的分数,即针对训练数据集拟合的准确性,实线:针对交叉验证数据集计算出来的分数,即针对交叉验证数据集预测的准确性。
从左图我们可以观察到,当模型欠拟合(High Bias,Under Fitting)时,随着训练数据集的增加,交叉验证数据集的准确性(实线)逐渐增大,逐渐和训练数据集的准确性(虚线)靠近,但其总体水平比较低,收敛在0.88左右。其训练数据集的准确性也比较低,收敛在0.90左右。这就是欠拟合的表现。从这个关系可以看出来,当发生高偏差时,增加训练样本数量不会对算法准确性有较大的改善。
从右图我们可以观察到,当模型过拟合(High Variance,Over Fitting)时,随着训练数据集的增加,交叉验证数据集的准确性(实线)也在增加,逐渐和训练数据集的准确性(虚线)靠近,但两者之间的间隙比较大。训练数据集的准确性很高,收敛在0.95左右,是三者中最高的,但交叉验证数据集的准确性值却较低,最终收敛在0.91左右。
中图,我们选择的三阶多项式较好地拟合了数据,最终训练数据集的准确性(虚线)和交叉验证数据集的准确性(实线)靠得很近,最终交叉验证数据集收敛在0.93附近,训练数据集的准确性收敛在0.94附近。3个模型对比,这个模型的准确性最好。
当需要改进学习算法时,可以画出学习曲线,以便判断算法是高偏差还是高方差问题。我们可以修改一些参数,观察学习曲线的变化规则。学习曲线是诊断模型算法准确性的一个非常重要的工具。
2.过拟合和欠拟合的特征
到此,我们可以总结过拟合和欠拟合的特点如下:
- 过拟合:模型对训练数据集的准确性比较高,其成本比较低;对交叉验证数据集的准确性比较低,其成本比较高。
- 欠拟合:模型对训练数据集的准确性比较低,其成本比较高;对交叉验证数据集的准确性也比较低,其成本比较高。
一个好的机器学习算法应该是对训练数据集准确性高、成本低,即较准确地拟合数据,同时对交叉验证数据集准确性高、成本低、误差小,即对未知数据有良好的预测性。