深度学习的优化算法——梯度下降和随机梯度下降
优化算法在深度学习中存在的问题
优化在深度学习中有很多挑战,下面描述其中两个,局部最小值和鞍点。
1.局部最小值
深度学习模型的目标函数可能存在若干极点——局部最优解(local mininum)。当一个优化问题的数值解在局部最优解附近时,此时目标函数关于自变量的梯度接近零甚至变成零,导致迭代无法继续进行下去,最终求的的数值解只能局部最优而非全局最优(global minimun)。
2.鞍点
刚刚我们提到,梯度接近或变为零可能是由于当前解在局部最优解附近造成的。事实上,另一种可能性是当前解在鞍点(saddle point) 附近。
以一维函数f(x)=x3为例,其鞍点位于(0,0)。
下面以二维函数f(x)=x2-y2为例,首先画出其图像。
import numpy as np
x,y=np.mgrid[-1:1:31j,-1:1:31j]
z=x**2-y**2
#划分网格。31j为复数,代表在-1到1之间插入31个值,若为实数则代表步长。
ax=plt.figure().add_subplot(111,projection='3d')
#111代表一共1x1个图,返回的是第一个图。projection代表投影方式,为3d投影
ax.plot_wireframe(x,y,z,**{'rstride':2,'cstride':2})
#由于先前将x,y分成了31x31的网格,这里不想看起来得太密,rstride和cstride就可以设置网格现实的步长,即每隔一条显示一条。**{}
#即把字典中的值取出,等价于rstride=2,cstride=2
ax.plot([0],[0],[0],'rx')
ticks=[-1,0,1]
plt.xticks(ticks)
plt.yticks(ticks)
ax.set_zticks(ticks)
在图的鞍点位置,目标函数在x方向上是局部最小值,但在y方向上是局部最大值。
假设一个函数的输入为k维度向量,输出为标量,那么他的**海森矩阵(Hessian matrix)**有k个特征值,该函数在梯度为零的位置上可能是局部最小值、局部最大值或鞍点。
- 当函数的海森矩阵在梯度为零的位置上的特征值全为正时,该函数得到局部最小值。
- 当函数的海森矩阵在梯度为零的位置上的特征值全为负时,该函数得到局部最大值。
- 当函数的海森矩阵在梯度为零的位置上的特征值有正有负时,该函数得到鞍点。
由于深度学习模型参数多数是高维的,目标函数的鞍点通常比局部最小值更常见。
梯度下降和随机梯度下降
1.一维梯度下降
根据泰勒公式
f
(
x
+
ϵ
)
≈
f
(
x
)
+
ϵ
f
′
(
x
)
f(x+\epsilon)\approx f(x)+\epsilon f'(x)
f(x+ϵ)≈f(x)+ϵf′(x)
ϵ
\epsilon
ϵ 越小越接近相等。
当
f
(
x
)
f(x)
f(x)为损失函数时,我们希望找到合适的
ϵ
\epsilon
ϵ , 使
f
(
x
+
ϵ
)
<
f
(
x
)
f(x+\epsilon)< f(x)
f(x+ϵ)<f(x),即令
ϵ
f
′
(
x
)
<
0
\epsilon f'(x)<0
ϵf′(x)<0 。接下来,找到一个常数
η
>
0
\eta>0
η>0,使
|
η
f
′
(
x
)
|
|\eta f'(x)|
|ηf′(x)|足够小,那么可以将
ϵ
\epsilon
ϵ替换成
−
η
f
′
(
x
)
-\eta f'(x)
−ηf′(x)并得到
f
(
x
−
η
f
′
(
x
)
)
≈
f
(
x
)
−
η
f
′
(
x
)
2
f(x-\eta f'(x))\approx f(x)-\eta f'(x)^2
f(x−ηf′(x))≈f(x)−ηf′(x)2如果导数
f
′
(
x
)
!
=
0
f'(x)!=0
f′(x)!=0,那么
η
f
′
(
x
)
2
>
0
\eta f'(x)^2>0
ηf′(x)2>0,所以
f
(
x
−
η
f
′
(
x
)
)
<
=
f
(
x
)
f(x-\eta f'(x))<= f(x)
f(x−ηf′(x))<=f(x)这意味着,如果通过
x
⇐
x
−
η
f
′
(
x
)
x\Leftarrow x-\eta f'(x)
x⇐x−ηf′(x)来迭代
x
x
x,损失函数值可能会降低。这解释了学习率大于零且不可过大。
下面以
f
(
x
)
=
x
2
f(x)=x^2
f(x)=x2为例解释梯度下降是怎么工作的。
import numpy as np
from matplotlib import pyplot as plt
def gd(lr):
x=10
results=[x]
for i in range(10):
x-=lr*2*x
results.append(x)
print('epoch 10,x:',x)
return results
def show_trace(res):
n=max(abs(min(res)),abs(max(res)),10)
f_line=np.arange(-n,n,0.1)
plt.figure()
plt.plot(f_line,[x**2 for x in f_line])
plt.plot(res,[x**2 for x in res],'-o')
plt.xlabel('x')
plt.ylabel('f(x)')
res=gd(0.2)
show_trace(res)
epoch 10,x: 0.06046617599999997
可见迭代10次后,x的值较接近最优解。
2.多维梯度下降
下面以多元函数 f ( x ) = x 1 2 + 2 x 2 2 f(x)=x_1^2+2x_2^2 f(x)=x12+2x22为例,通过绘制等高图来观察如何迭代到最优解的。我们将从(-5,-2)为起点对(x1,x2)进行迭代。
import numpy as np
from matplotlib import pyplot as plt
def train_2d(trainer):
x1,x2,s1,s2=-5,-2,0,0
results=[(x1,x2)]
for i in range(20):
x1,x2,s1,s2=trainer(x1,x2,s1,s2)
results.append((x1,x2))
print('epoch %d,x1 %f,x2 %f' % (i+1,x1,x2))
return results
def show_trace_2d(f,results):
plt.plot(*zip(*(results)),'-o',color='#ff7f0e')
#x,y=np.mgrid([-5.0,1.0,0.1],[-3.0,1.0,0.1])
x,y=np.meshgrid(np.arange(-5.0,1.0,0.1),np.arange(-3.0,1.0,0.1))
C=plt.contour(x,y,f(x,y),color='#1f77b4')
plt.xlabel('x1')
plt.ylabel('x2')
plt.clabel(C)
关于*zip(*(results))的说明:
results本是一个列表,其中每个元素为坐标(元组),例如results=[(1,2),(3,4),(5,6)]。
*results的作用在于将results“打散”,变为*results=(1,2),(3,4),(5,6)。
zip的作用在于“封装”,将上一步相同位置或相同地位的元素封装在一起,zip(*results)=(1,3,5),(2,4,6)。但该步骤得到的是一个zip实例,plt.plot()需要两个列表或者元组参数,所以需要将该zip实例再次打散,得到两个元组,传入plot()
lr=0.1
def f_2d(x1,x2):
return x1**2+2*x2**2
def gd_2d(x1,x2,s1,s2):
return x1-lr*2*x1,x2-lr*4*x2,0,0
show_trace_2d(f_2d,train_2d(gd_2d))
输出图像如下:good😌
3.随机梯度下降
在深度学习中,目标函数通常是训练数据集中各个样本的损失函数的平均,即
∇
f
(
x
)
=
1
n
∑
i
=
1
n
∇
f
i
(
x
)
\nabla f(x)=\frac{1}{n} \sum_{i=1}^n \nabla f_i(x)
∇f(x)=n1i=1∑n∇fi(x)如果使用梯度下降,每次迭代计算开销为
O
n
O_n
On,当训练数据样本数很大时,计算开销很高。
**随机梯度下降(stochastic gradient descent,SGD)**随机采样一个样本,以该样本代替原梯度。这样本次迭代的计算量由
O
n
O_n
On下降到了
O
1
O_1
O1。值得注意的是,随机梯度
∇
f
i
(
x
)
\nabla f_i(x)
∇fi(x)是对梯度
∇
f
(
x
)
\nabla f(x)
∇f(x)的无偏估计(期望相同)。