进阶Task1:《深度学习详解》(Datawhale X 李宏毅苹果书 AI夏令营)

前言 

文章概述《深度学习详解》- 3.1

书中解析了深度学习中遇到的局部极小值与鞍点问题,并提供了相应的解决策略。首先,文章指出在深度学习的优化过程中,容易陷入局部极小值或鞍点,导致优化效果不佳。随后,详细介绍了什么是局部极小值和鞍点,并通过实例说明了如何判断一个临界点属于哪一类。此外,文中还提到了利用海森矩阵分析误差曲面的方法,以及如何通过特征值的正负来区分局部极小值、局部极大值和鞍点。尽管计算海森矩阵的复杂度较高,文中也讨论了逃离鞍点的替代方法,包括基于负特征值方向的参数更新技巧。最后,通过对高维度空间的探讨,文章提出了一种观点,即在高维度中,局部极小值可能并非常见,大多数情况下优化过程会遇到鞍点。通过实验数据的支持,表明即使是那些看似“像”局部极小值的点,实际上可能是更高维度中的鞍点。综上所述,文章全面探讨了深度学习中遇到的优化难题,并提供了解决策略和思考方向。


个人学习笔记以及概念梳理,望对大家有所帮助。


思维导图3.1

梯度下降优化方向

先初步介绍一下基本的形式

Batch Gradient Descent(批量梯度下降):使用所有样本数据来计算梯度,这是最直观的方法,但是计算成本高,且可能因数据固有的噪声而收敛缓慢。

Stochastic Gradient Descent(随机梯度下降):每次更新只使用一个样本来估计梯度,这可以显著降低计算成本,但由于随机性可能导致更新不稳定。

Mini-Batch Gradient Descent(小批量梯度下降):结合了批量梯度下降和随机梯度下降的优点,每次更新使用一个小批量的数据来计算梯度。

详情可见https://www.cnblogs.com/suanfajin/p/18257713

优化的几种策略(补充)

涉及到的一些术语

术语

解释

局部极小值

损失函数上的一个点,在该点附近所有点的损失值均较高,但在全局中可能不是最小值。

鞍点

损失函数上的一个点,其梯度为零但不是局部极小值或局部极大值,通常意味着在某些方向上损失会增加,在其他方向上会减少。

临界点

损失函数上梯度为零的点,包括局部极小值、局部极大值和鞍点。

海森矩阵

一个方阵,包含损失函数关于参数的二阶导数。海森矩阵的特征值可用于确定临界点的类型。

特征值

一个标量,当一个向量乘以一个矩阵后仅改变大小而不改变方向时,该标量就是该向量对应的特征值。

特征向量

一个向量,当它乘以一个矩阵后仅改变大小而不改变方向。

泰勒级数展开

一种数学工具,用来近似函数在某一点附近的值。

学习率

梯度下降算法中更新参数的速度。

学习率调度

动态调整学习率的方法,以促进更好的收敛。

批量归一化

一种技术,通过对网络各层的输入进行标准化处理,来提高模型训练速度和稳定性。

梯度消失问题

在反向传播过程中,梯度变得非常小,导致权重几乎不再更新的现象。

梯度爆炸问题

在反向传播过程中,梯度变得非常大,导致权重更新过大,使训练过程不稳定的现象。

涉及的一些公式

学习过程遇到的一些问题的理解:

1如何逃离鞍点和局部极小值

逃离局部极小值

逃离鞍点

1.动量法 (Momentum)

使用动量可以帮助优化算法以更快的速度穿过鞍点区域。

进一步可见进阶Task1.2:《深度学习详解》(Datawhale X 李宏毅苹果书 AI夏令营)-CSDN博客

2.Nesterov 加速梯度 (NAG)

NAG 是一种改进的动量方法,它预测下一步的位置并根据该位置调整速度。

3.随机梯度下降 (SGD)

随机梯度下降通过引入噪声来帮助逃离鞍点。由于每次更新都是基于一个或几个样本,这可以打破对称性并帮助跳出鞍点。

4.二阶方法

例如牛顿法或拟牛顿法等二阶优化算法能够利用Hessian矩阵的信息来识别鞍点并避开它们。

代码运行

为了不同方法验证逃离鞍点的能力,我们可以构建一个具有明确鞍点特征的人工数据集。

这里,我们将创建一个简单的二维非凸函数,其中包含明显的鞍点,以便于观察和分析。

俯视图和展示图

计算过程(部分)

通过计算得f(x, y)的二阶偏导数矩阵Hessian:

以及对应的可能的临界点为 (0,0)(0,0), (1,0)(1,0), (1,1)(1,1), (1,−1)(1,−1), (−1,0)(−1,0), (−1,1)(−1,1), (−1,−1)(−1,−1), (0,1)(0,1), 和 (0,−1)(0,−1)。

带入临界点,逐个分析

最后可得

鞍点 (1,0) (−1,0) (0,1) (0,−1)   局部最小点(1,1) (1,−1) (−1,1) (−1,−1)    局部最大点(0,0)

代码部分

开始逃离鞍点的处理,以(1.01,0.01)为例尽可能的逼近(1,0)处的鞍点


简单解释一下原因:

避免因导致数值不稳定导致算法停滞不前 以及 陷入鞍点附近的小凹陷区域,从而导致无法正确逃离鞍点,同时,在实际应用中,我们通常不会确切地知道鞍点的位置,而是从一个估计的位置开始优化,会比较合理。


采用以下四种方法来验证逃离鞍点的能力

1.动量法 (Momentum) 主要代码    
def momentum_optimizer(x, y, lr=0.1, momentum=0.9, epochs=100):
    velocity_x = 0
    velocity_y = 0
    path = [(x.item(), y.item())]

    for _ in range(epochs):
        # 计算梯度
        grad_x, grad_y = grad_func(x, y)

        # 更新速度
        velocity_x = momentum * velocity_x - lr * grad_x
        velocity_y = momentum * velocity_y - lr * grad_y

        # 更新位置
        x_new = x + velocity_x
        y_new = y + velocity_y

        # 将新的值赋给 x 和 y
        x = x_new
        y = y_new

        path.append((x.item(), y.item()))

    return path
2.Nesterov 加速梯度 (NAG)  主要代码
def nesterov_momentum_optimizer(x, y, lr=0.1, momentum=0.9, epochs=100):
    velocity_x = 0
    velocity_y = 0
    path = [(x.item(), y.item())]

    for _ in range(epochs):
        # 预测下一步的位置
        x_pred = x + momentum * velocity_x - lr * velocity_x
        y_pred = y + momentum * velocity_y - lr * velocity_y

        # 计算梯度
        grad_x, grad_y = grad_func(x_pred, y_pred)

        # 更新速度
        velocity_x = momentum * velocity_x - lr * grad_x
        velocity_y = momentum * velocity_y - lr * grad_y

        # 更新位置
        x_new = x + velocity_x
        y_new = y + velocity_y

        # 将新的值赋给 x 和 y
        x = x_new
        y = y_new

        path.append((x.item(), y.item()))

    return path
3.随机梯度下降 (SGD)   主要代码
def sgd_optimizer(x, y, lr=0.1, epochs=100):
    path = [(x.item(), y.item())]

    for _ in range(epochs):
        # 计算梯度
        grad_x, grad_y = grad_func(x, y)

        # 更新位置
        x_new = x - lr * grad_x
        y_new = y - lr * grad_y

        # 将新的值赋给 x 和 y
        x = x_new
        y = y_new

        path.append((x.item(), y.item()))

    return path

前三个公用部分代码,此处以 动量法 (Momentum) 为例

import torch
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 定义目标函数
def func(x, y):
    return x**4 + y**4 - 2*x**2 - 2*y**2

# 使用PyTorch自动计算梯度
def grad_func(x, y):
    x = torch.tensor(x, requires_grad=True)
    y = torch.tensor(y, requires_grad=True)
    z = func(x, y)
    z.backward()
    return x.grad.item(), y.grad.item()

# 动量下降优化器
def momentum_optimizer(x, y, lr=0.1, momentum=0.9, epochs=100):
    velocity_x = 0
    velocity_y = 0
    path = [(x.item(), y.item())]

    for _ in range(epochs):
        # 计算梯度
        grad_x, grad_y = grad_func(x, y)

        # 更新速度
        velocity_x = momentum * velocity_x - lr * grad_x
        velocity_y = momentum * velocity_y - lr * grad_y

        # 更新位置
        x_new = x + velocity_x
        y_new = y + velocity_y

        # 将新的值赋给 x 和 y
        x = x_new
        y = y_new

        path.append((x.item(), y.item()))

    return path

# 创建 PyTorch 张量作为起始点
x = torch.tensor(1.01, requires_grad=True)
y = torch.tensor(0.01, requires_grad=True)

# 运行优化器
path = momentum_optimizer(x, y)    #注意此处不同方法 函数替换

# 绘制路径
fig = plt.figure(figsize=(10, 5))

# 俯视图
ax1 = fig.add_subplot(1, 2, 1)
X, Y = np.meshgrid(np.linspace(-2, 2, 400), np.linspace(-2, 2, 400))
Z = func(X, Y)
ax1.contour(X, Y, Z, levels=np.logspace(0, 3, 30))
ax1.plot(*zip(*path), marker='o', color='red', linestyle='-', linewidth=2, markersize=4)
# 标记最终位置为蓝色
final_x, final_y = path[-1]
ax1.plot(final_x, final_y, marker='o', color='blue', markersize=10)
ax1.set_title('Contour Plot')
ax1.set_xlabel('x')
ax1.set_ylabel('y')

# 3D 图
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
X, Y = np.meshgrid(np.linspace(-2, 2, 40), np.linspace(-2, 2, 40))
Z = func(X, Y)
ax2.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8, edgecolor='none')
ax2.plot(*zip(*path), [func(*xy) for xy in path], color='red', marker='o', linewidth=2, markersize=5)
# 在3D图中标记最终位置为蓝色
ax2.plot(final_x, final_y, func(final_x, final_y), marker='o', color='blue', markersize=10)
ax2.set_title('3D Surface Plot')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_zlabel('z')

plt.show()

运行结果,依次

动量法 (Momentum)

Nesterov 加速梯度 (NAG)

随机梯度下降 (SGD)

对比

4.二阶方法.拟牛顿法等二阶优化算法(BFGS)  整体代码

对于BFGS方法,我们只需要定义目标函数和梯度函数,剩下的优化步骤由minimize函数自动完成。

import torch
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.optimize import minimize

# 定义目标函数
def func(xy):
    x, y = xy
    return x**4 + y**4 - 2*x**2 - 2*y**2

# 使用PyTorch自动计算梯度
def grad_func(xy):
    x, y = xy
    x = torch.tensor(x, requires_grad=True)
    y = torch.tensor(y, requires_grad=True)
    z = func([x, y])
    z.backward()
    return x.grad.item(), y.grad.item()

# 起始点
x0 = np.array([1.01, 0.01])

# 使用BFGS方法进行优化
res = minimize(func, x0, jac=grad_func, method='BFGS')

# 记录每一步的位置
path = [x0]
x, y = x0
for _ in range(10):
    grad = grad_func([x, y])
    if np.linalg.norm(grad) < 1e-9:
        break
    x, y = res.x
    path.append([x, y])

# 绘制路径
fig = plt.figure(figsize=(10, 5))

# 俯视图
ax1 = fig.add_subplot(1, 2, 1)
X, Y = np.meshgrid(np.linspace(-2, 2, 400), np.linspace(-2, 2, 400))
Z = func([X, Y])
ax1.contour(X, Y, Z, levels=np.logspace(0, 3, 30))
ax1.plot(*zip(*path), marker='o', color='red', linestyle='-', linewidth=2, markersize=4)
# 标记最终位置为蓝色
final_x, final_y = path[-1]
ax1.plot(final_x, final_y, marker='o', color='blue', markersize=10)
ax1.set_title('Contour Plot')
ax1.set_xlabel('x')
ax1.set_ylabel('y')

# 3D 图
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
X, Y = np.meshgrid(np.linspace(-2, 2, 40), np.linspace(-2, 2, 40))
Z = func([X, Y])
ax2.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8, edgecolor='none')
ax2.plot(*zip(*path), [func(p) for p in path], color='red', marker='o', linewidth=2, markersize=5)
# 在3D图中标记最终位置为蓝色
ax2.plot(final_x, final_y, func([final_x, final_y]), marker='o', color='blue', markersize=10)
ax2.set_title('3D Surface Plot')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_zlabel('z')

plt.show()

运行结果

初步结论:整体上,都是可以脱离鞍点影响,抵达局部最小点(1,1)。

本次运行中,我们发现BFGS能够快速找到全局最小值,而NAG、SGD和Momentum则依次需要更多次的尝试才能找到全局最小值。


小结

优化策略

  • 批量梯度下降: 使用全部数据计算梯度,稳定但计算成本高。
  • 随机梯度下降: 每次更新使用单个样本,计算成本低但可能引入噪音。
  • 小批量梯度下降: 结合两者优点,使用一小批数据计算梯度。

逃离鞍点的方法

  • 动量法: 引入速度的概念,帮助算法更快穿过鞍点区域。
  • Nesterov加速梯度: 改进的动量方法,通过预估未来位置来调整速度。
  • 随机梯度下降: 利用梯度的随机性帮助跳出鞍点。
  • 二阶方法: 如牛顿法或BFGS等,利用二阶导数信息更准确地识别和逃离鞍点。

实验验证

  • 构建了一个包含鞍点的二维非凸函数。
  • 使用Python和相关库实现不同的优化方法。
  • 通过可视化路径,观察不同方法逃离鞍点的情况。

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值