文章首次发表于本人 CSDN 博客。原文链接为:【AI 数学】用梯度下降算法优化线性回归方程(含代码)_木盏 - CSDN 博客

众所周知,线性回归(Linear Regression)是最常见的机器学习算法之一,简单但超级实用。线性回归旨在用线性方程来拟合数据分布,在数据量小计算速度要求高的地方是神经网络的最佳替代品

LR 的一般表现形式为: 用梯度下降优化线性回归方程(含代码) - 图1

通常,LR 优化方式可以通过构建均方误差损失函数,得到一个凸函数,计算导数为 0 的位置来确定 用梯度下降优化线性回归方程(含代码) - 图2
用梯度下降优化线性回归方程(含代码) - 图3
,就如周志华老师西瓜书里描述的那样。在工程上,我们可以把 LR 当做一个简易的神经网络来对待,用梯度下降算法就可以优化。本文提供一个梯度下降算法优化 LR 的实验例子,有助于加深大家对 LR 以及梯度下降的理解。

实验意图

假设有一个绝对正确的函数 用梯度下降优化线性回归方程(含代码) - 图4
,对于任意输入 用梯度下降优化线性回归方程(含代码) - 图5
,都可以得到一个准确的 用梯度下降优化线性回归方程(含代码) - 图6
值。那咱们只需要知道真实的 用梯度下降优化线性回归方程(含代码) - 图7
用梯度下降优化线性回归方程(含代码) - 图8
即可。假定真值 用梯度下降优化线性回归方程(含代码) - 图9
,而真值 用梯度下降优化线性回归方程(含代码) - 图10
。首先咱们可以通过真值生成出大量的样本对 用梯度下降优化线性回归方程(含代码) - 图11
,然后通过这些样本可以训练一个初始化的函数。用梯度下降算法修正参数即可。

重点:咱们只需把随机初始化的 用梯度下降优化线性回归方程(含代码) - 图12
用梯度下降优化线性回归方程(含代码) - 图13
优化到接近 用梯度下降优化线性回归方程(含代码) - 图14
用梯度下降优化线性回归方程(含代码) - 图15
的数值即可。

先做一个简单的数学推导:(其中 用梯度下降优化线性回归方程(含代码) - 图16
,为损失函数)

用梯度下降优化线性回归方程(含代码) - 图17
的梯度: 用梯度下降优化线性回归方程(含代码) - 图18
用梯度下降优化线性回归方程(含代码) - 图19
用梯度下降优化线性回归方程(含代码) - 图20

用梯度下降优化线性回归方程(含代码) - 图21
的更新公式: 用梯度下降优化线性回归方程(含代码) - 图22

用梯度下降优化线性回归方程(含代码) - 图23
的梯度: 用梯度下降优化线性回归方程(含代码) - 图24

用梯度下降优化线性回归方程(含代码) - 图25
的更新公式: 用梯度下降优化线性回归方程(含代码) - 图26

根据上述公式,咱们可以写出 python 代码和 C++ 代码:(代码均为本人原创,借用请告知)

  1. ########################################################
  2. # @author: MuZhan
  3. # @contact: levio.pku@gmail.com
  4. # experiment: using GD to optimize Linear Regression
  5. # To fit `y=w*x+b`, where x and w are multi-dim vectors.
  6. ########################################################
  7. import numpy as np
  8. # initial setting
  9. np.random.seed(10)
  10. epochs = 30
  11. lr = .1 # learning rate
  12. w_ = np.array([3, 1, 4, 1, 5, 9, 2, 6]) # the ground truth w
  13. b_ = 3.7 # the ground truth b
  14. SAMPLE_NUM = 100
  15. x_dim = len(w_)
  16. # preparing random (x, y) pairs
  17. print('preparing data...')
  18. x_list = []
  19. y_list = []
  20. for i in range(SAMPLE_NUM):
  21. x = np.random.rand(x_dim)
  22. y = w_.dot(x) + b_
  23. x_list.append(x)
  24. y_list.append(y)
  25. # init w
  26. np.random.seed(10)
  27. w = np.random.rand(x_dim)
  28. # init b
  29. b = 1
  30. # training
  31. print('training...')
  32. for e in range(epochs):
  33. print('epoch: ', e, end='\t')
  34. sum_loss = 0
  35. for i in range(len(x_list)):
  36. x = x_list[i]
  37. y_ = y_list[i]
  38. y = w.dot(x) + b
  39. loss = (y - y_) ** 2
  40. sum_loss += loss
  41. # use Gradient Descent to update parameters
  42. w = w - 2 * lr * (y - y_) * x
  43. b = b - 2 * lr * (y - y_)
  44. print('loss: ', sum_loss)
  45. print('Ground Truth w: ', w_, end='\t')
  46. print('learned w: ', w)
  47. print('Ground Truth b: ', b_, end='\t')
  48. print('learned b: ', b)

欣赏一下 C++ 代码实现:

  1. #include<iostream>
  2. #include<vector>
  3. #include<cstdlib>
  4. #define SAMPLES_NUM 10000
  5. #define EPOCHS 20
  6. #define LEARNING_RATE 0.001
  7. using namespace std;
  8. float dot(float* x, float* w, int length){
  9. float res = 0;
  10. for(int i=0; i<length; ++i){
  11. res += x[i] * w[i];
  12. }
  13. return res;
  14. }
  15. float get_random(){
  16. return (rand() % 1000) / 100.0;
  17. }
  18. void update_weights(float* x, float* w, int length, float sqrt_loss)
  19. {
  20. for(int i=0; i<length; ++i){
  21. w[i] -= 2 * LEARNING_RATE * sqrt_loss * x[i];
  22. }
  23. }
  24. int main(){
  25. float x[8] = {1, 1, 2, 2, 3, 3, 4, 4};
  26. float w_[8] = {3, 1, 4, 1, 5, 9, 2, 6}; // Ground Truth w
  27. float b_ = 3.7; // Ground Truth b
  28. int length = int(sizeof(w_)/sizeof(w_[0]));
  29. float y;
  30. // collect samples
  31. vector<vector<float>> samples;
  32. vector<float> tmp;
  33. for(int i=0; i<=length; ++i) tmp.push_back(0);
  34. for(int i=0; i<SAMPLES_NUM; ++i){
  35. for(int j=0; j<length; ++j){
  36. tmp[j] = get_random();
  37. x[j] = tmp[j];
  38. }
  39. y = dot(x, w_, length) + b_;
  40. tmp[length] = y;
  41. samples.push_back(tmp);
  42. }
  43. // init w, b
  44. float w[8] = {1, 2, 3};
  45. float b = 1.0f;
  46. // training
  47. float x_tmp[length];
  48. float y_tmp, loss;
  49. for(int e=0; e<EPOCHS; ++e){
  50. for(int i=0; i<samples.size(); ++i){
  51. vector<float> sample = samples[i];
  52. copy(sample.begin(), sample.end(), x_tmp);
  53. y_tmp = sample[length];
  54. y = dot(x_tmp, w, length) + b;
  55. loss = (y - y_tmp) * (y - y_tmp);
  56. // update parameters
  57. update_weights(x_tmp, w, length, (y - y_tmp));
  58. b -= 2 * LEARNING_RATE * (y - y_tmp);
  59. }
  60. cout<<loss<<endl;
  61. }
  62. for(int i=0; i<length; ++i){
  63. cout<<w[i]<<" ";
  64. }
  65. cout<<endl;
  66. cout<<b<<endl;
  67. return 0;
  68. }

Python 代码输出结果:

用梯度下降优化线性回归方程(含代码) - 图27
https://zhuanlan.zhihu.com/p/431405093