1. 评估模型
一个模型在训练集上表现特别好,误差很小,一定是个好的模型吗? 不一定,因为创建模型的目的是能够预测新的数据。
在部署模型之前,如何测试一下模型的性能呢? 一般有两个办法:
- 将原始数据集拆分成训练集(Training)和测试集(Test)。 a. 使用训练数据拟合模型参数 b. 使用测试数据评估模型在新数据上的性能
- 使用 error 函数评估模型
1.1 拆分数据
建议数据的20%-40%作为测试集,使用sklearn的train_test_split函数拆分数据。
将18个数据拆分成训练集12个数据,测试集6个数据。训练数据和测试数据如图1所示。
2.2 线性回归模型的误差计算
在评估线性模型时,使用目标值和预测值之间的平方差进行平均,计算公式如下图。
2.3 比较测试数据和训练数据的表现
让我们构建一个高次多项式模型以最小化训练误差。然后计算该模型在测试数据和训练数据的误差值,如图3所示。
# create a model in sklearn, train on training data
degree = 10
lmodel = lin_model(degree)
lmodel.fit(X_train, y_train)
# predict on training data, find training error
yhat = lmodel.predict(X_train)
err_train = lmodel.mse(y_train, yhat)
# predict on test data, find error
yhat = lmodel.predict(X_test)
err_test = lmodel.mse(y_test, yhat)
结果表明,该模型在训练集上的误差为58.01, 但是在test集上的误差却高达:171215.01。 这样的模型会被描述为: 1) 过拟合 2) 高方差 3) 泛化性差
为什么呢? 把数据和模型曲线绘制出来就会发现:为了fit训练数据,模型是个非常复杂的函数,但是测试数据不是训练数据的一部分,所以这个复杂的高度定制的模型在测试数据上表现很差。如下图3:
如果把测试数据加入训练,模型在测试数据上也会变现的很好。问题是一旦把测试数据加入训练,那么测试数据也就不是新的Data了,也就无法测试模型在新的数据上的表现了。 因此,可以把数据分成三部分:训练数据、验证数据、测试数据。
3. 误差和方差
上面的例子,很明显是多项式模型的次数太高了。如何选择一个合适的呢? 测试集和验证集会给我们一些指导。通过尝试不同的degree值,在测试集和交叉验证集上评估性能,从而选择合适的次数。
3.2 寻找最佳的次数
使用不同的多项式次数训练模型。
max_degree = 9
err_train = np.zeros(max_degree)
err_cv = np.zeros(max_degree)
x = np.linspace(0,int(X.max()),100)
y_pred = np.zeros((100,max_degree)) #columns are lines to plot
for degree in range(max_degree):
lmodel = lin_model(degree+1)
lmodel.fit(X_train, y_train)
yhat = lmodel.predict(X_train)
err_train[degree] = lmodel.mse(y_train, yhat)
yhat = lmodel.predict(X_cv)
err_cv[degree] = lmodel.mse(y_cv, yhat)
y_pred[:,degree] = lmodel.predict(x)
optimal_degree = np.argmin(err_cv)+1
绘制不同的多项式次数获得的模型,分析不同的多项式对应的训练数据误差和交叉验证数据的误差。 我们把数据分成两部分,一部分数据用来fit模型,另一部分数据用来验证模型是否过拟合或者欠拟合。在这个例子中,我们通过增大所使用的多项式的次数,训练了一系列从过拟合到欠拟合的模型。
上图中的左边,实线代表不同次幂的模型。次幂为1的模型时一条直线,我们可以看到直线只与少数数据相交,而最高次幂的曲线非常接近于每个数据点。
右图,训练数据随着模型的复杂度增大,误差变小。交叉验证数据的误差最初随着模型开始拟合数据而减少,但随后随着模型开始在训练数据上过拟合(无法泛化)而增加。
3.3 调整正则化
正则化技术可以减少过拟合。 我们从高次多项式开始,调整正则化参数看看会发生什么?
lambda_range = np.array([0.0, 1e-6, 1e-5, 1e-4,1e-3,1e-2, 1e-1,1,10,100])
num_steps = len(lambda_range)
degree = 10
err_train = np.zeros(num_steps)
err_cv = np.zeros(num_steps)
x = np.linspace(0,int(X.max()),100)
y_pred = np.zeros((100,num_steps)) #columns are lines to plot
for i in range(num_steps):
lambda_= lambda_range[i]
lmodel = lin_model(degree, regularization=True, lambda_=lambda_)
lmodel.fit(X_train, y_train)
yhat = lmodel.predict(X_train)
err_train[i] = lmodel.mse(y_train, yhat)
yhat = lmodel.predict(X_cv)
err_cv[i] = lmodel.mse(y_cv, yhat)
y_pred[:,i] = lmodel.predict(x)
optimal_reg_idx = np.argmin(err_cv)
正则化参数·lambda· 从0逐渐变大到100时,多项式次数等于10,拟合到模型如下图所示:
从图中可以看出,随着正则化参数的增大,模型逐渐从高方差过拟合过渡到高偏差欠拟合。右侧的直线部分是合适的正则化参数。
3.4 获取更多的数据,增大训练集大小
当模型过拟合的时候,增大训练集数据可以改善这一点。我们做实验验证这一点:
图中左侧表明,当训练数据最多时,拟合曲线丝滑的在数据点中间。在右侧,当训练数据逐渐变大时,训练集和交叉验证集的误差逐渐收敛到同一个值。虽然曲线并不丝毫,但这个趋势依然表明更多的数据能够改善泛化性。
这是第一部分针对线性模型的优化,第二部分讲神经网络的评估和优化。
注:文中的图来自于课程中的实验截图。