机器学习实战之训练模型–泛化和学习曲线

作者: admin 分类: 应用            发布时间: 08月05日 10:25

本文介绍了多项式回归的过拟合和欠拟合、泛化能力和学习曲线,通过对比测试集和验证集的MSE的学习曲线,判断拟合是过拟合还是欠拟合,寻找到最合适的模型。

高阶多项式回归对训练数据的拟合,可能会比简单线性回归要好。当然太过高阶(如阶数200)的多项式回归模型可能会严重的过度拟合训练数据,而线性模型则拟合不足。
我们用一个例子来看看。

1.模块引用

首先我们引入本文需要用到的所有模块

import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler

2.生成我们的测试数据

我们用\(y=0.5x^2+2x+2+ε\)生成我们的测试数据,并在坐标系中画出来。

X = 10 * np.random.rand(100,1) - 5
X = np.sort(X,axis=0) ##为了方便作图,X和y的每一个组合的顺序没有关系的
y = 0.5* X ** 2 + 2 * X + 2 + np.random.randn(100,1)
plt.scatter(X,y,c='green', alpha=0.6)

3.欠拟合

如果我们用线性模型进行拟合,明显拟合不足。

lin_reg = LinearRegression()
lin_reg.fit(X, y)
y_predict = lin_reg.predict(X)
plt.scatter(X,y, c='green', alpha=0.6)
plt.plot(X, y_predict, color='blue',alpha=0.6)
plt.show()

4.过拟合

如果我们用非常高的阶数进行拟合,拟合会非常好。但如果有新的数据进来,预测会非常差。

poly_reg = Pipeline([
("poly", PolynomialFeatures(degree=200)),
("std_scaler", StandardScaler()),
("lin_reg", LinearRegression())
])

poly_reg.fit(X, y)
y_predict = poly_reg.predict(X)
plt.scatter(X,y, c='green', alpha=0.5)

plt.plot(X, y_predict, color='r',alpha=0.6)

plt.show()

上面的情况欠拟合和过拟合我们如何避免的?上述的例子是在我们知道生成的数据的二次函数的情况下做的,实际情况我们一般不知道数据的模式,如何确定模型的复杂度呢?如何才能判断模型是否过拟合或者欠拟合呢?

这篇文章《Python大规模建模的特征值选择和性能评估方法详解》我们使用了交叉验证来评估模型的泛化能力,如果模型在训练集上表现良好,而在交叉验证上的泛化表现很差,那么模型就是过度拟合。如果在二者上的表现都不佳,那就是拟合不足,这是判断模型太简单还是太复杂的一种方法。

5.泛化能力

我们把数据分成两部分,一部分用于训练,寻找到一个适合的拟合模型,另一部分用于验证,用于判定拟合是过拟合还是欠拟合。

先将数据划分为80%的训练集和20%的验证集。

X_train,X_val,y_train,y_val = train_test_split(X,y,test_size=0.2) #80%和20%划分X和y

我们用线性回归进行拟合

lin_reg = LinearRegression()
X_train,X_val,y_train,y_val = train_test_split(X,y,test_size=0.2) #80%和20%划分X和y
lin_reg.fit(X_train, y_train) #用训练集进行拟合训练
y_predict = lin_reg.predict(X_val) #用验证集的X来预测y
mean_squared_error(y_predict,y_val) #用预测得到的y和验证集的y计算均方误差

用训练集进行拟合训练,用验证集的X来预测y,用预测得到的y和验证集的y计算均方误差,结果如下:
MSE=19.22

接下来我们用100阶的多项式回归进行拟合。

poly_reg = Pipeline([
("poly", PolynomialFeatures(degree=100)),
("std_scaler", StandardScaler()),
("lin_reg", LinearRegression())
])

poly_reg.fit(X_train, y_train)
y_predict = poly_reg.predict(X_val)
mean_squared_error(y_predict,y_val)

结果如下:
MSE=39273456364906.516

上述两个拟合虽然差别很大,但MSE都比较大,不能满足我们的应用需要。
我们看看2阶多项式回归(因为我们的数据就是用二阶生成的)

poly_reg = Pipeline([
("poly", PolynomialFeatures(degree=2)),
("std_scaler", StandardScaler()),
("lin_reg", LinearRegression())
])

poly_reg.fit(X_train, y_train)
y_predict = poly_reg.predict(X_val)
mean_squared_error(y_predict,y_val)

结果如下:
MSE=1.1038
拟合得非常好。

6.学习曲线

我们看看不同的训练集的大小来进行训练,得到的结果用验证集来计算均方误差,看看均方误差的趋势是怎么样的,这就是学习曲线。

首先我们定义一个函数。

train_errors,val_errors = [],[]
def plot_learning_curves(model,X,y):
  X_train,X_val,y_train,y_val = train_test_split(X,y,test_size=0.2)
  for m in range(1,X_train.size):#循环1-79
    model.fit(X_train[:m],y_train[:m])
    y_train_predict = model.predict(X_train[:m])
    y_val_predict = model.predict(X_val)
    train_errors.append(mean_squared_error(y_train_predict,y_train[:m]))
    val_errors.append(mean_squared_error(y_val_predict,y_val))
plt.plot(range(1,X_train.size),np.sqrt(train_errors),label='train',c='green')
plt.plot(range(1,X_train.size),np.sqrt(val_errors),label='val',c='red')

上面的函数是把数据分为80%的训练集和20%的验证集(我们生成的数据是100个,两个集合分别为80个和20个。
从验证集中提取1个、2个、3个…80个记录进行训练,得到的模型用训练集(和样本一样的记录个数)和验证集分别进行计算MSE,把不同的样本数量和MSE进行画图对比。

接下来我们用线性回归进行模拟。

train_errors,val_errors = [],[]
lin_reg = LinearRegression()
plot_learning_curves(lin_reg,X,y)

得到下面的图,红色为验证集的MSE,绿色为训练集的MSE。

当训练样本只有一个的时候,用训练集MSE解释,模型完美拟合,但用验证集的MSE解释,完全不能接受的MSE大小,达到15左右。

随着新的样本进入训练,训练集的MSE开始上升,但训练样本多了后,MSE稳定在一定的水平。验证集的MSE开始下降,稳定到一定的水平。
这类型的曲线是典型的拟合不足

我们用100阶多项式回归进行拟合看看效果。

train_errors,val_errors = [],[]
poly_reg = Pipeline([
("poly", PolynomialFeatures(degree=100)),
("std_scaler", StandardScaler()),
("lin_reg", LinearRegression())
])

plot_learning_curves(poly_reg,X,y)

结果如下:

注意纵坐标单位非常大看不出来效果,导致0和10000的点基本重合。我们查看验证集的MSE看看。

出现非常大的数,我们无法和训练集的个位数的MSE在同一张图上表示。我们仅仅对验证集的MSE进行对数计算,减少它的值,以方便在同一张图表示。

pd_train_errors = pd.DataFrame(np.sqrt(train_errors))
pd_val_errors = np.log10(pd.DataFrame(np.sqrt(val_errors)))

plt.plot(range(1,80),pd_train_errors,linewidth=1,label='train',c='green', alpha=0.6)
plt.plot(range(1,80),pd_val_errors,linewidth=2,label='val',c='red', alpha=0.6)
![Alt text](./1564938218823.png)

可以很清楚的看出来,这个模型用训练集来计算MSE基本上在0-1之间,拟合非常好,但在验证集的拟合就非常差了,MSE取了10为底数的对数后的值也达到了5-10之间。这种情况就是过拟合了。

我们看看阶数为2的多项式回归的情况

train_errors,val_errors = [],[]
poly_reg = Pipeline([
("poly", PolynomialFeatures(degree=2)),
("std_scaler", StandardScaler()),
("lin_reg", LinearRegression())
])
plot_learning_curves(poly_reg,X,y)

不管是训练集合还是测试集的MSE都是非常小的(注意纵坐标单位)。当然啦,我们的数据就是2阶模拟出来的数据。

总结

本文介绍了多项式回归的过拟合和欠拟合及其泛化能力和学习曲线,可以通过对比测试集和验证集的MSE的学习曲线,判断拟合是否是过拟合还是欠拟合,寻找到最合适的模型。

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注