本文主要介绍了Adagrad、RMSProp、AdaDelta、Adam四种经典的深度学习的优化算法。在基于 PyTorch 的 MNIST数字图像数据集分类模型训练与评估的简单练习-CSDN博客一文中展示了深度学习的简单框架,在其基础上通过修改优化器便能得到不同优化方法的结果。下面将只展示修改的优化器部分。
一、Adagrad方法
该方法通过初始化一个变量s为0,然后每次将该参数的梯度平方求和累加到这个变量s上,然后更新参数设置学习率为
此处的是为了防止
为零时,导致学习率为无穷大而设置的一个很小的正数,通常设置为
。该方法核心是在梯度一直特别大时,学习率就会特别小,从而防止震荡。当梯度一直特别小时,学习率就会变大,从而快速更新参数。
但是该方法存在一些问题,比如,是损失值平方的累加,会越来越大,导致后期学习率会很小,导致还没收敛,学习率就特别低,使得进程十分缓慢。
优化器设置如下:
optimizer = torch.optim.Adagrad(net.parameters(), lr=0.01)
如果我们想自己实现优化函数,代码如下所示:
def sgd_adagrad(parameters, sqrs, learn_rate):
eps = 1e-10
for param, sqr in zip(parameters, sqrs):
sqr[:] = sqr + param.grad.data ** 2
change = learn_rate / torch.sqrt(sqr + eps) * param.grad.data
param.data = param.data - change
其中sqrs需要初始化为与参数大小相同的张量。
二、RMSProp方法
该方法仍然使用到了损失值平方累加,但在处理上与Adagrad方法不同。 该方法在损失值平方累加上还添加了一个指数加权移动平均来计算,如下所示:
其中就是添加的权重,而
是当前参数的梯度值。最终学习率就与上面的Adagrad方法一样了。
拥有这个参数后,在每次更新参数时,对于梯度较大的方向,其学习率会相对较小,从而避免了学习过程中的大幅震荡;对于梯度较小的方向,学习率相对较大,能够更快地收敛到最优值。同时这个系数也使得到了训练后期
不会打太,使得其依然能找到比较优秀的结果。
优化器设置如下:
optimizer = torch.optim.RMSprop(net.parameters(), 0.001, 0.9)
若自己实现优化器,代码如下:
def rmsprop(parameters, sqrs, lr, alpha):
eps = 1e-10
for param, sqr in zip(parameters, sqrs):
sqr[:] = alpha * sqr + (1 - alpha) * param.grad.data ** 2
div = lr / torch.sqrt(sqr + eps) * param.grad.data
param.data = param.data - div
其中sqrs需要初始化为与参数大小相同的张量,alpha通常设置为0.9。
三、AdaDelta方法
该方法算是Adagrad方法的延伸,与RMSProp方法一样,都是为了解决在Adagrad方法中学习率不断减小的问题。
该方法同RMSProp一样,先使用移动平均来计算
此处的与
同RMSProp方法的一样,分别是移动平均系数与当前参数梯度。然后计算参数更新的变化量,如下:
初始值设置为0的张量,更新
与上述更新
的方式相同。
最终参数更新如下。
优化器设置如下:
optimizer = torch.optim.Adadelta(net.parameters(), rho=0.9)
若自己实现优化器,代码如下:
ef adadelta(parameters, sqrs, deltas, alpha):
eps = 1e-6
for param, sqr, delta in zip(parameters, sqrs, deltas):
sqr[:] = alpha * sqr + (1 - alpha) * param.grad.data ** 2
cur_delta = torch.sqrt(delta + eps) / torch.sqrt(sqr + eps) * param.grad.data
delta[:] = alpha * delta + (1 - alpha) * cur_delta ** 2
param.data = param.data - cur_delta
其中sqrs、deltas均为初始为0的张量,alpha是平均移动系数。
四、Adam方法
该方法结合了动量法与RMSProp法,首先他将与
都设置为0的张量,然后通过迭代计算他们的更新值。
在该方法中为了减少s与v初始化为0张量对加权平均移动的影响,每次都会对与
做以下操作。
这里t是训练的次数,当、
位于0到1之间时,当训练的次数足够多时,
、
就越趋近于0,则不会影响
和
了。算法作者建议将
与
分别设置成0.9和0.999。然后将计算的到的
与
用于计算更新参数的变化之。
最后进行参数更新。
该优化算法结合动量法与RMSProp各自的优点,同时具有较好的参数更新稳定性。
优化器设置如下:
optimizer = torch.optim.Adam(net.parameters(), lr=0.001)
若自己实现优化器,代码如下:
def adam(parameters, vs, sqrs, lr, t, beta1=0.9, beta2=0.999):
eps = 1e-8
for param, v, sqr in zip(parameters, vs, sqrs):
v[:] = beta1 * v + (1 - beta1) * param.grad.data
sqr[:] = beta2 * sqr + (1 - beta2) * param.grad.data ** 2
v_hat = v / (1 - beta1 ** t)
s_hat = sqr / (1 - beta2 ** t)
param.data = param.data - lr * v_hat / torch.sqrt(s_hat + eps)
其中vs和sqrs为初始化为0的张量,t为训练的次数。
五、对比
使用上述四种方法训练相同的数据,通过matplotlab可视化以及控制台输出得到如下结果。
***********adagrad************
epoch:1,Train Loss:0.41554
epoch:2,Train Loss:0.26491
epoch:3,Train Loss:0.22543
epoch:4,Train Loss:0.20005
epoch:5,Train Loss:0.18108
所用时间为:19.04406
***********RMSProp************
epoch:1,Train Loss:0.37212
epoch:2,Train Loss:0.16852
epoch:3,Train Loss:0.12333
epoch:4,Train Loss:0.10148
epoch:5,Train Loss:0.08937
所用时间为:21.77974s
***********adadelt************
epoch:1,Train Loss:0.36424
epoch:2,Train Loss:0.15955
epoch:3,Train Loss:0.12493
epoch:4,Train Loss:0.10394
epoch:5,Train Loss:0.08919
所用时间为:20.12976
*************Adam*************
epoch:1,Train Loss:0.35856
epoch:2,Train Loss:0.17507
epoch:3,Train Loss:0.12567
epoch:4,Train Loss:0.10140
epoch:5,Train Loss:0.08493
所用时间为:23.16603