线性回归
在本节课中,学习什么是线性回归以及如何使用它。
什么是线性回归?
线性回归 可能是现实世界中最常用的模型之一。它是一种通过线性方法建模标量响应(因变量)与一个或多个解释变量(自变量)之间关系的技术。与分类任务输出某一类别的概率不同,回归任务的输出具有实际物理意义的数值,例如产品销量、员工参与度、未来房价、健康指标、GDP预测等。
线性回归的目标是从大量历史数据中学习因变量与自变量之间的关系。以房价预测为例:你获得了城市各区域的房屋价格数据,每个样本包含卧室数量、距市中心距离、距机场距离、附近是否有医院等信息。当下次遇到数据库中没有但具备这些信息的新房屋时,就可以使用训练好的模型给出准确的价格预测。
从数学角度看,线性回归旨在拟合数据,使每个数据点与其预测值之间的残差平方和最小化。如下图所示,红线是我们求解出的模型,蓝点是原始数据,点到红线的距离即为残差。我们的目标是最小化所有残差的总和。
以下是模型的形式表达式。其中 是需要从数据中学习的参数, 是输入数据或特征。第二个公式是第一个公式的向量表示形式:
和其他机器学习任务一样,我们需要为 线性回归 定义一个损失函数(或目标函数)。最常用的是均方误差(MSE),有时也会使用与其非常相似的均方根误差(RMSE)。
其中 表示第 个样本的真实值, 是模型对该样本的预测值,两者之差的平方即为该样本的损失。 是样本总数。将所有样本的损失相加并取平均,就得到整个数据集的损失值。我们的目标是最小化这个值。
求解该方程并获得最优参数的方法有很多,但我们不会深入探讨其背后的数学细节和原理。这里我们只关注如何使用 scikit-learn 来完成任务。
现在开始编码。
房价预测建模
数据加载和划分部分将稍后展示完整代码。对于普通线性回归,可以从 linear_model 模块创建一个 LinearRegression 对象。该类的参数不多,其中有一个 normalize 参数需要注意:它的作用是对特征进行标准化,避免因特征取值范围差异过大而影响结果。大多数情况下不需要启用此参数;只有当特征的取值范围差异较大时,才建议设置 normalize=True。
例如,某个数据集包含两个特征:人的身高和收入。身高的范围在140到200厘米之间,而收入则在2万到8万美元之间。这意味着这两个特征对结果的影响处于不同量级——由于收入数值更大,它会对结果产生更大的影响,但这并不意味着收入比身高更重要。
1from sklearn.linear_model import LinearRegression 2 3lr = LinearRegression() 4# train_x 和 train_y 是训练数据和标签 5lr.fit(train_x, train_y) 6pred_y = lr.predict(test_x)
模型训练完成后,接下来需要评估其性能。
对于回归任务,最常用的评估指标是 MSE 或 RMSE。本示例中我们使用 MSE 来评估模型表现。
1import sklearn.metrics as metrics 2 3# pred_y 是预测结果 4mse = metrics.mean_squared_error(test_y, pred_y)
以下是完整代码,你可以直接运行尝试:
1import sklearn.datasets as datasets 2from sklearn.model_selection import train_test_split 3from sklearn.linear_model import LinearRegression 4import sklearn.metrics as metrics 5 6house = datasets.load_boston() 7 8print("The data shape of house is {}".format(house.data.shape)) 9print("The number of feature in this data set is {}".format( 10 house.data.shape[1])) 11 12train_x, test_x, train_y, test_y = train_test_split(house.data, 13 house.target, 14 test_size=0.2, 15 random_state=42) 16 17print("The first five samples {}".format(train_x[:5])) 18print("The first five targets {}".format(train_y[:5])) 19print("The number of samples in train set is {}".format(train_x.shape[0])) 20print("The number of samples in test set is {}".format(test_x.shape[0])) 21 22lr = LinearRegression() 23lr.fit(train_x, train_y) 24 25pred_y = lr.predict(test_x) 26print("The first five prediction {}".format(pred_y[:5])) 27print("The real first five labels {}".format(test_y[:5])) 28 29mse = metrics.mean_squared_error(test_y, pred_y) 30print("Mean Squared Error {}".format(mse))
- 首先,在第6行调用
load_boston加载数据集。 - 接着,在第12行将数据集划分为训练集和测试集,训练集占80%。
- 在第23行创建并训练线性回归模型(在
sklearn中,“训练”对应fit方法)。 - 在第29行调用
mean_squared_error函数评估模型性能。
基于生成数据的建模
本示例旨在绘制拟合直线及其误差带。
我们的数据只有一个特征,因此模型是一条直线。由于可以从模型中获取系数(coefficient)和偏置项(bias),我们可以利用这些参数手动计算估计值。
当然,也可以直接使用 predict() 方法更方便地获得预测结果。此处未采用内置函数是为了帮助理解模型内部机制。以下是如何从模型中提取参数并计算预测值与误差的过程:
1import numpy as np 2from sklearn.linear_model import LinearRegression 3 4# 将一维数组重塑为二维数组 5x = np.linspace(0, 10, 11) 6y = [3.9, 4.4, 10.8, 10.3, 11.2, 13.1, 14.1, 9.9, 13.9, 15.1, 12.5] 7lr2 = LinearRegression() 8lr2.fit(x.reshape(-1, 1), y) 9 10# y_est 是预测值。通常可通过 predict() 方法获得 11y_est = x * lr2.coef_ + lr2.intercept_ 12y_err = x.std() * np.sqrt(1/len(x) + 13 (x - x.mean())**2 / np.sum((x - x.mean())**2))
下图展示了原始数据、拟合直线以及误差带。
完整代码如下,可供尝试:
1from sklearn.linear_model import LinearRegression 2import numpy as np 3import matplotlib.pyplot as plt 4 5x = np.linspace(0, 10, 11) 6y = [3.9, 4.4, 10.8, 10.3, 11.2, 13.1, 14.1, 9.9, 13.9, 15.1, 12.5] 7 8lr2 = LinearRegression() 9lr2.fit(x.reshape(-1, 1), y) 10print('Coefficients: {}'.format(lr2.coef_)) 11print('Bias term: {}'.format(lr2.intercept_)) 12 13y_est = x * lr2.coef_ + lr2.intercept_ 14y_err = x.std() * np.sqrt(1 / len(x) + 15 (x - x.mean())**2 / np.sum((x - x.mean())**2)) 16fig, ax = plt.subplots() 17ax.plot(x, y_est, '-') 18ax.fill_between(x, y_est - y_err, y_est + y_err, alpha=0.2) 19ax.plot(x, y, 'o') 20fig.savefig("output/img.png", dpi = 300) 21plt.close(fig)
- 第5行和第6行为训练数据,其中
x是特征(仅一个特征,每个数据点为一个实例/样本),y是目标值(标签)。 - 第8行创建了一个线性回归模型,并在第9行完成训练。
- 本例中未使用内置的
predict方法,而是根据学到的参数,按照公式 手动计算预测值(第13行),同时也在第14行计算了误差。 - 从第16行到第21行,绘制了原始数据点、拟合直线以及误差带。