Skip to content

Adam算法

Adam 是深度学习的 “全能优化器”—— 融合了动量法的 “惯性加速” 和 RMSProp 的 “自适应步长”,还解决了初始偏差的问题,就像登山者既带了登山杖(保持冲劲),又能根据路况自动调整步长(陡坡小步、平路大步),还能在起步时调整初始的小步幅,是深度学习的默认首选优化器。

一、核心逻辑:为啥 Adam 是 “全能登山法”?

1. 动量法和 RMSProp 的小痛点

  • 动量法:只有惯性,步长固定,遇到陡坡(梯度大的方向)容易震荡,就像登山者带着惯性冲坡,遇到陡坡时容易晃得摔倒;

  • RMSProp:只有自适应步长,没有惯性,收敛慢,就像登山者能调步长,但没有冲劲,平路也走得很慢。

2. Adam 的解决方案

Adam 给登山者加了 “全能装备”:

  • 带惯性:像动量法一样,记住过去的梯度方向,积累冲劲,顺坡方向加速;

  • 自适应步长:像 RMSProp 一样,根据梯度大小自动调整步长,陡坡小步、平路大步;

  • 偏差校正:解决初始时动量和梯度平方的估计偏差,起步时自动调整步幅,避免前期走得太慢。

简单说:Adam 让登山者 “既冲得快,又走得稳,起步还不拖沓”,是目前最实用的优化算法。

二、Adam 的核心原理

1. 核心公式

Adam 需要维护两个状态变量,加上偏差校正,共 4 个步骤:

其中:

  • :动量,是过去梯度的加权平均,相当于登山者的 “冲劲”;

  • :梯度平方的加权平均,相当于登山者的 “路况记录”;

  • :动量系数,常用 0.9,相当于记住最近 10 步的梯度;

  • :遗忘系数,常用 0.999,相当于记住最近 1000 步的梯度平方;

  • :全局学习率,常用 0.001,控制整体步长;

  • :极小常数(比如 ),防止除以 0;

  • :校正后的动量和梯度平方,解决初始时估计值太小的问题。

2. 公式解读

  • 动量更新 是过去梯度的加权和,越近的梯度权重越大, 时,10 步前的梯度权重只有 ,相当于慢慢遗忘更早的梯度;

  • 梯度平方更新 是过去梯度平方的加权和, 时,1000 步前的梯度平方权重只有 ,相当于记住很久的路况;

  • 偏差校正:初始时 ,前几步的估计值太小,校正后能让起步时的步幅更合理,避免前期走得太慢;

  • 参数更新:用校正后的动量和梯度平方调整步长, 相当于 “带惯性的自适应步长”,既保持冲劲,又适配路况。

3. 与动量法、RMSProp 的核心区别

特性动量法RMSPropAdam
惯性(动量)
自适应步长
偏差校正
收敛速度中等中等
震荡程度极小
适用场景凸问题、简单任务非凸问题、稀疏数据所有深度学习任务

三、实战对比:Adam vs 动量法 vs RMSProp

用二维函数 测试:

  • 动量法:需要设置 ,20 轮后 接近 0,但 还有震荡;

  • RMSProp:需要设置 ,20 轮后 都接近 0,但收敛速度比动量法慢;

  • Adam:设置 ,10 轮后就收敛到最小值,且轨迹非常平滑,没有震荡。

这就像动量法登山者冲得快但容易晃,RMSProp 登山者走得稳但慢,Adam 登山者既快又稳,还能在起步时调整步幅,快速到达山脚。

四、PyTorch 实战:全能登山流程

1. 从零开始实现

手动实现 Adam 的参数更新逻辑,适合理解原理:

python
import torch
from torch import nn
from d2l import torch as d2l

# 1. 初始化Adam状态(每个参数需要两个状态变量:动量、梯度平方)
def init_adam_states(feature_dim):
    v_w = torch.zeros((feature_dim, 1))  # 权重的动量
    s_w = torch.zeros((feature_dim, 1))  # 权重的梯度平方
    v_b = torch.zeros(1)  # 偏置的动量
    s_b = torch.zeros(1)  # 偏置的梯度平方
    return ((v_w, s_w), (v_b, s_b))

# 2. Adam更新函数
def adam(params, states, hyperparams):
    beta1, beta2, eps = 0.9, 0.999, 1e-6
    for p, (v, s) in zip(params, states):
        with torch.no_grad():
            # 计算动量的指数加权移动平均
            v[:] = beta1 * v + (1 - beta1) * p.grad
            # 计算梯度平方的指数加权移动平均
            s[:] = beta2 * s + (1 - beta2) * torch.square(p.grad)
            # 偏差校正
            v_bias_corr = v / (1 - beta1 ** hyperparams['t'])
            s_bias_corr = s / (1 - beta2 ** hyperparams['t'])
            # 更新参数
            p[:] -= hyperparams['lr'] * v_bias_corr / (torch.sqrt(s_bias_corr) + eps)
        p.grad.data.zero_()  # 清空梯度
    hyperparams['t'] += 1  # 时间步加1

# 3. 训练函数
def train_adam(lr, num_epochs=2):
    # 加载数据
    data_iter, feature_dim = d2l.get_data_ch11(batch_size=10)
    # 定义模型
    net = nn.Sequential(nn.Linear(feature_dim, 1))
    nn.init.normal_(net[0].weight, std=0.01)
    # 训练(初始化时间步t=1)
    d2l.train_ch11(
        adam,
        init_adam_states(feature_dim),
        {'lr': lr, 't': 1},
        data_iter, feature_dim, num_epochs
    )

# 启动训练(Adam的默认学习率是0.001)
train_adam(lr=0.001)

2. 框架简洁实现

用 PyTorch 内置的optim.Adam直接调用,更简洁:

python
# 直接使用框架内置的Adam
trainer = torch.optim.Adam
d2l.train_concise_ch11(
    trainer,
    {'lr': 0.001},  # 默认beta1=0.9,beta2=0.999
    data_iter
)

五、Adam 的改进版:Yogi

Adam 在某些场景下(比如梯度平方爆炸的稀疏数据)可能会发散,Yogi 是 Adam 的 “热补丁”,解决了这个问题:

  • 核心改进:把梯度平方的更新从 改为

  • 效果:避免了梯度平方的估计值爆炸,在稀疏数据场景下更稳定。

Yogi 的 PyTorch 实现

python
def yogi(params, states, hyperparams):
    beta1, beta2, eps = 0.9, 0.999, 1e-6
    for p, (v, s) in zip(params, states):
        with torch.no_grad():
            # 动量更新(和Adam一样)
            v[:] = beta1 * v + (1 - beta1) * p.grad
            # Yogi的梯度平方更新
            s[:] = s + (1 - beta2) * torch.sign(torch.square(p.grad) - s) * torch.square(p.grad)
            # 偏差校正(和Adam一样)
            v_bias_corr = v / (1 - beta1 ** hyperparams['t'])
            s_bias_corr = s / (1 - beta2 ** hyperparams['t'])
            # 参数更新
            p[:] -= hyperparams['lr'] * v_bias_corr / (torch.sqrt(s_bias_corr) + eps)
        p.grad.data.zero_()
    hyperparams['t'] += 1

六、优缺点:什么时候用 Adam?

1. 优点

  • 收敛快且稳定:既有惯性加速,又有自适应步长,还能校正初始偏差,是目前最均衡的优化器;

  • 不需要手动调参:默认参数( )适合大部分任务;

  • 适用范围广:几乎所有深度学习任务都能用,包括图像分类、自然语言处理、推荐系统等。

2. 缺点

  • 在凸问题中可能不如 SGD:Adam 可能收敛到局部最小值,而 SGD 在凸问题中能收敛到全局最小值;

  • 稀疏数据场景下可能过拟合:自适应步长可能让低频特征的步长太大,导致过拟合;

  • 计算量略大:需要维护两个状态变量,比动量法和 RMSProp 的计算量略大。

3. 适用场景

  • 大部分深度学习任务:Adam 是默认首选,尤其是复杂的非凸任务(比如 Transformer、CNN);

  • 快速上手的场景:不想手动调参时,Adam 的默认参数就能取得不错的效果;

  • 替代动量法和 RMSProp 的场景:当动量法震荡太大、RMSProp 收敛太慢时,用 Adam 替代。

七、常见误区:避开这 3 个坑

  1. 学习率设置太大:Adam 的默认学习率是 0.001,不要像 SGD 那样用 0.01,否则会震荡发散;

  2. 忽略偏差校正:初始几步的偏差校正很重要,去掉的话前期收敛会很慢;

  3. 在凸问题中滥用 Adam:凸问题中 SGD 可能收敛到更好的全局最小值,Adam 可能卡在局部最小值。

八、核心总结

  1. Adam 本质:带惯性的自适应步长优化器,融合了动量法和 RMSProp 的优点,还解决了初始偏差的问题;

  2. 关键参数: (惯性大小), (遗忘系数), (全局学习率);

  3. 核心优势:收敛快、稳定,不需要手动调参,是深度学习的默认首选;

  4. 一句话记忆:登山时既带冲劲又能自动调步长,起步还能调整步幅,全能装备带你高效下山~

(注:文档部分内容可能由 AI 生成)

京ICP备2024093538号-1