• 深度学习笔记之优化算法(八)Adam算法的简单认识


    引言

    上一节介绍了基于 Nesterov \text{Nesterov} Nesterov动量与 RMSProp \text{RMSProp} RMSProp的融合算法,本节将介绍《深度学习(花书)》 P187 8.5 \text{P187 8.5} P187 8.5自适应学习率算法中的最后一个算法 Adam \text{Adam} Adam算法

    回顾:基于Nesterov动量的RMSProp算法

    基于 Nesterov \text{Nesterov} Nesterov动量的 RMSProp \text{RMSProp} RMSProp算法,其特点在于:对梯度大小(学习率)梯度方向同时优化。其对应的迭代公式表示如下:
    关于动量、学习率加权平均方法的差异性描述,详见上一节链接。
    { θ ^ t = θ t − 1 + γ ⋅ m t − 1 G t = ∇ θ ; t − 1 J ( θ ^ t ) R t = β ⋅ R t − 1 + ( 1 − β ) ⋅ G t ⊙ G t m t = γ ⋅ m t − 1 − η R t ⊙ G t θ t = θ t − 1 + m t {ˆθt=θt1+γmt1Gt=θ;t1J(ˆθt)Rt=βRt1+(1β)GtGtmt=γmt1ηRtGtθt=θt1+mt θ^t=θt1+γmt1Gt=θ;t1J(θ^t)Rt=βRt1+(1β)GtGtmt=γmt1Rt ηGtθt=θt1+mt

    Adam算法的简单认识

    Adam \text{Adam} Adam算法与上述算法的思想相同,即迭代过程中,对梯度大小、方向均进行优化。不同点在于:

    • 无论是梯度大小(学习率)还是梯度方向,均使用指数加权移动平均法进行更新:
      { G = ∇ θ ; t − 1 J ( θ t − 1 ) m t = ρ 1 ⋅ m t − 1 + ( 1 − ρ 1 ) ⋅ G R t = ρ 2 ⋅ R t − 1 + ( 1 − ρ 2 ) ⋅ G ⊙ G {G=θ;t1J(θt1)mt=ρ1mt1+(1ρ1)GRt=ρ2Rt1+(1ρ2)GG G=θ;t1J(θt1)mt=ρ1mt1+(1ρ1)GRt=ρ2Rt1+(1ρ2)GG
    • 使用指数加权移动平均法更新的基础上,分别对更新结果 m t , R t m_t,\mathcal R_t mt,Rt进行偏差修正
      • 关于第一个公式,我们对累积梯度(向量) m t m_t mt进行修正,其本质是对梯度向量 G \mathcal G G进行修正,因而称其为一阶矩偏差修正
      • 同理,第二个公式,我们对累积梯度内积(标量) R t \mathcal R_t Rt进行修正,其本质对梯度内积 G ⊙ G \mathcal G \odot \mathcal G GG进行修正,因而称其为二阶矩偏差修正
      • 其中 t t t表示迭代步骤的编号
        { m ^ t = 1 1 − ( ρ 1 ) t ⋅ m t R ^ t = 1 1 − ( ρ 2 ) t ⋅ R t {ˆmt=11(ρ1)tmtˆRt=11(ρ2)tRt m^tR^t=1(ρ1)t1mt=1(ρ2)t1Rt
    • 最终对权重进行更新:
      该操作与 AdaGrad,RMSProp \text{AdaGrad,RMSProp} AdaGrad,RMSProp原理相同。
      θ t = θ t − 1 + Δ θ = θ t − 1 − η ϵ + R ^ t ⋅ m ^ t θt=θt1+Δθ=θt1ηϵ+ˆRtˆmt θt=θt1+Δθ=θt1ϵ+R^t ηm^t

    下面从个人理解的角度认知:为什么要使用一个关于迭代步骤 t t t非线性函数对一阶矩、二阶矩的偏差进行修正

    一阶矩、二阶矩修正偏差的功能

    首先,从《深度学习(花书)》中关于 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2描述开始:

    • ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2分别是调整当前梯度/梯度内积与历史累积梯度/梯度内积的比例因子

    • 在书中关于 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2初始化步骤中分别为: ρ 1 = 0.9 , ρ 2 = 0.999 \rho_1 = 0.9,\rho_2 = 0.999 ρ1=0.9,ρ2=0.999。可以看出:无论是梯度还是梯度内积,在迭代过程中极其依赖历史信息,而不是当前步骤信息

      与此同时,完全可以作出 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2固定条件下,一阶矩系数 1 1 − ( ρ 1 ) t 11(ρ1)t 1(ρ1)t1、二阶矩系数 1 1 − ( ρ 2 ) t 11(ρ2)t 1(ρ2)t1随迭代步骤 t t t增长的修正变化曲线
      修正变化曲线效果图
      结合上面的权重更新公式可以看出:在迭代初始的几个步骤内,给予 m t ⇒ m ^ t m_t \Rightarrow \hat {m}_t mtm^t较高的增长;但与此同时,同样使用较低 η ϵ + R ^ t ηϵ+ˆRt ϵ+R^t η约束 m ^ t \hat {m}_t m^t增长的幅度
      虽然从图中可以看出迭代初期 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2之间的函数结果相差几十倍,但通过 ⋅ \sqrt{\cdot} 的消减,使得它们的增长与约束处于同一个量级

    很明显,这是一场对抗,但这场对抗仅仅持续了迭代初期的若干次步骤中。那么换一种思路:为什么在迭代初期的对抗最激烈 ? ? ? 迭代初期发生了什么 ? ? ? 不要忘记,由于 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2取值的原因,导致整个迭代过程都非常依赖历史信息,并且初始点通常是随机初始化的,也就是说:初始位置的梯度信息是不确定、不稳定的

    初始的几次迭代步骤,可能会出现大幅度的折叠、震荡,而这种变化剧烈的梯度若累积历史梯度/历史梯度内积中,会导致后续的迭代不稳定。虽然这种不稳定被系数 1 1 − ( ρ 1 ) t 11(ρ1)t 1(ρ1)t1小规模放大,但同样被强劲的系数 1 ϵ + R t 1ϵ+Rt ϵ+Rt 1压制,使其虽然梯度方向震荡的很厉害(梯度方向较大),但这种状态没有办法移动较大的步长(梯度大小较小),从而压制住震荡的产生

    该部分更多是对算法的个人理解,不否认,我们可以尝试修改 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2的值,但需要知道的是:两者之间的取值存在一种均衡关系

    Adam的算法过程描述

    基于 Adam \text{Adam} Adam算法步骤表示如下:
    初始化操作

    • 学习率 η \eta η;一阶矩、二阶矩衰减速率 ρ 1 , ρ 2 ∈ [ 0 , 1 ) ( 0.9 , 0.999 ) \rho_1,\rho_2 \in [0,1)(0.9,0.999) ρ1,ρ2[0,1)(0.9,0.999)
    • 超参数 ϵ = 1 0 − 8 \epsilon = 10^{-8} ϵ=108;初始权重参数 θ \theta θ;初始化迭代步骤 t = 0 t=0 t=0
    • 初始化历史累积梯度 m = O m = \mathcal O m=O( O \mathcal O O表示零向量);初始化历史累积梯度内积 R = 0 \mathcal R = 0 R=0

    算法过程

    • While \text{While} While没有达到停止准则 do \text{do} do
    • 从训练集 D \mathcal D D中采集出包含 k k k个样本的小批量 { ( x ( i ) , y ( i ) ) } i = 1 k \{(x^{(i)},y^{(i)})\}_{i=1}^k {(x(i),y(i))}i=1k
    • 计算当前迭代步骤参数 θ \theta θ梯度信息 G \mathcal G G
      G ⇐ 1 k ∑ i = 1 k ∇ θ L [ f ( x ( i ) ; θ ) , y ( i ) ] \mathcal G \Leftarrow \frac{1}{k} \sum_{i=1}^k \nabla_{\theta} \mathcal L[f(x^{(i)};\theta),y^{(i)}] Gk1i=1kθL[f(x(i);θ),y(i)]
    • 迭代步骤 t ⇐ t + 1 t \Leftarrow t + 1 tt+1
    • 使用指数加权移动平均法历史累积梯度 m m m进行更新
      m ⇐ ρ 1 ⋅ m + ( 1 − ρ 1 ) ⋅ G m \Leftarrow \rho_1 \cdot m + (1 - \rho_1) \cdot \mathcal G mρ1m+(1ρ1)G
    • 使用指数加权移动平均法历史累积梯度内积 R \mathcal R R进行更新
      R ⇐ ρ 2 ⋅ R + ( 1 − ρ 2 ) ⋅ G ⊙ G \mathcal R \Leftarrow \rho_2 \cdot \mathcal R + (1 - \rho_2) \cdot \mathcal G \odot \mathcal G Rρ2R+(1ρ2)GG
    • 历史累积梯度 m m m进行偏差修正
      m ^ ⇐ 1 1 − ( ρ 1 ) t ⋅ m \hat m \Leftarrow \frac{1}{1 - (\rho_1)^t} \cdot m m^1(ρ1)t1m
    • 历史累积梯度内积 R \mathcal R R进行偏差修正
      R ^ ⇐ 1 1 − ( ρ 2 ) t ⋅ R \hat {\mathcal R} \Leftarrow \frac{1}{1 - (\rho_2)^t}\cdot \mathcal R R^1(ρ2)t1R
    • 计算当前迭代步骤权重参数更新量 Δ θ \Delta \theta Δθ
      标量乘向量,即向量中的每一个分量均乘一个 − η ϵ + R ^ ηϵ+ˆR ϵ+R^ η
      Δ θ = − η ϵ + R ^ ⋅ m ^ \Delta \theta = -\frac{\eta}{\sqrt{\epsilon + \hat {\mathcal R}}} \cdot \hat {m} Δθ=ϵ+R^ ηm^
    • 应用更新:
      θ ⇐ θ + Δ θ \theta \Leftarrow \theta + \Delta\theta θθ+Δθ
    • End While \text{End While} End While

    Adam示例代码

    依然使用凸函数 f ( x ) = x T Q x ; x = ( x 1 , x 2 ) T ; Q = ( 0.5 0 0 20 ) f(x) = x^T \mathcal Qx;x=(x_1,x_2)^T;\mathcal Q = (0.50020) f(x)=xTQx;x=(x1,x2)T;Q=(0.50020)作为目标函数,观察其迭代过程。对应代码表示如下:
    复制粘贴过来的,哈哈~

    import numpy as np
    import math
    import matplotlib.pyplot as plt
    from tqdm import tqdm
    
    def f(x, y):
        return 0.5 * (x ** 2) + 20 * (y ** 2)
    
    def ConTourFunction(x, Contour):
        return math.sqrt(0.05 * (Contour - (0.5 * (x ** 2))))
    
    def Derfx(x):
        return x
    
    def Derfy(y):
        return 40 * y
    
    def DrawBackGround():
        ContourList = [0.2, 1.0, 4.0, 8.0, 16.0, 32.0]
        LimitParameter = 0.0001
        for Contour in ContourList:
            # 设置范围时,需要满足x的定义域描述。
            x = np.linspace(-1 * math.sqrt(2 * Contour) + LimitParameter, math.sqrt(2 * Contour) - LimitParameter, 200)
            y1 = [ConTourFunction(i, Contour) for i in x]
            y2 = [-1 * j for j in y1]
            plt.plot(x, y1, '--', c="tab:blue")
            plt.plot(x, y2, '--', c="tab:blue")
    
    def Adam():
        def DeviationCorrection(Input,RhoParameter,Step):
            if type(Input) == tuple:
                Res = (Input[0] / (1 - (RhoParameter ** Step)),Input[1] / (1 - (RhoParameter ** Step)))
                return Res
            else:
                return Input / (1 - (RhoParameter ** Step))
    
        Start = (8.0, 1.0)
        LocList = list()
        LocList.append(Start)
        StartMomentum = (0.0, 0.0)
        R = 0.0
        Eta = 0.3
        Step = 0
        Rho1 = 0.9
        Rho2 = 0.999
        Epsilon = 0.00000001
        Delta = 0.1
    
        while True:
            DerStart = (Derfx(Start[0]),Derfy(Start[1]))
            Step += 1
    
            UpdateMomentum = ((Rho1 * StartMomentum[0]) + ((1 - Rho1) * DerStart[0]),
                              (Rho1 * StartMomentum[1]) + ((1 - Rho1) * DerStart[1]))
            InnerProduct = (DerStart[0] ** 2) + (DerStart[1] ** 2)
            
            DecayR = R * Rho2
            R = DecayR + ((1.0 - Rho2) * InnerProduct)
            CorrectionMomentum = DeviationCorrection(UpdateMomentum,Rho1,Step)
            CorrectionR = DeviationCorrection(R,Rho2,Step)
    
            UpdateMessage = (-1 * (Eta * CorrectionMomentum[0]) / (math.sqrt(CorrectionR) + Epsilon),
                             -1 * (Eta * CorrectionMomentum[1]) / (math.sqrt(CorrectionR) + Epsilon))
            Next = (Start[0] + UpdateMessage[0],Start[1] + UpdateMessage[1])
            DerNext = (Derfx(Next[0]),Derfy(Next[1]))
    
            if math.sqrt((DerNext[0] ** 2) + (DerNext[1] ** 2)) < Delta:
                break
            else:
                LocList.append(Next)
                StartMomentum = UpdateMomentum
                Start = Next
        return LocList
    
    def DrawPicture():
        NesterovRMSPropLocList = Adam()
        plt.figure(figsize=(10,5))
        NesterovRMSPropplotList = list()
        DrawBackGround()
        for (x, y) in tqdm(NesterovRMSPropLocList):
            NesterovRMSPropplotList.append((x, y))
            plt.scatter(x, y, s=30, facecolor="none", edgecolors="tab:red", marker='o')
            if len(NesterovRMSPropplotList) < 2:
                continue
            else:
                plt.plot([NesterovRMSPropplotList[0][0], NesterovRMSPropplotList[1][0]], [NesterovRMSPropplotList[0][1], NesterovRMSPropplotList[1][1]], c="tab:red")
                NesterovRMSPropplotList.pop(0)
        plt.show()
    
    if __name__ == '__main__':
        DrawPicture()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    对应图像结果表示如下:
    Adam算法示例
    关于 ρ 1 , ρ 2 \rho_1,\rho_2 ρ1,ρ2取值情况,为了保证它们之间的均衡关系,在取值过程中需要注意一下。例如: ρ 1 = 0.3 , ρ 2 = 0.9 \rho_1 = 0.3,\rho_2 = 0.9 ρ1=0.3,ρ2=0.9对应的函数图像结果表示如下:
    因为这个凸函数示例过于简单,大家可以试一试其他的参数组合方式~
    Adam算法示例2
    至此,深度学习中的优化方法暂时告一段落。

    Reference \text{Reference} Reference
    《深度学习(花书)》 P189 8.5.3 Adam \text{P189 8.5.3 Adam} P189 8.5.3 Adam

  • 相关阅读:
    【王道】计算机组成原理第二章数据的表示与运算(二)
    [UE]常见数学和插值
    刷题记录(NC16692 [NOIP2001]求先序排列,NC204382 中序序列,NC23046 华华教月月做数学)
    MySQL中最左匹配相关实践总结
    前端关于对象中套用对象传参的小问题
    【概率论与数理统计】计算机保研复习
    多线程进阶
    WINDOWS+PHP+Mysql+Apache环境中部署SQLi-Labs、XSS-Labs、UPload-Labs、DVWA、pikachu等靶场环境
    lab1-1 恶意代码分析实战
    Unet语义分割训练和TensorRT部署
  • 原文地址:https://blog.csdn.net/qq_34758157/article/details/133774912