梯度下降优化方法总结
随机梯度下降stochastic gradient descent algorithm(SGD):
包括GD(batchsize=all),SGD(batchsize=1),mini-batch SGD(batchsize=mini-batch)
其中GD训练过程中可以不调整学习率,保持学习率不变训练到收敛
SGD,mini-batch SGD训练过程中必须保证不断减少学习率
当训练数据较大,GD每次迭代计算开销较大,因而mini-batch SGD更受青睐。
缺点:
- 学习率的选择比较困难,学习率过大过小都有问题。合适的学习率要靠实验来调。
- 收敛速度慢
代码:
# Mini-batch stochastic gradient descent.
def sgd(params, lr, batch_size):
for param in params:
param[:] = param - lr * param.grad / batch_size
SGD+momentum
论文:On the Momentum Term in Gradient Descent Learning Algorithms
其中a一般取值为0.5,0.9,0.99分别代表最大速度的2倍,10倍,100倍(放大倍数使用1/(1-a)计算)。和学习率一样,超参数a在训练中也随着时间不断变大。但是调整参数a没有调整学习率重要。
优点:
- 加速了SGD的收敛速度
缺点:
- 引入另外2个超参数a,v
- 所有的训练参数使用同样大小的学习率
代码:
def sgd_momentum(params, vs, lr, mom, batch_size):
for param, v in zip(params, vs):
v[:] = mom * v + lr * param.grad / batch_size
param[:] = param - v
Adagrad:
论文:Adaptive Subgradient Methods for Online Learning and Stochastic Optimization∗
Adagrad在sgd的基础上,分母除以了一个二阶矩累加梯度的平方和,然后开根号。Adagrad是一个在迭代过程中不断自我调整学习率,并让模型参数中每个元素都使用不同学习率的优化算法。
优点:
- 相比sgd+momentum,减少了参数量,去掉了a,v
- 不同的训练参数使用不同大小的学习率
- 在某些深度学习模型上效果不错,但不是全部
缺点:
(1)从训练开始累积的梯度平方会导致有效学习率过早和过量减少。由于分母不断的累加,如果刚开始累加的太多,会使得学习率下降的太快,导致训练到后期学习率很小,无法逃离局部极小值点。
代码:
# Adagrad.
def adagrad(params, sqrs, lr, batch_size):
eps_stable = 1e-7
for param, sqr in zip(params, sqrs):
g = param.grad / batch_size
sqr[:] += nd.square(g)
div = lr * g / nd.sqrt(sqr + eps_stable)
param[:] -= div
RMSprop:
未发表论文,Hinton课堂上提出,Lecture 6a Overview of mini-batch gradient descent
RMSprop在adagrad基础上,将分母由梯度平方和变为梯度的EMA
优点:
- 不同的训练参数使用不同大小的学习率,
- 对Adagrad进行了改进,改变梯度积累为指数加权移动平均。即对分母除的部分使用EMA进行计算,从而保证分母的值随着迭代次数不断累加,最终保持不变,这样最终学习率下降到某个点就不再下降,从而保证可以逃出局部极小值。
缺点:
- 分母刚开始的累加,由于初始化原因,假设初始化为0,则分母刚开始很小,导致学习率刚开始很大,造成误差较大,没解决冷启动问题
代码:
# RMSProp.
def rmsprop(params, sqrs, lr, gamma, batch_size):
eps_stable = 1e-8
for param, sqr in zip(params, sqrs):
g = param.grad / batch_size
sqr[:] = gamma * sqr + (1. - gamma) * nd.square(g)
div = lr * g / nd.sqrt(sqr + eps_stable)
param[:] -= div
Adadelta:
论文:ADADELTA: AN ADAPTIVE LEARNING RATE METHOD
在RMSprop基础上,将学习率替换为每个参数的平方和开根号,即分子为每个参数的EMA然后开根号,分母还是和RMSprop中一样,为每个梯度的EMA然后开根号。
优点:
- 没有学习率参数
- 不同的训练参数使用不同大小的学习率
缺点:
- 还是没解决冷启动问题
代码:
# Adadalta.
def adadelta(params, sqrs, deltas, rho, batch_size):
eps_stable = 1e-5
for param, sqr, delta in zip(params, sqrs, deltas):
g = param.grad / batch_size
sqr[:] = rho * sqr + (1. - rho) * nd.square(g)
cur_delta = nd.sqrt(delta + eps_stable) / nd.sqrt(sqr + eps_stable) * g
delta[:] = rho * delta + (1. - rho) * cur_delta * cur_delta
# update weight
param[:] -= cur_delta
Adam:
论文:ADAM: A METHOD FOR STOCHASTIC OPTIMIZATION
Adam组合了动量法和RMSProp。在RMSprop基础上,分子上梯度一阶矩加上了动量。分母保持RMSprop的梯度二阶矩,即EMA。并且为了解决冷启动问题,对分子的动量,分母的EMA都除了(1-p^t),从而保证刚开始值较大,随着迭代值再慢慢减少。
优点:
- 解决冷启动问题
- 不同的训练参数使用不同大小的学习率
代码:
# Adam.
def adam(params, vs, sqrs, lr, batch_size, t):
beta1 = 0.9
beta2 = 0.999
eps_stable = 1e-8
for param, v, sqr in zip(params, vs, sqrs):
g = param.grad / batch_size
v[:] = beta1 * v + (1. - beta1) * g
sqr[:] = beta2 * sqr + (1. - beta2) * nd.square(g)
v_bias_corr = v / (1. - beta1 ** t)
sqr_bias_corr = sqr / (1. - beta2 ** t)
div = lr * v_bias_corr / (nd.sqrt(sqr_bias_corr) + eps_stable)
param[:] = param - div
对比:
References: