Skip to content

Adadelta算法

Adadelta 是 RMSProp 的 “全自动升级版”—— 彻底抛弃了全局学习率的设置,用参数本身的变化率来自动调整步长,就像登山者完全根据之前的步幅自动调整下一步的大小,不用预先设定步长,是 “零学习率参数” 的自适应优化算法。

一、核心逻辑:为啥不需要 “预设步长”?

1. RMSProp 的小痛点

RMSProp 解决了 Adagrad 学习率衰减太快的问题,但还是需要手动设置全局学习率

  • 如果 太大,前期步长太大容易震荡;

  • 如果 太小,后期步长太小收敛慢;

  • 不同任务的最优 不一样,需要手动调试,很麻烦。

这就像登山时需要预先设定步长大小,遇到陡坡和平路都要手动调整,很费精力。

2. Adadelta 的解决方案

Adadelta 给登山者加了 “全自动步长调整” 的能力:用参数的变化率来替代全局学习率,不需要手动设置 ,完全自适应:

  • 先记录梯度的二阶导数的泄漏平均值(和 RMSProp 一样),知道路况的崎岖程度;

  • 再记录参数变化的二阶导数的泄漏平均值,知道之前的步幅大小;

  • 用之前的步幅大小来调整当前的步长,自动适配路况。

简单说:Adadelta 让登山者 “根据之前走的步幅,自动调整下一步的大小”,不用预先设定步长,全自动适配路况。

二、Adadelta 的核心原理

1. 核心公式

Adadelta 需要维护两个状态变量,实现完全自适应的步长调整:

其中:

  • :梯度平方的泄漏平均值,和 RMSProp 的 一样,记录路况的崎岖程度;

  • :参数变化平方的泄漏平均值,记录之前的步幅大小;

  • :遗忘系数,常用值 0.9,控制保留的历史长度,和 RMSProp 的 一样;

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

  • :按元素相乘,每个参数用自己的自适应步长更新。

2. 公式解读

Adadelta 的核心是用 替代了 RMSProp 中的全局学习率

  • 是之前参数变化的均方根,相当于之前的平均步幅;

  • 用之前的平均步幅来调整当前的梯度,相当于 “用过去的步长来适配当前的路况”,自动确定合适的步长;

  • 时,相当于保留最近 10 步的梯度和参数变化的平均值。

3. 与 RMSProp 的核心区别

特性RMSPropAdadelta
学习率设置需要手动设置全局学习率 不需要手动设置学习率,用参数变化率自动适配
状态变量只需要维护一个状态变量 (梯度平方的泄漏平均值)需要维护两个状态变量 (梯度和参数变化的泄漏平均值)
步长计算
适用场景需要精细调学习率的场景不想手动调学习率的快速上手场景

三、实战对比:Adadelta vs RMSProp

用二维函数 测试:

  • RMSProp:需要设置 ,才能收敛;

  • Adadelta:只需要设置 ,不需要设置学习率,就能自动收敛到最小值,步长会自动适配路况。

这就像 RMSProp 登山者需要预先设定步长,而 Adadelta 登山者能自动根据路况调整步长,不用手动干预。

四、PyTorch 实战:全自动登山流程

1. 从零开始实现

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

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

# 1. 初始化Adadelta状态(每个参数需要两个状态变量:梯度平方平均值、参数变化平方平均值)
def init_adadelta_states(feature_dim):
    s_w = torch.zeros((feature_dim, 1))  # 权重的梯度平方平均值
    delta_w = torch.zeros((feature_dim, 1))  # 权重的参数变化平方平均值
    s_b = torch.zeros(1)  # 偏置的梯度平方平均值
    delta_b = torch.zeros(1)  # 偏置的参数变化平方平均值
    return ((s_w, delta_w), (s_b, delta_b))

# 2. Adadelta更新函数
def adadelta(params, states, hyperparams):
    rho, eps = hyperparams['rho'], 1e-5
    for p, (s, delta) in zip(params, states):
        with torch.no_grad():
            # 计算梯度平方的泄漏平均值
            s[:] = rho * s + (1 - rho) * torch.square(p.grad)
            # 计算调整后的梯度(用参数变化的平均值替代学习率)
            g = (torch.sqrt(delta + eps) / torch.sqrt(s + eps)) * p.grad
            # 更新参数
            p[:] -= g
            # 计算参数变化平方的泄漏平均值
            delta[:] = rho * delta + (1 - rho) * torch.square(g)
        p.grad.data.zero_()  # 清空梯度

# 3. 训练函数
def train_adadelta(rho, 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)
    # 训练
    d2l.train_ch11(
        adadelta,
        init_adadelta_states(feature_dim),
        {'rho': rho},
        data_iter, feature_dim, num_epochs
    )

# 启动训练(只需要设置rho=0.9,不需要设置学习率)
train_adadelta(rho=0.9)

2. 框架简洁实现

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

python
# 直接使用框架内置的Adadelta
trainer = torch.optim.Adadelta
d2l.train_concise_ch11(
    trainer,
    {'rho': 0.9},  # 只需要设置rho,不需要设置lr
    data_iter
)

五、优缺点:什么时候用 Adadelta?

1. 优点

  • 完全自适应:不需要手动设置全局学习率,避免了学习率调试的麻烦;

  • 解决 Adagrad 的痛点:和 RMSProp 一样,用泄漏平均值替代累加,学习率不会一直衰减;

  • 适应性强:不同任务都能自动适配步长,快速上手。

2. 缺点

  • 计算量略大:需要维护两个状态变量,比 RMSProp 多一个状态变量的计算;

  • 选择敏感 太大(比如 0.99),泄漏平均值更新太慢,步长调整不及时; 太小(比如 0.5),噪声太大,步长不稳定;

  • 收敛速度略慢:因为步长是自动调整的,有时候比手动调优的 RMSProp 收敛慢一点。

3. 适用场景

  • 不想手动调学习率的场景:比如快速上手的小任务,或者对学习率不熟悉的新手;

  • 对学习率敏感的任务:比如一些难以调学习率的非凸任务;

  • 替代 RMSProp 的场景:当你觉得 RMSProp 的学习率调试太麻烦时,用 Adadelta 替代。

六、常见误区:避开这 2 个坑

  1. 设置太大 时,保留的历史太长,步长调整不及时,会像 Adagrad 一样后期步长太小;

  2. 认为 Adadelta 不需要调参:虽然不需要调学习率,但 还是需要调整,默认 0.9 是比较通用的选择。

七、核心总结

  1. Adadelta 本质:全自动自适应的 RMSProp,用参数变化的平均值替代全局学习率,不需要手动设置学习率;

  2. 关键参数: (遗忘系数,默认 0.9),控制保留的历史长度;

  3. 核心优势:不需要手动调学习率,完全自适应,适合快速上手的场景;

  4. 一句话记忆:登山时不用预设步长,根据之前的步幅自动调整,全自动适配路况,轻松下山~

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

京ICP备2024093538号-1