在第二章的时候,我们使用简单的线性回归对一个解释变量(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

好了,看完数据直接上代码。

  1. import numpy as np
  2. from numpy.linalg import lstsq
  3. from numpy.linalg import inv
  4. from numpy import dot, transpose
  5. from sklearn.linear_model import LinearRegression
  6. def main():
  7. X_train = [
  8. [6, 2],
  9. [8, 1],
  10. [10, 0],
  11. [14, 2],
  12. [18, 0]
  13. ]
  14. y_train = [7, 9, 13, 17.5, 18]
  15. X_test = [
  16. [8, 2],
  17. [9, 0],
  18. [11, 2],
  19. [16, 2],
  20. [12, 0]
  21. ]
  22. y_test = [11, 8.5, 15, 18, 11]
  23. X_train = np.array(X_train)
  24. y_train = np.array(y_train)
  25. X_test = np.array(X_test)
  26. y_test = np.array(y_test)
  27. model = LinearRegression()
  28. model.fit(X_train, y_train)
  29. preds_y = model.predict(X_test).reshape(-1, 1)
  30. print("R2:", model.score(X_test, y_test)) # R2: 0.7701677731318468
  31. for i, pred_y in enumerate(preds_y):
  32. print(f"真实价格为{y_test[i]}, 预测价格为{pred_y[0]}")
  33. """
  34. 真实价格为11.0, 预测价格为10.062500000000002
  35. 真实价格为8.5, 预测价格为10.28125
  36. 真实价格为15.0, 预测价格为13.093750000000002
  37. 真实价格为18.0, 预测价格为18.145833333333336
  38. 真实价格为11.0, 预测价格为13.3125
  39. """
  40. print(f"模型的参数为:{model.coef_},截距b为:{model.intercept_}")
  41. # 模型的参数为:[1.01041667 0.39583333],截距b为:1.1875
  42. # 我们也可以通过Numpy的最小二乘函数库lstsq来计算出上面的参数值和截距。
  43. X_train = np.insert(X_train, 0, values=1, axis=1) # 在X_train的第二个维度所有索引为0的位置添加1
  44. print(X_train)
  45. """
  46. [[ 1 6 2]
  47. [ 1 8 1]
  48. [ 1 10 0]
  49. [ 1 14 2]
  50. [ 1 18 0]]
  51. """
  52. print(lstsq(X_train, y_train)[0]) # [1.1875 1.01041667 0.39583333]
  53. # 方法二求参数
  54. print(dot(inv(dot(transpose(X_train), X_train)), dot(transpose(X_train), y_train)))
  55. # [1.1875 1.01041667 0.39583333]
  56. if __name__ == '__main__':
  57. main()

如果你仔细看过上面的代码,可能会有一个疑问,当我们用Numpy去求参数的时候,为什么先在每个对象index为0的位置insert了1🤔。
ok,下面我们来讲一下。
多元线性回归的公式如下所示:
第五章 从简单线性回归到多元线性回归 - 图1
转换成矩阵运算表示为:
第五章 从简单线性回归到多元线性回归 - 图2
等同于
第五章 从简单线性回归到多元线性回归 - 图3
看到上面的式子是不是恍然大悟😀
另外我们也通过下面的式子求参数第五章 从简单线性回归到多元线性回归 - 图4
第五章 从简单线性回归到多元线性回归 - 图5 => 第五章 从简单线性回归到多元线性回归 - 图6

二、多项式回归

在上面线性回归的例子里,我们假设的前提:解释变量和响应变量之间的关系是线性的。但是但多数的现实案例不是这么理想的情况。在这部分,我们对响应变量盒多项式特征项之间的关系进行建模,练习多项式回归
先来让我们看一下多项式的公式:
第五章 从简单线性回归到多元线性回归 - 图7
下面我们依然是拿披萨问题来作为案例。假设披萨直径做为唯一解释变量,然后用相同的数据集来比较一下线性回归和多项式回归之间的不同。
训练数据:

训练实例 直径(单位:英寸) 价格(单位:美元)
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

废话少说,直接干代码

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from sklearn.preprocessing import PolynomialFeatures
  4. from sklearn.linear_model import LinearRegression
  5. plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 中文显示
  6. def main():
  7. X_train = [
  8. [6],
  9. [8],
  10. [10],
  11. [14],
  12. [18]
  13. ]
  14. y_train = [7, 9, 13, 17.5, 18]
  15. X_test = [
  16. [6],
  17. [8],
  18. [11],
  19. [16],
  20. ]
  21. y_test = [8, 12, 15, 18]
  22. xx = np.linspace(0, 26, 100).reshape(-1, 1)
  23. X_train = np.array(X_train)
  24. y_train = np.array(y_train)
  25. X_test = np.array(X_test)
  26. y_test = np.array(y_test)
  27. model = LinearRegression()
  28. model.fit(X_train, y_train)
  29. preds_y = model.predict(xx)
  30. print(preds_y)
  31. quadratic_featurizer = PolynomialFeatures(degree=2) # 2-阶多项式
  32. quadratic_featurizer_3 = PolynomialFeatures(degree=3) # 3-阶多项式
  33. quadratic_featurizer_9 = PolynomialFeatures(degree=9) # 9-阶多项式
  34. # 训练数据特征转换2-阶多项式
  35. quadratic_X_train_2 = quadratic_featurizer.fit_transform(X_train)
  36. quadratic_X_test_2 = quadratic_featurizer.fit_transform(X_test)
  37. quadratic_xx_2 = quadratic_featurizer.fit_transform(xx)
  38. # 训练数据特征转换3-阶多项式
  39. quadratic_X_train_3 = quadratic_featurizer_3.fit_transform(X_train)
  40. quadratic_X_test_3 = quadratic_featurizer_3.fit_transform(X_test)
  41. quadratic_xx_3 = quadratic_featurizer_3.fit_transform(xx)
  42. # 训练数据特征转换9-阶多项式
  43. quadratic_X_train_9 = quadratic_featurizer_9.fit_transform(X_train)
  44. quadratic_X_test_9 = quadratic_featurizer_9.fit_transform(X_test)
  45. quadratic_xx_9 = quadratic_featurizer_9.fit_transform(xx)
  46. # 训练2-阶多项式回归模型
  47. quadratic_model_2 = LinearRegression()
  48. quadratic_model_2.fit(quadratic_X_train_2, y_train)
  49. quadratic_preds_y_2 = quadratic_model_2.predict(quadratic_xx_2)
  50. # 训练3-阶多项式回归模型
  51. quadratic_model_3 = LinearRegression()
  52. quadratic_model_3.fit(quadratic_X_train_3, y_train)
  53. quadratic_preds_y_3 = quadratic_model_3.predict(quadratic_xx_3)
  54. # 训练9-阶多项式回归模型
  55. quadratic_model_9 = LinearRegression()
  56. quadratic_model_9.fit(quadratic_X_train_9, y_train)
  57. quadratic_preds_y_9 = quadratic_model_9.predict(quadratic_xx_9)
  58. # 计算r2
  59. linear_score = model.score(X_test, y_test)
  60. q2_score = quadratic_model_2.score(quadratic_X_test_2, y_test)
  61. q3_score = quadratic_model_3.score(quadratic_X_test_3, y_test)
  62. q9_score = quadratic_model_9.score(quadratic_X_test_9, y_test)
  63. print(f"R2得分:\n 线性回归:{linear_score}\n"
  64. f"2阶多项式:{q2_score}\n"
  65. f"3阶多项式:{q3_score}\n"
  66. f"9阶多项式:{q9_score}")
  67. """
  68. R2得分:
  69. 线性回归:0.809726797707665
  70. 2阶多项式:0.8675443656345054
  71. 3阶多项式:0.8356924156037133
  72. 9阶多项式:-0.09435666704315704
  73. """
  74. plt.figure()
  75. l1 = plt.plot(xx, preds_y,label="线性回归")
  76. l2 = plt.plot(xx, quadratic_preds_y_2, c='r', linestyle='--', label='2阶多项式')
  77. l3 = plt.plot(xx, quadratic_preds_y_3, c='b', linestyle=':', label="3阶多项式")
  78. l9 = plt.plot(xx, quadratic_preds_y_9, c='y', linestyle='-.', label="9阶多项式")
  79. plt.legend()
  80. plt.grid(True)
  81. plt.scatter(X_train, y_train) # 画坐标点
  82. plt.axis([0, 25, 0, 25]) # 设置坐标轴长度
  83. plt.title("披萨价格的回归拟合")
  84. plt.xlabel("披萨的直径(单位:英寸)")
  85. plt.ylabel('披萨的价格(单位:美元)')
  86. plt.show()
  87. if __name__ == '__main__':
  88. main()

最后我们得到下图
Figure_1.png
结合R方得分和上图,我们可以判断出2阶的效果最好。另外,我们可以看到9阶几乎完全拟合了训练数据,并且R方的得分为-0.09,由此我们可以判断出9阶的模型过拟合

三、 正则化

多项式回归最后的部分,我们提到了过拟合。这部分我们要讲的正则化就是防止过拟合的一个方法。你可以理解为为问题增加一个信息(惩罚项)。
sklearn库主要提供了下面几种正则化方法。

岭回归 LASSO回归 弹性网正则化

岭回归

LASSO回归通过对代价函数增加第五章 从简单线性回归到多元线性回归 - 图9范数来惩罚系数。公式如下:
第五章 从简单线性回归到多元线性回归 - 图10
第五章 从简单线性回归到多元线性回归 - 图11是一个控制惩罚力度的超参数,超参数是模型控制学习算法如何学习的参数。随着第五章 从简单线性回归到多元线性回归 - 图12的增加,惩罚力度也增加,代价函数的值也增加。当第五章 从简单线性回归到多元线性回归 - 图13为0时,岭回归等于线性回归。

LASSO回归

LASSO回归通过对代价函数增加第五章 从简单线性回归到多元线性回归 - 图14范数来惩罚系数。公式如下:
第五章 从简单线性回归到多元线性回归 - 图15
LASSO回归产出的参数,大多数系数将变为0,模型将依赖于特征的一个小型子集。相反,岭回归产出模型的大多数参数都很小但不是0

弹性网正则化

最后,sklearn还提供了弹性网正则化的实现。它是LASSO回归的第五章 从简单线性回归到多元线性回归 - 图16和岭回归第五章 从简单线性回归到多元线性回归 - 图17的线形组合。也就是说,LASSO回归和岭回归都是弹性网正则超参数为0的时候的特殊形式。
公式如下:
第五章 从简单线性回归到多元线性回归 - 图18

四、线性回归实战

在前面我们已经学习线性回归模型相关的一些知识。现在我们用一个真实的数据集来进行一下实战。
并这里比较一下用随机梯度下降和最小二乘的不同

  1. from sklearn.datasets import load_boston
  2. from sklearn.preprocessing import StandardScaler
  3. from sklearn.model_selection import train_test_split
  4. from sklearn.linear_model import SGDRegressor, LinearRegression
  5. def main():
  6. datas = load_boston()
  7. X_train, X_test, y_train, y_test = train_test_split(datas.data, datas.target, test_size=0.2,
  8. random_state=2020, shuffle=True)
  9. x_scaler = StandardScaler()
  10. y_scaler = StandardScaler()
  11. X_train = x_scaler.fit_transform(X_train)
  12. y_train = y_scaler.fit_transform(y_train.reshape(-1, 1))
  13. X_test = x_scaler.transform(X_test)
  14. y_test = y_scaler.transform(y_test.reshape(-1, 1))
  15. model = SGDRegressor(loss='squared_loss', learning_rate='adaptive')
  16. model.fit(X_train, y_train)
  17. print("随机梯度下降模型R方得分:\n", model.score(X_test, y_test.reshape(-1, 1)))
  18. model = LinearRegression()
  19. model.fit(X_train, y_train)
  20. print("最小二乘模型R方得分:\n", model.score(X_test, y_test.reshape(-1, 1)))
  21. """
  22. 随机梯度下降模型R方得分:
  23. 0.7680884678774204
  24. 最小二乘模型R方得分:
  25. 0.7682699602324874
  26. """
  27. if __name__ == '__main__':
  28. main()