梯度下降
梯度下降法(Gradient Descent Algorithm,GD)是以负梯度方向求解目标函数J(θ)的全局最小值的一种迭代方法。正梯度的方向是上升方向。
首先理解什么是梯度?通俗来说,梯度就是表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在当前位置的导数。
上式中,θ是自变量,f(θ)是关于θ的函数,θ表示梯度。
如果函数f(θ)是凸函数,那么就可以使用梯度下降算法进行优化。梯度下降算法的公式我们已经很熟悉了:
其中,θo是自变量参数,即下山位置坐标,η是学习因子,即下山每次前进的一小步(步进长度),θ是更新后的θo,即下山移动一小步之后的位置。
当我们在迭代了很多次之后,对每次迭代的结果进行可视化的展示,我们会看到一条不规则的函数图像,
这时候我们可以使用指数加权平均wi的方法,将图像变得平滑。
在日后的训练中,如果你发现你最终训练的结果,并没有收敛,那么宣告此次训练失败了,你需要重新进行训练,比如修改学习率,修改损失函数等。
问答
对于梯度下降来讲,找的是局部最优点,也就是我们常说的贪心算法;
Q:为什么不找全局最优点?
A:因为很多不规则的函数,也就是所谓的非凸函数,并没有办法找到全局最优点,他在前序局部最优点的时候,就已经停止了迭代。
Q:那为什么还使用梯度下降?
A:因为大量的实验发现,并没有很多的局部最优点,在大部分的数据中梯度下降还是效果比较好的算法。
PS:鞍点:就是梯度等于0的点。也就是这个区间的函数值都相同,由于函数值都相同,所以在这段区间中,我们的迭代就走不动了,因为怎么走,最终的得到的W都是原本的值。
这里推荐下刘老师的课程,
随机梯度下降
我们之前用梯度下降用的是整体目标函数的损失来作为梯度下降的依据,而随机梯度下降就是从这些损失中随机选择一个,进行更新。
为什么要使用随机呢?因为样本函数中可能存在鞍点,噪声,如果使用随机梯度下降,我们有可能会跳过这个鞍点,进行更有效的预测。并且经过神经网络的验证之后,确认随机梯度下降对模型的训练有效。
在深度学习中,我们可能会遇到在计算X和Xi+1的函数值的时候,因为两个函数没有依赖关系,所以我们是可以并行操作的。
- 梯度下降的前后两个函数值没有依赖关系,可以并行进行,所以效率很高,但是容易陷入局部最优点的陷阱中;
- 随机梯度可以减少陷入局部最优的几率,但是因为前后函数值有依赖关系,所以没有办法进行并行,效率不高。
所以我们最终会折中,也就是两折兼顾。我们会在深度学习中,使用分治的思想,就是Batch方法,批量的随机梯度下降。现在深度学习中随机梯度下降,默认的就是采用Batch方法。
案例:画图模拟梯度下降的过程
- 整理训练集数据,自定义梯度下降算法规则,求出w0 , w1 ,绘制回归线。
import numpy as np
import matplotlib.pyplot as mp
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
times = 1000 # 定义梯度下降次数
lrate = 0.01 # 记录每次梯度下降参数变化率
w0, w1 = [1], [1]
for i in range(1, times + 1):
# d0是损失函数在w0方向上的偏导数
d0 = (w0[-1] + w1[-1] * train_x - train_y).sum()
# d1是损失函数在w1方向上的偏导数
d1 = (((w0[-1] + w1[-1] * train_x) - train_y) * train_x).sum()
# 让w0 w1不断更新
w0.append(w0[-1] - lrate * d0)
w1.append(w1[-1] - lrate * d1)
pred_train_y = w0[-1] + w1[-1] * train_x
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(train_x, train_y, marker='s', c='dodgerblue', alpha=0.5, s=80, label='Training')
mp.plot(train_x, pred_train_y, '--', c='limegreen', label='Regression', linewidth=1)
mp.legend()
mp.show()
# 绘制随着每次梯度下降,w0,w1,loss的变化曲线。
w0 = w0[:-1]
w1 = w1[:-1]
mp.figure('Training Progress', facecolor='lightgray')
mp.subplot(311)
mp.title('Training Progress', fontsize=16)
mp.ylabel('w0', fontsize=13)
mp.grid(linestyle=':')
mp.plot(epoches, w0, color='dodgerblue',
label='w0')
mp.legend()
mp.subplot(312)
mp.ylabel('w1', fontsize=13)
mp.grid(linestyle=':')
mp.plot(epoches, w1, color='orangered',
label='w1')
mp.legend()
mp.subplot(313)
mp.title('Training Progress', fontsize=16)
mp.ylabel('loss', fontsize=13)
mp.grid(linestyle=':')
mp.plot(epoches, losses, color='red',
label='loss')
mp.legend()
mp.tight_layout()
mp.show()
基于三维曲面绘制梯度下降过程中的每一个点。
# 基于三维曲面绘制梯度下降过程中的每一个点
import mpl_toolkits.mplot3d as axes3d
grid_w0, grid_w1 = np.meshgrid(
np.linspace(0, 9, 500),
np.linspace(0, 3.5, 500))
grid_loss = np.zeros_like(grid_w0)
for x, y in zip(train_x, train_y):
grid_loss += (grid_w0+grid_w1*x - y)**2 / 2
# 绘图
mp.figure('Loss Function')
ax3d = mp.gca(projection='3d')
ax3d.set_xlabel('w0', fontsize=14)
ax3d.set_ylabel('w1', fontsize=14)
ax3d.set_zlabel('loss', fontsize=14)
ax3d.plot_surface(grid_w0, grid_w1, grid_loss,
rstride=30, cstride=30, cmap='jet')
ax3d.plot(w0, w1, losses, 'o-',
color='orangered', label='BGD')
以等高线的方式绘制梯度下降的过程。
mp.figure('Batch Gradient Descent', facecolor='lightgray')
mp.title('Batch Gradient Descent', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.contourf(grid_w0, grid_w1, grid_loss, 10, cmap='jet')
cntr = mp.contour(grid_w0, grid_w1, grid_loss, 10,
colors='black', linewidths=0.5)
mp.clabel(cntr, inline_spacing=0.1, fmt='%.2f',
fontsize=8)
mp.plot(w0, w1, 'o-', c='orangered', label='BGD')
mp.legend()
mp.show()