1.5 线性回归项目实战
1.5.1 波士顿房价数据集简介
波士顿房价数据集是美国波士顿1970年的真实房价数据。在这个数据集中共有506个样本,其中每一个样本代表一所房子相关的特征与房价。每一所房子都有13个特征值与1个对应的标签值,标签值即为房子的价格。因为每一所房子的相关信息都使用13个特征值来描述,所以这些特征值能够很好地捕获房子的属性,有利于模型的训练与预测。这13个特征值分别表示城镇人均犯罪率、住宅平均房间数、到波士顿5个中心区域的加权距离、城镇师生比例、自住房平均价格等。
因为在sklearn(scikit-learn)模块中已经封装了波士顿房价数据集,所以可以直接在程序中使用sklearn模块加载数据集。sklearn模块是使用Python编程语言编写的机器学习工具,在本书中主要应用其对数据集进行预处理。使用sklearn模块加载波士顿房价数据集的代码如下所示。
from sklearn.datasets import load_boston
dataset = load_boston()
X = dataset.data
y = dataset.target
1.5.2 数据集特征值的标准化
在把训练集数据加载到模型中用于模型训练之前,需要把训练集的全部样本的特征值进行标准化(standardization)。其原因可以通过下面这个示例进行解释。如果训练集数据中的每个样本都由3个特征值组成,分别记为、、,其中特征值 的范围为0~1,特征值的范围为0~100,特征值的范围为0~1 000。因为样本的每一个特征值的范围有很大的不同,使模型训练变得非常困难,所以应用标准化将每一个特征值的范围都转换成0~1,能够提高模型的训练速度。
当将数据集中的样本进行标准化时,实际为对数据集中所有样本在同一个位置的特征值进行标准化。对于一个有个样本的数据集,每一个样本中的特征使用来表示。每一个样本有3个特征值,分别使用、、来表示第个样本的3个特征值。可以通过以下几个公式对这个数据集中所有样本的特征值进行标准化。
首先计算出所有样本中特征值的均值,均值的计算公式为将所有值相加再除以值的总数,如下所示。
计算出均值后,根据均值计算出所有样本特征值的方差,计算公式如下所示。
最后,将所有样本特征值减去均值并除以方差,即可得到训练集中每一个样本经过标准化以后的特征值,如下所示。
如果训练集的数据已经被分割成训练集与测试集,则需要对训练集与测试集的数据分别进行标准化。在训练集中计算出的用于标准化样本中每一个特征值的均值与方差会同样应用于测试集数据的标准化。也就是说,标准化测试集的数据时,不需要重新计算测试集的样本中每一个特征值的均值与方差,而应该使用标准化训练集样本时使用的均值与方差。当训练集与测试集用同样的方式进行标准化以后,才可以将训练集用于模型训练,将测试集用于测试模型预测的准确率。
对于波士顿房价数据集的标准化可以使用以下代码实现。首先求数据集中所有样本特征值的均值,然后求数据集中所有样本特征值的方差,最后利用计算出的均值与方差完成对数据集的标准化。
# 求数据集中所有样本特征值的均值
mean = X.mean(axis=0)
# 求数据集中所有样本特征值的方差
std = X.std(axis=0)
# 对数据集进行标准化
X = (X - mean) / std
1.5.3 线性回归模型的构建与训练
在将数据集进行标准化以后,需要将整个数据集分为用于线性回归模型训练的训练集与用于模型验证的测试集。在sklearn模块中提供了train_test_split
函数用于按照指定比例划分数据集,函数中的test_size
参数为划分的测试集占全部数据集中数据的比例,如将其指定为0.2时,会将数据集中80%的数据划分为训练集数据,其余的20%划分为测试集数据。将数据集划分好以后,分别使用n_train
变量与n_features
变量表示训练集中的样本个数与每个样本的特征值个数。具体代码如下。
# 将数据集分为训练集与测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 训练集中的样本个数
n_train = X_train.shape[0]
# 训练集中每个样本的特征值个数
n_features = X_train.shape[1]
接下来,对模型的参数w
与b
进行初始化。模型的权重w
中值的个数必须与每个样本中特征值的个数一致。指定模型在使用梯度下降算法进行训练时的学习率为0.001,对模型进行3 000次迭代训练,并定义线性回归模型。具体代码如下。
import numpy as np
# 模型参数中的权重
w = np.random.rand(n_features)
# 模型参数中的偏差
b = 1.1
# 指定学习率的值
lr = 0.001
# 指定模型迭代训练的次数
epochs = 3000
# 定义线性回归模型
def model(x):
y_hat = w.dot(x) + b
return y_hat
最后对模型使用梯度下降算法进行训练。在训练过程中,为了防止严重过拟合现象的出现,在损失函数中加入了正则项。模型训练的代码如下所示。
# 指定正则项中lambda的值
reg = 0.5
# 使用梯度下降算法对模型进行迭代训练
for epoch in range(epochs):
sum_w = 0.0
sum_b = 0.0
for i in range(n_train):
xi = X_train[i]
yi = y_train[i]
yi_hat = model(xi)
sum_w += (yi_hat - yi) * xi
sum_b += (yi_hat - yi)
grad_w = (2.0 / n_train) * sum_w + (2.0 * reg * w)
grad_b = (2.0 / n_train) * sum_b
w = w - lr * grad_w
b = b - lr * grad_b
接下来使用在之前定义的loss_function
函数来计算当前训练好的线性回归模型分别在训练集与测试集上的平均预测损失值,如以下代码所示。
train_loss = loss_funtion(X_train, y_train)
test_loss = loss_funtion(X_test, y_test)
这个线性回归模型在训练集与测试集上的平均预测损失值分别为26.7与23.7,因为模型在测试集上得到的平均预测损失值比训练集上的平均预测损失值还要小,说明正则项有效地减小了过拟合现象出现的概率。