梯度下降法
参考文献:【梯度下降(Gradient Descent)小结】,【深入浅出–梯度下降法及其实现】
在求解机器学习算法的模型参数,即无约束优化问题时,梯度下降(Gradient Descent)是最常用的方法之一。
1. 梯度的解释
在微积分里面,对多元函数的参数求
∂
∂
∂偏导数,把求得的各个参数的偏导数以向量的形式写出来,就是梯度。
比如函数
f
(
x
,
y
)
f(x,y)
f(x,y), 分别对
x
x
x,
y
y
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)
grad f(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(x_0,y_0)
▽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,以此类推。
那么这个梯度向量求出来有什么意义呢?他的意义从几何意义上讲,就是函数变化增加最快的地方。具体来说,对于函数 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 , y ) f(x,y) f(x,y)增加最快的地方。或者说,沿着梯度向量的方向,更加容易找到函数的最大值。反过来说,沿着梯度向量相反的方向,也就是 − ( ∂ f / ∂ x 0 , ∂ f / ∂ y 0 ) T -(∂f/∂x_0,∂f/∂y_0)^T −(∂f/∂x0,∂f/∂y0)T的方向,梯度减少最快,也就是更加容易找到函数的最小值。
2. 梯度下降与梯度上升
在机器学习算法中,在最小化损失函数时,可以通过梯度下降法来一步步的迭代求解,得到最小化的损失函数,和模型参数值。反过来,如果我们需要求解损失函数的最大值,这时就需要用梯度上升法来迭代了。
梯度下降法和梯度上升法是可以互相转化的。比如我们需要求解损失函数
f
(
θ
)
f(θ)
f(θ)的最小值,这时我们需要用梯度下降法来迭代求解。但是实际上,我们可以反过来求解损失函数
−
f
(
θ
)
-f(θ)
−f(θ)的最大值,这时梯度上升法就派上用场了。
3. 梯度下降法算法
3.1 直观解释
比如我们在一座大山上的某处位置,由于我们不知道怎么下山,于是决定走一步算一步,也就是在每走到一个位置的时候,求解当前位置的梯度,沿着梯度的负方向,也就是当前最陡峭的位置向下走一步,然后继续求解当前位置梯度,向这一步所在位置沿着最陡峭最易下山的位置走一步。这样一步步的走下去,一直走到觉得我们已经到了山脚。当然这样走下去,有可能我们不能走到山脚,而是到了某一个局部的山峰低处。
梯度下降不一定能够找到全局的最优解,有可能是一个局部最优解。当然,如果损失函数是凸函数,梯度下降法得到的解就一定是全局最优解。
3.2 相关概念解释
- 步长(Learning rate):步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度。用下山的例子,步长就是在当前这一步所在位置沿着最陡峭最易下山的位置走的那一步的长度。
- 特征(feature):指的是样本中输入部分,比如2个单特征的样本 ( x ( 0 ) , y ( 0 ) ) (x^{(0)},y^{(0)}) (x(0),y(0)), ( x ( 1 ) , y ( 1 ) ) (x^{(1)},y^{(1)}) (x(1),y(1)),则第一个样本特征为 x ( 0 ) x^{(0)} x(0),第一个样本输出为 y ( 0 ) y^{(0)} y(0)。
- 假设函数(hypothesis function):在监督学习中,为了拟合输入样本,而使用的假设函数,记为 h θ ( x ) h_{\theta}(x) hθ(x)。比如对于单个特征的 m m 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 θ = θ 0 + θ 1 x h_{\theta}=\theta_0+\theta_1x hθ=θ0+θ1x。
- 损失函数(loss function):为了评估模型拟合的好坏,通常用损失函数来度量拟合的程度。损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优参数。在线性回归中,损失函数通常为样本输出和假设函数的差取平方。比如对于
m
m
m个样本
(
x
i
,
y
i
)
(
i
=
1
,
2
,
.
.
.
,
m
)
(x_i,y_i)(i=1,2,...,m)
(xi,yi)(i=1,2,...,m),采用线性回归,损失函数为:
J ( θ 0 , θ 1 ) = ∑ i = 1 m ( h θ ( x i ) − y i ) 2 (1) J(\theta_0,\theta_1)=\sum_{i=1}^{m} (h_{\theta}(x_i)-y_i)^2 \tag 1 J(θ0,θ1)=i=1∑m(hθ(xi)−yi)2(1)
其中 x i x_i xi表示第 i i i个样本特征, y i y_i yi表示第 i i i个样本对应的输出, h θ ( x i ) h_{\theta}(x_i) hθ(xi)为假设函数。
3.3 梯度下降法的代数方式描述
- 先决条件:确认优化模型的假设函数和损失函数。比如对于线性回归,假设函数表示为
h
θ
(
x
1
,
x
2
,
.
.
.
,
x
n
)
=
θ
0
+
θ
1
x
1
+
.
.
.
+
θ
n
x
n
h_{\theta}(x_1,x_2,...,x_n)=\theta_0+\theta_1x_1+...+\theta_nx_n
hθ(x1,x2,...,xn)=θ0+θ1x1+...+θnxn,其中
θ
i
(
i
=
0
,
1
,
2
,
.
.
.
,
n
)
\theta_i(i=0,1,2,...,n)
θi(i=0,1,2,...,n)模型参数,
x
i
(
i
=
0
,
1
,
2
,
.
.
.
,
n
)
x_i(i=0,1,2,...,n)
xi(i=0,1,2,...,n)为每个样本的
n
n
n个特征值。这个表示可以简化,我们增加一个特征
x
0
=
1
x_0=1
x0=1,这样
h
θ
(
x
0
,
x
1
,
x
2
,
.
.
.
.
,
x
n
)
=
∑
i
=
0
n
θ
i
x
i
h_{\theta}(x_0,x_1,x_2,....,x_n)=\sum_{i=0}^{n} \theta_ix_i
hθ(x0,x1,x2,....,xn)=∑i=0nθixi。
同样是线性回归,对应于上面的假设函数,损失函数(均方误差代价函数)为:
J ( θ 0 , θ 1 , . . . , θ n ) = 1 2 m ∑ j = 1 m ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . . , x n ( j ) ) − y j ) 2 (2) J(\theta_0,\theta_1,...,\theta_n) = \frac{1}{2m} \sum_{j=1}^{m}(h_{\theta}(x_0^{(j)},x_1^{(j)},....,x_n^{(j)})-y_j)^2 \tag 2 J(θ0,θ1,...,θn)=2m1j=1∑m(hθ(x0(j),x1(j),....,xn(j))−yj)2(2) - 算法相关参数初始化:主要是初始化 θ 0 , θ 1 , . . . , θ n θ_0,θ_1,...,θ_n θ0,θ1,...,θn,算法终止距离 ε \varepsilon ε以及步长 α \alpha α。
- 算法过程:
(1) 确定当前位置的损失函数的梯度,对于 θ i \theta_i θi其梯度表达式如下:
∂ ∂ θ i J ( θ 0 , θ 1 , . . . , θ n ) (3) \frac{\partial}{\partial \theta_i} J(\theta_0,\theta_1,...,\theta_n) \tag 3 ∂θi∂J(θ0,θ1,...,θn)(3)
(2) 用步长乘以损失函数的梯度,得到当前位置下降的距离,即 α ∂ ∂ θ i J ( θ 0 , θ 1 , . . . , θ n ) \alpha \frac{\partial}{\partial \theta_i} J(\theta_0,\theta_1,...,\theta_n) α∂θi∂J(θ0,θ1,...,θn)对应于前面登山例子中的某一步。
(3) 确定是否所有的 θ i \theta_i θi,梯度下降的距离都小于 ε \varepsilon ε,如果小于 ε \varepsilon ε则算法终止,当前所有的 θ i ( i = 0 , 1 , . . . , n ) \theta_i(i=0,1,...,n) θi(i=0,1,...,n)即为最终结果。否则进入步骤4。
(4) 更新所有的 θ \theta θ,对于 θ i \theta_i θi,其更新表达式如下。更新完毕后继续转入步骤1。
θ i = θ i − α ∂ ∂ θ i J ( θ 0 , θ 1 , . . . , θ n ) (4) \theta_i=\theta_i-\alpha \frac{\partial}{\partial \theta_i} J(\theta_0,\theta_1,...,\theta_n) \tag 4 θi=θi−α∂θi∂J(θ0,θ1,...,θn)(4)
3.4 算法调优
- 算法的步长选择。在前面的算法描述中,我提到取步长为1,但是实际上取值取决于数据样本,可以多取一些值,从大到小,分别运行算法,看看迭代效果,如果损失函数在变小,说明取值有效,否则要增大步长。前面说了。步长太大,会导致迭代过快,甚至有可能错过最优解。步长太小,迭代速度太慢,很长时间算法都不能结束。所以算法的步长需要多次运行后才能得到一个较为优的值。
- 算法参数的初始值选择。 初始值不同,获得的最小值也有可能不同,因此梯度下降求得的只是局部最小值;当然如果损失函数是凸函数则一定是最优解。由于有局部最优解的风险,需要多次用不同初始值运行算法,关键损失函数的最小值,选择损失函数最小化的初值。
- 归一化。由于样本不同特征的取值范围不一样,可能导致迭代很慢,为了减少特征取值的影响,可以对特征数据归一化,也就是对于每个特征x,求出它的期望x和标准差std(x),然后转化为:
x − x ‾ s t d ( x ) \frac{x-\overline{x}}{std(x)} std(x)x−x
这样特征的新期望为0,新方差为1,迭代速度可以大大加快。
4. 梯度下降法大家族
4.1 批量梯度下降法(Batch Gradient Descent)
4.2 随机梯度下降法(Stochastic Gradient Descent)
4.3 小批量梯度下降法(Mini-batch Gradient Descent)
5. 算法
定义数据集和学习率
:
import numpy as np
# Size of the points dataset.
m = 20
# Points x-coordinate and dummy value (x0, x1).
X0 = np.ones((m, 1)) # m行1列值为1的量
X1 = np.arange(1, m+1).reshape(m, 1) # 1到20个数
X = np.hstack((X0, X1)) # 水平方向放置
# Points y-coordinate
y = np.array([
3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12,
11, 13, 13, 16, 17, 18, 17, 19, 21
]).reshape(m, 1)
# The Learning Rate alpha.
alpha = 0.01
损失函数
:
def error_function(theta, X, y):
'''Error function J definition.'''
diff = np.dot(X, theta) - y
return (1./2*m) * np.dot(np.transpose(diff), diff)
损失函数的梯度
:
def gradient_function(theta, X, y):
'''Gradient of the function J definition.'''
diff = np.dot(X, theta) - y
return (1./m) * np.dot(np.transpose(X), diff)
梯度下降迭代计算
:
def gradient_descent(X, y, alpha):
'''Perform gradient descent.'''
theta = np.array([1, 1]).reshape(2, 1)
gradient = gradient_function(theta, X, y)
while not np.all(np.absolute(gradient) <= 1e-5):
theta = theta - alpha * gradient
gradient = gradient_function(theta, X, y)
return theta
当梯度小于
1e-5
时,说明已经进入了比较平滑的状态,类似于山谷的状态,这时候再继续迭代效果也不大了,所以这个时候可以退出循环!
调用
:
optimal = gradient_descent(X, y, alpha)
print('optimal:', optimal)
print('error function:', error_function(optimal, X, y)[0,0])
迭代结果
:
optimal: [[0.51583286] [0.96992163]]
error function: 405.98496249324046