【神经网络】梯度下降的优化方法【数学公式+代码示例】

文章目录

  • 1、简介
  • 2、指数加权平均
    • 2.1、公式
    • 2.2、代码
  • 3、Momentum⭐
    • 3.1、公式演变
    • 3.2、代码
  • 4、AdaGrad
    • 4.1、计算步骤
    • 4.2、代码示例
  • 5、RMSProp
    • 5.1、公式
    • 5.2、代码
    • 5.3、小结
  • 6、Adam
    • 6.1、公式和步骤解释⭐
    • 6.2、代码⭐
    • 6.3、优点
  • 7、何为鞍点
  • 8、小结

🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家博主,专注于Java领域学习,擅长web应用开发、数据结构和算法,初步涉猎人工智能和前端开发。
🦅个人主页:@逐梦苍穹
📕所属专栏:人工智能
🌻gitee地址:xzl的人工智能代码仓库
✈ 您的一键三连,是我创作的最大动力🌹

1、简介

传统的梯度下降优化算法中,可能会碰到以下情况:
碰到平缓区域,梯度值较小,参数优化变慢,碰到 “鞍点” ,梯度为 0,参数无法优化,碰到局部最小值。
对于这些问题, 出现了一些对梯度下降算法的优化方法,例如:Momentum、AdaGrad、RMSprop、Adam 等.

2、指数加权平均

我们最常见的算数平均指的是将所有数加起来除以数的个数,每个数的权重是相同的。
加权平均指的是给每个数赋予不同的权重求得平均数。
移动平均数,指的是计算最近邻的 N 个数来获得平均数。
指数移动加权平均则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。
比如:明天气温怎么样,和昨天气温有很大关系,而和一个月前的气温关系就小一些。

2.1、公式

计算公式可以用下面的式子来表示: [ S t = { Y 1 , t = 0 β ∗ S t − 1 + ( 1 − β ) ∗ Y t , t > 0 ] [ S_t = \begin{cases} Y_1, & \text{t = 0} \\ \beta \ast S_{t-1} + (1 - \beta) \ast Y_t, & \text{t > 0} \end{cases} ] [St={Y1,βSt1+(1β)Yt,t = 0t > 0]

  1. S t S_t St表示指数加权平均值;
  2. Y t Y_t Yt表示 t 时刻的值;
  3. β β β 调节权重系数,该值越大平均数越平缓。

2.2、代码

我们接下来通过一段代码来看下结果,我们随机产生进 30 天的气温数据:

# -*- coding: utf-8 -*-
# @Author: CSDN@逐梦苍穹
# @Time: 2024/7/29 1:34
import torch
import matplotlib.pyplot as pltELEMENT_NUMBER = 30  # 定义温度数据的天数# 1. 实际平均温度
def test01():# 固定随机数种子,确保每次运行结果一致torch.manual_seed(0)# 产生30天的随机温度数据,温度服从正态分布,均值为0,标准差为10temperature = torch.randn(size=[ELEMENT_NUMBER, ]) * 10print(temperature)# 生成代表天数的数组,从1到30days = torch.arange(1, ELEMENT_NUMBER + 1, 1)# 绘制温度变化曲线plt.figure()plt.plot(days, temperature, color='r')plt.scatter(days, temperature)  # 绘制散点图plt.xlabel('Days')  # X轴标签plt.ylabel('Temperature')  # Y轴标签plt.title('Actual Temperature Over 30 Days')  # 图标题# plt.show()# 2. 指数加权平均温度
def test02(beta=0.9):# 固定随机数种子,确保每次运行结果一致torch.manual_seed(0)# 产生30天的随机温度数据,温度服从正态分布,均值为0,标准差为10temperature = torch.randn(size=[ELEMENT_NUMBER, ]) * 10print(temperature)exp_weight_avg = []  # 存储指数加权平均温度的列表# 计算每一天的指数加权平均温度for idx, temp in enumerate(temperature, 1):# 第一个元素的 EWA 值等于自身if idx == 1:exp_weight_avg.append(temp)continue# 第二个及之后的元素的 EWA 值等于上一个 EWA 乘以 β + 当前温度乘以 (1-β)new_temp = exp_weight_avg[idx - 2] * beta + (1 - beta) * tempexp_weight_avg.append(new_temp)# 生成代表天数的数组,从1到30days = torch.arange(1, ELEMENT_NUMBER + 1, 1)# 绘制指数加权平均温度变化曲线plt.figure()plt.plot(days, exp_weight_avg, color='r')plt.scatter(days, temperature)  # 绘制实际温度的散点图plt.xlabel('Days')  # X轴标签plt.ylabel('Temperature')  # Y轴标签plt.title(f'Exponentially Weighted Average Temperature (beta={beta})')  # 图标题,包含beta值# plt.show()if __name__ == '__main__':# 调用test01函数,绘制实际温度图test01()# 调用test02函数,绘制beta为0.5的EWA温度图test02(0.5)# 调用test02函数,绘制beta为0.9的EWA温度图test02(0.9)plt.show()

程序结果如下:
image.png
从程序运行结果可以看到:
指数加权平均绘制出的气氛变化曲线更加平缓;
β 的值越大,则绘制出的折线越加平缓; β 值一般默认都是 0.9.

3、Momentum⭐

Momentum->动量

当梯度下降碰到 “峡谷” 、”平缓”、”鞍点” 区域时, 参数更新速度变慢。
Momentum 通过指数加权平均法累计历史梯度值,进行参数更新,越近的梯度值当前参数更新重要性越大

3.1、公式演变

梯度的指数加权平均(Exponential Moving Average, EMA)的一般公式如下:
S t = β S t − 1 + ( 1 − β ) D t S_t = \beta S_{t-1} + (1 - \beta) D_t St=βSt1+(1β)Dt
其中:

  • S t S_t St:当前时刻的加权移动平均值
  • S t − 1 S_{t-1} St1:上一个时刻的加权移动平均值
  • D t D_t Dt:当前时刻的梯度值
  • β \beta β:权重系数(0 到 1 之间的值)

解释:

  1. S t − 1 S_{t-1} St1 表示历史梯度的移动加权平均值(上一个时刻的 EMA)。
  2. D t D_t Dt 表示当前时刻的梯度值。
  3. β \beta β 为权重系数,表示历史梯度与前 EMA 中的权重。

咱们举个例子,假设:权重 β \beta β 为 0.9,例如:
第一次梯度值: s 1 = d 1 = w 1 s_1 = d_1 = w_1 s1=d1=w1
第二次梯度值: s 2 = 0.9 ∗ s 1 + d 2 ∗ 0.1 s_2 = 0.9 \ast s_1 + d_2 \ast 0.1 s2=0.9s1+d20.1
第三次梯度值: s 3 = 0.9 ∗ s 2 + d 3 ∗ 0.1 s_3 = 0.9 \ast s_2 + d_3 \ast 0.1 s3=0.9s2+d30.1
第四次梯度值: s 4 = 0.9 ∗ s 3 + d 4 ∗ 0.1 s_4 = 0.9 \ast s_3 + d_4 \ast 0.1 s4=0.9s3+d40.1

  1. w w w 表示初始梯度
  2. d d d 表示当前轮数计算出的梯度值
  3. s s s 表示历史梯度值

梯度下降公式中梯度的计算,就不再是当前时刻 t t t 的梯度值,而是历史梯度值的指数移动加权平均值。
公式修改为: W t + 1 = W t − α ∗ D t W_{t+1} = W_t - \alpha \ast D_t Wt+1=WtαDt
那么,Monmentum 优化方法是如何一定程度上克服 “平缓”、”鞍点”、”峡谷” 的问题呢?
42.png
当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。
但是 Momentum 动量梯度下降算法已经在先前积累了一些梯度值,很有可能使得跨过鞍点。
由于 mini-batch 普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。
Momentum 使用移动加权平均,平滑了梯度的变化,使得前进方向更加平缓,有利于加快训练过程。一定程度上有利于降低 “峡谷” 问题的影响。

峡谷问题:就是会使得参数更新出现剧烈震荡

Momentum 算法可以理解为是对梯度值的一种调整,我们知道梯度下降算法中还有一个很重要的学习率,Momentum 并没有学习率进行优化。

3.2、代码

# -*- coding: utf-8 -*-
# @Author: CSDN@逐梦苍穹
# @Time: 2024/7/29 2:23
import numpy as np
import matplotlib.pyplot as plt# 初始化参数
theta = np.random.randn(2)  # 假设我们有两个参数,随机初始化
alpha = 0.1  # 初始学习率
beta = 0.9  # 动量系数
velocity = np.zeros_like(theta)  # 初始化动量为零向量# 定义一个简单的二次损失函数
def loss_function(theta):return theta[0] ** 2 + theta[1] ** 2  # 损失函数:J(θ) = θ[0]^2 + θ[1]^2# 计算梯度
def compute_gradient(theta):return 2 * theta  # 梯度:∇J(θ) = [2*θ[0], 2*θ[1]]# 进行梯度下降迭代
iterations = 100  # 设定迭代次数
theta_history = []  # 存储每次迭代的theta值
loss_history = []  # 存储每次迭代的损失值for _ in range(iterations):gradient = compute_gradient(theta)  # 计算梯度velocity = beta * velocity + (1 - beta) * gradient  # 更新动量项 v_t = β * v_{t-1} + (1 - β) * g_ttheta -= alpha * velocity  # 更新参数 θ = θ - α * v_t# 记录参数和损失值以便后续绘图theta_history.append(theta.copy())  # 记录当前theta值loss_history.append(loss_function(theta))  # 记录当前损失值print(f"Updated parameters: {theta}, Loss: {loss_function(theta)}")  # 打印当前参数和损失值print(f"Optimized parameters: {theta}")  # 打印最终优化后的参数# 绘制参数更新轨迹
theta_history = np.array(theta_history)  # 将theta历史记录转换为NumPy数组
plt.figure(figsize=(12, 6))  # 创建一个12x6英寸的图形plt.subplot(1, 2, 1)  # 创建1行2列的子图,选择第一个子图
plt.plot(theta_history[:, 0], theta_history[:, 1], 'o-', markersize=4)  # 绘制theta[0]和theta[1]的变化轨迹
plt.title('Parameter Update Path with Momentum')  # 设置子图标题
plt.xlabel('Theta[0]')  # 设置x轴标签
plt.ylabel('Theta[1]')  # 设置y轴标签# 绘制损失函数值变化
plt.subplot(1, 2, 2)  # 选择第二个子图
plt.plot(loss_history, 'r-')  # 绘制损失值变化曲线,红色实线
plt.title('Loss Function Value with Momentum')  # 设置子图标题
plt.xlabel('Iteration')  # 设置x轴标签
plt.ylabel('Loss')  # 设置y轴标签plt.tight_layout()  # 自动调整子图布局
plt.show()  # 显示图形

结果:
image.png
解释

  • 损失函数值在初始时较高,随着迭代次数的增加,损失值迅速下降。
  • 在前几次迭代中,损失值下降速度非常快,这是因为动量法在初始阶段累积了较大的梯度,使得参数更新步长较大。
  • 在迭代到大约20次时,损失值出现了一些波动,这是动量法的特性导致的,它在接近极小值时可能会因累积的动量过大而出现短暂的反弹。
  • 最终,损失值趋于平稳,表明模型参数已接近最优值,优化过程收敛。

4、AdaGrad

AdaGrad 通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小。
这是因为 AdaGrad 认为:在起初时,我们距离最优目标仍较远,可以使用较大的学习率,加快训练速度,随着迭代次数的增加,学习率逐渐下降。

4.1、计算步骤

AdaGrad计算步骤如下:

  1. 初始化
    • 初始学习率 α \alpha α
    • 初始参数 θ \theta θ
    • 小常数 ϵ \epsilon ϵ,通常为 1 × 1 0 − 6 1 \times 10^{-6} 1×106
    • 初始化梯度累积变量 s = 0 s = 0 s=0
  2. 从训练集中采样
    • 从训练集中采样 m m m 个样本的小批量,计算梯度 g g g
  3. 累积平方梯度
    • 更新累积平方梯度: s = s + g ⊙ g s = s + g \odot g s=s+gg
      其中, ⊙ \odot 表示逐个分量相乘(即逐元素相乘)
  4. 调整学习率
    • 根据累积平方梯度调整学习率 α \alpha α α t = α s + ϵ \alpha_t = \frac{\alpha}{\sqrt{s + \epsilon}} αt=s+ϵ α
  5. 更新参数
    • 使用调整后的学习率更新参数 θ \theta θ θ = θ − α t ⊙ g \theta = \theta - \alpha_t \odot g θ=θαtg
  6. 重复步骤2-5
    • 继续重复步骤2-5,直到满足停止条件(如迭代次数或误差足够小)

参数更新公式如下:

  1. 累积平方梯度公式: s = s + g ⊙ g s = s + g \odot g s=s+gg
  2. 调整学习率公式: α t = α s + ϵ \alpha_t = \frac{\alpha}{\sqrt{s + \epsilon}} αt=s+ϵ α
  3. 参数更新公式: θ = θ − α t ⊙ g \theta = \theta - \alpha_t \odot g θ=θαtg

4.2、代码示例

# -*- coding: utf-8 -*-
# @Author: CSDN@逐梦苍穹
# @Time: 2024/7/29 2:08import numpy as np  # 导入NumPy库,用于数值计算
import matplotlib.pyplot as plt  # 导入Matplotlib库,用于绘图# 初始化参数
theta = np.random.randn(2)  # 假设我们有两个参数,随机初始化
alpha = 0.1  # 初始学习率
eps = 1e-10  # 防止除零的小常数
s = np.zeros_like(theta)  # 初始化累积梯度平方和为与theta相同形状的零向量# 定义一个简单的二次损失函数
def loss_function(theta):return theta[0]**2 + theta[1]**2  # 损失函数:J(θ) = θ[0]^2 + θ[1]^2# 计算梯度
def compute_gradient(theta):return 2 * theta  # 梯度:∇J(θ) = [2*θ[0], 2*θ[1]]# 进行梯度下降迭代
iterations = 100  # 设定迭代次数
theta_history = []  # 存储每次迭代的theta值
loss_history = []  # 存储每次迭代的损失值for _ in range(iterations):gradient = compute_gradient(theta)  # 计算梯度s += gradient**2  # 更新累积梯度平方和 s = s + g_t ⊙ g_tadjusted_alpha = alpha / (np.sqrt(s) + eps)  # 调整学习率 α_t = α / (√s + ε)theta -= adjusted_alpha * gradient  # 更新参数 θ = θ - α_t ⊙ g_t# 记录参数和损失值以便后续绘图theta_history.append(theta.copy())  # 记录当前theta值loss_history.append(loss_function(theta))  # 记录当前损失值print(f"Updated parameters: {theta}, Loss: {loss_function(theta)}")  # 打印当前参数和损失值print(f"Optimized parameters: {theta}")  # 打印最终优化后的参数# 绘制参数更新轨迹
theta_history = np.array(theta_history)  # 将theta历史记录转换为NumPy数组
plt.figure(figsize=(12, 6))  # 创建一个12x6英寸的图形plt.subplot(1, 2, 1)  # 创建1行2列的子图,选择第一个子图
plt.plot(theta_history[:, 0], theta_history[:, 1], 'o-', markersize=4)  # 绘制theta[0]和theta[1]的变化轨迹
plt.title('Parameter Update Path')  # 设置子图标题
plt.xlabel('Theta[0]')  # 设置x轴标签
plt.ylabel('Theta[1]')  # 设置y轴标签# 绘制损失函数值变化
plt.subplot(1, 2, 2)  # 选择第二个子图
plt.plot(loss_history, 'r-')  # 绘制损失值变化曲线,红色实线
plt.title('Loss Function Value')  # 设置子图标题
plt.xlabel('Iteration')  # 设置x轴标签
plt.ylabel('Loss')  # 设置y轴标签plt.tight_layout()  # 自动调整子图布局
plt.show()  # 显示图形

结果:
image.png

这两个子图展示了AdaGrad算法在优化过程中如何通过自适应调整学习率来更新参数,从而有效地减少损失函数值。
具体来说:

  • 参数更新路径:展示了参数在迭代过程中如何逐渐接近最优值,路径上的点和线条表明参数更新的方向和幅度。
  • 损失函数值:展示了损失值如何随迭代次数减少,反映了模型逐步优化的过程。

AdaGrad 缺点是可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解。

5、RMSProp

RMSProp 优化算法是对 AdaGrad 的优化。
最主要的不同是:使用指数移动加权平均梯度替换历史梯度的平方和

5.1、公式

其计算过程如下:

  1. 初始化学习率 α \alpha α、初始化参数 θ \theta θ、小常数 σ = 1 e − 6 \sigma = 1e-6 σ=1e6
  2. 初始化参数 θ \theta θ
  3. 初始化梯度累计变量 s s s
  4. 从训练集中采样 m m m 个样本的小批量,计算梯度 g g g
  5. 使用指数移动平均累积历史梯度,公式: s = β ⋅ s + ( 1 − β ) g ⊙ g s = \beta \cdot s + (1 - \beta)g \odot g s=βs+(1β)gg
    • β \beta β 是权重系数,控制历史梯度和当前梯度的比例。
    • s s s 是梯度的累积平方和,用于调整学习率。
    • g g g 是当前的梯度, g ⊙ g g \odot g gg 表示逐元素平方。

学习率 α \alpha α 的计算公式: α = α s + σ \alpha = \frac{\alpha}{\sqrt{s + \sigma}} α=s+σ α

  • 这个公式计算的是调整后的学习率。
  • α \alpha α 是初始学习率。
  • s s s 是累积的梯度平方和。
  • σ \sigma σ 是一个小常数,防止分母为零。

参数更新公式: θ = θ − α s + σ ⋅ g \theta = \theta - \frac{\alpha}{\sqrt{s + \sigma}} \cdot g θ=θs+σ αg

  • 这个公式用于更新参数 θ \theta θ
  • θ \theta θ 是当前的参数值。
  • α \alpha α 是初始学习率。
  • s s s 是累积的梯度平方和。
  • σ \sigma σ 是一个小常数。
  • g g g 是当前的梯度。

这些公式共同作用,通过动态调整学习率来更新参数,使模型逐步逼近最优解。

5.2、代码

# -*- coding: utf-8 -*-
# @Author: CSDN@逐梦苍穹
# @Time: 2024/7/29 2:30
import numpy as np
import matplotlib.pyplot as plt# 初始化参数
theta = np.random.randn(2)  # 假设我们有两个参数,随机初始化
alpha = 0.1  # 初始学习率
beta = 0.9  # 指数移动平均的衰减系数
eps = 1e-6  # 防止除零的小常数
s = np.zeros_like(theta)  # 初始化累积梯度平方和# 定义一个简单的二次损失函数
def loss_function(theta):return theta[0] ** 2 + theta[1] ** 2  # 损失函数:J(θ) = θ[0]^2 + θ[1]^2# 计算梯度
def compute_gradient(theta):return 2 * theta  # 梯度:∇J(θ) = [2*θ[0], 2*θ[1]]# 进行梯度下降迭代
iterations = 100  # 设定迭代次数
theta_history = []  # 存储每次迭代的theta值
loss_history = []  # 存储每次迭代的损失值for _ in range(iterations):gradient = compute_gradient(theta)  # 计算梯度s = beta * s + (1 - beta) * (gradient ** 2)  # 更新累积梯度平方和 s = β * s + (1 - β) * g_t ⊙ g_tadjusted_alpha = alpha / (np.sqrt(s) + eps)  # 调整学习率 α_t = α / (√s + ε)theta -= adjusted_alpha * gradient  # 更新参数 θ = θ - α_t ⊙ g_t# 记录参数和损失值以便后续绘图theta_history.append(theta.copy())  # 记录当前theta值loss_history.append(loss_function(theta))  # 记录当前损失值print(f"Updated parameters: {theta}, Loss: {loss_function(theta)}")  # 打印当前参数和损失值print(f"Optimized parameters: {theta}")  # 打印最终优化后的参数# 绘制参数更新轨迹
theta_history = np.array(theta_history)  # 将theta历史记录转换为NumPy数组
plt.figure(figsize=(12, 6))  # 创建一个12x6英寸的图形plt.subplot(1, 2, 1)  # 创建1行2列的子图,选择第一个子图
plt.plot(theta_history[:, 0], theta_history[:, 1], 'o-', markersize=4)  # 绘制theta[0]和theta[1]的变化轨迹
plt.title('Parameter Update Path with RMSProp')  # 设置子图标题
plt.xlabel('Theta[0]')  # 设置x轴标签
plt.ylabel('Theta[1]')  # 设置y轴标签# 绘制损失函数值变化
plt.subplot(1, 2, 2)  # 选择第二个子图
plt.plot(loss_history, 'r-')  # 绘制损失值变化曲线,红色实线
plt.title('Loss Function Value with RMSProp')  # 设置子图标题
plt.xlabel('Iteration')  # 设置x轴标签
plt.ylabel('Loss')  # 设置y轴标签plt.tight_layout()  # 自动调整子图布局
plt.show()  # 显示图形

运行:
image.png
解释

  • 损失函数值在初始时较高,随着迭代次数的增加,损失值迅速下降。
  • 在前几次迭代中,损失值下降速度非常快,这是因为RMSProp算法在初始阶段调整了每个参数的学习率,使得参数更新步长较大。
  • 随着迭代次数的增加,损失值逐渐趋于平稳,表明模型参数已接近最优值,优化过程收敛。

5.3、小结

RMSProp 与 AdaGrad 最大的区别是对梯度的累积方式不同,对于每个梯度分量仍然使用不同的学习率。
RMSProp 通过引入衰减系数 β,控制历史梯度对历史梯度信息获取的多少。
被证明在神经网络非凸条件下的优化更好,学习率衰减更加合理一些。
需要注意的是:
AdaGrad 和 RMSProp 都是对于不同的参数分量使用不同的学习率,
如果某个参数分量梯度值较大,则对应的学习率就会较小
如果某个参数分量的梯度较小,则对应的学习率就会较大一些

6、Adam

Momentum (Adaptive Moment Estimation)使用指数加权平均计算当前的梯度值、AdaGrad、RMSProp 使用自适应的学习率,Adam 结合了 Momentum、RMSProp 的优点,
使用:移动加权平均的梯度移动加权平均的学习率
使得能够自适应学习率的同时,也能够使用 Momentum 的优点。

6.1、公式和步骤解释⭐

  1. 初始化:
    • 学习率 α \alpha α
    • 参数 θ \theta θ
    • 一阶动量估计 m m m 初始化为0
    • 二阶动量估计 v v v 初始化为0
    • 小常数 ϵ \epsilon ϵ,防止除零错误,通常为 1 0 − 8 10^{-8} 108
    • 一阶动量估计的衰减系数 β 1 \beta_1 β1,通常为0.9
    • 二阶动量估计的衰减系数 β 2 \beta_2 β2,通常为0.999
  2. **计算梯度:**从训练集中采样 m m m 个样本的小批量,计算梯度 g g g
  3. 更新一阶动量估计: m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1)g_t mt=β1mt1+(1β1)gt
  4. 更新二阶动量估计: v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1 - \beta_2)g_t^2 vt=β2vt1+(1β2)gt2
  5. 计算一阶动量估计的偏差修正: m t ^ = m t 1 − β 1 t \hat{m_t} = \frac{m_t}{1 - \beta_1^t} mt^=1β1tmt
  6. 计算二阶动量估计的偏差修正: v t ^ = v t 1 − β 2 t \hat{v_t} = \frac{v_t}{1 - \beta_2^t} vt^=1β2tvt
  7. 更新参数: θ = θ − α m t ^ v t ^ + ϵ \theta = \theta - \alpha \frac{\hat{m_t}}{\sqrt{\hat{v_t}} + \epsilon} θ=θαvt^ +ϵmt^
  8. 重复步骤2-7,直到满足停止条件(如迭代次数或误差足够小)

解释:

  1. 一阶动量估计 m t m_t mt
    • m t m_t mt 是梯度的指数加权平均。
    • β 1 \beta_1 β1 是动量项的衰减率,接近1时动量项的影响较大。
  2. 二阶动量估计 v t v_t vt
    • v t v_t vt 是梯度平方的指数加权平均。
    • β 2 \beta_2 β2 是RMSProp中的衰减率,控制过去梯度平方的影响。
  3. 偏差修正:
    • 由于 m t m_t mt v t v_t vt 在初始化时为0,前几步的估计值会有偏差。通过除以 ( 1 − β 1 t ) (1 - \beta_1^t) (1β1t) ( 1 − β 2 t ) (1 - \beta_2^t) (1β2t) 对其进行修正。
  4. 参数更新:
    • 使用修正后的动量估计和梯度平方估计更新参数。
    • ϵ \epsilon ϵ 防止分母为零,提高数值稳定性。

这些步骤确保了Adam优化算法能够快速收敛,并且在不同问题和数据集上表现良好。

6.2、代码⭐

# -*- coding: utf-8 -*-
# @Author: CSDN@逐梦苍穹
# @Time: 2024/7/29 2:43
import numpy as np
import matplotlib.pyplot as plt# 初始化参数
theta = np.random.randn(2)  # 假设我们有两个参数,随机初始化
alpha = 0.1  # 初始学习率
beta1 = 0.9  # 一阶动量估计的衰减系数
beta2 = 0.999  # 二阶动量估计的衰减系数
eps = 1e-8  # 防止除零的小常数
m = np.zeros_like(theta)  # 初始化一阶动量估计
v = np.zeros_like(theta)  # 初始化二阶动量估计# 定义一个简单的二次损失函数
def loss_function(theta):return theta[0] ** 2 + theta[1] ** 2  # 损失函数:J(θ) = θ[0]^2 + θ[1]^2# 计算梯度
def compute_gradient(theta):return 2 * theta  # 梯度:∇J(θ) = [2*θ[0], 2*θ[1]]# 进行梯度下降迭代
iterations = 100  # 设定迭代次数
theta_history = []  # 存储每次迭代的theta值
loss_history = []  # 存储每次迭代的损失值for t in range(1, iterations + 1):gradient = compute_gradient(theta)  # 计算梯度m = beta1 * m + (1 - beta1) * gradient  # 更新一阶动量估计 m_t = β1 * m_{t-1} + (1 - β1) * g_tv = beta2 * v + (1 - beta2) * (gradient ** 2)  # 更新二阶动量估计 v_t = β2 * v_{t-1} + (1 - β2) * g_t^2m_hat = m / (1 - beta1 ** t)  # 计算一阶动量估计的偏差修正 m_hat_t = m_t / (1 - β1^t)v_hat = v / (1 - beta2 ** t)  # 计算二阶动量估计的偏差修正 v_hat_t = v_t / (1 - β2^t)theta -= alpha * m_hat / (np.sqrt(v_hat) + eps)  # 更新参数 θ = θ - α * m_hat_t / (√v_hat_t + ε)# 记录参数和损失值以便后续绘图theta_history.append(theta.copy())  # 记录当前theta值loss_history.append(loss_function(theta))  # 记录当前损失值print(f"Updated parameters: {theta}, Loss: {loss_function(theta)}")  # 打印当前参数和损失值print(f"Optimized parameters: {theta}")  # 打印最终优化后的参数# 绘制参数更新轨迹
theta_history = np.array(theta_history)  # 将theta历史记录转换为NumPy数组
plt.figure(figsize=(12, 6))  # 创建一个12x6英寸的图形plt.subplot(1, 2, 1)  # 创建1行2列的子图,选择第一个子图
plt.plot(theta_history[:, 0], theta_history[:, 1], 'o-', markersize=4)  # 绘制theta[0]和theta[1]的变化轨迹
plt.title('Parameter Update Path with Adam')  # 设置子图标题
plt.xlabel('Theta[0]')  # 设置x轴标签
plt.ylabel('Theta[1]')  # 设置y轴标签# 绘制损失函数值变化
plt.subplot(1, 2, 2)  # 选择第二个子图
plt.plot(loss_history, 'r-')  # 绘制损失值变化曲线,红色实线
plt.title('Loss Function Value with Adam')  # 设置子图标题
plt.xlabel('Iteration')  # 设置x轴标签
plt.ylabel('Loss')  # 设置y轴标签plt.tight_layout()  # 自动调整子图布局
plt.show()  # 显示图形

结果:
image.png
解释

  • 损失函数值在初始时较高,随着迭代次数的增加,损失值迅速下降。
  • 在前几次迭代中,损失值下降速度非常快,这是因为Adam算法在初期阶段通过一阶和二阶动量的结合,使得参数更新步长较大。
  • 随着迭代次数的增加,损失值逐渐趋于平稳,表明模型参数已接近最优值,优化过程收敛。
  • 中间出现了几次小的波动,这可能是由于参数在接近局部最优值时调整的结果,但总体趋势是下降的。

6.3、优点

  1. 自适应学习率:Adam通过计算每个参数的自适应学习率,使得算法在训练过程中更加稳定。
  2. 动量加速:Adam使用动量估计(Momentum)来加速梯度下降,提高收敛速度。
  3. 偏差校正:Adam对一阶和二阶动量估计进行偏差校正,使得估计值更加准确。
  4. 适用于大规模数据和高维参数:Adam在处理大规模数据和高维参数时表现出色,特别适用于神经网络和深度学习模型的训练。
  5. 鲁棒性强:Adam对超参数的选择不敏感,通常默认的超参数设置(如 β1=0.9\beta_1 = 0.9β1=0.9,β2=0.999\beta_2 = 0.999β2=0.999)在多数情况下表现良好。

7、何为鞍点

这张图展示了一个典型的鞍点。鞍点在图的中心区域,表面在X轴方向上呈现凹陷,在Y轴方向上呈现上升,形成了一个鞍形。图中的蓝色箭头表示梯度下降的路径,可以看到这些路径在鞍点附近变慢和弯曲,表明梯度在鞍点处非常小,使得优化过程在该区域变得缓慢和不稳定。
image.png
代码:

import numpy as np
import matplotlib.pyplot as plt# 创建网格
x = np.linspace(-2, 2, 400)  # 在-2到2之间生成400个等距点
y = np.linspace(-2, 2, 400)  # 在-2到2之间生成400个等距点
X, Y = np.meshgrid(x, y)  # 生成网格点
Z = X**2 - Y**2  # 定义鞍点函数 Z = X^2 - Y^2# 绘制3D表面图
fig = plt.figure()  # 创建一个新图形
ax = fig.add_subplot(111, projection='3d')  # 添加一个3D子图
ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)  # 绘制3D表面,使用'viridis'颜色映射,透明度为0.8# 定义一个函数来绘制箭头表示梯度下降路径
def plot_arrow(ax, start, direction, length=0.2, color='r'):ax.quiver(start[0], start[1], start[2],  # 箭头起点direction[0], direction[1], direction[2],  # 箭头方向color=color, length=length, arrow_length_ratio=0.3)  # 颜色、长度和箭头长度比例# 梯度下降路径
start_points = [(-1.5, 1.5), (1.5, -1.5), (-1.5, -1.5), (1.5, 1.5)]  # 定义四个起始点
for x0, y0 in start_points:  # 遍历每个起始点point = np.array([x0, y0, x0**2 - y0**2])  # 计算起始点的初始位置for _ in range(10):  # 模拟梯度下降的迭代grad = np.array([2*point[0], -2*point[1], 0])  # 计算当前梯度plot_arrow(ax, point, -grad, color='blue')  # 绘制梯度下降路径的箭头point = point - 0.1 * grad  # 更新点位置,步长为0.1# 设置轴标签和标题
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
ax.set_title('3D Surface with Saddle Point and Gradient Descent')# 显示图像
plt.show()  # 显示绘制的图形

image.png
image.png

8、小结

介绍常见的一些对普通梯度下降算法的优化方法,主要有 Momentum、AdaGrad、RMSProp、Adam 等优化方法。
其中 Momentum 使用指数加权平均参考了历史梯度,使得梯度值的变化更加平缓;
AdaGrad 则是针对学习率进行了自适应优化,由于其实现可能会导致学习率下降过快,RMSProp 对 AdaGrad 的学习率自适应计算方法进行了优化;
Adam 则是综合了 Momentum 和 RMSProp 的优点,在很多场景下,Adam 的表示都很不错
选择标准

  • 简单性:如果你需要快速实现一个优化算法,且对精度要求不高,可以选择最基本的梯度下降或者指数加权平均。
  • 加速收敛:在需要快速收敛的场景下,Momentum和Adam是很好的选择。
  • 自适应学习率:如果数据稀疏或维度较高,AdaGrad和RMSProp是不错的选择。
  • 综合表现在大多数情况下,Adam是最推荐的算法,因为它结合了自适应学习率和动量的优点,表现出色且对超参数不敏感。

建议

  • 在选择优化算法时,可以根据模型复杂度、数据特性和实验结果进行调试和选择。
  • 初始尝试Adam,如果有特定需求或发现Adam表现不佳,再根据具体情况尝试其他优化算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国防科技大学深圳地区新生欢送会圆满举行

2024年7月28日,第97个八一建军节来临之际,在这个充满希望的盛夏时节,深圳地区迎来了13名即将踏入国防科技大学的优秀学子。 为了庆祝这一荣耀时刻,并表达对新生的深切祝福,在国防科技大学深圳校友会黄丹会长的积极倡议…

书生大模型实战营--L1关卡-Llamaindex RAG实践

一、安装llamaindex库 pip install llama-index pip install llama-index-embeddings-huggingface 二、问2024年巴黎奥运会 中国队获得几枚金牌,无法回答该问题 三、构建Llamaindex RAG 1、初始化llm 2、构建词向量模型 下载模型:git clone https://…

基于k8s快速搭建docker镜像服务的demo

基于k8s快速搭建docker镜像服务的demo 一、环境准备 如标题,你需要环境中有和2个平台,并且服务器上也已经安装好docker服务 接下来我来构建一个docker镜像,然后使用harbork8s来快速部署服务demo 二、部署概述 使用docker构建镜像&#x…

(2024,通用逼近定理(UAT),函数逼近,Kolmogorov–Arnold定理(KAT),任意深度/宽度的网络逼近)综述

A Survey on Universal Approximation Theorems 公和众与号:EDPJ(进 Q 交流群:922230617 或加 VX:CV_EDPJ 进 V 交流群) 目录 0. 摘要 1. 简介 2. 神经网络(NN) 3. 通用逼近定理&#xff0…

我的NAS是怎么连接Amazon Web Services S3的

作为IT爱好者,很多家庭都配备了Network Attached Storage(NAS),用于存储和管理大量数据。一个常见的挑战是如何实现异地备份,以确保数据的安全性和可恢复性。以下是一些解决方案和工具,可以帮助用户有效地管…

“手撕”MySQL的索引

目录 二、索引的作用 三、索引的缺点 四、如何使用索引 查看索引: 创建索引: ​编辑 删除索引: 五、索引的底层原理 那什么是B树,什么是B树呢? B树的好处: 总结: 一、什么是索引 索…

C语言的周末小练习(贰)

周末小练习&#xff1a; 5、输入身高和体重&#xff0c;计算BMI指数(BMI w/(h*h))。 #include <stdio.h>int main() {float w,h,BMI;/*printf("请输入体重\n");scanf("%f",&w);printf("请输入身高\n");scanf("%f",&h…

vue3 快速入门 (五) : Flex布局

1. 如何变成Flex布局 变成Flex容器&#xff0c;只需在容器布局的节点的CSS中&#xff0c;增加display : flex .mylayout {/* 省略了其他代码 */display: flex; }2. flex direction : 方向 row : 以行排列 row-reverse &#xff1a; 以行反向排列 column &#xff1a;以列排列…

Matlab编程资源库(11)多项式计算

一、多项式的四则运算 1&#xff0e;多项式的加减运算 2&#xff0e;多项式乘法运算 函数conv(P1,P2)用于求多项式P1和P2的乘积。 这里&#xff0c;P1、P2是两个多项式系数向量。 3&#xff0e;多项式除法 函数[Q,r]deconv(P1,P2)用于对多项式P1和P2作除法运算。其中Q返回多项…

【前端 09】JavaScript中的对象与JSON

JavaScript中的对象与JSON 在JavaScript中&#xff0c;对象和JSON&#xff08;JavaScript Object Notation&#xff09;是两个紧密相连但又有区别的概念。它们都在数据处理和交换中扮演着重要角色。本文将详细讲解JavaScript中的自定义对象以及JSON对象的基本概念、格式、用法…

基于微信小程序+SpringBoot+Vue的教务管理系统(带1w+文档)

基于微信小程序SpringBootVue的教务管理系统(带1w文档) 基于微信小程序SpringBootVue的教务管理系统(带1w文档) 在目前的情况下&#xff0c;可以引进一款基于web的高校教务管理系统这样的现代化管理工具&#xff0c;这个工具就是解决上述问题的最好的解决方案。它不仅可以实时完…

python拼接字符串方法

文章目录 1. 使用加号&#xff08;&#xff09;2. 使用str.join()方法3. 使用格式化字符串&#xff08;f-strings, % 操作符, .format() 方法&#xff09;4. 使用列表推导式和join()结合 性能对比 在Python中&#xff0c;字符串拼接是将两个或多个字符串合并成一个新字符串的过…

生成RSA公钥的n值

简介&#xff1a;RSA算法是一种非对称加密算法&#xff0c;与对称加密算法不同的是,RSA算法有两个不同的密钥,一个是公钥,一个是私钥。 原理是&#xff1a;根据数论&#xff0c;寻求两个大素数比较简单&#xff0c;而将它们的乘积进行因式分解却极其困难&#xff0c;因此可以将…

【leetcode 详解】生成特殊数字的最少操作【中等】(C++思路精析)

题目见下&#xff1a; 测试数据: 解题思路笔记&#xff1a; 最初拿到这道题是很蒙的&#xff0c;联想不到什么数据结构的模型&#xff08;肯定是笔者积累太少了&#xff09;&#xff0c;甚至惯性地想怎么实现“删除数字”的操作&#xff1a;在原字符串中抽出一个字符然后将剩…

VS code 与Pycharm 的使用区别(个人)

注明&#xff1a;本文从这开始VS code简称VS&#xff0c;Pycharm简称PY 安装包大小 VS:PY 1:0 安装后实际大小 vs py VS:PY 2:0 界面ui&#xff08;简易&#xff09; vs py VS:PY 2:1 启动速度 VS:PY 3:1 注&#xff1a;以上为个人测评&#xff0c;无特殊意图

为什么AI会一本正经地胡说八道

泛泛地说&#xff0c;AI一本正经地胡说八道的原因可以归结为&#xff1a;AI的理解能力受到其训练数据和算法的限制&#xff0c;如果问题表达不清晰或者背景信息不足&#xff0c;AI可能会产生错误的推理或输出&#xff1b;AI语言模型本质上是基于统计学习和模式匹配的&#xff0…

第一个设计模式——单例模式

目录 一、特点&#xff1a; 二、实现单例模式步骤 三、饿汉式 四、懒汉式 五、双重检查锁 六、静态内部类 七、枚举 八、可能被反序列化和反射破坏什么意思&#xff1f; 九、如何解决呢&#xff1f; 一、特点&#xff1a; 唯一性&#xff0c;单例模式确保程序中只有一…

数据结构 -- 算法的时间复杂度和空间复杂度

数据结构 -- 算法的时间复杂度和空间复杂度 1.算法效率1.1 如何衡量一个算法的好坏1.2 算法的复杂度 2.时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例 3.空间复杂度4. 常见复杂度对比 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法…

我在高职教STM32——EXTI之外部按键中断(1)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课件分…

人工智能学习①

LLM背景知识介绍 大语言模型 (LLM) 背景 用于理解和生成人类语言&#xff0c;能够处理诸如文本分类、问答、翻译和对话等多种自然语言任务。 语言模型 (Language Model, LM) &#xff1a;给定一个短语&#xff08;一个词组或者一句话&#xff09;语言模型可以生成&#xff0…