在最近学习中,用Adam作为优化器,在训练时打印学习率发现学习率并没有改变。这好像与之前理解的自适应的学习率有所矛盾?
Adam的理论知识
Adam论文:https://arxiv.org/pdf/1412.6980.pdf
上图就是Adam优化算法在深度学习应用于梯度下降方法的详细过程,有一些参数要做出说明:
问题1 指数滑动平均是什么?
Exponential Moving Average (EMA) 指数滑动平均指各数值的加权系数随时间呈指数式递减,越靠近当前时刻的数值加权系数就越大。
以为例,从上面的推导可以看到,越远离时刻t,其梯度所占的比重就越小,在梯度在不断的更新的过程中,虽然使用了历史梯度,但是在不同时刻的梯度对当前贡献不同,离时刻t越近,对的影响就越大,离时刻t越远,对的影响就越小。
问题2 为什么需要进行修正?
(1)通俗解释:
当对不进行修正时():
=0
==
==
=
依次类推,我们可以看到由于=0,导致,均向0进行偏置,也会离越来越远。
(2)理论公式解释:
从上面的更新公式可以看出来,相当于是梯度一阶距估计,所以我们计算的期望:
从上面的公式可以看到,需要将修正为 ,才能近似认为为梯度的无偏距估计。同样的思路可以解释的修正。
问题3 学习率是如何变化的?
在Adam论文中指出可以将下面三行公式:
等价替换成:
在pytorch源代码中也是按照上述这种写法(附在最后面)。
那这样写是不是说明学习率的变化是由来决定呢,那这样还能称之为自适应的学习率吗?
我们知道梯度下降法的定义公式:
按照梯度下降法的定义公式,我们可以将参数更新公式写成:
其中,将视为梯度的一阶距估计,那么可以看作在t时刻,参数
最后附上Adam源码
我通过pytorch1.2/lib/python3.7/site-packages/torch/optim/找到adam.py文件,下面是代码:
def step(self, closure=None):
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
for p in group['params']:
if p.grad is None:
continue
grad = p.grad.data
if grad.is_sparse:
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
amsgrad = group['amsgrad']
state = self.state[p]
# State initialization
if len(state) == 0:
state['step'] = 0
# Exponential moving average of gradient values
state['exp_avg'] = torch.zeros_like(p.data)
# Exponential moving average of squared gradient values
state['exp_avg_sq'] = torch.zeros_like(p.data)
if amsgrad:
# Maintains max of all exp. moving avg. of sq. grad. values
state['max_exp_avg_sq'] = torch.zeros_like(p.data)
exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
if amsgrad:
max_exp_avg_sq = state['max_exp_avg_sq']
beta1, beta2 = group['betas']
state['step'] += 1
if group['weight_decay'] != 0:
grad.add_(group['weight_decay'], p.data)
# Decay the first and second moment running average coefficient
exp_avg.mul_(beta1).add_(1 - beta1, grad)
exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
if amsgrad:
# Maintains the maximum of all 2nd moment running avg. till now
torch.max(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
# Use the max. for normalizing running avg. of gradient
denom = max_exp_avg_sq.sqrt().add_(group['eps'])
else:
denom = exp_avg_sq.sqrt().add_(group['eps'])
bias_correction1 = 1 - beta1 ** state['step']
bias_correction2 = 1 - beta2 ** state['step']
step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1
p.data.addcdiv_(-step_size, exp_avg, denom)
return loss
以上均是我个人的理解,如果各位有更好的想法可以留言,共同学习进步!