梯度下降总结
梯度下降
以一个简单的实例,理解梯度下降。
问题: y = sin x cos x − 2 y = \frac{{\sin x}}{{ \cos x-2 }} y=cosx−2sinx, x ∈ ( 0 , π ) x \in (0,\pi ) x∈(0,π) 求y的最小值。
显然,面对这个简单的函数我们可以很轻易的画出其函数图像,可以看到在
(
0
,
π
)
(0,\pi )
(0,π)中存在唯一极值。
解析解:
题目的函数是可导的,废话不多说直接求导,可以得到:
y ′ = cos x ( cos x − 2 ) + sin 2 x ( cos − 2 ) 2 = 0 ⇒ cos x ( cos x − 2 ) + sin 2 x = 0 ⇒ − 2 cos x + 1 = 0 ⇒ cos x = 1 2 , x ∈ ( 0 , π ) \begin{array}{l} y' = \frac{{ \cos x( \cos x-2) + {{\sin }^2}x}}{{{{(\cos-2 )}^2}}} = 0\\ \\ \Rightarrow \cos x(\cos x-2) + {\sin ^2}x = 0\\ \\ \Rightarrow - 2\cos x + 1 = 0\\ \\ \Rightarrow \cos x = \frac{1}{2},x \in (0,\pi ) \end{array} y′=(cos−2)2cosx(cosx−2)+sin2x=0⇒cosx(cosx−2)+sin2x=0⇒−2cosx+1=0⇒cosx=21,x∈(0,π)
即当
x
=
π
3
x = \frac{\pi }{3}
x=3π时,y取得极值
y
min
=
−
3
3
{y_{\min }} = - \frac{{\sqrt 3 }}{3}
ymin=−33
梯度下降:
换一个思路,如果把函数看成是一个优化问题,那么问题就变成:
min y = sin ( x ) cos ( x ) − 2 s . t . 0 < x < π \begin{array}{l} \min {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} y = \frac{{\sin (x)}}{{\cos (x) - 2}}\\ s.t.{\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} 0 < x < \pi \end{array} miny=cos(x)−2sin(x)s.t.0<x<π
梯度下降的核心思想很简单,极值点导数(多维就是偏导数)为0,那么沿着梯度大的地方走,直到走不动(梯度接近于0)为止,这样就能最快到达底部,找到函数极小值,反之上升则极大值。
Python 代码如下:
"""
梯度下降简单示例
"""
import numpy as np
import matplotlib.pyplot as plt
def func_fx(x):
"""目标函数"""
return np.sin(x)/(np.cos(x)-2)
def grad_fx(x):
""" 目标函数1阶导数y'=(1-2*cosx)/(cosx-2)^2"""
return (1-2*np.cos(x))/(np.cos(x)-2)**2
def gradient_descent(grad, cur_x=0.1, learning_rate=0.01, precision=0.0001, max_iters=10000):
"""
:param grad: 目标函数梯度
:param cur_x: 当前的x值
:param learning_rate: 学习率
:param precision: 收敛精度
:param max_iters: 最大迭代次数
:return: x_min
"""
for i in range(max_iters):
grad_cur = grad(cur_x)
if abs(grad_cur) < precision: #当梯度收敛到接近0结束
break
cur_x = cur_x - grad_cur * learning_rate
print("第", i, "次迭代:x 值为 ", cur_x)
plt.plot(cur_x,func_fx(cur_x),'.',color='b')
plt.show()
return cur_x
if __name__ == '__main__':
# cur_x can be any number between 0 and pi
xmin = gradient_descent(grad_fx, cur_x=0.1, learning_rate=0.01, precision=0.0001, max_iters=2000)
print("局部最小值 xmin =",xmin)
print("局部最小值 ymin =", func_fx(xmin))
类似的方法—牛顿法
类似的方法还有牛顿法,与梯度下降不同,简单来说,梯度下降奔着目标函数最小值去,通过梯度判断方向, min f ( x ) \min {\kern 1pt} {\kern 1pt} {\kern 1pt} f(x) minf(x);而牛顿法则是奔着目标函数导数为零去, min f ′ ( x ) \min {\kern 1pt} {\kern 1pt} {\kern 1pt} f'(x) minf′(x),间接的求目标函数极小值。公示可以写成:
当θ是向量时,牛顿法可以使用下面式子表示:
其中H叫做Hessian矩阵,其实就是目标函数对参数θ的二阶导数。
通过比较牛顿法和梯度下降法的迭代公式,可以发现两者及其相似,两者都是迭代求解。Hessian矩阵的逆就好比梯度下降法的学习率参数alpha。牛顿法收敛速度相比梯度下降法很快,而且由于Hessian矩阵的的逆在迭代中不断减小,起到逐渐缩小步长的效果。但其缺点也很明显,每一次迭代需要求解Hessian的逆矩阵,当样本量很大时,需要求一个超级大的逆矩阵,这时就很难或者很慢才能求解解析解了,使用迭代的梯度下降法比较有优势。总之,使用牛顿法/拟牛顿法收敛更快,但是每次迭代的时间比梯度下降法长。梯度下降每次迭代时间很短,且构造简单,被广泛应用。
梯度下降法大家族(BGD,SGD,MBGD)
基本概念和变量的定义:
-
假设有n个样本: ( x i , y i ) ( i = 1 , 2.... n ) (x^i,y^i)\left( i=1,2....n \right) (xi,yi)(i=1,2....n),每个样本特征 x i x^i xi与样本输出 y i y^i yi一一对应,即训练的数据。
-
假设函数: f θ ( x ) = θ 1 x 1 + . . . + θ n x n = ∑ i = 0 n θ i x i f_{\theta}(x)={\theta}_1x1+...+{\theta}_nx_n=\sum_{i=0}^n{\theta_ix_i} fθ(x)=θ1x1+...+θnxn=∑i=0nθixi, f θ ( x ) f_\theta(x) fθ(x)即监督学习中要学习的模型, θ \theta θ为模型参数,最终学习(训练)完得到 f θ ( x ) f_\theta(x) fθ(x),对于新的 x ^ \widehat{x} x ,可以得到预测值 y ^ = f θ ( x ^ ) \widehat{y}=f_\theta(\widehat{x}) y =fθ(x )
-
步长:α
1.批量梯度下降法(Batch Gradient Descent)
批量梯度下降法,具体做法也就是在更新参数时使用所有的样本来进行更新。
θ
i
=
θ
i
−
α
∑
j
=
1
n
(
f
θ
(
x
0
(
j
)
,
x
1
(
j
)
,
.
.
.
x
n
(
j
)
)
−
y
j
)
x
i
(
j
)
\mathrm{\theta}_{\mathrm{i}}=\mathrm{\theta}_{\mathrm{i}}-\mathrm{\alpha}\sum_{\mathrm{j}=1}^{\mathrm{n}}{\left( f_{\mathrm{\theta}}\left( \mathrm{x}_{0}^{\left( \mathrm{j} \right)},\mathrm{x}_{1}^{\left( \mathrm{j} \right)},...\mathrm{x}_{\mathrm{n}}^{\left( \mathrm{j} \right)} \right) -\mathrm{y}_{\mathrm{j}} \right)}\mathrm{x}_{\mathrm{i}}^{\left( \mathrm{j} \right)}
θi=θi−αj=1∑n(fθ(x0(j),x1(j),...xn(j))−yj)xi(j)
2.随机梯度下降法(Stochastic Gradient Descent)
随机梯度下降法,其实和批量梯度下降法原理类似,区别在与求梯度时没有用所有的n个样本的数据,而是仅仅选取一个样本j来求梯度。对应的更新公式是:
θ
i
=
θ
i
−
α
(
f
θ
(
x
0
(
j
)
,
x
1
(
j
)
,
.
.
.
x
n
(
j
)
)
−
y
j
)
x
i
(
j
)
\mathrm{\theta}_{\mathrm{i}}=\mathrm{\theta}_{\mathrm{i}}-\mathrm{\alpha}\left( f_{\mathrm{\theta}}\left( \mathrm{x}_{0}^{\left( \mathrm{j} \right)},\mathrm{x}_{1}^{\left( \mathrm{j} \right)},...\mathrm{x}_{\mathrm{n}}^{\left( \mathrm{j} \right)} \right) -\mathrm{y}_{\mathrm{j}} \right) \mathrm{x}_{\mathrm{i}}^{\left( \mathrm{j} \right)}
θi=θi−α(fθ(x0(j),x1(j),...xn(j))−yj)xi(j)
随机梯度下降法,和的批量梯度下降法是两个极端,一个采用所有数据来梯度下降,一个用一个样本来梯度下降。
3.小批量梯度下降法(Mini-batch Gradient Descent)
小批量梯度下降法是批量梯度下降法和随机梯度下降法的折衷,也就是对于n个样本,我们采用x个样子来迭代,1<x<n。当然根据样本的数据,可以调整这个x的值。对应的更新公式是:
θ
i
=
θ
i
−
α
∑
j
=
t
t
+
x
−
1
(
h
θ
(
x
0
(
j
)
,
x
1
(
j
)
,
.
.
.
x
n
(
j
)
)
−
y
j
)
x
i
(
j
)
\mathrm{\theta}_{\mathrm{i}}=\mathrm{\theta}_{\mathrm{i}}-\mathrm{\alpha}\sum_{\mathrm{j}=\mathrm{t}}^{\mathrm{t}+\mathrm{x}-1}{\left( \mathrm{h}_{\mathrm{\theta}}\left( \mathrm{x}_{0}^{\left( \mathrm{j} \right)},\mathrm{x}_{1}^{\left( \mathrm{j} \right)},...\mathrm{x}_{\mathrm{n}}^{\left( \mathrm{j} \right)} \right) -\mathrm{y}_{\mathrm{j}} \right)}\mathrm{x}_{\mathrm{i}}^{\left( \mathrm{j} \right)}
θi=θi−αj=t∑t+x−1(hθ(x0(j),x1(j),...xn(j))−yj)xi(j)
小结:显然,三种梯度下降方法主要区别就在于更新参数时使用多少样本,BGD:使用全部样本,SGD:随机挑选一个样本,MBGD:选取部分样本。值得注意的是,虽然SGD一次迭代只用一个样本,导致迭代方向变化很大,不能很快的收敛到局部最优解,但这也大大减少了每一步的计算复杂度。因此对于大规模样本训练用的最多的还是SGD。
总结
梯度下降的局限性
显然,范例中的函数比较简单,在(0,π)取值范围内属于严格凸函数(超过这个定义域就有多个极值了),如果遇到更为复杂的函数,有着多个极值,甚至我们不能写出其显示表达,大多时候无法得到解析解,梯度下降(GD)也很容易陷入局部极值。例如下图,多个极值问题,以及大面积平坦的鞍点(导数接近于0)。
挖个坑:虽说梯度下降容易陷入局部极值,但在大规模神经网络训练中(高维非凸)却能取得很好的效果,这是为什么呢?