zoukankan      html  css  js  c++  java
  • 机器学习:批量梯度下降法(线性回归中的使用)

    一、推导目标函数

     1)基础概念

    • 多元线性回归模型:
    • 多元线性回归的损失函数:
    • 参数 theta:θ = (θ0, θ1, θ3, ..., θn)
    • n:表示模型中有 n 个特征参数;
    • θ1:表示
    • 梯度:,对每一个 θi 求一次偏导数;
    • 梯度代表方向:对应 J 增大最快的方向;
    • 偏导数:函数 J 中含有 n 个未知数,每次知道其中的一个未知数求导,其它数看作常量,求得的数是函数 J 的偏导数;
    • 学习率:η
    • theta每次变化量: | 学习率 X 梯度 | == -η *  ▽J ;(带负号 “ - ” ,因为损失函数与参数负相关,其导数值为负,变化量要为正数)

     2)梯度下降原理

    1. 此图为有两个参数的梯度下降法的可视化:z = x2 + 2y2
    2. 一圈圈的红线为等高线,也就是每次参数x、y的变化后目标函数 z 的取值;
    3. 越外圈的 z 的取值越大,中心位置表示 z 的最小值;
    4. z 的取值可以延不同的方向逐层下降,箭头表示梯度下降的方向,也是 z 的取值变化最快的方向;
    5. 圈与圈之间的间距为目标函数 z 的变化量;
    6. 最外圈的间距较大,也就是 z 的变化量较大;

     3)推导目标函数

    • 线性回归算法中的目标函数的第一次变形
    • 分析目标函数
    1. ▽J(θ) 中,θ 是未知数,X 是样本中的已知数;
    2. 公式变形思路:▽J(θ) 中的每一项都是 m 项的求和,因此梯度的大小跟样本数量有关,样本数量越大,梯度中的每一个元素值也就越大,因此所求得的梯度中的每一个元素的值,受到了 m 的影响,而在优化的过程中,梯度中的每一个元素的值最好和 m 无关;
    • 确定最终的目标函数
    1. 目标函数变形——确定使用梯度法所要优化的最终的目标函数:J(θ) = MSE (y, ý)
    2. 添加一个 1/m 是为了减小梯度的元素值;

     4)思考总结

    • 当使用梯度下降法求解目标函数的最小值时,需要特殊设计目标函数,不见得所有的目标函数都非常合适此方法,虽然理论上即使梯度中的元素值很大,依然可以通过调整 η 得到想要的结果,但是这样可能会影响效率;

    二、算法的实现(1)

     1)解决简单线性回归算法

    • 模拟简单线性回归
      import numpy as np
      
      np.random.seed(666)
      x = 2 * np.random.random(size=100)
      y = x * 3. + 4. + np.random.normal(size=100)
      
      X = x.reshape(-1, 1)

      # np.random.normal(size=100):噪音,用均值为0,方差为1的随机正态分布生成

    • 计算损失函数值(损失函数:J(θ) = MSE(y, ý))
      # x_b:变形后的X_train中的Xb,增加了第一列全为1之后的矩阵
      # y:y_train
      def J(theta, x_b, y):
          try:
              return np.sum((y - x_b.dot(theta)) ** 2) / len(x_b)
          except:
              return float('inf')

      # 计算损失函数的过程中要进行一场捕捉:正常运行时执行try,出现异常时执行except;

    • 计算梯度值(梯度:J(θ))
      def gradient_descent(x_b, y, initial_theta, eta, n_iters = 10**4, epsilon=10**-8):
          
          theta = initial_theta
          i_iter = 0
          
          while i_iter < n_iters:
              gradient = dJ(theta, x_b, y)
              last_theta = theta
              theta = theta - eta * gradient
          
              if(abs(J(theta, x_b, y) - J(last_theta, x_b, y)) < epsilon):
                  break
               
              i_iter += 1
              
          return theta
    1. x_b:变形后的X_train数据集;
    2. y:训练数据集y_train;
    3. initial_theta:用于存储搜索过程中的theta值;
    4. eta:xuexilv;
    5. n_iters:搜索循环的次数;
    6. epsilon:搜索精度;
    • 初始化theta和eta
      # 变形数据集X,得到x_b
      x_b = np.hstack([np.ones((len(X), 1), dtype=float), X])
      
      # 初始化theta向量:使用便利 initial_theta,大小为x_b.shape[1],也就是数据集的特征种类数
      initial_theta = np.zeros(x_b.shape[1])
      
      # 初始化eta
      eta = 0.01
      
      theta = gradient_descent(x_b, y, initial_theta, eta)
      theta
      # 输出:array([4.02145786, 3.00706277])

      # theta[0]为截距,theta[1:]表示系数,这里也可以理解为斜率(简单线性回归中);

     2)封装即调用

    • 封装在线性回归算法LinearRegression类中,作为一种拟合方法
          def fit_gd(self, X_train, y_train, eta=0.01, n_iters=1e4):
              """根据训练数据集X_train, y_train, 使用梯度下降法训练Linear Regression模型"""
              assert X_train.shape[0] == y_train.shape[0], 
                  "the size of X_train must be equal to the size of y_train"
      
              def J(theta, X_b, y):
                  try:
                      return np.sum((y - X_b.dot(theta)) ** 2) / len(y)
                  except:
                      return float('inf')
      
              def dJ(theta, X_b, y):
                  res = np.empty(len(theta))
                  res[0] = np.sum(X_b.dot(theta) - y)
                  for i in range(1, len(theta)):
                      res[i] = (X_b.dot(theta) - y).dot(X_b[:, i])
                  return res * 2 / len(X_b)
                  # return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(X_b)
      
              def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):
      
                  theta = initial_theta
                  cur_iter = 0
      
                  while cur_iter < n_iters:
                      gradient = dJ(theta, X_b, y)
                      last_theta = theta
                      theta = theta - eta * gradient
                      if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
                          break
      
                      cur_iter += 1
      
                  return theta
      
              X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
              initial_theta = np.zeros(X_b.shape[1])
              self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)
      
              self.intercept_ = self._theta[0]
              self.coef_ = self._theta[1:]
      
              return self

      # self._theta:为LinearRegression类的参数;

     3)在Jupyter NoteBook中调用封装的代码

    • from LR.LinearRegression import LinearRegression
      
      lin_reg = LinearRegression()
      lin_reg.fit_gd(X, y)
      
      # 查看系数
      lin_reg.coef_
      # 输出:array([3.00706277])
      
      # 查看截距
      lin_reg.intercept_
      # 输出:4.021457858204859

    三、梯度下降法的向量化和数据的标准化

     1)对梯度多向量化处理

    • 对计算公式向量化后,使计算更简单
    • 向量化过程
    • 最终的向量化结果
    1. XbT == Xb . T:是 Xb 的转置矩阵;
    • 改变梯度的计算方式(更改函数数 dJ():)
      # res = np.empty(len(theta))
      # res[0] = np.sum(X_b.dot(theta) - y)
      # for i in range(1, len(theta)):
           # res[i] = (X_b.dot(theta) - y).dot(X_b[:, i])
      # return res * 2 / len(X_b)
      
      # 更换为:
      return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(X_b)

     2)实例

    • 数据处理:分割

      import numpy as np
      from sklearn import datasets
      
      boston = datasets.load_boston()
      X = boston.data
      y = boston.target
      
      X = X[y < 50.0]
      y = y[y < 50.0]
      
      from ALG.data_split import train_test_split
      
      X_train, X_test, y_train, y_test = train_test_split(X, y, seed=666)
    • 使用正规方程优化损失函数
      from LR.LinearRegression import LinearRegression
      
      lin_reg1 = LinearRegression()
      
      # %time 语句:返回该行代码执行所用的时间
      %time lin_reg1.fit_normal(X_train, y_train)
      lin_reg1.score(X_test, y_test)
      
      # 输出:Wall time: 35.7 ms
             0.8129802602658466
    • 使用梯度下降法优化损失函数
      lin_reg2 = LinearRegression()
      %time lin_reg2.fit_gd(X_train, y_train)
      lin_reg2.score(X_test, y_test)
      
      # 输出:Wall time: 478 ms
              nan
    1. 问题一:没有得到准确度,说明优化时损失函数没有收敛

    2. 分析:数据集中的数据值的大小差异较大,使用默认的 eta 求得的梯度非常大,使得梯度下降法的结果不收敛;
    3. 方案:修改 eata 值
    • 修改 eta 值,再次优化
      lin_reg2.fit_gd(X_train, y_train, eta=0.000001)
      
      lin_reg2.score(X_test, y_test)
      # 输出:0.2755663485338923
    1. 问题二:准确度太小(与正规方程优化结果比较而言);
    2. 分析: eta 值太小,根据设定的循环次数没能找到损失函数的最小值;
    3. 方案:修改循环次数
    • 修改循环次数(n_iters),再次优化
      lin_reg2.fit_gd(X_train, y_train, eta=0.000001, n_iters=10**6)
      
      lin_reg2.score(X_test, y_test)
      # 输出:0.7541852353980764
    1. 问题三:准确度还是太小(与正规方程优化结果比较而言),没有到达损失函数的最小值;
    2. 分析:循环次数太小,或者 eta 值太小;
    3. 问题四:如果循环次数过大,或者 eta 太小,循环耗时比较大
    4. 分析(1):之所以出现这种现象,因为整体的数据集的数值不在同一个规模上,也就是大小相差太大;
    5. 分析(2):由于有 eta 这个变量,如果最终数据集的数据不在一个维度上(大小相差很大)将会影响梯度的结果,梯度的结果 乘与 eta 是损失函数真正每次变化的步长,则步长有可能或者太大或者太小,如果步长太大会导致损结果不收敛,如果步长太小会导致搜索过程太慢;
    6. 方案:对数据集做归一化处理
    • 用正规方程解线性回归的损失函数时,不需要对数据集归一化处理,因为正规方程中的模型的求解过程,整体变成了一个公式的计算,在公式计算过程中牵涉到的搜索的过程比较少

    • 对数据做归一化处理,再次使用梯度下降法
      from sklearn.preprocessing import StandardScaler
      
      standardScaler = StandardScaler()
      standardScaler.fit(X_train, y_train)
      
      
      # 归一化处理数据
      # X_train_standard:归一化后的训练数据集
      # X_test_standard:归一化后的测试数据集
      X_train_standard = standardScaler.transform(X_train)
      X_test_standard = standardScaler.transform(X_test)
      
      
      # 再次使用梯度下降法
      lin_reg3 = LinearRegression()
      %time lin_reg3.fit_gd(X_train_standard, y_train)
      # 输出:Wall time: 233 ms
      # 优点:没有更改默认的 eta 和循环次数 n_iters,fit 速度很快:269ms
      
      lin_reg3.score(X_test_standard, y_test)
      # 输出:0.8129880620122235
      # 与正规化方程所得结果相近

      3)梯度下降法比正规化方程优化法的优势

    • 实例
      import numpy as np
      from LR.LinearRegression import LinearRegression
      
      m = 1000
      n = 5000
      
      big_x = np.random.normal(size=(m, n))
      true_theta = np.random.uniform(0.0, 100.0, size=n+1)
      big_y = big_x.dot(true_theta[1:]) + true_theta[0] + np.random.normal(0., 10, size=m)
      
      
      # 正规方程法优化
      big_reg1 = LinearRegression()
      %time big_reg1.fit_normal(big_x, big_y)
      # 输出:Wall time: 8.94 s
      
      
      # 梯度下降法优化
      big_reg2 = LinearRegression()
      % time big_reg2.fit_gd(big_x, big_y)
      # 输出:Wall time: 4.62 s
    1. 优点:使用梯度下降法优化,耗时更少

    2. 原因:正规方程处理的是 m 行 n 列的矩阵,对矩阵进行非常多的乘法运算,如果矩阵比较大时,用正规方程法优化耗时较多
    • 问题:本次举例中,样本数量(m)小于特征量(n),目前在使用梯度下降法计算梯度时,要让每一个样本都参与计算,如果样本量比较大时,计算梯度也比较慢
    • 方案:使用随机梯度下降法
  • 相关阅读:
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 15—Anomaly Detection异常检测
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 14—Dimensionality Reduction 降维
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 13—Clustering 聚类
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 12—Support Vector Machines 支持向量机
    【原】机器学习公开课 目录(课程笔记、测验习题答案、编程作业源码)...持续更新...
    【原】Coursera—Andrew Ng机器学习—Week 11 习题—Photo OCR
    【原】Coursera—Andrew Ng机器学习—Week 10 习题—大规模机器学习
    【原】Coursera—Andrew Ng机器学习—Week 9 习题—异常检测
    【原】Coursera—Andrew Ng机器学习—Week 8 习题—聚类 和 降维
    【原】Coursera—Andrew Ng机器学习—Week 7 习题—支持向量机SVM
  • 原文地址:https://www.cnblogs.com/volcao/p/9116868.html
Copyright © 2011-2022 走看看