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中,我们设置初始动量
v
0
v^{0}
v0。即
第一次下降时的动量:
v
1
v^{1}
v1=
λ
v
0
λv^0
λv0-η
∂
(
L
)
∂
(
θ
0
)
\frac{∂(L)}{∂(θ^0)}
∂(θ0)∂(L)
然后移动
θ
0
θ^{0}
θ0到
θ
1
θ^{1}
θ1=
θ
0
θ^{0}
θ0+
v
1
v^{1}
v1。
第二次下降时的动量:
v
2
v^{2}
v2=
λ
v
1
λv^1
λv1-η
∂
(
L
)
∂
(
θ
1
)
\frac{∂(L)}{∂(θ^1)}
∂(θ1)∂(L)
然后移动
θ
1
θ^{1}
θ1到
θ
2
θ^{2}
θ2=
θ
1
θ^{1}
θ1+
v
2
v^{2}
v2。
…
第t次下降时的动量:
v
t
v^{t}
vt=
λ
v
t
−
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+
v
t
v^{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()
图像如下: