Python深度学习与项目实战
上QQ阅读APP看书,第一时间看更新

1.2 梯度下降算法

梯度下降算法是一种用来求使函数取最小值的参数的值的算法。在梯度下降算法中,首先,随机选取一个自变量的值,作为自变量的初始值。然后,在函数中自变量初始值的位置计算函数对于自变量的梯度。接下来,根据计算的梯度值,对自变量的值进行一次调整。接着,从改变过的自变量值的位置处,对函数求梯度,并再次根据计算出的梯度值对自变量的值进行调整。像这样对自变量的值进行一次调整的过程称为一次迭代(iteration),经过多次这样的迭代以后,就能够找到让函数取最小值的自变量的值。这里为了统一术语,把一元函数的导数统称为梯度,但是实际上多元函数的导数才是梯度。

可以通过一个实例来应用梯度下降算法找到使得函数取最小值的自变量的值。例如,当应用梯度下降算法求函数的最小值时,先求这个函数的导数。

按照梯度下降算法的工作原理,首先随机初始化自变量的值,将自变量初始化为−18,即,接下来算一下在点处的梯度值(导数值),梯度值为−36(即2)。利用这个梯度值就可以对自变量的值进行调整,调整的方式为= −lr,这里的lr= 0.1是自定义的一个常数,称为学习率(learning rate)。学习率决定了每次更新自变量值的幅度。当学习率的值设置为较大的值时,每次使用梯度值对自变量的调整较大;当学习率的值设置为较小的值时,每次对自变量的调整较小。自变量经过第一次调整以后,得到的自变量的值为−14.4。这样就完成了一次对自变量的更新,更新以后的自变量的值()能够让函数的值比在点处的值更小。接下来对自变量的值进行第二次更新。同样地,首先计算在点处的梯度值,结果为−28.8,然后通过公式自变量进行更新,更新以后的自变量的值记为能够让函数的值比在点处的值小。这样经过多次对梯度值的计算,使用梯度值与学习率对自变量的值进行更新,最终就能够找到让函数取得最小值的自变量的值,或者让函数取得接近最小值的自变量的值,这个变量的值记为

掌握了梯度下降算法的原理以后,通过代码来实际学习如何应用梯度下降算法求得函数的最小值。很显然,让这个函数取得最小值的自变量的值为0。接下来,学习一下如何应用梯度下降算法来找到让该函数取得最小值的自变量的值。首先在程序中加载用于数值计算的NumPy库和用于绘制函数图像的Matplotlib库,然后定义这个函数,如以下代码所示。

# 加载依赖库
import numpy as np
import matplotlib.pyplot as plt 
# 定义 y=x^2+1 函数
def function(x):
     y = x ** 2 + 1
     return y

接下来开始应用梯度下降算法求函数的最小值。首先把自变量的值随机初始化为−18,记为。然后通过get_gradient函数求对应的梯度值,get_gradient函数用于求这个函数在指定位置处的梯度值。得到了函数在点处的梯度值以后,把梯度值乘以学习率,并和的值相减,最后把相减以后得到的自变量的值记为,这样就完成了一次对自变量的更新。依次类推,在这里一共进行了50次更新。更新的次数使用epochs变量来表示。定义一个名为 trajectory的列表来存储每次更新后的值,以便在找到值以后,可视化使用梯度下降算法对自变量更新的过程。具体代码如下。

# 指定自变量更新的次数(迭代的次数)
epochs = 50
# 指定学习率的值
lr = 0.1
# 对自变量的值进行初始化
xi = -18
# 求函数的梯度值
def get_gradient(x):
     gradient = 2 * x
     return gradient
# 用于存储每次自变量更新后的值
trajectory = []
# 利用梯度下降算法找到使得函数取最小值的自变量的值x_star
def get_x_star(xi):
     for i in range(epochs):
          trajectory.append(xi)
          xi = xi - lr * get_gradient(xi)
     x_star = xi
     return x_star
# 运行get_x_star函数
get_x_star(xi)

get_x_star函数中传入初始化的自变量的值后,运行以上代码,就可以利用梯度下降算法找到让函数取最小值的自变量的值get_x_star函数的输出值为−0.000 25,实际上,的值为0。可以看出,使用梯度下降算法计算出的函数最小值与实际的函数最小值几乎没有任何差别。

接下来可以将自变量在更新过程中对应的函数值减小的过程进行可视化。以下这段代码可以把自变量每次更新的值以及对应的函数值画在函数图像上。

x = np.arange(-20, 20, 0.1)
y = function(x)
# 画出函数图像
plt.plot(x, y)
x_trajectory = np.array(trajectory)
y_trajectory = function(trajectory)
# 画出更新过程中的自变量及其对应的函数的值
plt.scatter(x_trajectory, y_trajectory)
plt.show()

可视化的结果如图1.1所示,从图中可以看出,将自变量的值初始化为−18以后,每一次使用梯度下降算法对自变量的值进行调整都会让函数的值变得更小,最终得到让函数取得最小值的自变量的值。

在梯度下降算法中,如果把学习率的值设置为很小的值(如0.001),就需要很多次更新才能得到让函数取最小值的自变量的值;如果把学习率的值设置为比较大的值(如0.5),自变量每次更新的幅度就会比较大,可能使自变量很难更新到让函数取最小值对应的值。因此,选取合适的学习率的值,对得到让函数取最小值对应的自变量的值至关重要。

图1.1 可视化的结果