2022年 11月 5日

Python实现线性回归(公式推导+源代码)

写这篇文章之前,首先要对自己做一个小小的反思,很多时候在学习新技术的时候,看到出了什么什么框架,在这个框架上什么什么方法可以直接拿过来用,这样的好处就是我们可以减少写代码量,几个函数就可以帮我们解决需要写几十行代码才能解决的问题。这样看起来很好是建立在你对这个函数的底层有一个深入研究,如果你是一个新手,第一次你可能在网上查阅资料明白一个大概,但是你下次还是不会,究其原因:知其然,而不知其所以然。

对我而言,我身上也存在着这种通病,为了能:知其然,知其所以然。于是乎,我打算在学习机器学习算法的过程中,不调用机器学习函数库里面的一些封装方法,通过纯Python去实现,这一篇文章算是作为我践行:知其然,知其所以然这种思想的第一步。

好了,话不多说,开始进入我们的主题。

线性回归,就是能够用一条直线较为精确地描述数据之间的关系。这样当出现新的数据的时候,就能够预测出一个简单的值。线性回归中最常见的就是房价的问题。

在这里插入图片描述在这里插入图片描述

左图代表的是房价(纵坐标)与房间面积(横坐标)的关系,右图代表的是通过一条直线f = kx + b 来预测房屋关系。通过这条直线我们就可以大概知道房间面积对应的房价。

下面我们通过一个具体的例子通过Python来实现一个简单的线性回归

比如我们有一组数据,数据的格式如下所示(数据地址):

       

我们将这些数据通过python绘制出来,如下所示:

然后我们需要做的是找一条直线去最大化的拟合这些点,理想情况是所有点都落在直线上。希望所有点离直线的距离最近。简单起见,将距离求平方,误差可以表示为

image.png

找到最能拟合数据的直线,也就是最小化误差。通常我们使用的是最小二乘法

上述公式只有m, b未知,因此可以看最一个m, b的二次方程,求Loss的问题就转变成了求极值问题。 这里不做详细说明。另每个变量的偏导数为0, 求方程组的解。

image.png

image.png

然后我们通过梯度下降的方法去更新m和b。

在动手写算法之前我们需要理一下编写思路。回归模型主体部分较为简单,关键在于如何在给出 MSE损失函数之后基于梯度下降的参数更新过程。首先我们需要写出模型的主体和损失函数以及基于损失函数的参数求导结果,然后对参数进行初始化,最后写出基于梯度下降法的参数更新过程。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. # 计算loss
  4. def liner_loss(w,b,data):
  5. """
  6. :param w:
  7. :param b:
  8. :param data:
  9. :return:
  10. """
  11. x = data[:,0] # 代表的是第一列数据
  12. y = data[:,1] # 代表的是第二列数据
  13. # 损失函数:使用的是均方误差(MES)损失
  14. loss = np.sum((y - w * x - b) ** 2) / data.shape[0]
  15. # 返回loss
  16. return loss
  17. # 计算梯度并更新参数
  18. def liner_gradient(w,b,data,lr):
  19. """
  20. :param w:
  21. :param b:
  22. :param data:
  23. :param lr:
  24. :return:
  25. """
  26. # 数据集行数
  27. N = float(len(data))
  28. # 提取数据
  29. x = data[:,0]
  30. y = data[:,1]
  31. # 求梯度
  32. dw = np.sum(-(2 / N) * x * (y - w * x -b))
  33. db = np.sum(-(2 / N) * (y - w * x -b))
  34. # 更新参数
  35. w = w - (lr * dw)
  36. b = b - (lr * db)
  37. return w,b
  38. # 每次迭代做梯度下降
  39. def optimer(data,w,b,lr,epcoh):
  40. """
  41. :param data:
  42. :param w:
  43. :param b:
  44. :param lr:
  45. :param epcoh:训练的次数
  46. :return:
  47. """
  48. for i in range(epcoh):
  49. # 通过每次循环不断更新w,b的值
  50. w,b = liner_gradient(w,b,data,lr)
  51. # 每训练100次更新下loss值
  52. if i % 100 == 0 :
  53. print('epoch {0}:loss={1}'.format(i,liner_loss(w,b,data)))
  54. return w,b
  55. # 绘图
  56. def plot_data(data,w,b):
  57. """
  58. :param data:
  59. :param w:
  60. :param b:
  61. """
  62. x = data[:,0]
  63. y = data[:,1]
  64. y_predict = w * x + b
  65. plt.plot(x, y, 'o')
  66. plt.plot(x, y_predict, 'k-')
  67. plt.show()
  68. def liner_regression():
  69. """
  70. 构建模型
  71. """
  72. # 加载数据
  73. data = np.loadtxt('D:\python-workspace\Machine_Learing\Regression\data.csv',delimiter=',')
  74. # 显示原始数据的分布
  75. x = data[:, 0]
  76. y = data[:, 1]
  77. plt.plot(x, y, 'o')
  78. plt.show()
  79. # 初始化参数
  80. lr = 0.01 # 学习率
  81. epoch = 1000 # 训练次数
  82. w = 0.0 # 权重
  83. b = 0.0 # 偏置
  84. # 输出各个参数初始值
  85. print('initial variables:\n initial_b = {0}\n intial_w = {1}\n loss of begin = {2} \n'\
  86. .format(b,w,liner_loss(w,b,data)))
  87. # 更新w和b
  88. w,b = optimer(data,w,b,lr,epoch)
  89. # 输出各个参数的最终值
  90. print('final formula parmaters:\n b = {1}\n w = {2}\n loss of end = {3} \n'.format(epoch,b,w,liner_loss(w,b,data)))
  91. # 显示
  92. plot_data(data,w,b)
  93. if __name__ == '__main__':
  94. liner_regression()

然后我们运行,结果如下:

initial variables:
 initial_b = 0.0
 intial_w = 0.0
 loss of begin = 184.68365853658537 

 

epoch 0:loss=3.265436338536489
epoch 100:loss=1.4187213286545117
epoch 200:loss=1.3652986742281288
epoch 300:loss=1.3437697330412992
epoch 400:loss=1.3350937263246236
epoch 500:loss=1.3315973587190568
epoch 600:loss=1.330188348003359
epoch 700:loss=1.329620526932774
epoch 800:loss=1.3293916991711343
epoch 900:loss=1.3292994832474747

final formula parmaters:
 b = 1.2393038013472177
 w = 1.8672419688724071
 loss of end = 1.3292625499252577 

 

 

 

 

 

 

 

 

 

 

 

 

好了,以上就是Python实现的一个简单的线性回归。

不积跬步,无以至千里。加油!