模型正则化欠拟合与过拟合线性回归模型2次多项式回归4次多项式回归评估3种回归模型在测试数据集上的性能表现L1范数正则化Lasso模型在4次多项式特征上的拟合表现L2范数正则化
模型正则化
任何机器学习模型在训练集上的性能表现,都不能作为其对未知测试数据预测能力的评估。要重视模型的泛化力(Generalization)。本文将详细解释什么是模型的泛化力,以及如何保证模型的泛化力。欠拟合与过拟合将首先阐述模型复杂度与泛化力的关系,紧接着,L1范数正则化与L2范数正则化将分别介绍如何使用这两种正则化(Regularization)的方式来加强模型的泛化力,避免模型参数过拟合(Overfitting)。
欠拟合与过拟合
所谓拟合,是指机器学习模型在训练的过程中,通过更新参数,使得模型不断契合可观测数据(训练集)的过程。本文将使用一个“比萨饼价格预测”的例子来说明。美国一家比萨饼店出售不同尺寸的比萨,其中每种直径(Diameter)都对应一个报价。我们所要做的,就是设计一个学习模型,可以有效地根据第二章表中比萨的直径特征来预测售价。
Training Instance | Diameter(in inches) | Price(in U.S. dollars) |
---|---|---|
1 | 6 | 7 |
2 | 8 | 9 |
3 | 10 | 13 |
4 | 14 | 17.5 |
5 | 18 | 18 |
Training Instance | Diameter(in inches) | Price(in U.S. dollars) |
---|---|---|
1 | 6 | ? |
2 | 8 | ? |
3 | 11 | ? |
4 | 16 | ? |
共有5组训练数据、4组测试数据,并且其中测试数据的比萨报价未知。先只考虑比萨的尺寸与售价的关系,那么使用线性回归模型比较直观。
线性回归模型
代码语言:javascript复制#输入训练样本的特征以及目标值,分别存储在变量x_ train与y_ train之中。
x_train = [[6],[8],[10], [14],[18]]
y_train = [[7],[9],[13],[17.5],[18]]
#从sklearn.linear model中导入LinearRegression.
from sklearn.linear_model import LinearRegression
#使用默认配置初始化线性回归模型。
regressor = LinearRegression()
#直接以比萨的直径作为特征训练模型。
regressor.fit(x_train, y_train)
#导入numpy并且重命名为np.
import numpy as np
#在x轴上从0至25均匀采样100个数据点。
xx = np.linspace(0, 26, 100)
xx = xx.reshape (xx.shape[0],1)
#以上述100个数据点作为基准,预测回归直线。
yy = regressor.predict(xx)
#对回归预测到的直线进行作图。
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(x_train, y_train)
plt1,=plt.plot(xx, yy, label= "Degree= 1")
plt.axis([0, 25, 0, 25])
plt.xlabel('Diameter of Pizza')
plt.ylabel ('Price of Pizza')
plt.legend (handles= [plt1])
plt.show ()
#输出线性回归模型在训练样本上的R-squared值。
print(' The R- squared value of Linear Regressor performing on the training I data is',
regressor.score(x_train, y_train))
在这里插入图片描述
代码语言:javascript复制 The R- squared value of Linear Regressor performing on the training I data is 0.9100015964240102
根据代码输出的图,以及当前模型在训练集上的表现( R-squared值为0.9100),可以进一步猜测,也许比萨饼的面积与售价的线性关系中更加显。因此,可以尝试将原特征升高一个维度,使用(2次)多项式回归( Polynominal Regression) 对训练样本进行拟合。
2次多项式回归
代码语言:javascript复制#从sklearn. preproessing中导入多项式特征产生器
from sklearn.preprocessing import PolynomialFeatures
#使用PolynominalFeatures (degree=2)映射出2次多项式特征,存储在变量x_ train_poly2中。
poly2 = PolynomialFeatures(degree=2)
x_train_poly2 = poly2.fit_transform(x_train)
#以线性回归器为基础,初始化回归模型。尽管特征的维度有提升,但是模型基础仍然是线性模型。
regressor_poly2 = LinearRegression()
#对2次多项式回归模型进行训练。
regressor_poly2.fit(x_train_poly2, y_train)
#从新映射绘图用x轴采样数据。
xx_poly2 = poly2.transform(xx)
#使用2次多项式回归模型对应x轴采样数据进行回归预测。
yy_poly2 = regressor_poly2.predict(xx_poly2)
#分别对训练数据点、线性回归直线、2次多项式回归曲线进行作图。
plt.scatter(x_train, y_train)
plt1,= plt.plot(xx, yy, label= 'Degree=1')
plt2,= plt.plot(xx, yy_poly2, label= 'Degree=2')
plt.axis([0, 25, 0, 25])
plt.xlabel(' Diameter of Pizza')
plt.ylabel('Price of Pizza')
plt.legend(handles= [plt1, plt2])
plt.show()
#输出2次多项式回归模型在训练样本上的R-squared值。
print('The R- squared value of Polynominal Regressor (Degree= 2) performing on the training data is',
regressor_poly2.score(x_train_poly2, y_train))
在这里插入图片描述
代码语言:javascript复制The R- squared value of Polynominal Regressor (Degree= 2) performing on the training data is 0.9816421639597427
果然,在升高了特征维度之后,2次多项式回归模型在训练样本上的性能表现更加突出,R-squared值从0.910.上升到0.982。并且根据所输出的图示,2次多项式回归曲线(绿色)比起线性回归直线(蓝色),对训练数据的拟合程度也增加了许多。由此,尝试更加大胆地进一步升高特征维度,增加到4次多项式。
4次多项式回归
代码语言:javascript复制#从sklearn.preprocessing导人多项式特征生成器。
from sklearn. preprocessing import PolynomialFeatures
#初始化4次多项式特征生成器。
poly4 = PolynomialFeatures (degree = 4)
x_train_poly4 = poly4.fit_transform(x_train)
#使用默认配置初始化4次多项式回归器。
regressor_poly4 = LinearRegression()
#对4次多项式回归模型进行训练。
regressor_poly4.fit(x_train_poly4, y_train)
#从新映射绘图用x轴采样数据。
xx_poly4 = poly4.transform(xx)
#使用4次多项式回归模型对应x轴采样数据进行回归预测。
yy_poly4 = regressor_poly4.predict(xx_poly4)
#分别对训练数据点、线性回归直线、2次多项式以及4次多项式回归曲线进行作图。
plt.scatter(x_train, y_train)
plt1,=plt.plot(xx, yy, label= 'Degree=1' )
plt2,=plt.plot(xx, yy_poly2, label= 'Degree=2')
plt4,=plt.plot(xx, yy_poly4, label= 'Degree=4')
plt.axis([0, 25, 0, 25])
plt.xlabel('Diameter of Pizza')
plt.ylabel('Price of Pizza')
plt.legend(handles= [plt1, plt2, plt4])
plt.show()
print('The R- squared value of Pol ynominal Regressor (Degree=4) per forming on the training data is',
regressor_poly4.score(x_train_poly4, y_train))
在这里插入图片描述
代码语言:javascript复制The R- squared value of Pol ynominal Regressor (Degree=4) per forming on the training data is 1.0
如上图所示,4次多项式曲线几乎完全拟合了所有的训练数据点,对应的R-squared值也为1.0。但是,如果这时觉得已经找到了完美的模型,那么显然是高兴过早了,接下来揭示测试比萨的真实价格。
Training Instance | Diameter(in inches) | Price(in U.S. dollars) |
---|---|---|
1 | 6 | 8 |
2 | 8 | 12 |
3 | 11 | 15 |
4 | 16 | 18 |
评估3种回归模型在测试数据集上的性能表现
代码语言:javascript复制#准备测试数据。
x_test = [[6], [8], [11], [16]]
y_test = [[8], [12], [15], [18]]
#使用测试数据对线性回归模型的性能进行评估。
regressor.score(x_test, y_test)
代码语言:javascript复制0.809726797707665
代码语言:javascript复制#使用测试数据对2次多项式回归模型的性能进行评估。
x_test_poly2 = poly2.transform(x_test)
regressor_poly2.score(x_test_poly2, y_test)
代码语言:javascript复制0.8675443656345073
代码语言:javascript复制#使用测试数据对4次多项式回归模型的性能进行评估。
x_test_poly4 = poly4.transform(y_test)
regressor_poly4.score(x_test_poly4, y_test)
代码语言:javascript复制0.5418565138748649
如果将上述3种模型在测试集上的表现,制成表格;最终的结果却令人咋舌:当模型复杂度很低(Degree=1)时,模型不仅没有对训练集上的数据有良好的拟合状态,而且在测试集上也表现平平,这种情况叫做欠拟合(Underfitting);但是,当我们一味追求很高的模型复杂度(Degree=4),尽管模型几乎完全拟合了所有的训练数据,但如图3-4所示,模型也变得非常波动,几乎丧失了对未知数据的预测能力,这种情况叫做过拟(Overfitting)。这两种情况都是缺乏模型泛化力的表现。
特征多项式次数 | 训练集Rsquared值 | 测试集Rsquared值 |
---|---|---|
1 | 0.9100 | 0.8097 |
2 | 0.9816 | 0.868 |
4 | 1.0 | 0.542 |
L1范数正则化
正则化( Regularization)的目的在于提高模型在未知测试数据上的泛化力,避免参数过拟合。由上面的“比萨饼价格预测"的例子可以看出,2次多项式回归是相对较好的模型假设。之所以出现如4次多项式那样的过拟合情景,是由于4次方项对应的系数过大,或者不为0所导致。接下来,将再次进行试验,继续使用4次多项式特征,但是换成Lasso模型检验L1范数正则化后的性能和参数。
Lasso模型在4次多项式特征上的拟合表现
代码语言:javascript复制#从sklearn.linear model 中导入Lasso。
from sklearn.linear_model import Lasso
#从使用默认配置初始化Lasso。
lasso_poly4 = Lasso()
#从使用Lasso对4次多项式特征进行拟合。
lasso_poly4.fit(x_train_poly4, y_train)
#对Lasso模型在测试样本上的回归性能进行评估。
print(lasso_poly4.score(x_test_poly4, y_test))
代码语言:javascript复制0.5517159962045822
C:ProgramDataAnaconda3libsite-packagessklearnlinear_modelcoordinate_descent.py:491: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Fitting data with very small alpha may cause precision problems.
ConvergenceWarning)
代码语言:javascript复制#输出Lasso模型的参数列表。
print(lasso_poly4.coef_)
代码语言:javascript复制[ 0.00000000e 00 0.00000000e 00 1.17900534e-01 5.42646770e-05
-2.23027128e-04]
代码语言:javascript复制#回顾普通4次多项式回归模型过拟合之后的性能。
print(regressor_poly4.score(x_test_poly4, y_test))
代码语言:javascript复制0.5418565138748649
代码语言:javascript复制#回顾普通4次多项式回归模型的参数列表。
print(regressor_poly4.coef_)
代码语言:javascript复制[[ 0.00000000e 00 -2.51739583e 01 3.68906250e 00 -2.12760417e-01
4.29687500e-03]]
通过对代码输出的观察,验证了Lasso模型的特点:
- 相比于普通4次多项式回归模型在测试集上的表现,默认配置的Lasso模型性能提高了大约1%;
- 相较之下,Lasso模型拟合后的参数列表中,4次与3次特征的参数均为0.0,使得特征更加稀疏。
L2范数正则化
与L1范数正则化略有不同的是,L2范数正则化则在原优化目标的基础上,增加了参数向量的L2范数的惩罚项。为了使新优化目标最小化,这种正则化方法的结果会让参数向量中的大部分元素都变得很小,压制了参数之间的差异性。而这种压制参数之间差异性的L2正则化模型,通常被称为Ridge。 接下来继续使用4次多项式特征做实验,但是换成Ridge模型检验L2范数正则化后的性能和参数。
代码语言:javascript复制#输出普通4次多项式回归模型的参数列表。
print(regressor_poly4.coef_)
代码语言:javascript复制[[ 0.00000000e 00 -2.51739583e 01 3.68906250e 00 -2.12760417e-01
4.29687500e-03]]
代码语言:javascript复制#输出上述这些参数的平方和,验证参数之间的巨大差异。
print(np.sum(regressor_poly4.coef_**2))
代码语言:javascript复制647.3826457369564
代码语言:javascript复制#从sklearn.linear mode1导入Ridge。
from sklearn.linear_model import Ridge
#使用默认配置初始化Riedge。
ridge_poly4 = Ridge()
#使用Ridge模型对4次多项式特征进行拟合。
ridge_poly4.fit(x_train_poly4, y_train)
#输出Ridge模型在测试样本上的回归性能。
print(ridge_poly4.score(x_test_poly4, y_test))
代码语言:javascript复制0.5420900084405247
代码语言:javascript复制#输出Ridge模型的参数列表,观察参数差异。
print(ridge_poly4.coef_)
代码语言:javascript复制[[ 0. -0.00492536 0.12439632 -0.00046471 -0.00021205]]
代码语言:javascript复制#计算Ridge模型拟合后参数的平方和。
print(np. sum(ridge_poly4.coef_**2))
代码语言:javascript复制0.015498965203571016
通过对代码输出的观察,验证了Ridge模型的特点:
- 相比于普通4次多项式回归模型在测试集上的表现,默认配置的Ridge模型性能有所提升;
- 与普通4次多项式回归模型不同的是,Ridge模型拟合后的参数之间差异非常小。