SGD-Stochastic Gradient Descent随机梯度下降
首先回顾一下梯度下降公式:
θ1θ^1θ1=θ0θ^0θ0-η∂(L)∂(θ0)\frac{∂(L)}{∂(θ^0)}∂(θ0)∂(L)
θ2θ^2θ2=θ1θ^1θ1-η∂(L)∂(θ1)\frac{∂(L)}{∂(θ^1)}∂(θ1)∂(L)
…
θt+1θ^{t+1}θt+1=θtθ^tθt-η∂(L)∂(θt)\frac{∂(L)}{∂(θ^t)}∂(θt)∂(L)
每次计算出梯度方向,然后把该点朝梯度的相反方向移动。反复操作直到计算出梯度值为0,即η∂(L)∂(θt)\frac{∂(L)}{∂(θ^t)}∂(θt)∂(L)=0时,达到local minimal。
当loss function作用在整个训练集上的时候,此时函数上会存在很多个local minimal。最原始使用的是BGD批量梯度下降,使用的是整个数据集。每进行一次梯度下降,就会遍历整个数据集,当数据集特别大的时候,效率就特别低。因此SGD每次选择一部分数据集进行梯度下降,这部分数据集可能也就只有几百条数据而已。由于BGD使用的是整个数据集,因此函数图像上就会存在很多个local minimal,并且BGD每次下降方式是梯度的相反方向,因此BGD很容易陷入一个local minimal,并且整个local minimal并不是一个最优的minimal或者说不是一个global minimal,此时就很难再跳出这个local minimal;但是SGD使用的是部分数据集,当陷入一个local minimal时,进行下一次梯度下降会使用另一组数据集,此时就可以跳出local minimal。
但是SGD会产生震荡。这是因为在选择部分数据集时,如果选择的数据集包含的数据特别少,则下降到global minimal时所经历的曲线就会很曲折,此时我们就要适当增大所选择的部分数据集,即适当增大batch size。但是特别注意,当batch size达到一定的数量级时,SGD就会变成BGD。减少震荡的方法比如SGDM,即在梯度下降时增加一个动量momentum。
SGD-Stochastic Gradient Descent Momentum带动量的梯度下降
所谓带动量的梯度下降,就是每次下降时,都要带上前一次下降的动量。这就是为了解决在SGD中当batch size特别小时会产生动荡的问题。
在SGD中,我们是这样进行下降的:
θt+1θ^{t+1}θt+1=θtθ^tθt-η∂(L)∂(θt)\frac{∂(L)}{∂(θ^t)}∂(θt)∂(L)
在SGDM中,我们设置初始动量v0v^{0}v0。即
第一次下降时的动量:v1v^{1}v1=λv0λv^0λv0-η∂(L)∂(θ0)\frac{∂(L)}{∂(θ^0)}∂(θ0)∂(L)
然后移动θ0θ^{0}θ0到θ1θ^{1}θ1=θ0θ^{0}θ0+v1v^{1}v1。
第二次下降时的动量:v2v^{2}v2=λv1λv^1λv1-η∂(L)∂(θ1)\frac{∂(L)}{∂(θ^1)}∂(θ1)∂(L)
然后移动θ1θ^{1}θ1到θ2θ^{2}θ2=θ1θ^{1}θ1+v2v^{2}v2。
…
第t次下降时的动量:vtv^{t}vt=λvt−1λv^{t-1}λvt−1-η∂(L)∂(θt−1)\frac{∂(L)}{∂(θ^{t-1})}∂(θt−1)∂(L)
然后移动θt−1θ^{t-1}θt−1到θtθ^{t}θt=θt−1θ^{t-1}θt−1+vtv^{t}vt。
简单的梯度下降应用-对函数y=0.5*(x−0.25)2(x-0.25)^{2}(x−0.25)2求最小值
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['font.family'] = 'sans-serif'
mpl.rcParams['font.sans-serif'] = 'SimHei'
mpl.rcParams['axes.unicode_minus'] = False
# 构建函数
def func(x):
return 0.5 * (x-0.25) ** 2
# 构建该函数的一阶导数
def derivative(x):
return x-0.25
GD_X = []
GD_Y = []
x = 2 # 初始值
step = 0.05 # 步长
y = func(x) # 求y
temp = y # 记录当前y的值以便计算变化率
GD_X.append(x)
GD_Y.append(y)
rateY = temp # 初始变化率
iter_num = 0
# 构造x变化函数
def changeX(x):
return x - step * derivative(x)
# 梯度下降
while rateY > 1e-10 and iter_num < 1000:
iter_num += 1
temp = y # 上次y的值存在temp中
x = changeX(x) # 移动x
y = func(x) # 移动x之后y的值
rateY = np.abs(y - temp) # y的变化率,
GD_X.append(x) # 变化率太大,迭代次数增加,变化率太小,产生震荡
GD_Y.append(y)
print("最终计算结果:(%.5f,%.5f)" % (x, temp))
print("迭代次数:%d" % iter_num)
print(GD_X)
print(GD_Y)
X = np.arange(-4.5, 4.5, 0.05)
Y = np.array(list(map(lambda t: func(t), X)))
plt.figure(facecolor="w")
plt.plot(X, Y, "r-", linewidth=2)
plt.plot(GD_X, GD_Y, "bo--", linewidth=2)
plt.title(u"一元函数0.5 * (x - 0.25)^2进行梯度下降求函数最小值\n 学习率:%.3f,最终值:%.3f,迭代次数:%d" % (step, y, iter_num))
plt.show()
图像如下: