梯度下降法

最简单的梯度下降法

  梯度下降法本质上是由微分公式推导而来的,即dy=f(x)dxdy=f ^{ \prime }(x)dx。由公式可知,如果x沿着导数f(x)f^{\prime}(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()

梯度下降法
  我们用J(θ)J({\theta})来表示目标函数,用dJdθ\frac { d J } { d \theta}表示目标函数的导函数。用代码表示如下所示:

def J(theta):
	return (theta - 2.5) ** 2 - 1.0

def dJ(theta):
	return 2*(theta - 2.5)

  在本次任务中,假设theta=0theta = 0为起始点,假设学习率η=0.0001\eta =0.0001为起始值。代码实现如下所示:

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))

学习率衰减的梯度下降

  最原始的梯度下降法存在着一个问题,即学习率是恒定不变的。但学习应该是先快后慢会更好,即ηt=η/t+1\eta^{t}=\eta/ \sqrt{t+1}。这里的+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))

Adagrad

wt+1=wtηtσtgt w^{t+1} = w^{t} - \frac{\eta^{t}}{\sigma^{t}}g^{t}
ηt=ηt+1 \eta^{t}=\frac{\eta}{\sqrt {t+1}}
σt=1t+1i=0t(gi)2 \sigma^{t}=\sqrt{\frac {1}{t+1} \sum_{i=0}^{t}(g^{i})^2}
wt+1=wtηtσtgt=wtηi=0t(gi)2gt w^{t+1} = w^{t} - \frac{\eta^{t}}{\sigma^{t}}g^{t}=w^{t}-\frac{\eta}{\sqrt{\sum_{i=0}^{t}(g^{i})^2}}g^{t}