在最近学习中,用Adam作为优化器,在训练时打印学习率发现学习率并没有改变。这好像与之前理解的自适应的学习率有所矛盾?

Adam的理论知识

Adam论文:https://arxiv.org/pdf/1412.6980.pdf

机器学习adam学习率最大值 adam算法原理_深度学习

上图就是Adam优化算法在深度学习应用于梯度下降方法的详细过程,有一些参数要做出说明:

机器学习adam学习率最大值 adam算法原理_梯度下降法_02

具体可以通过来理解Adam的原理。

问题1 指数滑动平均是什么?

Exponential Moving Average (EMA) 指数滑动平均指各数值的加权系数随时间呈指数式递减,越靠近当前时刻的数值加权系数就越大。

机器学习adam学习率最大值 adam算法原理_自适应_03


机器学习adam学习率最大值 adam算法原理_算法_04为例,从上面的推导可以看到,越远离时刻t,其梯度所占的比重就越小,在梯度在不断的更新的过程中,虽然使用了历史梯度,但是在不同时刻的梯度对当前机器学习adam学习率最大值 adam算法原理_算法_04贡献不同,离时刻t越近,对机器学习adam学习率最大值 adam算法原理_算法_04的影响就越大,离时刻t越远,对机器学习adam学习率最大值 adam算法原理_算法_04的影响就越小。

问题2 为什么需要进行修正?
(1)通俗解释:

当对机器学习adam学习率最大值 adam算法原理_算法_04不进行修正时(机器学习adam学习率最大值 adam算法原理_梯度下降法_09):
机器学习adam学习率最大值 adam算法原理_算法_10=0
机器学习adam学习率最大值 adam算法原理_机器学习adam学习率最大值_11=机器学习adam学习率最大值 adam算法原理_深度学习_12=机器学习adam学习率最大值 adam算法原理_深度学习_13
机器学习adam学习率最大值 adam算法原理_自适应_14=机器学习adam学习率最大值 adam算法原理_深度学习_15=机器学习adam学习率最大值 adam算法原理_自适应_16
机器学习adam学习率最大值 adam算法原理_机器学习adam学习率最大值_17=机器学习adam学习率最大值 adam算法原理_算法_18
依次类推,我们可以看到由于机器学习adam学习率最大值 adam算法原理_算法_10=0,导致,机器学习adam学习率最大值 adam算法原理_算法_04均向0进行偏置,也会离机器学习adam学习率最大值 adam算法原理_深度学习_21越来越远。

(2)理论公式解释:

从上面机器学习adam学习率最大值 adam算法原理_算法_04的更新公式可以看出来,机器学习adam学习率最大值 adam算法原理_算法_04相当于是梯度机器学习adam学习率最大值 adam算法原理_深度学习_21一阶距估计,所以我们计算机器学习adam学习率最大值 adam算法原理_算法_04的期望:

机器学习adam学习率最大值 adam算法原理_算法_26


从上面的公式可以看到,需要将机器学习adam学习率最大值 adam算法原理_算法_04修正为机器学习adam学习率最大值 adam算法原理_深度学习_28 ,才能近似认为机器学习adam学习率最大值 adam算法原理_算法_04为梯度机器学习adam学习率最大值 adam算法原理_深度学习_21的无偏距估计。同样的思路可以解释机器学习adam学习率最大值 adam算法原理_机器学习adam学习率最大值_31的修正。

问题3 学习率是如何变化的?

在Adam论文中指出可以将下面三行公式:

机器学习adam学习率最大值 adam算法原理_机器学习adam学习率最大值_32


等价替换成:

机器学习adam学习率最大值 adam算法原理_梯度下降法_33


在pytorch源代码中也是按照上述这种写法(附在最后面)。

那这样写是不是说明学习率的变化是由机器学习adam学习率最大值 adam算法原理_梯度下降法_34来决定呢,那这样还能称之为自适应的学习率吗?

我们知道梯度下降法的定义公式:

机器学习adam学习率最大值 adam算法原理_深度学习_35


按照梯度下降法的定义公式,我们可以将参数更新公式写成:

机器学习adam学习率最大值 adam算法原理_自适应_36
其中,将机器学习adam学习率最大值 adam算法原理_算法_04视为梯度机器学习adam学习率最大值 adam算法原理_深度学习_21的一阶距估计,那么机器学习adam学习率最大值 adam算法原理_梯度下降法_39可以看作在t时刻,参数机器学习adam学习率最大值 adam算法原理_梯度下降法_40

最后附上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

以上均是我个人的理解,如果各位有更好的想法可以留言,共同学习进步!