11.9 AdaDelta
除了RMSProp算法以外,另一个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 [1]。有意思的是,AdaDelta算法没有学习率这一超参数。
Algorithm
AdaDelta算法也像RMSProp算法一样,使用了小批量随机梯度按元素平方的指数加权移动平均变量。在时间步0,它的所有元素被初始化为0。给定超参数,同RMSProp算法一样计算
与RMSProp算法不同的是,AdaDelta算法还维护一个额外的状态变量,其元素同样在时间步0时被初始化为0。我们使用来计算自变量的变化量:
其中是为了维持数值稳定性而添加的常数,如。接着更新自变量:
最后,我们使用来记录自变量变化量按元素平方的指数加权移动平均:
可以看到,如不考虑的影响,AdaDelta算法与RMSProp算法的不同之处在于使用来替代超参数。
Implement
AdaDelta算法需要对每个自变量维护两个状态变量,即和。我们按AdaDelta算法中的公式实现该算法。
def init_adadelta_states(): s_w, s_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32) delta_w, delta_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32) return ((s_w, delta_w), (s_b, delta_b)) def adadelta(params, states, hyperparams): rho, eps = hyperparams['rho'], 1e-5 for p, (s, delta) in zip(params, states): s[:] = rho * s + (1 - rho) * (p.grad.data**2) g = p.grad.data * torch.sqrt((delta + eps) / (s + eps)) p.data -= g delta[:] = rho * delta + (1 - rho) * g * g
d2l.train_ch7(adadelta, init_adadelta_states(), {'rho': 0.9}, features, labels)
loss: 0.243485, 0.084914 sec per epoch
Pytorch Class
通过名称为“adadelta”的Trainer
实例,我们便可使用pytorch提供的AdaDelta算法。它的超参数可以通过rho
来指定。
d2l.train_pytorch_ch7(torch.optim.Adadelta, {'rho': 0.9}, features, labels)
loss: 0.267756, 0.061329 sec per epoch
11.10 Adam
Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 [1]。下面我们来介绍这个算法。
Algorithm
Adam算法使用了动量变量和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量,并在时间步0将它们中每个元素初始化为0。给定超参数(算法作者建议设为0.9),时间步的动量变量即小批量随机梯度的指数加权移动平均:
和RMSProp算法中一样,给定超参数(算法作者建议设为0.999),
将小批量随机梯度按元素平方后的项做指数加权移动平均得到:
由于我们将和中的元素都初始化为0,
在时间步我们得到。将过去各时间步小批量随机梯度的权值相加,得到 。需要注意的是,当较小时,过去各时间步小批量随机梯度权值之和会较小。例如,当时,。为了消除这样的影响,对于任意时间步,我们可以将再除以,从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。在Adam算法中,我们对变量和均作偏差修正:
接下来,Adam算法使用以上偏差修正后的变量和,将模型参数中每个元素的学习率通过按元素运算重新调整:
其中是学习率,是为了维持数值稳定性而添加的常数,如。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。最后,使用迭代自变量:
Implement
我们按照Adam算法中的公式实现该算法。其中时间步通过hyperparams
参数传入adam
函数。
%matplotlib inline import torch import sys sys.path.append("/home/kesci/input") import d2lzh1981 as d2l def get_data_ch7(): data = np.genfromtxt('/home/kesci/input/airfoil4755/airfoil_self_noise.dat', delimiter='\t') data = (data - data.mean(axis=0)) / data.std(axis=0) return torch.tensor(data[:1500, :-1], dtype=torch.float32), \ torch.tensor(data[:1500, -1], dtype=torch.float32) features, labels = get_data_ch7() def init_adam_states(): v_w, v_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32) s_w, s_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32) return ((v_w, s_w), (v_b, s_b)) def adam(params, states, hyperparams): beta1, beta2, eps = 0.9, 0.999, 1e-6 for p, (v, s) in zip(params, states): v[:] = beta1 * v + (1 - beta1) * p.grad.data s[:] = beta2 * s + (1 - beta2) * p.grad.data**2 v_bias_corr = v / (1 - beta1 ** hyperparams['t']) s_bias_corr = s / (1 - beta2 ** hyperparams['t']) p.data -= hyperparams['lr'] * v_bias_corr / (torch.sqrt(s_bias_corr) + eps) hyperparams['t'] += 1
d2l.train_ch7(adam, init_adam_states(), {'lr': 0.01, 't': 1}, features, labels)
loss: 0.242722, 0.089254 sec per epoch
Pytorch Class
d2l.train_pytorch_ch7(torch.optim.Adam, {'lr': 0.01}, features, labels)
loss: 0.242389, 0.073228 sec per epoch