Adman 梯度下降算法核心思想是,对于每个参数,根据其梯度的历史信息,动态调整其学习率从而实现更快的收敛和更好的性能。Adman 梯度下降算法使用两个参数,一个是动量参数,另一个是自适应学习率参数。

  • 动量参数用于加速参数的更新
  • 自适应学习率参数则根据参数梯度的历史信息,动态调整参数的学习率。

在学习 adam 梯度下降算法之前,有必要先了解一下梯度下降算法和动量梯度下降算法。

# 梯度下降算法

  • 梯度下降算法(Gradient Descent, GD) 是深度学习的核心之一,用于最小化目标函数。其基本思想是,在每次迭代中,沿着目标函数的负梯度方向更新参数,从而逐步逼近最优解。
  • 随机梯度下降算法(Stochastic Gradient Descent, SGD) 是梯度下降算法的一种改进,它每次迭代只使用一部分样本进行计算,从而加快了训练速度。
  • 动量梯度下降算法(Momentum Gradient Descent, MGD) 是梯度下降算法的另一种改进,它引入了动量参数,用于加速参数的更新。
  • 自适应学习AdaGrad算法RMSProp算法 训练过程中可以动态调整学习率,从而实现更快的收敛和更好的性能。
  • Adam梯度下降算法(Adaptive Moment Estimation, Adam) 是梯度下降算法的另一种改进,它结合了动量梯度下降算法和自适应学习率算法,是一种常用的优化算法。

在此之前,复习下损失函数。

# 损失函数

在深度学习中,网络参数的迭代更新由损失函数的梯度决定。梯度下降算法通过计算损失函数相对于参数的梯度,然后沿着梯度的负方向更新参数,从而逐步逼近最优解。

假设一个很简单的线性回归模型,输入为 x , 输出为 y ,模型参数为 wb

其模型为:

y^=wx+b\hat{y} = wx + b

损失函数为:

L=12(y^y)2L = \frac{1}{2}(\hat{y} - y)^2

梯度下降算法的更新规则为:

w=wηLww = w - \eta \frac{\partial L}{\partial w}

b=bηLbb = b - \eta \frac{\partial L}{\partial b}

其中,η\eta 为学习率,Lw\frac{\partial L}{\partial w}Lb\frac{\partial L}{\partial b} 分别为损失函数 L 对参数 w 和 b 的梯度。

θ=θηL(θ)\theta = \theta - \eta \nabla L(\theta)

其中,θ\theta 为参数向量,L(θ)\nabla L(\theta) 为损失函数 L 对参数θ\theta 的梯度向量。
可以看出,每次更新参数时,都会乘以一个系数η\eta,如果η\eta 过大,可能会导致参数更新过大,从而错过最优解;如果η\eta 过小,可能会导致参数更新过慢,从而收敛速度过慢。因此,选择合适的学习率是非常重要的。

而且对于高维情况,普通梯度下降算法存在局限性
例如:

  1. 输入元素: xxyy

  2. 损失函数为: f(x,y)=x2+y2f(x,y) = x^2 + y^2

  3. 梯度求偏导: fx\frac{\partial f}{\partial x}fy\frac{\partial f}{\partial y}:

    fx=2x\frac{\partial f}{\partial x} = 2x

    fy=2y\frac{\partial f}{\partial y} = 2y

  4. 更新参数:xxyy

    x=xηfx=x2ηxx = x - \eta \frac{\partial f}{\partial x} = x - 2\eta x

    y=yηfy=y2ηyy = y - \eta \frac{\partial f}{\partial y} = y - 2\eta y

示例代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
# 配置中文字体(可选)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 定义损失函数(示例使用二次函数)
def loss_function(x, y):
    return x**2 + y**2
# 计算梯度
def compute_gradient(x, y):
    dx = 2*x
    dy = 2*y
    return dx, dy
# 梯度下降参数设置
lr = 0.1       # 学习率
steps = 15     # 迭代次数
start_point = (-4, 3.5)  # 初始点
# 生成网格数据
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = loss_function(X, Y)
# 执行梯度下降
path = [start_point]
current_x, current_y = start_point
for _ in range(steps):
    grad_x, grad_y = compute_gradient(current_x, current_y)
    current_x -= lr * grad_x
    current_y -= lr * grad_y
    path.append((current_x, current_y))
# 转换为数组便于绘图
path = np.array(path)
x_path, y_path = path[:,0], path[:,1]
z_path = loss_function(x_path, y_path)
# 创建三维画布
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 绘制损失函数曲面
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                       alpha=0.6, linewidth=0,
                       antialiased=False)
# 绘制梯度下降路径
ax.plot(x_path, y_path, z_path, 'r-o', 
        markersize=8, linewidth=2,
        markerfacecolor='yellow')
# 添加梯度箭头
for i in range(len(path)-1):
    dx = path[i+1,0] - path[i,0]
    dy = path[i+1,1] - path[i,1]
    dz = z_path[i+1] - z_path[i]
    ax.quiver(path[i,0], path[i,1], z_path[i],
              dx*0.8, dy*0.8, dz*0.8,
              color='black', arrow_length_ratio=0.1)
# 添加标签和标题
ax.set_xlabel('X 参数', labelpad=12)
ax.set_ylabel('Y 参数', labelpad=12)
ax.set_zlabel('损失值', labelpad=12)
ax.set_title('三维梯度下降过程可视化', y=1.02, fontsize=14)
# 设置观察角度
ax.view_init(elev=30, azim=-140)
# 显示颜色标尺
fig.colorbar(surf, shrink=0.5, aspect=5)
# 显示图形
plt.tight_layout()
plt.show()

可视化结果:
images/梯度更新.png

缺点:

  • 梯度下降算法在训练过程中可能会陷入局部最小值,导致模型无法收敛到全局最优解。
  • 梯度下降算法的学习率需要手动调整,如果学习率过大,可能会导致模型在最优解附近震荡,如果学习率过小,可能会导致模型收敛速度过慢。

# 动量梯度下降算法

不同于上面的梯度下降算法,动量梯度下降算法引入了历史梯度信息 (可以理解为保留上次梯度更新方向的一定力)

公式为:

v=βvηL(θ)v = \beta v - \eta \nabla L(\theta)

vv 为历史梯度信息的保留量,β\beta 为动量参数,取值范围为 0 到 1 之间,通常取 0.9。η\eta 为学习率,L(θ)\nabla L(\theta) 为损失函数 L 对参数θ\theta 的梯度向量。
参数更新变为:

θ=θ+v\theta = \theta + v

θ\theta 为参数向量。

优点:

  • 动量梯度下降算法可以加速参数的更新,减少训练时间。
  • 动量梯度下降算法可以避免局部最小值,提高模型的收敛速度。

缺点:

  • 动量梯度下降算法需要手动调整 动量参数学习率 ,如果参数设置不当,可能会导致模型收敛速度过慢或者震荡。

# 学习率

学习率是梯度下降算法中最重要的参数之一,它决定了参数更新的步长。如果学习率过大,可能会导致模型在最优解附近震荡,如果学习率过小,可能会导致模型收敛速度过慢。因此,选择合适的学习率是非常重要的。

  1. 引入变量rr (AdaGrad 算法 2011)

r=r+L(θ)2r = r + \nabla L(\theta)^2

重点:
- $r$是一个变量,其值随着迭代不断增大,导致学习率下降。
- $r$每次增加的量由计算的梯度值$\nabla L(\theta)$决定。
参数更新:

θ=θηr+ϵL(θ)\theta = \theta - \frac{\eta}{\sqrt{r} + \epsilon} \nabla L(\theta)

ϵ\epsilon 为防止除 0 操作,通常取一个很小的值,如10810^{-8}

这样当梯度变化较大时,$r$增加的多,学习率减小的快,当梯度变化较小时,$r$增加的少,学习减小的慢。

问题: AdaGrad 算法rr 值只与梯度有关,可能导致学习率过早的变小。

  1. 引入变量ρ\rho (RMSProp 算法 2012)

r=ρv+(1ρ)L(θ)2r = \rho v + (1 - \rho) \nabla L(\theta)^2

θ=θηr+ϵL(θ)\theta = \theta - \frac{\eta}{\sqrt{r} + \epsilon} \nabla L(\theta)

其中: ρ\rho 为手动调节参数。

# Adam 梯度下降算法 (2014)

Adam 梯度下降算法结合了动量梯度下降算法和自适应学习率算法,是一种常用的优化算法。

自适应动量:

v=ρ1v+(1ρ1)L(θ)v = \rho_1 v + (1 - \rho_1) \nabla L(\theta)

ρ1\rho_1 是动量参数。

r=ρ2r+(1ρ2)L(θ)2r = \rho_2 r + (1 - \rho_2) \nabla L(\theta)^2

ρ2\rho_2 是自适应学习率参数。

修正动量和学习率:

v^=v1ρ1t\widehat{v} = \frac{v}{1 - \rho_1^t}

r^=r1ρ2t\widehat{r} = \frac{r}{1 - \rho_2^t}

tt 为迭代次数,修正后使得在训练之初,动量和学习率比较大,帮助快速收敛。

参数更新:

θ=θηv^r^+ϵv\theta = \theta - \frac{\eta \widehat{v}}{\sqrt{\widehat{r}} + \epsilon} v