前面的博文里说了SGD,最基础的一个梯度下降优化算法,在SGD之后还有很多改进版本的算法,比如动量法,下面我降动量法扥别作用于两个函数,第一个是完美凸函数,第二个则是非凸的香蕉函数
动量法的参数更新公式:
v就是动量,实际上是速度,可以认为是单位质量下的动量
第一个式子是说v是上一个v和刚计算出来的梯度的指数加权平均(但是这里我们不要求 α + η = 1 \alpha+\eta=1 α+η=1),上一个v的权重是 α \alpha α,即程序代码中的momentum参数, 而梯度的权重是 η \eta η,也就是学习率
通过v就引入了之前的梯度,包括大小和方向,所以不再像SGD那样,每次只考虑当前位置算出来的刚出炉的新鲜梯度,每次都独立的更新参数,而是把之前的更新记忆起来,指导现在的更新,新算出来的梯度的值反而占权重很小,对参数更新影响程度也小的多。
如果 α = 0.9 \alpha=0.9 α=0.9,则收敛比SGD近似快10倍
如果 α = 0.99 \alpha=0.99 α=0.99,则收敛比SGD近似快100倍
这个观点我在哪看到过,记不清了,以后弄清楚了过来记录
函数1:(Rosenbrock函数)
f ( x , y ) = ( a − x ) 2 + b ( y − x 2 ) 2 f(x,y)=(a-x)^2+b(y-x^2)^2 f(x,y)=(a−x)2+b(y−x2)2
取a=1,b=100
从下图可以看出,动量法确实比SGD快的多,我的另一篇讲SGD的博文有实验图和代码,学习率0.0035走10000步也还是离最小值点很远,而且后面由于梯度太小走的跟没走一样······而动量梯度下降(这里学习率0.001,动量0.9(即比普通梯度下降快10倍))只用不到1000步就到达最小值点了,但是动量法对于之字形下降缓解不大,只是加快收敛。下面代码中的参数可以改一改看看实验效果,学习率稍微再大一点比如0.0015,动量法都会学的太快导致跑出画布,毕竟跑得太远也不利于最终收敛,我们可以改小学习率,或者改大momentum参数(减小增速倍数)来控制学习速率。终于明白为啥别人都说深度学习就是调参了······如果使用完善的框架的话,不用自己搭网络,确实可以这么说。
# Momentum.py
# 动量梯度下降法应用于Rosenbrock函数
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
class Momentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
# 第一次调用
self.v = {
}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum * self.v[key] - self.lr * grads[int(key)]
params[key] += self.v[key]
return params
# Rosenbrock函数
def func(x):
return (1 - x[0]) ** 2 + 100 * (x[1] - x[0]**2) ** 2
def gradient_descent(f, init_x, lr=0.1, step_num=100):
x = init_x
x_history = []
for i in range