在第二章的时候,我们使用简单的线性回归对一个解释变量(x)和一个连续响应变量(label)之间的关系进行建模。
在第三章的时候,我们介绍了KNN,并使用多于一个的解释变量去进行预测的分类器和回归器。
在这一章,我们来讲一下多元的线性回归,和第二章的不同就在于,它是一种将一个连续响应变量在多个特征上进行回归的简单线性回归泛化形式。
一、多元线性回归
在第二章的时候,我们的问题是通过一个披萨的尺寸来预测他的价格。根据我们实际的经验,披萨🍕的价格可能不仅仅和它的尺寸有关系,可能和其他因素还有关联,这就涉及到多个解释变量,即我们平时说到的特征。
在这里,我们丰富一下题目,主观的认为他还和配料的数量有关系。好了,让我们来看一下为大家准备的新鲜的训练数据和测试数据。
训练数据:
| 直径(单位:英寸) | 配料数量(单位:种) | 价格(单位:美元) |
|---|---|---|
| 6 | 2 | 7 |
| 8 | 1 | 9 |
| 10 | 0 | 13 |
| 14 | 2 | 17.5 |
| 18 | 0 | 18 |
测试数据:
| 直径(单位:英寸) | 配料数量(单位:种) | 价格(单位:美元) |
|---|---|---|
| 8 | 2 | 11 |
| 9 | 0 | 8.5 |
| 11 | 2 | 15 |
| 16 | 2 | 18 |
| 12 | 0 | 11 |
好了,看完数据直接上代码。
import numpy as npfrom numpy.linalg import lstsqfrom numpy.linalg import invfrom numpy import dot, transposefrom sklearn.linear_model import LinearRegressiondef main():X_train = [[6, 2],[8, 1],[10, 0],[14, 2],[18, 0]]y_train = [7, 9, 13, 17.5, 18]X_test = [[8, 2],[9, 0],[11, 2],[16, 2],[12, 0]]y_test = [11, 8.5, 15, 18, 11]X_train = np.array(X_train)y_train = np.array(y_train)X_test = np.array(X_test)y_test = np.array(y_test)model = LinearRegression()model.fit(X_train, y_train)preds_y = model.predict(X_test).reshape(-1, 1)print("R2:", model.score(X_test, y_test)) # R2: 0.7701677731318468for i, pred_y in enumerate(preds_y):print(f"真实价格为{y_test[i]}, 预测价格为{pred_y[0]}")"""真实价格为11.0, 预测价格为10.062500000000002真实价格为8.5, 预测价格为10.28125真实价格为15.0, 预测价格为13.093750000000002真实价格为18.0, 预测价格为18.145833333333336真实价格为11.0, 预测价格为13.3125"""print(f"模型的参数为:{model.coef_},截距b为:{model.intercept_}")# 模型的参数为:[1.01041667 0.39583333],截距b为:1.1875# 我们也可以通过Numpy的最小二乘函数库lstsq来计算出上面的参数值和截距。X_train = np.insert(X_train, 0, values=1, axis=1) # 在X_train的第二个维度所有索引为0的位置添加1print(X_train)"""[[ 1 6 2][ 1 8 1][ 1 10 0][ 1 14 2][ 1 18 0]]"""print(lstsq(X_train, y_train)[0]) # [1.1875 1.01041667 0.39583333]# 方法二求参数print(dot(inv(dot(transpose(X_train), X_train)), dot(transpose(X_train), y_train)))# [1.1875 1.01041667 0.39583333]if __name__ == '__main__':main()
如果你仔细看过上面的代码,可能会有一个疑问,当我们用Numpy去求参数的时候,为什么先在每个对象index为0的位置insert了1🤔。
ok,下面我们来讲一下。
多元线性回归的公式如下所示:
转换成矩阵运算表示为:
等同于
看到上面的式子是不是恍然大悟😀
另外我们也通过下面的式子求参数 =>
二、多项式回归
在上面线性回归的例子里,我们假设的前提:解释变量和响应变量之间的关系是线性的。但是但多数的现实案例不是这么理想的情况。在这部分,我们对响应变量盒多项式特征项之间的关系进行建模,练习多项式回归。
先来让我们看一下多项式的公式:
下面我们依然是拿披萨问题来作为案例。假设披萨直径做为唯一解释变量,然后用相同的数据集来比较一下线性回归和多项式回归之间的不同。
训练数据:
| 训练实例 | 直径(单位:英寸) | 价格(单位:美元) |
|---|---|---|
| 1 | 6 | 7 |
| 2 | 8 | 9 |
| 3 | 10 | 13 |
| 4 | 14 | 17.5 |
| 5 | 18 | 18 |
测试数据:
| 测试实例 | 直径(单位:英寸) | 价格(单位:美元) |
|---|---|---|
| 1 | 6 | 8 |
| 2 | 8 | 12 |
| 3 | 11 | 15 |
| 4 | 16 | 18 |
废话少说,直接干代码
import numpy as npimport matplotlib.pyplot as pltfrom sklearn.preprocessing import PolynomialFeaturesfrom sklearn.linear_model import LinearRegressionplt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 中文显示def main():X_train = [[6],[8],[10],[14],[18]]y_train = [7, 9, 13, 17.5, 18]X_test = [[6],[8],[11],[16],]y_test = [8, 12, 15, 18]xx = np.linspace(0, 26, 100).reshape(-1, 1)X_train = np.array(X_train)y_train = np.array(y_train)X_test = np.array(X_test)y_test = np.array(y_test)model = LinearRegression()model.fit(X_train, y_train)preds_y = model.predict(xx)print(preds_y)quadratic_featurizer = PolynomialFeatures(degree=2) # 2-阶多项式quadratic_featurizer_3 = PolynomialFeatures(degree=3) # 3-阶多项式quadratic_featurizer_9 = PolynomialFeatures(degree=9) # 9-阶多项式# 训练数据特征转换2-阶多项式quadratic_X_train_2 = quadratic_featurizer.fit_transform(X_train)quadratic_X_test_2 = quadratic_featurizer.fit_transform(X_test)quadratic_xx_2 = quadratic_featurizer.fit_transform(xx)# 训练数据特征转换3-阶多项式quadratic_X_train_3 = quadratic_featurizer_3.fit_transform(X_train)quadratic_X_test_3 = quadratic_featurizer_3.fit_transform(X_test)quadratic_xx_3 = quadratic_featurizer_3.fit_transform(xx)# 训练数据特征转换9-阶多项式quadratic_X_train_9 = quadratic_featurizer_9.fit_transform(X_train)quadratic_X_test_9 = quadratic_featurizer_9.fit_transform(X_test)quadratic_xx_9 = quadratic_featurizer_9.fit_transform(xx)# 训练2-阶多项式回归模型quadratic_model_2 = LinearRegression()quadratic_model_2.fit(quadratic_X_train_2, y_train)quadratic_preds_y_2 = quadratic_model_2.predict(quadratic_xx_2)# 训练3-阶多项式回归模型quadratic_model_3 = LinearRegression()quadratic_model_3.fit(quadratic_X_train_3, y_train)quadratic_preds_y_3 = quadratic_model_3.predict(quadratic_xx_3)# 训练9-阶多项式回归模型quadratic_model_9 = LinearRegression()quadratic_model_9.fit(quadratic_X_train_9, y_train)quadratic_preds_y_9 = quadratic_model_9.predict(quadratic_xx_9)# 计算r2linear_score = model.score(X_test, y_test)q2_score = quadratic_model_2.score(quadratic_X_test_2, y_test)q3_score = quadratic_model_3.score(quadratic_X_test_3, y_test)q9_score = quadratic_model_9.score(quadratic_X_test_9, y_test)print(f"R2得分:\n 线性回归:{linear_score}\n"f"2阶多项式:{q2_score}\n"f"3阶多项式:{q3_score}\n"f"9阶多项式:{q9_score}")"""R2得分:线性回归:0.8097267977076652阶多项式:0.86754436563450543阶多项式:0.83569241560371339阶多项式:-0.09435666704315704"""plt.figure()l1 = plt.plot(xx, preds_y,label="线性回归")l2 = plt.plot(xx, quadratic_preds_y_2, c='r', linestyle='--', label='2阶多项式')l3 = plt.plot(xx, quadratic_preds_y_3, c='b', linestyle=':', label="3阶多项式")l9 = plt.plot(xx, quadratic_preds_y_9, c='y', linestyle='-.', label="9阶多项式")plt.legend()plt.grid(True)plt.scatter(X_train, y_train) # 画坐标点plt.axis([0, 25, 0, 25]) # 设置坐标轴长度plt.title("披萨价格的回归拟合")plt.xlabel("披萨的直径(单位:英寸)")plt.ylabel('披萨的价格(单位:美元)')plt.show()if __name__ == '__main__':main()
最后我们得到下图
结合R方得分和上图,我们可以判断出2阶的效果最好。另外,我们可以看到9阶几乎完全拟合了训练数据,并且R方的得分为-0.09,由此我们可以判断出9阶的模型过拟合
三、 正则化
在多项式回归最后的部分,我们提到了过拟合。这部分我们要讲的正则化就是防止过拟合的一个方法。你可以理解为为问题增加一个信息(惩罚项)。
sklearn库主要提供了下面几种正则化方法。
岭回归 LASSO回归 弹性网正则化
岭回归
LASSO回归通过对代价函数增加范数来惩罚系数。公式如下:
是一个控制惩罚力度的超参数,超参数是模型控制学习算法如何学习的参数。随着
的增加,惩罚力度也增加,代价函数的值也增加。当
为0时,岭回归等于线性回归。
LASSO回归
LASSO回归通过对代价函数增加范数来惩罚系数。公式如下:
LASSO回归产出的参数,大多数系数将变为0,模型将依赖于特征的一个小型子集。相反,岭回归产出模型的大多数参数都很小但不是0。
弹性网正则化
最后,sklearn还提供了弹性网正则化的实现。它是LASSO回归的和岭回归
的线形组合。也就是说,LASSO回归和岭回归都是弹性网正则超参数为0的时候的特殊形式。
公式如下:
四、线性回归实战
在前面我们已经学习线性回归模型相关的一些知识。现在我们用一个真实的数据集来进行一下实战。
并这里比较一下用随机梯度下降和最小二乘的不同
from sklearn.datasets import load_bostonfrom sklearn.preprocessing import StandardScalerfrom sklearn.model_selection import train_test_splitfrom sklearn.linear_model import SGDRegressor, LinearRegressiondef main():datas = load_boston()X_train, X_test, y_train, y_test = train_test_split(datas.data, datas.target, test_size=0.2,random_state=2020, shuffle=True)x_scaler = StandardScaler()y_scaler = StandardScaler()X_train = x_scaler.fit_transform(X_train)y_train = y_scaler.fit_transform(y_train.reshape(-1, 1))X_test = x_scaler.transform(X_test)y_test = y_scaler.transform(y_test.reshape(-1, 1))model = SGDRegressor(loss='squared_loss', learning_rate='adaptive')model.fit(X_train, y_train)print("随机梯度下降模型R方得分:\n", model.score(X_test, y_test.reshape(-1, 1)))model = LinearRegression()model.fit(X_train, y_train)print("最小二乘模型R方得分:\n", model.score(X_test, y_test.reshape(-1, 1)))"""随机梯度下降模型R方得分:0.7680884678774204最小二乘模型R方得分:0.7682699602324874"""if __name__ == '__main__':main()
