上一节说到了模型欠拟合和过拟合的概念知道,过拟合会导致模型的泛化能力很差,增加新的数据的话并不能预测较为准确的值,甚至错的离谱。那么我们怎么知道一个模型泛化能力的好坏呢?很容易想到的就是使用训练集测试集分离,我们用训练集进行模型的fit,然后拿测试集进行预测,最后对比测试集的特征数据预测出来的结果 对比测试集合的真实结果,就能知道这个模型预测的准确性和泛化能力。
除此之外也可以通过学习曲线取看一个模型的泛化能力,学习曲线随着样本的增多,算法训练出的模型的变现能力。让你能够清楚的知道使用train_test_split的意义,测试数据集的意义。 如果只有训练数据集,我们fit出来的模型进行测试的时候可能很好,出现可过拟合也不知到。通过测试集可以判断模型的好坏,不好的话,调整超参数等从新训练模型。但是也有问题,也有可能拟合测试数据集,即我们针对了测试数据集进行调参,一直到测试数据集达到好的效果,这就有可能出现这个模型效果好是只针对这个测试数据集。那么怎么解决呢?解决办法就是:验证数据集。验证数据集给训练数据集fit好的模型,然后调参等是模型达到较好效果,这样的话测试数据集就整个过程就没接触到模型的调整,最后进行预测就不会出现拟合测试数据集的情况,拿到泛化能力更好的模型。
首先生成这样一个数据
np.random.seed(888)
x=np.random.uniform(-3.0,3.0,size=100) #生成x特征 -3到3 100个
X=x.reshape(-1,1)#将x编程100行1列的矩阵
y=0.5*x**2+x+2+np.random.normal(0,1,size=100)#模拟的是标记y 对应的是x的二次函数
plt.scatter(x,y)
绘制学习曲线,得到模型在训练数据集和测试数据集上的表现,训练数据集和测试数据集的性能变化。
from sklearn.model_selection import train_test_split#导入测试集训练集分隔函数
X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=10)#分割出训练集和测试集
from sklearn.metrics import mean_squared_error#导入MSE函数
train_score=[]#及记录使用训练集数据的mse
test_score=[]#记录使用测试集的mse
#因为有75个样本 需要记录他们的学习过程
for i in range(1,76):
lin_reg4=LinearRegression()#线性回归函数
lin_reg4.fit(X_train[:i],y_train[:i])#拟合0到i的训练集
#记录0到i训练数据的预测结果
y_train_pre=lin_reg4.predict(X_train[:i])
#记录0到i使用训练数据集得到的mse
train_score.append(mean_squared_error(y_train[:i],y_train_pre))
#使用训练数据集0到i拟合后的模型,记录使用测试数据预测的结果
y_test_pre =lin_reg4.predict(X_test)
#测试数据集mse
test_score.append(mean_squared_error(y_test,y_test_pre))
#画出的是均方根误差
plt.plot([i for i in range(1,76)],np.sqrt(train_score),label="train")
plt.plot([i for i in range(1,76)],np.sqrt(test_score),label="test")
plt.legend()
plt.show()
观察图可得,趋势上,训练数据集的误差是逐渐生高的,数据越多越难拟合。测试数据集的误差是下滑的曲线,使用非常少的样本训练的时候训练出的模型使用测试集误差很大,随着样本量的增多测试的误差就会逐渐减小,到达一定程度区域不变。总体趋于1.7,误差比较大,属于欠拟合情况
将以上求学习曲线方式封装成函数,只需要传入机器学习算法,即可得到学习曲线。
#封装成函数
def plot_learning_curve(algo,X_train,X_test,y_train,y_test):
train_score=[]
test_score=[]
for i in range(1,len(X_train)+1):
algo.fit(X_train[:i],y_train[:i])
y_train_pre=algo.predict(X_train[:i])
train_score.append(mean_squared_error(y_train[:i],y_train_pre))
y_test_pre =algo.predict(X_test)
test_score.append(mean_squared_error(y_test,y_test_pre))
plt.plot([i for i in range(1,len(X_train)+1)],
np.sqrt(train_score),label="train")
plt.plot([i for i in range(1,len(X_train)+1)],
np.sqrt(test_score),label="test")
plt.legend()
plt.axis([0,len(X_train)+1,0,4])
plt.show()
plot_learning_curve(LinearRegression(),X_train,X_test,y_train,y_test)
利用以上函数 求多项式回归的学习曲线首先构建Pipeline,使用Pipeline进行多项式回归预测,Pipeline直接封装了将特征扩展多项式,数据归一化,构建线性回归,使用直接三部自动调用,sklearn并没有多项式回归的包。
degree=2的情况。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
def PolynomialRegression(degree):
return Pipeline([#构建Pipeline
("poly",PolynomialFeatures(degree=degree)),#构建PolynomialFeatures
("std_scaler",StandardScaler()),#构建归一化StandardScaler
("lin_reg",LinearRegression())#构建线性回归LinearRegression
])
#
poly_reg2=PolynomialRegression(degree=2)
plot_learning_curve(poly_reg2,X_train,X_test,y_train,y_test)
对比观察线性回归学习曲线的区别,前者误差最终总体区趋于1.7左右,多项式回归总体趋于1.0左右。二阶的多项式拟合的结果比较好。这既是正好拟合的情况
degree=20的情况
poly_reg20=PolynomialRegression(degree=20)
plot_learning_curve(poly_reg10,X_train,X_test,y_train,y_test)
整体趋势到1.0, 稳定是间距还是比较大, 在训练数据上表现好,但是测试数据集上表现很差,说明出现了过拟合的情况。
总结:
过拟合验证数据集,验证数据集出现极端数据,解决方法 交叉验证: