梯度下降—Gradient Descent
一、梯度
在微积分里面,对多元函数的参数求∂偏导数,把求得的各个参数的偏导数以向量的形式写出来,就是梯度。比如函数f(x,y), 分别对x,y求偏导数,求得的梯度向量就是 ( ∂ f / ∂ x , ∂ f / ∂ y ) T (∂f/∂x, ∂f/∂y)^T (∂f/∂x,∂f/∂y)T,简称 g r a d f ( x , y ) grad f(x,y) gradf(x,y)或者 ▽ f ( x , y ) ▽f(x,y) ▽f(x,y)。对于在点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)的具体梯度向量就是 ( ∂ f / ∂ x 0 , ∂ f / ∂ y 0 ) T (∂f/∂x_0, ∂f/∂y_0)^T (∂f/∂x0,∂f/∂y0)T.或者 ▽ f ( x 0 , y 0 ) ▽f(x0,y0) ▽f(x0,y0),如果是3个参数的向量梯度,就是 ( ∂ f / ∂ x , ∂ f / ∂ y , ∂ f / ∂ z ) T (∂f/∂x, ∂f/∂y,∂f/∂z)^T (∂f/∂x,∂f/∂y,∂f/∂z)T,以此类推。
二、梯度下降算法
2.1 主要相关概念
- 步长(learning rate):也叫学习率,步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度。
- 特征(feature):指的是样本中输入部分
- 假设函数(hypothesis function):在监督学习中了拟合输入样本,而使用的假设函数,记为 h θ ( x ) h θ ( x ) hθ(x)hθ(x) hθ(x)hθ(x)。比如对于单个特征的m个样本 ( x ( i ) , y ( i ) ) ( i = 1 , 2 , . . . m ) ( x ( i ) , y ( i ) ) ( i = 1 , 2 , . . . m ) (x(i),y(i))(i=1,2,...m)(x(i),y(i))(i=1,2,...m) (x(i),y(i))(i=1,2,...m)(x(i),y(i))(i=1,2,...m),可以采用拟合函数如下:$ h_θ(x)=θ_0+θ_1x$。
- 损失函数(loss function):为了评估模型拟合的好坏,通常用损失函数来度量拟合的程度。损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优参数。
2.2 梯度下降的代数算法
-
先决条件:确定优化模型的假设函数和损失函数
-
算法相关参数初始化:主要是初始化 θ 0 , θ 1 . . . , θ n θ_0,θ_1...,θ_n θ0,θ1...,θn,算法终止距离ε以及步长 α α α。在没有任何先验知识的时候,我们可以将参数按照高斯分布初始化, 将步长初始化为0.01。在调优的时候再优化。
-
算法过程:
1). 确定当前位置的损失函数的梯度,对于 θ i θ_i θi,其梯度表达式如下:
∂ ∂ θ i J ( θ 0 , θ 1 , . . . , θ n ) \frac{\partial}{\partial\theta_i}J(\theta_0,\theta_1,...,\theta_n) ∂θi∂J(θ0,θ1,...,θn)
2). 用步长乘以损失函数的梯度,得到当前位置下降的距离,即 α ∂ ∂ θ i J ( θ 0 , θ 1 , . . . , θ n ) α\frac{\partial}{\partial\theta_i}J(\theta_0,\theta_1,...,\theta_n) α∂θi∂J(θ0,θ1,...,θn)。
3).确定是否所有的 θ i θ_i θi,梯度下降的距离都小于 ε ε ε,如果小于 ε ε ε则算法终止,当前所有的 θ i ( i = 0 , 1 , . . . n ) θi(i=0,1,...n) θi(i=0,1,...n)即为最终结果。否则进入步骤4.
4). 更新所有的
θ
θ
θ,对于
θ
i
θ_i
θi,其更新表达式如下。更新完毕后继续转入步骤1.
θ
i
=
θ
i
−
α
∂
∂
θ
i
J
(
θ
0
,
θ
1
.
.
.
,
θ
n
)
θ_i=θ_i−α\frac{\partial}{\partial\theta_i}J(θ_0,θ_1...,θ_n)
θi=θi−α∂θi∂J(θ0,θ1...,θn)
2.3 梯度下降的矩阵算法
-
先决条件:确定优化模型的假设函数和损失函数
-
算法相关参数初始化:主要是初始化 θ 0 , θ 1 . . . , θ n θ_0,θ_1...,θ_n θ0,θ1...,θn,算法终止距离ε以及步长 α α α。在没有任何先验知识的时候,我们可以将参数按照高斯分布初始化, 将步长初始化为0.01。在调优的时候再优化。
-
算法过程:
1). 确定当前位置的损失函数的梯度,对于 θ θ θ向量,其梯度表达式如下:
∂ ∂ θ i J ( θ ) \frac{\partial}{\partial\theta_i}J(\theta) ∂θi∂J(θ)
2). 用步长乘以损失函数的梯度,得到当前位置下降的距离,即 α ∂ ∂ θ J ( θ ) α\frac{\partial}{\partial\theta}J(\theta) α∂θ∂J(θ)。
3).确定是否所有的 θ i θ_i θi,梯度下降的距离都小于 ε ε ε,如果小于 ε ε ε则算法终止,当前所有的 θ i ( i = 0 , 1 , . . . n ) θi(i=0,1,...n) θi(i=0,1,...n)即为最终结果。否则进入步骤4.
4). 更新所有的θ,对于θi,其更新表达式如下。更新完毕后继续转入步骤1.
θ
=
θ
−
α
∂
∂
θ
J
(
θ
)
θ = θ−α\frac{\partial}{\partial\theta}J(\theta)
θ=θ−α∂θ∂J(θ)
矩阵算法和代数算法的区别在于可以以矩的方式直接计算出梯度
2.4 梯度下降的算法调优
在使用梯度下降的时候,哪些地方可以调优呢?
- 算法的学习率:在实际运行中,可以多取一些值运行算法 ,观察损失函数的变化。如果学习率过大,会导致迭代过快,容易产生震荡而得不到最优解;如果学习率过小,会导致迭代过慢,算法很难收敛。在实际中,我们可以使用学习率变化的算法,迭代初期,学习率大一下,后面逐渐减小,这样有利于快速迭代而不会产生震荡。
- 算法的初始参数选择:算法的初始值不同,获得的最小值也可能不同;如果一个函数是凸函数,那么获得的局部最优解一定是全局最优解。而如果函数非凸,初值的选择可能会导致最优解为局部最优解
- 归一化:由于样本不同特征的取值范围不一样,可能导致迭代速度很慢,为了减少特征取值的影响,可以对特征数据取值归一化,也就是对每一个特征 x x x求出期望 x ˉ \bar{x} xˉ和标准差 s t d ( x ) std(x) std(x)得到:
x − x ˉ s t d ( x ) \frac{x-\bar{x}}{std(x)} std(x)x−xˉ
这样特征的新期望值为0,新方差为1,可以加快迭代速度。
三、梯度下降家族
3.1 批量梯度下降法(Batch Gradient Desent,BGD)
批量梯度下降法是梯度下降常用算法,表示在更新梯度参数时使用所有的样本来参与更新。比如我们常见的线性回归梯度更新公式:
θ
i
=
θ
i
−
α
∑
j
=
1
m
(
h
θ
(
x
0
j
,
x
1
j
,
.
.
.
,
x
n
j
)
−
y
j
)
x
i
j
\theta_i = \theta_i-\alpha\sum\limits^{m}_{j=1}(h_\theta(x_0^j,x_1^j,...,x_n^j)-y_j)x_i^j
θi=θi−αj=1∑m(hθ(x0j,x1j,...,xnj)−yj)xij
这个就是批量梯度下降,因为m个样本都参与了梯度更新
3.2 随机梯度下降法(Stochastic Gradient Descent,SGD)
随机梯度下降法,其实和批量梯度下降法原理类似,区别在与求梯度时没有用所有的m个样本的数据,而是仅仅选取一个样本j来求梯度。对应的线性回归更新公式是:
θ
i
=
θ
i
−
α
(
h
θ
(
x
0
j
,
x
1
j
,
.
.
.
,
x
n
j
)
−
y
j
)
x
i
j
\theta_i = \theta_i-\alpha(h_\theta(x_0^j,x_1^j,...,x_n^j)-y_j)x_i^j
θi=θi−α(hθ(x0j,x1j,...,xnj)−yj)xij
3.3 小批量梯度下降法(Mini-batch Gradient Descent)
小批量梯度下降法是批量梯度下降法和随机梯度下降法的折衷,也就是对于m个样本,我们采用x个样子来迭代,1<x<m。一般可以取x=10,当然根据样本的数据,可以调整这个x的值。
四、梯度下降法和其他无约束优化算法的比较
在机器学习中的无约束优化算法,除了梯度下降以外,还有前面提到的最小二乘法,此外还有牛顿法和拟牛顿法。
梯度下降法和最小二乘法相比,梯度下降法需要选择步长,而最小二乘法不需要。梯度下降法是迭代求解,最小二乘法是计算解析解。如果样本量不算很大,且存在解析解,最小二乘法比起梯度下降法要有优势,计算速度很快。但是如果样本量很大,用最小二乘法由于需要求一个超级大的逆矩阵,这时就很难或者很慢才能求解解析解了,使用迭代的梯度下降法比较有优势。
梯度下降法和牛顿法/拟牛顿法相比,两者都是迭代求解,不过梯度下降法是梯度求解,而牛顿法/拟牛顿法是用二阶的海森矩阵的逆矩阵或伪逆矩阵求解。相对而言,使用牛顿法/拟牛顿法收敛更快。但是每次迭代的时间比梯度下降法长。
五、手撕梯度下降算法代码
本节我们使用python写出梯度下降代码优化Himmelblau函数:
f
(
x
,
y
)
=
(
x
2
+
y
−
11
)
2
+
(
x
+
y
2
−
7
)
2
f(x,y) = (x^2+y-11)^2+(x+y^2-7)^2
f(x,y)=(x2+y−11)2+(x+y2−7)2
'''
手写梯度下降算法:更新函数(x[0]**2+x[1]-11)**2+(x[0]+x[1]**2-7)**2
'''
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
LEARN_RATE = 0.001
STOP_VALUE = 1e-5
listValue =[]
def himmelblau(x):
'''himmeblau函数'''
return (x[0]**2+x[1]-11)**2+(x[0]+x[1]**2-7)**2
def derihimme(x):
'''himme函数导数'''
y = [2*(x[0]**2+x[1]-11)*(2*x[0])+2*(x[0]+x[1]**2-7),
2*(x[0]**2+x[1]-11)+2*(x[0]+x[1]**2-7)*(2*x[1])]
return y
def drawhimme(listV):
'''画图'''
x = np.arange(-6.,6.,0.1)
y = np.arange(-6.,6.,0.1)
print("x,y range:",x.shape,y.shape)
#计算
X,Y = np.meshgrid(x,y)
Z = himmelblau([X,Y])
#显示
fig = plt.figure("himmelblau")
ax = Axes3D(fig)
ax.plot_surface(X, Y, Z,
rstride=1, # rstride(row)指定行的跨度
cstride=1, # cstride(column)指定列的跨度
cmap=plt.get_cmap('rainbow')) # 设置颜色映射
ax.view_init(60,-30)
ax.set_xlabel('x')
ax.set_ylabel('y')
cm = plt.cm.get_cmap('RdYlBu')
for v in listV:
ax.scatter(v[0],v[1],himmelblau(v),
edgecolors='k',
cmap=plt.cm.get_cmap('RdYlBu'),
s=10)
plt.show()
def gradentUpdate(initValue):
'''梯度更新'''
y_first = himmelblau(initValue)
listValue.append(initValue)
y_gradent = derihimme(initValue)
valueUpdate = [initValue[0]-y_gradent[0]*LEARN_RATE,
initValue[1]-y_gradent[1]*LEARN_RATE]
y_update = himmelblau(valueUpdate)
y_abs = np.abs(y_update-y_first)
for step in range(2000):
initValue = valueUpdate
y_first = y_update
listValue.append(initValue)
y_gradent = derihimme(initValue)
valueUpdate = [initValue[0] - y_gradent[0] * LEARN_RATE,
initValue[1] - y_gradent[1] * LEARN_RATE]
y_update = himmelblau(valueUpdate)
y_abs = np.abs(y_update - y_first)
if(y_abs<STOP_VALUE):
break
return valueUpdate
if __name__== "__main__":
initValue0 = [4., 4.]
initValue1 = [4., -4.]
initValue2 = [-5., 4.]
initValue3 = [-5., -5.]
#不同的初始值对应不同的局部最小值
print(gradentUpdate(initValue0))
print(gradentUpdate(initValue1))
print(gradentUpdate(initValue2))
print(gradentUpdate(initValue3))
drawhimme(listValue)
#out:非常接近各不同的解析解
#[2.9985528523044143, 2.0034789022320547]
#[3.584730946678452, -1.8514418324715358]
#[-2.806509818820845, 3.131577669902277]
#[-3.7799833500723787, -3.2842689582740467]
结论:不同的初始值最终优化为不同的局部最小值
学习参考来自:https://www.cnblogs.com/pinard/p/5970503.html