梯度下降算法
在本部分主要对重要的算法-梯度下降进行学习,包括理论与实践。
再介绍梯度下降算法之前,先了解什么是梯度。首先,在我们高中时期就学习过导数的概念,而梯度就是带方向的导数,其数值与导数相等,梯度的方向就是下降最快的方向。
为了更加深入的理解梯度下降算法,我们可使用这个例子来感受。假设你现在在一个山顶,你可以用什么样的方式以最快的速度下山(假设每一步都是安全的)?不难想到以下方法:假设当前在山顶,那么以当前位置为基准,找到当前所处位置最陡峭的方向(即下降最快的方向),然后沿着该方向行走一步距离到达新的位置,然后再以新的位置为基准位置,找到最陡峭的地方,再走一步距离,直到到达山地。这个方法中涉及到两个重要的问题去解决,第一个是如何找到最陡峭哦的点,该问题可以通过梯度解决;另一个是每一步走多远,如果每一步太长,可能出现跨过了山谷,如果一步太短,则走的步数会太多,这个步长称之为学习率,学习率是一个超参数,选取需要根据经验。
从山上到山下的过程,我们可以理解为最小化海拔的过程,进行推广可以得知,梯度下降算法可以实现求一个函数的最小值。在前面我们提到,机器学习的目标是训练到一个好的模型,使得损失函数尽可能的小,让预测值和真实值尽可能的接近,因此我们可以将其转换为损失函数最小化的问题,因此可以用梯度下降算法来求解(通过梯度下降算法,学习到合适的参数,使得损失函数最小)。损失函数定义为
L
(
θ
)
\mathcal{L}(\theta)
L(θ),是关于模型参数
θ
\theta
θ的函数,目标是通过梯度下降算法求的最佳参数
θ
∗
\theta^*
θ∗使得损失函数最小,参数更新公式表示为:
θ
t
+
1
=
θ
t
−
α
∂
L
(
θ
)
∂
θ
\theta_{t+1} = \theta_t - \alpha\frac{ \partial \mathcal{L(\theta)}}{ \partial \theta}
θt+1=θt−α∂θ∂L(θ)
其中
θ
t
+
1
\theta_{t+1}
θt+1、
θ
t
\theta_t
θt分别表示第
t
t
t步更新后的参数与更新前的参数,为了更好的理解该更新公式,我们用一个简单的例子来解释梯度下降算法。假设存在函数
y
=
x
2
,
x
∈
[
−
10
,
10
]
y = x^2,x\in[-10,10]
y=x2,x∈[−10,10],需要实现计算机找到最小值点,如下图,在这个例子中,参数就是自变量
x
x
x,需要实现的就是找到最小的
y
y
y下对应的
x
x
x。在该图中,有
A
A
A、
B
B
B、
O
O
O三个点,坐标分别为
(
8
,
64
)
(8,64)
(8,64)、
(
−
8
,
64
)
(-8,64)
(−8,64)、
(
0
,
0
)
(0,0)
(0,0),显然在点
O
O
O处去的最小值,而我们的目标便是让计算机能够自动的找到
O
O
O点,换句话说,如果当前坐标在原点以右,需要向左移动,反之向右移动。首先我们要明确,梯度是以X轴的正方向作为梯度的正方向,因此,在
A
A
A点时,其梯度的大小
2
x
x
=
8
=
16
2x_{x =8} = 16
2xx=8=16,方向为该点的切线向右部分,为正,梯度表示为
+
16
+16
+16。在
B
B
B点时,其梯度的大小
∣
2
x
x
=
−
8
∣
=
16
|2x_{x =-8}| = 16
∣2xx=−8∣=16,方向为该点的切线向左部分,为负,梯度表示为
−
16
-16
−16。因此。当前位置在
A
A
A点时,根据参数更新公式,
x
x
x将要向左移动,如当前位置在
B
B
B点,根据参数更新公式,
x
x
x将要向右移动,直到停止到
O
O
O。在这其中,控制每一步移动的大小为学习率
α
∈
(
0
,
1
)
\alpha\in(0,1)
α∈(0,1)。
在上述例子中,代码实现与最终结果如下:
import torch
def get_gradient(x):
x_value = torch.tensor([x])
x_value.requires_grad = True
y_value = x_value ** 2
y_value.backward()
return x_value.grad.item()
def gradient_decent_sigal(x_now, interation_max, learning_rate):
for interation_index in range(interation_max):
gradient = get_gradient(x_now)
x_now = x_now - learning_rate * gradient
return x_now
if __name__ == '__main__':
x_init = 10.0
interation = 1000
lr = 0.01
x_final = gradient_decent_sigal(x_init, interation, lr)
print("x_final:", x_final)
print("y_final", x_final ** 2)
上述例子非常简单,在实际的机器学习中,损失函数远比一元函数复杂,但是不论数据集怎么复杂,基本的思路是类似的。对于一个实际的任务,其数据集是非常复杂的,为了提高速度,梯度下降又可以分为随机梯度下降、批量梯度下降、小批量梯度下降。
(1)批量梯度下降(BGD):每次更新参数都需要遍历所有的数据,好处是能够获得全局最优解,坏处是训练速度慢。
(2)随机梯度下降(SGD):每次仅仅选取数据中一条数据进行参数更新,好处是训练快,但可能会收敛到局部最优解。
(3)小批量梯度下降法(MBGD):结合BGD和SGD,每次从数据集中选择
K
K
K条样本进行训练。