首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

深度学习笔记之优化算法(三)动量法的简单认识

  • 24-03-09 02:01
  • 3228
  • 8345
blog.csdn.net

深度学习笔记之优化算法——动量法的简单认识

  • 引言
    • 回顾:条件数与随机梯度下降的相应缺陷
    • 动量法简单认识
    • 动量法的算法过程描述
    • 附:动量法示例代码

引言

上一节介绍了随机梯度下降 ( Stochastic Gradient Descent,SGD ) (\text{Stochastic Gradient Descent,SGD}) (Stochastic Gradient Descent,SGD),本节将介绍动量法。

回顾:条件数与随机梯度下降的相应缺陷

早在梯度下降法在强凸函数的收敛性分析中介绍了条件数 ( Condition Number ) (\text{Condition Number}) (Condition Number)的概念。如果目标函数 f ( ⋅ ) f(\cdot) f(⋅)在某点处的 Hessian Matrix ⇒ ∇ 2 f ( ⋅ ) \text{Hessian Matrix} \Rightarrow \nabla^2 f(\cdot) Hessian Matrix⇒∇2f(⋅)存在并且它具备:
这意味着 Hessian Matrix \text{Hessian Matrix} Hessian Matrix必然是正定矩阵。
∇ 2 f ( ⋅ ) ≽ I \nabla^2 f(\cdot) \succcurlyeq \mathcal I ∇2f(⋅)≽I
那么它的条件数 C \mathcal C C可表示为:
C = λ m a x λ m i n \mathcal C = \frac{\lambda_{max}}{\lambda_{min}} C=λmin​λmax​​
其中 λ m a x \lambda_{max} λmax​与 λ m i n \lambda_{min} λmin​分别表示 ∇ 2 f ( ⋅ ) \nabla^2 f(\cdot) ∇2f(⋅)特征值的最大、最小值。如果 C \mathcal C C过大,会导致:使用梯度下降法处理 f ( ⋅ ) f(\cdot) f(⋅)的优化问题,当 C ⇒ ∞ \mathcal C \Rightarrow \infty C⇒∞时,那么算法的收敛速度由线性收敛退化至次线性收敛。这种现象也被称作 Hessain Matrix \text{Hessain Matrix} Hessain Matrix的病态条件。

上面仅仅是理论上的描述。在真实环境下,会出现什么样的效果 ? ? ?以标准二次型 f ( x ) = x T Q x f(x) = x^T \mathcal Q x f(x)=xTQx为例,其中 Q = ( 0.5 0 0 20 ) , x = ( x 1 , x 2 ) T \mathcal Q = (0.50020)

(0.50020)
,x=(x_1,x_2)^T Q=(0.50020​),x=(x1​,x2​)T。使用梯度下降法对该目标函数求解最小值的迭代过程见下图:

  • 由于 Q \mathcal Q Q是对角阵,因而它的特征值分别是 0.5 , 20 0.5,20 0.5,20因而在 f ( x ) f(x) f(x)定义域内的点,其对应 Hessian Matrix \text{Hessian Matrix} Hessian Matrix的条件数也是不低的。
  • 如果从目标函数的角度观察,它会是一个中间狭窄,两端狭长的船形形状。
  • 关于该图代码见文章末尾,下同~
    梯度下降法关于病态问题的收敛效果描述

在该示例中,我们并不否认其最终收敛到最优解,但使用迭代步骤的数量是比较夸张的:

  • 其中一个原因是梯度下降法不具备二次终止性——即便已经到达最优解附近,但依然不能基于有限迭代步骤内接近最优解;
  • 在整个迭代过程中,梯度下降法在 f ( ⋅ ) f(\cdot) f(⋅)的收敛路径过于冗余(线性收敛退化至次线性收敛的效果)——完全可以通过震荡更小的、路径更直接的方式实现收敛过程。

动量法简单认识

关于上述梯度下降法的迭代公式表示如下:
θ ⇐ θ − η ⋅ ∇ θ J ( θ ) \theta \Leftarrow \theta - \eta \cdot \nabla_{\theta} \mathcal J(\theta) θ⇐θ−η⋅∇θ​J(θ)
其中 η \eta η表示学习率。它可以被理解为每一迭代步骤,负梯度方向上调整的步长大小。随着迭代步骤的推移,梯度结果逐渐减小,而学习率是固定值,从而导致收敛速度越来越慢:

  • 如果目标函数 f ( ⋅ ) f(\cdot) f(⋅)至少是严格凸函数,那么 f ( ⋅ ) f(\cdot) f(⋅)存在全局解,这种情况充其量是不具备二次终止性——需要花费较长时间收敛到最优解附近;
  • 但如果目标函数非常复杂,收敛速度慢可能导致:数值解陷入局部最优或者鞍点。

关于动量法的迭代公式表示如下:
一些文章中也描述为: { m ⇐ γ ⋅ m + η ⋅ ∇ θ J ( θ ) θ ⇐ θ − m {m⇐γ⋅m+η⋅∇θJ(θ)θ⇐θ−m

{m⇐γ⋅m+η⋅∇θJ(θ)θ⇐θ−m
{m⇐γ⋅m+η⋅∇θ​J(θ)θ⇐θ−m​,两者之间等价,因为负梯度方向的描述就是 − ∇ θ J ( θ ) -\nabla_{\theta} \mathcal J(\theta) −∇θ​J(θ)该式首先仅将梯度数值部分进行累积,再执行更新。
{ m ⇐ γ ⋅ m − η ⋅ ∇ θ J ( θ ) θ ⇐ θ + m {m⇐γ⋅m−η⋅∇θJ(θ)θ⇐θ+m
{m⇐γ⋅m−η⋅∇θ​J(θ)θ⇐θ+m​

其中 m m m表示动量;并且该变量在每次迭代过程中均被更新——它在每一次迭代过程中都对梯度元素 η ⋅ ∇ θ J ( θ ) \eta \cdot \nabla_{\theta} \mathcal J(\theta) η⋅∇θ​J(θ)进行累积; γ ∈ [ 0 , 1 ) \gamma \in [0,1) γ∈[0,1)表示动量因子,它决定了之前迭代步骤累积得到的动量衰减的程度效果。

而动量法的核心思想是:利用过去累积的梯度元素与当前迭代步骤的梯度元素进行加权运算,使其朝着最优解更快收敛:

  • 如果 γ = 0 \gamma = 0 γ=0意味着:在当前迭代步骤中,之前累积的梯度元素没有任何比重。也就是说,每次迭代过程仅与当前迭代步骤的梯度相关,此时的动量法也退化为梯度下降法。这里以 γ = 0.1 \gamma = 0.1 γ=0.1图像进行示例:
    其中绿色图像表示梯度下降法的迭代路径;红色图像则表示 γ = 0.1 \gamma=0.1 γ=0.1时动量法的迭代路径。此时两者已经非常接近了,但动量法的迭代步骤明显小于梯度法。
    gamma=0.1
  • 如果 γ ⇒ 1 \gamma \Rightarrow 1 γ⇒1意味着:当前迭代步骤中的梯度信息几乎不起作用,而过去累积的梯度元素占主导部分。这里以 γ = 0.9 \gamma=0.9 γ=0.9为例,对应图像结果表示如下:
    很明显,通过红色图像可知,当前迭代步骤的梯度元素也是有意义的,不能过于否定当前结果而过于依赖过去信息。
    gamma=0.9
  • 因而需要找到合适的 γ \gamma γ,使其能够稳定收敛的基础上,减少迭代路径的距离。例如 γ = 0.6 \gamma=0.6 γ=0.6时的图像结果:
    可以明显看出:随着迭代步骤的更新,动量法迭代的幅度(跨越的距离长度)越来越大。即:过去梯度元素牵制着当前步骤的梯度方向。从而会有效减少更新步骤。并且,对于这类病态条件的二次目标函数有着不错的效果。
    gamma=0.6

关于动量法的更新速度,见如下示例:

  • m t m_t mt​表示第 t t t次迭代步骤的动量;假设每次迭代的梯度结果均相同,为 G = ∇ θ J ( θ ) \mathcal G = \nabla_{\theta} \mathcal J(\theta) G=∇θ​J(θ),从初始时刻 m 0 = 0 m_0=0 m0​=0开始,有如下迭代过程:
    常用泰勒公式,需要满足 γ ∈ ( − 1 , 1 ) \gamma \in (-1,1) γ∈(−1,1)
    m 0 = 0 m 1 = γ ⋅ m 0 + η ⋅ G = η ⋅ G m 2 = γ ⋅ m 1 + η ⋅ G = ( 1 + γ ) η ⋅ G m 3 = γ ⋅ m 2 + η ⋅ G = ( 1 + γ + γ 2 ) η ⋅ G ⋮ m + ∞ = ( 1 + γ + γ 2 + γ 3 + ⋯   ) η ⋅ G = 1 1 − γ η ⋅ G m_0 = 0 \\ m_1 = \gamma \cdot m_0 + \eta \cdot \mathcal G = \eta \cdot \mathcal G \\ m_2 = \gamma \cdot m_1 + \eta \cdot \mathcal G = (1 + \gamma) \eta \cdot \mathcal G \\ m_3 = \gamma \cdot m_2 + \eta \cdot \mathcal G = (1 + \gamma + \gamma^2) \eta \cdot \mathcal G\\ \vdots \\ m_{+\infty} = (1 + \gamma + \gamma^2 + \gamma^3 + \cdots) \eta \cdot \mathcal G = \frac{1}{1 - \gamma} \eta \cdot \mathcal G m0​=0m1​=γ⋅m0​+η⋅G=η⋅Gm2​=γ⋅m1​+η⋅G=(1+γ)η⋅Gm3​=γ⋅m2​+η⋅G=(1+γ+γ2)η⋅G⋮m+∞​=(1+γ+γ2+γ3+⋯)η⋅G=1−γ1​η⋅G
  • 这意味着:动量在迭代过程中进行累积, γ \gamma γ越大,动量越大,它的迭代幅度越大,更新速度也更快(见上图 2 2 2)。反之,一直快下去也不是优质的选择,我们同样需要当前迭代步骤的 η ⋅ G \eta \cdot \mathcal G η⋅G对动量进行约束。

动量法的算法过程描述

基于动量法的随机梯度下降的算法步骤表示如下:
初始化操作:

  • 学习率 η \eta η,动量因子 γ \gamma γ;
  • 初始化参数 θ \theta θ,初始动量 m m m;

算法过程:

  • 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​;
  • 计算当前迭代步骤的梯度估计:
    其中 f ( x ( i ) ; θ ) f(x^{(i)};\theta) f(x(i);θ)表示模型预测结果; L ( ⋅ ) \mathcal L(\cdot) L(⋅)表示损失函数;
    G = 1 k ∑ i = 1 k ∇ θ L [ f ( x ( i ) ; θ ) , y ( i ) ] \mathcal G = \frac{1}{k} \sum_{i=1}^k \nabla_{\theta} \mathcal L[f(x^{(i)};\theta),y^{(i)}] G=k1​i=1∑k​∇θ​L[f(x(i);θ),y(i)]
  • 计算动量更新:
    m ⇐ γ ⋅ m − η ⋅ G m \Leftarrow \gamma \cdot m - \eta \cdot \mathcal G m⇐γ⋅m−η⋅G
  • 计算参数 θ \theta θ更新:
    θ ⇐ θ + m \theta \Leftarrow \theta + m θ⇐θ+m
  • End While \text{End While} End While

附:动量法示例代码

注:代码中没有使用确定的学习率作为步长,是对最速下降法代码的一个修改,使用查找的方式找到一个优质步长实现迭代过程。

import numpy as np
import math
import matplotlib.pyplot as plt

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
    plt.figure(figsize=(10, 5))
    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 GradientDescent(stepTime=80,epsilon=5.0,mode="momentum"):

    assert mode in ["SGD","momentum"]
    Start = (8.0, 0.5)
    StartV = (0.0, 0.0)
    alpha = 0.6
    LocList = list()
    LocList.append(Start)
    
    for _ in range(stepTime):
    
        DerStart = (Derfx(Start[0]), Derfy(Start[1]))
        for _,step in enumerate(list(np.linspace(0.0, 1.0, 1000))):

            if mode == "momentum":
                NextV = (alpha * StartV[0] - step * DerStart[0], alpha * StartV[1] - step * DerStart[1])
                Next = (Start[0] + NextV[0],Start[1] + NextV[1])
                DerfNext = Derfx(Next[0]) * (-1 * DerStart[0]) + Derfy(Next[1]) * (-1 * DerStart[1])

                if abs(DerfNext) <= epsilon:
                    LocList.append(Next)
                    StartV = NextV
                    Start = Next
                    epsilon /= 1.1
                    break
            else:
                Next = (Start[0] - (DerStart[0] * step), Start[1] - (DerStart[1] * step))
                DerfNext = Derfx(Next[0]) * (-1 * DerStart[0]) + Derfy(Next[1]) * (-1 * DerStart[1])

                if abs(DerfNext) <= epsilon:
                    LocList.append(Next)
                    Start = Next
                    epsilon /= 1.1
                    break

    plotList = list()
    if mode == "momentum":
        c = "tab:red"
    else:
        c = "tab:green"

    for (x, y) in LocList:
        plotList.append((x, y))
        plt.scatter(x, y, s=30, facecolor="none", edgecolors=c, marker='o')
        if len(plotList) < 2:
            continue
        else:
            plt.plot([plotList[0][0], plotList[1][0]], [plotList[0][1], plotList[1][1]], c=c)
            plotList.pop(0)

if __name__ == '__main__':
    DrawBackGround()
    GradientDescent(mode="SGD")
    GradientDescent(mode="momentum")
    plt.show()
  • 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

Reference \text{Reference} Reference:
谈谈优化算法之一(动量法、Nesterov法、自然梯度法)
深度学习(一)优化算法之动量法详解

注:本文转载自blog.csdn.net的静静的喝酒的文章"https://blog.csdn.net/qq_34758157/article/details/133646646"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top