梯度下降法
最简单的梯度下降法
梯度下降法本质上是由微分公式推导而来的,即。由公式可知,如果x沿着导数的方向行进,就会使得y增加。反之,如果沿着导数的方向行进,就会使得y减少。当函数为凸函数的时候,我们就可以通过梯度下降法得到它的最小值。
要注意的是,微分公式要成立的前提是什么呢?右侧的dx必须足够小,只有在它足够小的前提下,此公式才成立。这也就引出了学习率这一重要概念。所以梯度下降法就是要沿着导数的反方向小步幅不断的行进,直至导数为0停止。
我们以一个开口朝上的抛物线方程为例,目标是求出它的最小值。代码和图像如下所示:
import numpy as np
import matplotlib.pyplot as plt
plot_x = np.linspace(-1.0, 6.0, 141)
plot_y = (plot_x-2.5)**2 - 1.0
plt.plot(plot_x, plot_y)
plt.show()
我们用,用表示目标函数的导函数。用代码表示如下所示:
def J(theta):
return (theta - 2.5) ** 2 - 1.0
def dJ(theta):
return 2*(theta - 2.5)
在本次任务中,假设为起始点,假设学习率为起始值。代码实现如下所示:
theta = 0
eta = 0.0001
epsilon=1e-8
times = 0
while True:
gradient = dJ(theta)
if abs(gradient) < epsilon:
break
theta = theta - eta * gradient
times += 1
print("theta is {}, times is {}".format(theta, times))
结果为theta is 2.4999999950004193, times is 100141。10W多的迭代次数有些多。那我们不妨把学习率修改大一些。将学习率设置成0.001、0.01、0.1、1,结果绘制到一张表格如下所示:
学习率 | theta | 循环次数 |
---|---|---|
0.0001 | 2.5 | 100141 |
0.001 | 2.5 | 10006 |
0.01 | 2.5 | 992 |
0.1 | 2.5 | 90 |
1 | — | ---- |
注:当学习率为1的时候,死循环无结果。
又上图可知,学习率是和循环次数线性减少的,这大概是由于dJ(theta)是一次函数的缘故吧。问题:是否存在优化的算法,比如对学习率进行动态的调整呢?
参考代码如下所示:
eta_list = [0.0001, 0.001, 0.01, 0.1, 1]
epsilon = 1e-8
for eta in eta_list:
theta = 0
times = 0
while True:
gradient = dJ(theta)
if abs(gradient) < epsilon:
break
theta = theta - eta * gradient
times += 1
print("eta is {}, theta is {}, times is {}".format(eta, theta, times))
学习率衰减的梯度下降
最原始的梯度下降法存在着一个问题,即学习率是恒定不变的。但学习应该是先快后慢会更好,即。这里的+1是平滑项,目的是防止分母为0。
将学习率设置成0.01、0.1、1,结果绘制到表格如下所示:
学习率 | theta | 循环次数 |
---|---|---|
0.01 | 2.5 | 251420 |
0.1 | 2.5 | 2537 |
1 | 4 |
参考代码如下所示:
from math import sqrt
eta_list = [0.01, 0.1, 1]
epsilon = 1e-8
for eta in eta_list:
theta = 0
times = 0
while True:
gradient = dJ(theta)
if abs(gradient) < epsilon:
break
theta = theta - eta * gradient / sqrt(times + 1)
times += 1
print("eta is {}, theta is {}, times is {}".format(eta, theta, times))