python
机器学习
文献学习
原创demo
谢绝转载
- Momentum
借用了物理学中动量的概念,在更新梯度时引入了历史平均动量,
其中为模型参数,为学习率。递归计算公式为
其中,为损失函数,为衰减系数,
带动量的梯度下降法,相比于传统方法的一大优点是可以有效避免振荡。一个典型的例子就是向下倾斜的峡谷型表面。
Momentum.jpg
- NAG
内斯特洛夫加速梯度,更新公式类似动量下降,区别是在平均梯度下降方向的递推公式
可以发现等式右边第二项的梯度方向不再是由当前位置计算得到,而是变为依第一项的方向移动后的“预计位置”计算得到。直觉上,通过这种方法修正过的梯度方向对更新结果是有“预见性”的。而事实上,在一些情况中(例如在更新轨迹上损失函数经历先减后增的情况),内斯特洛夫加速梯度确实能够加速收敛。
NAG.jpg
-
Adagrad
即自适应性梯度下降法,该方法能够平衡最大梯度方向的不同分量之间差异,使得参数过程更为稳定。Adagrad相较前几种优化,引入了历史梯度平方和作为衰减因子。具体地,每一步的更新方向由下式决定
其中. 以上公式运算均为element-wise. 有一种说法是Adagrad能够惩罚高频分量,补偿低频分量,从而提高学习效率。 -
Adadelta (RMSprop)
Adagrad_Adadelta.jpg
是Adagrad的一个直接改进,将历史梯度平方和替换为历史平均平方和,且引入了根据参数尺度决定各分量步长的学习机制。首先,每一步的更新方向由下式决定
其中. 容易看出,如此改进可以一定程度解决历史求和导致学习速率衰减过快的问题。其次,关于学习率不再是标量化的统一学习速率,而是改为由下式确定
其中, 更新公式则变为
-
Adam
即Adaptive Moment Estimation. 类似动量梯度下降的思路,结合了以上方法的优点,引入了两种历史平均动量
更新公式变为
其中 . 是针对初始化时作出的修正。
理论和公式部分参考论文(强烈推荐):
An overview of gradient descent optimization algorithms
个人示例源代码(setting):
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def link_points(xs,ys,num=20):
assert xs.shape == ys.shape
tempx = []
tempy = []
for i in range(len(xs)-1):
tempx.append(np.linspace(xs[i],xs[i+1],num))
tempy.append(np.linspace(ys[i],ys[i+1],num))
return np.hstack(tempx),np.hstack(tempy)
def get_trail(x_init,y_init,steps=100,lr=.1,method="sgd",alpha=.9,epsilon=10**-3):
xs = np.zeros((steps+1,))
ys = np.zeros((steps+1,))
xs[0],ys[0] = x_init,y_init
if method == "sgd":
for i in range(1,steps+1):
grad_x,grad_y = get_grad(xs[i-1],ys[i-1])
xs[i] = xs[i-1]-lr*grad_x
ys[i] = ys[i-1]-lr*grad_y
if method == "mom":
moms = np.zeros((steps+1,2))
for i in range(1,steps+1):
grad_x,grad_y = get_grad(xs[i-1],ys[i-1])
moms[i] = alpha*moms[i-1]+(1-alpha)*np.array([grad_x,grad_y])
xs[i] = xs[i-1]-lr*moms[i,0]
ys[i] = ys[i-1]-lr*moms[i,1]
if method == "nag":
moms = np.zeros((steps+1,2))
for i in range(1,steps+1):
grad_x,grad_y = get_grad(xs[i-1]-alpha*moms[i-1,0],ys[i-1]-alpha*moms[i-1,1])
moms[i] = alpha*moms[i-1]+(1-alpha)*np.array([grad_x,grad_y])
xs[i] = xs[i-1]-lr*moms[i,0]
ys[i] = ys[i-1]-lr*moms[i,1]
if method == "adagrad":
rss = np.zeros((steps+1,2))
rss[0] = epsilon*np.ones((2,))
for i in range(1,steps+1):
grad_x,grad_y = get_grad(xs[i-1],ys[i-1])
rss[i] = rss[i-1]+np.array([grad_x,grad_y])**2
xs[i] = xs[i-1]-lr*(rss[i,0])**(-1/2)*grad_x
ys[i] = ys[i-1]-lr*(rss[i,1])**(-1/2)*grad_y
if method == "adadelta":
rss = np.zeros((steps+1,2))
rss[0] = epsilon*np.ones((2,))
eta = np.zeros((steps+1,2))
eta[0] = epsilon*np.ones((2,))
for i in range(1,steps+1):
grad_x,grad_y = get_grad(xs[i-1],ys[i-1])
rss[i] = alpha*rss[i-1]+(1-alpha)*(np.array([grad_x,grad_y])**2+epsilon)
delta = (eta[i-1]/rss[i])**(1/2)*np.array([grad_x,grad_y])
eta[i] = alpha*eta[i-1]+(1-alpha)*delta**2
xs[i] = xs[i-1]-delta[0]
ys[i] = ys[i-1]-delta[1]
return link_points(xs,ys)
def skate_ramp(X,Y):
return 9.9*X**2+100-Y
def get_grad(x,y):
return 19.8*x,-1
网友评论