当前位置: 首页 > 工具软件 > adam-blog > 使用案例 >

模型优化-Adam

隗轶
2023-12-01

Adam(Adaptive Moment Estimation) 优化算法实质上是将 Momentum 和 RMSprop 进行结合。Momentum 具有保持惯性的优点,RMSprop 实际上根据参数来调整学习率的衰减,体现环境感知能力。Adam 结合 Momentum 和 RMSprop,因此同时拥有惯性保持和环境感知这两个优点,而这两个优点也是缓解山谷震荡和鞍部停滞的关键动力。

简单地介绍了什么是 Adam 之后,我们再来看看如何使用 Adam 算法。

【计算过程】:

  • 计算每个参数的梯度。
    d w = ∂ L ( w ) w dw = \frac{\partial L(w)}{w} dw=wL(w)
  • 计算速度更新量以及修正后的速度更新量。
    v = β 1 v + ( 1 − β 1 ) d w v ′ = v 1 − β 1 t v = \beta_1 v + (1 - \beta_1)dw \quad v' = \frac{v}{1 - \beta_1^t} v=β1v+(1β1)dwv=1β1tv
  • 计算梯度累积平方以及修正后的梯度累积平方。
    S d w = β 2 S d w + ( 1 − β 2 ) d w 2 S d w ′ = S d w 1 − β 2 t Sdw = \beta_2 Sdw + (1 - \beta_2)dw^2 \quad Sdw' = \frac{Sdw}{1 - \beta_2^t} Sdw=β2Sdw+(1β2)dw2Sdw=1β2tSdw
  • 更新参数。
    w = w − η S d w ′ + σ v ′ w = w - \frac{\eta}{\sqrt{Sdw' + \sigma}}v' w=wSdw+σ ηv

需要注意的上,t 的初始值为 1,也就是说第一次迭代时,t 就等于 1,因此修正后的 v 和 Sdw 分母不会为零。

【问】:为什么要对 v 和 Sdw 进行修正?

假设,v 的初始值为 0,β1 = 0.9,第一轮迭代后的 dw = 30,根据 v = β 1 v + ( 1 − β 1 ) d w v = \beta_1 v + (1 - \beta_1)dw v=β1v+(1β1)dw 计算所得的 v = 0.9 * 0 + 0.1 * 30 = 3,这和 dw = 30 差距太大了,我们知道梯度下降算法每次参数更新使用的公式为 w = w − η d w w = w - \eta dw w=wηdw,在不考虑学习率的前提下,Adam 公式计算的结果与梯度下降算法的结果相差接近 10 倍!为什么会这样?

因为 v 的初始值为零,也就是说在第一轮迭代中,v 的计算并没有历史信息可以借鉴,根据 v 的计算公式可知,这一轮对 v 的影响程度只有 (1 - β),通常只有 10%,而 90% 都落在历史信息上。正是基于该原因,v 在前几轮迭代过程中没有足够的历史信息,从而值较小,影响收敛速度。因此,我们需要想办法消除这种影响。

办法就是添加修正项,除上 ( 1 − β t ) (1 - \beta^t) (1βt)。v = 3,v / (1 - 0.9) = 30,添加修正项后 v 的取值大致接近 dw。

解释了 Adam 算法为什么需要进行修正,我们再来讨论 Adam 算法中的参数该如何进行设置。

吴恩达老师在深度学习课程中建议:

  • β 1 \beta_1 β1:0.9
  • β 2 \beta_2 β2:0.999
  • σ \sigma σ:10e-8

学习率则需要通过调参操作来选择合适的参数。

在其他的资料中,dw 往往被称为矩阵的第一阶矩(first moment), d w 2 dw^2 dw2 被称为矩阵的第二阶矩(second moment),这也是 Adam 名字的由来。

【代码实现】:以线性回归为例。

def Adam(x, y, step=0.01, iter_count=500, batch_size=4, beta1=0.9, beta2=0.999):
    length, features = x.shape
    
    # 初始化 v 和 Sdw 以及整合数据集
    data = np.column_stack((x, np.ones((length, 1))))
    w = np.zeros((features + 1, 1))
    v, Sdw, eta = 0, 0, 10e-8
    start, end = 0, batch_size
    
    # 开始迭代,从 1 开始
    for i in range(1, iter_count + 1):
        # 计算梯度
        dw = np.sum((np.dot(data[start:end], w) - y[start:end]) * data[start:end], axis=0).reshape((features + 1, 1)) / length
        
        # 计算速度更新量以及修正后的速度更新量
        v = beta1 * v + (1 - beta1) * dw
        v_corrected = v / (1 - beta1**i)
        
        # 计算梯度累积平方以及修正后的梯度累积平方
        Sdw = beta2 * Sdw + (1 - beta2) * np.dot(dw.T, dw)
        Sdw_corrected = Sdw / (1 - beta2**i)
        
        # 更新参数
        w = w - (step / np.sqrt(eta + Sdw_corrected)) * v_corrected
        
        start = (start + batch_size) % length
        if start > length:
            start -= length
        end = (end + batch_size) % length
        if end > length:
            end -= length
    return w

完整代码可从 传送门 中获得。

参考

 类似资料: