zoukankan      html  css  js  c++  java
  • 梯度下降算法(1)

    • 算法介绍
      梯度下降算法是一种利用一次导数信息求取目标函数极值的方法,也是目前应用最为广泛的局部优化算法之一。其具有实现简单、容易迁移、收敛速度较快的特征。在求解过程中,从预设的种子点开始,根据梯度信息逐步迭代更新,使得种子点逐渐向目标函数的极小值点移动,最终到达目标函数的极小值点。
      注意,沿梯度正向移动,将获取目标函数局部极大值梯度上升算法);沿梯度反向移动,将获取目标函数局部极小值梯度下降算法)。
    • 迭代公式
      设向量$vec g_k$表示目标函数在种子点$vec x_k$处的梯度(即一次导数)。此时,根据梯度信息的指导,可以使得种子点更加接近该向量方向的极值点(注意,目标函数真实的极值点是全方向的)。
      求取极小值,沿梯度反方向移动(即梯度下降):
      egin{equation}label{eq_1}
      vec x_{k+1} = vec x_k - {lambda}_k vec s_k
      end{equation}
      求取极大值,沿梯度正方向移动(即梯度上升):
      egin{equation}label{eq_2}
      vec x_{k+1} = vec x_k + {lambda}_k vec s_k
      end{equation}
      其中,$vec s_k = frac{vec g_k}{left| vec g_k ight|}$代表归一化梯度,${lambda}_k$代表种子点沿梯度方向移动的步长幅度参数
      很显然,对幅度参数${lambda}_k$的设置也属于算法的一部分。最常见的有两种方法:1)线性搜寻法;2)可调步长法
      线性搜寻法中,在种子点的梯度方向上搜寻到极值点附近的步长幅度参数${lambda}_k$,然后移动种子点至该方向的极值点处。继续计算种子点新的梯度方向,并在该方向上移动。直到种子点到达全方向的极值点处,迭代即可终止。
      可调步长法中,通常先将${lambda}_k$设为1。然后依据上面的迭代公式(式$ ef{eq_1}$或式$ ef{eq_2}$),预先计算下一步可能的$x_{k+1}$。如果$x_{k+1}$满足接近极值点的要求,则将种子点由$x_k$移至$x_{k+1}$,并增加${lambda}_k$值为原先的$1.2$倍;否则,不移动种子点,并将${lambda}_k$值减小为原先的$0.5$倍。如此反复迭代计算,逐步移动种子点并改变${lambda}_k$值至找到极值点为止。由于${lambda}_k$值随下一步的预计算情况逐步作出调整,因此笔者也将其称为动态调整技术
      从节省计算资源的角度考虑,以下笔者将采用动态调整技术完成对梯度下降算法的示例,仅供参考!
    • Python代码实现
       1 import matplotlib.pyplot as plt
       2 import numpy
       3 
       4 
       5 class GD(object):
       6 
       7     def __init__(self, seed=None, precision=1.E-6):
       8         self.seed = GD.get_seed(seed)                    # 梯度下降算法的种子点
       9         self.prec = precision                            # 梯度下降算法的计算精度
      10 
      11         self.path = list()                               # 记录种子点的路径及相应的目标函数值
      12         self.solve()                                     # 求解主体
      13         self.display()                                   # 数据可视化展示
      14 
      15     def solve(self):
      16         x_curr = self.seed
      17         val_curr = GD.func(*x_curr)
      18         self.path.append((x_curr, val_curr))
      19 
      20         omega = 1
      21         while omega > self.prec:
      22             x_delta = omega * GD.get_grad(*x_curr)
      23             x_next = x_curr - x_delta                    # 沿梯度反向迭代
      24             val_next = GD.func(*x_next)
      25             
      26             if numpy.abs(val_next - val_curr) < self.prec:
      27                 break
      28 
      29             if val_next < val_curr:
      30                 x_curr = x_next
      31                 val_curr = val_next
      32                 omega *= 1.2
      33                 self.path.append((x_curr, val_curr))
      34             else:
      35                 omega *= 0.5
      36 
      37     def display(self):
      38         print('Iteration steps: {}'.format(len(self.path)))
      39         print('Seed: ({})'.format(', '.join(str(item) for item in self.path[0][0])))
      40         print('Solution: ({})'.format(', '.join(str(item) for item in self.path[-1][0])))
      41 
      42         fig = plt.figure(figsize=(10, 4))
      43 
      44         ax1 = plt.subplot(1, 2, 1)
      45         ax2 = plt.subplot(1, 2, 2)
      46 
      47         ax1.plot(numpy.array(range(len(self.path))) + 1, numpy.array(list(item[1] for item in self.path)), 'k.')
      48         ax1.plot(1, self.path[0][1], 'go', label='starting point')
      49         ax1.plot(len(self.path), self.path[-1][1], 'r*', label='solution')
      50         ax1.set(xlabel='$iterCnt$', ylabel='$iterVal$')
      51         ax1.legend()
      52 
      53         x = numpy.linspace(-100, 100, 500)
      54         y = numpy.linspace(-100, 100, 500)
      55         x, y = numpy.meshgrid(x, y)
      56         z = GD.func(x, y)
      57         ax2.contour(x, y, z, levels=36)
      58 
      59         x2 = numpy.array(list(item[0][0] for item in self.path))
      60         y2 = numpy.array(list(item[0][1] for item in self.path))
      61         ax2.plot(x2, y2, 'k--', linewidth=2)
      62         ax2.plot(x2[0], y2[0], 'go', label='starting point')
      63         ax2.plot(x2[-1], y2[-1], 'r*', label='solution')
      64 
      65         ax2.set(xlabel='$x$', ylabel='$y$')
      66         ax2.legend()
      67 
      68         fig.tight_layout()
      69         fig.savefig('test_plot.png', dpi=500)
      70 
      71         plt.show()
      72         plt.close()
      73 
      74     # 内部种子生成函数
      75     @staticmethod
      76     def get_seed(seed):
      77         if seed is not None:
      78             return numpy.array(seed)
      79         return numpy.random.uniform(-100, 100, 2)
      80 
      81     # 目标函数
      82     @staticmethod
      83     def func(x, y):
      84         return 5 * x ** 2 + 2 * y ** 2 + 3 * x - 10 * y + 4
      85 
      86     # 目标函数的归一化梯度
      87     @staticmethod
      88     def get_grad(x, y):
      89         grad_ori = numpy.array([10 * x + 3, 4 * y - 10])
      90         length = numpy.linalg.norm(grad_ori)
      91         if length == 0:
      92             return numpy.zeros(2)
      93         return grad_ori / length
      94 
      95 
      96 if __name__ == '__main__':
      97     GD()
      View Code

      笔者所用示例函数为:
      egin{equation}
      f(x, y) = 5x^2 + 2y^2 + 3x - 10y + 4
      end{equation}

    • 结果展示
  • 相关阅读:
    走读OpenSSL代码从一张奇怪的证书说起(二)
    从数学到密码学(十八)
    从数学到密码学(十七)
    走读OpenSSL代码从一张奇怪的证书说起(一)
    从数学到密码学(十六)
    从数学到密码学(十五)
    从数学到密码学(十三)
    从数学到密码学(十九)
    从数学到密码学(十四)
    关于Terracotta DSO 安装
  • 原文地址:https://www.cnblogs.com/xxhbdk/p/10023110.html
Copyright © 2011-2022 走看看