当前位置: 代码迷 >> 综合 >> 【论文】Adam
  详细解决方案

【论文】Adam

热度:64   发布时间:2023-12-13 03:45:23.0

【论文】Kingma D , Ba J . Adam: A Method for Stochastic Optimization[J]. Computer ence, 2014.(pdf)


论文首次提出了 Adam 算法——基于一阶导数的随机梯度下降算法

Adam 是对 SGD、AdaGrad 和 RMSProp 算法的优化

Adam 结合 AdaGrad 和 RMSProp 两种算法的优点,对梯度的一阶矩估计和二阶矩估计都进行综合考虑,具体算法如下
在这里插入图片描述
算法流程,

  1. 计算 ttt 时刻目标函数对 θ\thetaθ 的梯度
  2. 计算梯度的一阶矩,即前面梯度与当前梯度的平均
  3. 计算梯度的二阶矩,即前面梯度与当前梯度平方的平均
  4. 对一阶矩 mtm_tmt? 进行校正,因为 m0m_0m0? 初始化为 000,会导致 mtm_tmt? 偏向于 000
  5. 对二阶矩 vtv_tvt? 进行校正,因为 v0v_0v0? 初始化为 000,会导致 vtv_tvt? 偏向于 000
  6. 更新参数 θt\theta_tθt?,注意此时可将 αv^t+?\frac{\alpha}{\sqrt{\hat v_t}+\epsilon}v^t? ?+?α? 视为更新参数 θt\theta_tθt? 的学习率,m^t\hat m_tm^t? 视为更新参数 θt\theta_tθt? 的梯度

注意上述算法可以通过改变计算顺序而提高效率,将循环的最后三行替代为下面两条句

αt=α?1?β2t/(1?β1t)θt←θt?1?αt?mt/(vt+?^)\alpha_t=\alpha\cdot\sqrt{1-\beta_2^t}/(1-\beta_1^t) \\ \theta_t\leftarrow\theta_{t-1}-\alpha_t\cdot m_t / (\sqrt{v_t}+\hat\epsilon)αt?=α?1?β2t? ?/(1?β1t?)θt?θt?1??αt??mt?/(vt? ?+?^)

代码实现

# ADAM
# 以 y=x1+2*x2为例
import math
import numpy as npdef adam():# 训练集,每个样本有三个分量x = np.array([(1, 1), (1, 2), (2, 2), (3, 1), (1, 3), (2, 4), (2, 3), (3,3)])y = np.array([3, 5, 6, 5, 7, 10, 8, 9])# 初始化m, dim = x.shapetheta = np.zeros(dim)  # 参数alpha = 0.01  # 学习率momentum = 0.1  # 冲量threshold = 0.0001  # 停止迭代的错误阈值iterations = 3000  # 迭代次数error = 0  # 初始错误为0b1 = 0.9  # 算法作者建议的默认值b2 = 0.999  # 算法作者建议的默认值e = 0.00000001  #算法作者建议的默认值mt = np.zeros(dim)vt = np.zeros(dim)for i in range(iterations):j = i % merror = 1 / (2 * m) * np.dot((np.dot(x, theta) - y).T,(np.dot(x, theta) - y))if abs(error) <= threshold:breakgradient = x[j] * (np.dot(x[j], theta) - y[j])mt = b1 * mt + (1 - b1) * gradientvt = b2 * vt + (1 - b2) * (gradient**2)mtt = mt / (1 - (b1**(i + 1)))vtt = vt / (1 - (b2**(i + 1)))vtt_sqrt = np.array([math.sqrt(vtt[0]),math.sqrt(vtt[1])])  # 因为只能对标量进行开方theta = theta - alpha * mtt / (vtt_sqrt + e)print('迭代次数:%d' % (i + 1), 'theta:', theta, 'error:%f' % error)if __name__ == '__main__':adam()

mtm_tmt?vtv_tvt? 的偏差修正

可以将 vt=β2?vt?1+(1?β2)?gt2v_t=\beta_2\cdot v_{t-1}+(1-\beta_2)\cdot g_t^2vt?=β2??vt?1?+(1?β2?)?gt2? 改写为所有时间步上只包含梯度和衰减率的函数,即 vt=(1?β2)∑i=1tβ2t?i?gi2v_t=(1-\beta_2)\sum^t_{i=1}\beta_2^{t-i}\cdot g_i^2vt?=(1?β2?)i=1t?β2t?i??gi2?

下面我们用数学归纳法简单证明一下

请添加图片描述
我们知道梯度的真实一阶矩为 E(gt)E(g_t)E(gt?),真实的二阶矩为 E(gt2)E(g_t^2)E(gt2?)。现在,我们希望知道的是时间步 ttt 上指数移动均值的期望 E(vt)E(v_t)E(vt?) 与真实的二阶矩 E(gt2)E(g_t^2)E(gt2?) 之间的差异,这样才好对这两个量之间的偏差进行修正
请添加图片描述

我们可以简单通过下面代码模拟看一下初始值的影响

import numpy as np
import matplotlib.pyplot as pltbeta = 0.9
num_samples = 100np.random.seed(0)
raw_data =  np.random.randint(32, 38, size = num_samples)
x_index = np.arange(num_samples)v_ema = []
v_pre = 0
for i, t in enumerate(raw_data):v_t = beta * v_pre + (1 - beta) * tv_ema.append(v_t)v_pre = v_tv_ema_corr = []for i, t in enumerate(v_ema):v_ema_corr.append(t / (1 - np.power(beta, i + 1)))plt.plot(x_index, raw_data, label='raw_data')  # Plot some data on the (implicit) axes.
plt.plot(x_index, v_ema, label='v_ema')  # etc.
plt.plot(x_index, v_ema_corr, label='v_ema_corr')
plt.xlabel('time')
plt.ylabel('T')
plt.title("exponential moving average")
plt.legend()
plt.savefig('./ema.png')
plt.show()

请添加图片描述
可以看到不经过修正的指数移动平均值在初始阶段阶段的结果与真实的曲线有很大的偏差,但是这种偏差随着步数的增加会越来越小;当然,经过修正的指数移动平均值在开始就可以很好的跟踪真实变化趋势

一阶矩、二阶矩

由前面可知,一阶矩 E(gt)E(g_t)E(gt?) 即当前梯度 gtg_tgt? 的期望(估计一阶矩 mtm_tmt?),由于当前梯度 gtg_tgt? 是随机采样得到的估计结果,因此更关注它在统计意义上的期望

二阶矩 E(gt2)E(g_t^2)E(gt2?) 即当前梯度的平方 gt2g_t^2gt2? 的期望(估计二阶矩),考虑它的意义要分四种情况:

  • vtv_tvt? 很大且 ∣∣mt∣∣||m_t||mt? 很大时,说明梯度大且稳定。vtv_tvt? 是梯度平方的指数移动均值自然结果为正,当 vtv_tvt? 很大,说明过往大部分的梯度与当前梯度的绝对值都不会太小。∣∣mt∣∣||m_t||mt? 指的是当前梯度指数移动均值的绝对值,当 ∣∣mt∣∣||m_t||mt? 很大,则说明过往梯度与当前梯度正负抵消的很少,即过往梯度与当前梯度一般是同号。这样,梯度更新相对稳定,过度平滑,可以看成梯度下降在**『走大下坡』**,梯度下降方向明确
  • vtv_tvt? 很大而 ∣∣mt∣∣||m_t||mt? 却很小时,则说明过往的大部分梯度和当前梯度的绝对值都很大,但是出现了很多的正负抵消的情况。此时,梯度更新**『处于震荡的状态』**,一会儿正,一会儿负,但由于 gt2g_t^2gt2? 的指数移动均值很大,说明单个梯度的绝对值很大,可能是向下更新到一个局部的波谷,有进行一波反弹
  • ∣∣mt∣∣||m_t||mt? 但是 vtv_tvt? 却趋于零,这种情况不可能会出现
  • ∣∣mt∣∣||m_t||mt? 趋于零且 vtv_tvt? 也趋于零时,『可能达到局部最低点,也可能走到一个极度平缓的地方』,此次要避免陷入平原

梯度更新

Adam 更新规则的一个重要特性时其步长的谨慎选择。假设 ?=0\epsilon=0?=0,时间步 ttt 下参数空间中的有效步长是 Δt=α?m^t/v^t\Delta_t=\alpha\cdot\hat m_t/\sqrt{\hat v_t}Δt?=α?m^t?/v^t? ?

这个有效步长有两个明确的上界:

  1. (1?β1)>1?β2(1-\beta_1)>\sqrt{1-\beta_2}(1?β1?)>1?β2? ? 的情况下,有效步长的上确界满足 ∣Δt∣?α?(1?β1)/1?β2|\Delta_t|\leqslant\alpha\cdot(1-\beta_1)/\sqrt{1-\beta_2}Δt??α?(1?β1?)/1?β2? ?
  2. 在其他情况下 ∣Δt∣<α|\Delta_t|<\alphaΔt?<α

第一种情况只有在极稀疏的情况下才会发生:即梯度除了当前时间步不为 0,其余时间步上梯度都为 0;而在不那么稀疏的情况下,有效步长会变得更小

(1?β1)=1?β2(1-\beta_1)=\sqrt{1-\beta_2}(1?β1?)=1?β2? ? 时,我们有 ∣m^t/v^t∣<1|\hat m_t/\hat v_t|<1m^t?/v^t?<1,此时我们可以确定出上确界 ∣Δ∣<α|\Delta|<\alphaΔ<α

在更通用的场景中,因为 ∣E(g)/g2∣?1|E(g)/\sqrt{g^2}|\leqslant1E(g)/g2 ??1,我们有 m^t/v^t≈±1\hat m_t/\sqrt{\hat v_t}\approx \pm1m^t?/v^t? ?±1,于是每一个时间步的有效步长在参数空间中的量级近似受限于步长因子 α\alphaα, 即 ∣Δt∣<αor∣Δt∣≈α|\Delta_t|<\alpha\ or\ |\Delta_t| \approx\alphaΔt?<α or Δt?α。这可以理解为在当前参数值下确定了一个置信域区间,这个置信域区间提供了一些当前梯度估计没有提供的信息。于是,通过其通常便可以提前知道正确的 α\alphaα 取值的右边界

对于多数机器学习模型来说,我们知道好的最优状态是在参数空间内的集合域上有着极高的概率,例如,我们可以在参数上有一个先验分布。α\alphaα 确定了参数空间内有效步长的上确界,通常也就可以推断出 α\alphaα 的正确量级,而最优解也可以从 θ0\theta_0θ0? 开始通过一定数量的跌倒到达这个正确的量级

我们将 m^t/v^t\hat m_t/\hat v_tm^t?/v^t? 称作信噪比(SNR),如果 SNR 值较小,那么有效步长 Δt\Delta_tΔt? 将接近于 0,目标函数也将收敛到极值。这是十分令人满意的,因为越小的 SNR 意味着就判断 m^t\hat m_tm^t? 的方向是否符合真实梯度方向这点上有着越大的不确定性。例如,SNR 值在最优解附近通常会趋近于 0,这同时也导致了在参数空间中使用更小的有效步长,这类似于一种自动退火的机制

对于不同的梯度范围来说,有效步长 Δt\Delta_tΔt? 是不变的,如果将梯度 ggg 乘以一个系数 ccc 进行缩放,那么 m^t\hat m_tm^t? 也要乘以一个一样的系数因子 ccc,而 v^t\hat v_tv^t? 则乘以系数 c2c^2c2,而最终的结果并不产生变化 (c?m^t)/(c2?v^t)=m^t/v^t(c\cdot\hat m_t)/(\sqrt{c^2\cdot\hat v_t})=\hat m_t/\sqrt{\hat v_t}(c?m^t?)/(c2?v^t? ?)=m^t?/v^t? ?

Adam 算法的优点

  • 惯性保持
    Adam 记录了梯度的一阶矩,即过往梯度与当前梯度的指数移动平均值,是的每一次更新时,上一次更新的梯度与当前更新的梯度不会相差太大,即梯度平滑、稳定的过度,可以适应不稳定的目标函数
  • 环境感知:
    Adam 记录了梯度的二阶矩,即过往梯度批平方与当前梯度平方的平均,为不同参数产生自适应的学习速率
  • 超参数易控制
    α、β1、β2、?\alpha、\beta_1、\beta_2、\epsilonαβ1?β2?? 具有良好的解释性,且通常无需调整参数或仅需很少的调整

AdaMax

前面我们有 vt=(1?β2)∑i=1tβ2t?i?gi2v_t=(1-\beta_2)\sum^t_{i=1}\beta_2^{t-i}\cdot g_i^2vt?=(1?β2?)i=1t?β2t?i??gi2?,这可以看成一个关于 gig_igi?L2L^2L2 范数,换句话说,权重 θt\theta_tθt? 的各维度上的增量是根据该维度上当前和过往梯度的 L2L^2L2 范数。我们可以从 L2L^2L2 范式的更新规则推广到 LpL^pLp 范数的更新规则,但是 ppp 值越大,推广算法在数值上将变得不稳定。在特例中,作者让 p→∞p\rightarrow\inftyp 可以的得到一个极其稳定且简单的算法

LpL^pLp 范数的情况下,vt=(1?β2p)∑i=1tβ2p(t?i)?∣gi∣pv_t=(1-\beta_2^p)\sum^t_{i=1}\beta_2^{p(t-i)}\cdot| g_i|^pvt?=(1?β2p?)i=1t?β2p(t?i)??gi?p

在这里插入图片描述请添加图片描述
于是,我们就得到了算法 2 里面的公式 ut=max(β2?ut?1,∣gt∣)u_t=max(\beta_2\cdot u_{t-1}, |g_t|)ut?=max(β2??ut?1?,gt?),初始化时 u0=0u_0=0u0?=0

在算法 2 里面我们不需要对初始化偏差作出修正。同时,参数更新的范围也有一个更加简洁的界限:∣Δt∣?α|\Delta_t|\leqslant\alphaΔt??α