50行代码,带你理解梯度下降法(Gradient Descent Method)

梯度下降法是一种常见的优化算法,在机器学习中应用很广泛。本文从代码的角度,带你理解梯度下降法。

在这里插入图片描述

优化算法

优化指的是改变x以最小化或最大化某个函数 f(x) 的任务。通常以最小化 f(x) 指代大多数最优化问题。最大化可以通过最小化 -f(x) 来实现。

在机器学习领域,我们把要最小化或最大化的函数称为目标函数(objective function)或准则(criterion)。当我们对其进行最小化时,也把它称为代价函数(cost function)、损失函数(loss function)或误差函数(error function)1

优化算法中常用的迭代方法有线性搜索和置信域方法等。线性搜索的策略是寻找方向和步长,具体算法有:梯度下降法、牛顿法、共轭梯度法等。2

梯度下降法为一阶收敛算法,当靠近局部最小解时梯度变小,收敛速度会变慢,并且可能以“之字形”的方式下降。如果目标函数为二阶连续可微,可以采用牛顿法,牛顿法(Newton’smethod)为二阶收敛算法,收敛速度更快,但是每次迭代需要计算Hessian矩阵的逆矩阵,复杂度较高。2下一章解析牛顿法。

梯度下降法

梯度(gradient)是相对一个向量求导的导数: f f f的导数是包含所有偏导数的向量,记为 ∇ x f ( x ) \nabla_\boldsymbol{x} f(\boldsymbol x) xf(x)。梯度的第 i i i个元素是 f f f关于 x i \boldsymbol{x}_i xi的偏导数。在多维情况下,临界点是梯度中所有元素都为零的点。

梯度下降(gradient descent)的原理是,梯度向量指向上坡,负梯度向量指向下坡。在负梯度方向上移动可以减小f。

所以,为了让目标函数继续减小,第t+1步的更新值由第t步的值向负梯度方向移动:
x t + 1 = x t − α ∇ x f ( x ) \boldsymbol x_{t+1} = \boldsymbol x_t - α\nabla_\boldsymbol{x} f(\boldsymbol x) xt+1=xtαxf(x)
α是学习率,用于控制梯度下降的速度。一般初始取值0.01、0.001等,可以在训练过程中动态调整。

采用梯度下降法的前提是,我们需要构建一个可以求偏导的目标函数,然后通过不断迭代,得到一个符合预期的结果。

具体的,假如f的值由x、y两个变量确定,则梯度可以这样表示:
∇ f ( x , y ) = ∂ f ∂ x i + ∂ f ∂ y j \nabla f(x,y)=\frac{\partial{f}}{\partial{x}}\bold{i}+\frac{\partial{f}}{\partial{y}}\bold{j} f(x,y)=xfi+yfj
下面用代码具体举例梯度下降的算法步骤。

代码示例

我们用梯度下降法来求多元函数z=f(x,y)的最小值,计算每一次迭代后的坐标位置,并绘制在三维空间中,查看目标函数值是否按照期望下降。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

# 目标函数
def f(x,y):
    return x**2 + y**2 + x*y

# 对x求偏导
def partial_x(x,y):
    return 2*x+y
    
# 对y求偏导
def partial_y(x,y):
    return 2*y+x

X = np.arange(-10, 10, 1)
Y = np.arange(-10, 10, 1)
X, Y = np.meshgrid(X, Y)
Z = f(X,Y)

# 绘制曲面
fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_wireframe(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis)
#ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

# 随机初始点
x = 8
y = 5
z = f(x,y)
next_x = x
next_y = y

xlist=[x]
ylist=[y]
zlist=[z]

# 设定一个学习率
step = 0.01
while True:
    next_x = x-step*partial_x(x,y)
    next_y = y-step*partial_y(x,y)    
    next_z = f(next_x,next_y)
    #print(next_x,next_y,next_z)
    
    # 小于阈值时,停止下降
    if z - next_z < 1e-9:
        break
    x = next_x
    y = next_y
    z = f(x,y)
    xlist.append(x)
    ylist.append(y)
    zlist.append(z)

ax.plot(xlist,ylist,zlist,'r--')
plt.show()

当梯度下降停止时,当前到达的(x,y,z)坐标为:

x=0.000222924064513216,y=-0.00022292403194797448,z=4.9695131279517545e-08

在这里插入图片描述

与f(x,y)的最小值误差在可接受范围内,优化完成。


  1. 《深度学习》 ↩︎

  2. 《神经网络与深度学习》 ↩︎ ↩︎

猜你喜欢

转载自blog.csdn.net/Bit_Coders/article/details/117433301