
1.5 项目实战
最后,我们来做一个线性回归的实战项目:分别使用OLSLinearRegression和GDLinearRegression预测红酒口感,如表1-1所示。
表1-1 红酒口感数据集(https://archive.ics.uci.edu/ml/datasets/wine+quality)

数据集中包含1599条数据,其中每一行包含红酒的11个化学特征以及专家评定的口感值。虽然口感值只是3~8的整数,但我们依然把该问题当作回归问题处理,而不是当作包含6种类别(3~8)的分类问题处理。如果当作分类问题,则预测出的类别间无法比较好坏,例如我们不清楚第1类口感是否比第5类口感好,但我们明确知道5.3比4.8口感好。
读者可使用任意方式将数据集文件winequality-red.csv下载到本地,此文件所在的URL为:https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequalityred.csv。
1.5.1 准备数据
调用Numpy的genfromtxt函数加载数据集:

1.5.2 模型训练与测试
我们要训练并测试两种不同方法实现的线性回归模型:OLSLinearRegression和GDLinearRegression。
1. OLSLinearRegression
先从更为简单的OLSLinearRegression开始。
首先创建模型:

创建OLSLinearRegression时无须传入任何参数。
然后,调用sklearn中的train_test_split函数将数据集切分为训练集和测试集(比例 为7:3):

接下来,训练模型:

因为训练集容量及实例特征数量都不大,所以很短时间内便可完成训练。
使用已训练好的模型对测试集中的实例进行预测:

仍以均方误差(MSE)衡量回归模型的性能,调用sklearn中的mean_squared_error函数计算MSE:

模型在测试集上的MSE为0.421,其平方根约为0.649。还可以测试模型在训练集上的MSE:

模型在训练集与测试集的性能相差不大,表明未发生过度拟合现象。
注意
过度拟合也称为过拟合,不过在中文上下文中使用“过拟合”容易产生歧义,故本书统一使用“过度拟合”。
另一个常用的衡量回归模型的指标是平均绝对误差(MAE),其定义如下:

MAE的含义更加直观一些:所有实例预测值与实际值之误差绝对值的平均值。
调用sklearn中的mean_absolute_error函数计算模型在测试集上的MAE:

MAE为0.492,即预测口感值比实际口感值平均差了0.492。
2. GDLinearRegression
再来训练并测试GDLinearRegression,该过程比之前的OLSLinearRegression麻烦一些,因为它有3个超参数需要我们设置,而最优的超参数组合通常需要通过大量实验得到。
GDLinearRegression的超参数有:
(1)梯度下降最大迭代次数n_iter
(2)学习率eta
(3)损失降低阈值tol(tol不为None时,开启早期停止法)
先以超参数(n_iter=3000,eta=0.001,tol=0.00001)创建模型:

为了与之前的OLSLinearRegression进行对比,我们使用与之前相同的训练集和测试集(不重新切分X,y)训练模型:

以上输出表明,经过一步梯度下降以后,损失Loss不降反升,然后算法便停止了,这说明步长太大,已经迈到对面山坡上了,需调小学习率。将学习率调整为eta=0.0001再次尝试:

这次虽然损失随着迭代逐渐下降了,但是迭代到了最大次数3000,算法依然没有收敛,最终损失(在训练集上的MSE)为0.539,距离之前用最小二乘法计算出的最小值0.417还差很远,并且发现后面每次迭代损失下降得非常小。这种状况主要是由于中各特征尺寸相差较大造成的,观察
中各特征的均值:

可看出各特征尺寸差距确实很大,有的特征间相差了好几个数量级。以两个特征为例,如果特征尺寸比
的小很多(如图1-5所示),通常
的变化对损失函数值影响更大,梯度下降时就会先沿着接近
轴的方向下山,再沿着
轴进入一段长长的几乎平坦的山谷,用下山时谨慎的小步走平地,速度慢得像蜗牛爬,虽然最终也可以抵达最小值点,但需要更多的迭代次数,花费更长时间。

图1-5
相反,如果和
特征尺寸相同(见图1-6),梯度下降时将更直接地走向最小值点,算法收敛更快。

图1-6
接下来我们把各特征缩放到相同尺寸,然后重新训练模型。将特征缩放到相同尺寸有两种常用方法:归一化(Normalization)和标准化(Standardization)。
归一化是指使用min-max缩放将各特征的值缩放至[0, 1]区间。对于第i个实例的第j个特征,归一化转换公式为:

其中,和
分别为所有实例第j个特征的最大值和最小值。调用sklearn中的MinMaxScaler函数可以完成归一化转换。
标准化是指将各特征的均值设置为0,方差设置为1。对于第i个实例的第j个特征,标准化转换公式为:

其中,和
分别为所有实例第j个特征的均值和标准差。调用sklearn中的StandardScaler函数可以完成标准化转换。
对于大多数机器学习算法而言,标准化更加实用,因为标准化保持了异常值所蕴含的有用信息。这里我们调用sklearn中的StandardScaler函数对各特征进行缩放:

这里需要注意,在以上的代码中,StandardScaler只对训练集进行拟合(计算均值和标准差
),然后使用相同的拟合参数对训练集和测试集进行转换,因为在预测时测试集对于我们是未知的。
现在各特征值被缩放到了相同的尺寸。接下来重新创建模型,并使用已缩放的数据进行训练:

这次我们将eta大幅提高到了0.05,经过136次迭代后算法收敛,目前损失(在训练集上的MSE)为0.428,已接近用最小二乘法计算出的最小值0.417。
最后使用已训练好的模型对测试集中的实例进行预测,并评估性能:

此时MSE为0.393,MAE为0.492,与之前使用OLSLinearRegression的性能差不多。读者可以继续调整超参数进行优化,但性能不会有太明显的提升,毕竟线性回归是非常简单的模型。
至此,线性回归的项目就完成了。