
文档教程人工智能深度学习NLP计算机视觉强化学习【免费下载链接】d2l-enInteractive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.项目地址https://gitcode.com/gh_mirrors/d2/d2l-en点击查看免费下载AdagradAdaptive Gradient自适应梯度算法是《Dive into Deep Learning》D2L优化章节中承上启下的关键算法它在动量法momentum之后、RMSProp/Adadelta/Adam 之前登场核心贡献是为每个参数坐标维护独立的自适应学习率梯度大的坐标被大幅缩小更新步长梯度小的坐标则获得更温和的待遇。本文以 chapter_optimization/adagrad.md 为主体结合仓库中 d2l 工具包的实现源码从稀疏特征场景、预调节preconditioning视角、算法推导、二维实验、从零实现到框架原生优化器调用完整还原 Adagrad 的原理与实战并对照 chapter_optimization/minibatch-sgd.md 的实验环境验证其训练效果。一、问题背景稀疏特征与全局学习率的矛盾考虑一个典型的语言模型训练场景为了获得好的精度通常需要在训练过程中不断降低学习率典型做法是让学习率按 $\mathcal{O}(t^{-\frac{1}{2}})$ 或更慢的速度衰减。现在引入稀疏特征sparse features——即只偶尔出现的特征。这在自然语言处理中非常常见例如单词preconditioning预调节出现的频率远低于learning学习在计算广告computational advertising和个性化协同过滤personalized collaborative filtering等领域也同样普遍因为大量物品只对少数人感兴趣。稀疏特征对应的参数只有在特征真正出现时才会收到有意义的梯度更新。如果使用全局递减的学习率就会出现两难局面频繁特征出现次数多参数很快收敛到最优值附近稀疏特征出现次数少在最优值确定之前参数还没能被足够频繁地观测和更新。换句话说全局递减的学习率对频繁特征来说下降得太慢对稀疏特征来说又下降得太快。一个朴素的对策是按特征计数来当钟表统计每个特征被观测到的次数用它来调节学习率。即不再使用形如$$\eta \frac{\eta_0}{\sqrt{t c}}$$的全局学习率而是对每个特征 $i$ 使用$$\eta_i \frac{\eta_0}{\sqrt{s(i, t) c}},$$其中 $s(i, t)$ 统计到时刻 $t$ 为止特征 $i$ 的非零出现次数。这个方案实现起来几乎没有额外开销但它在另一种场景下会失效当数据并非真稀疏而只是梯度经常很小、偶尔很大时很难划清某次观测算不算一个特征出现的界线。Adagrad由 Duchi、Hazan 和 Singer 于 2011 年提出参见仓库 d2l.bib 中的Duchi.Hazan.Singer.2011条目用历史梯度平方的累积和替代了这个粗糙的计数器 $s(i, t)$即$$s(i, t1) s(i, t) \left(\partial_i f(\mathbf{x})\right)^2.$$这样做有两个好处无需判定阈值不再需要决定梯度多大才算大随梯度幅值自动缩放经常产生大梯度的坐标被显著缩小学习率梯度小的坐标得到更温和的处理。在实践中这一机制对计算广告及类似问题非常有效。不过它也隐藏了 Adagrad 的额外优势这些优势最好放在预调节preconditioning的框架下理解。二、预调节从条件数到梯度代理凸优化问题适合分析算法的性质——大多数非凸问题难以给出有意义的理论保证但直觉与洞见往往可以迁移。考虑最小化如下二次型目标函数$$f(\mathbf{x}) \frac{1}{2} \mathbf{x}^\top \mathbf{Q} \mathbf{x} \mathbf{c}^\top \mathbf{x} b.$$如 chapter_optimization/momentum.md 所述可以利用特征分解 $\mathbf{Q} \mathbf{U}^\top \boldsymbol{\Lambda} \mathbf{U}$ 将其重写为每个坐标可独立求解的简化问题$$f(\mathbf{x}) \bar{f}(\bar{\mathbf{x}}) \frac{1}{2} \bar{\mathbf{x}}^\top \boldsymbol{\Lambda} \bar{\mathbf{x}} \bar{\mathbf{c}}^\top \bar{\mathbf{x}} b,$$其中 $\bar{\mathbf{x}} \mathbf{U} \mathbf{x}$$\bar{\mathbf{c}} \mathbf{U} \mathbf{c}$。该问题的最小点为 $\bar{\mathbf{x}} -\boldsymbol{\Lambda}^{-1} \bar{\mathbf{c}}$最小值为 $-\frac{1}{2} \bar{\mathbf{c}}^\top \boldsymbol{\Lambda}^{-1} \bar{\mathbf{c}} b$。由于 $\boldsymbol{\Lambda}$ 是包含 $\mathbf{Q}$ 特征值的对角矩阵计算变得非常容易。扰动敏感性与条件数如果对 $\mathbf{c}$ 施加轻微扰动我们希望 $f$ 的最小点也只发生轻微变化——但事实并非如此。$\mathbf{c}$ 的轻微变化确实只引起 $\bar{\mathbf{c}}$ 的轻微变化但对于 $f$以及 $\bar{f}$的最小点却并非如此当特征值 $\boldsymbol{\Lambda}_i$ 很大时$\bar{x}_i$ 和 $\bar{f}$ 的最小值只发生微小变化而当 $\boldsymbol{\Lambda}_i$ 很小时$\bar{x}_i$ 的变化可能非常剧烈。最大特征值与最小特征值之比被称为优化问题的条件数condition number$$\kappa \frac{\boldsymbol{\Lambda}_1}{\boldsymbol{\Lambda}_d}.$$条件数 $\kappa$ 越大越难以精确求解优化问题——必须小心处理大动态范围内的各种取值。理论上的理想方案能否通过扭曲空间让所有特征值都变成 1理论上很简单只需用 $\mathbf{Q}$ 的特征值和特征向量把问题从 $\mathbf{x}$ 重缩放到 $\mathbf{z} \stackrel{\textrm{def}}{} \boldsymbol{\Lambda}^{\frac{1}{2}} \mathbf{U} \mathbf{x}$在新坐标系中 $\mathbf{x}^\top \mathbf{Q} \mathbf{x}$ 简化为 $|\mathbf{z}|^2$。可惜这并不实用计算特征值和特征向量通常比求解原问题本身昂贵得多。实用的折中——对角线缩放精确计算特征值昂贵但猜测甚至近似计算它们也比什么都不做强得多。特别地可以用 $\mathbf{Q}$ 的对角元来重缩放$$\tilde{\mathbf{Q}} \textrm{diag}^{-\frac{1}{2}}(\mathbf{Q}) \mathbf{Q} \textrm{diag}^{-\frac{1}{2}}(\mathbf{Q}).$$此时 $\tilde{\mathbf{Q}}{ij} \mathbf{Q}{ij} / \sqrt{\mathbf{Q}{ii} \mathbf{Q}{jj}}$特别地 $\tilde{\mathbf{Q}}_{ii} 1$ 对所有 $i$ 成立。在大多数情况下这能显著改善条件数对前文提到的轴对齐问题甚至可以完全消除困难。深度学习中的真正障碍我们通常连目标函数的二阶导数都无法获取——对于 $\mathbf{x} \in \mathbb{R}^d$即使在一个小批量minibatch上计算二阶导数也可能需要 $\mathcal{O}(d^2)$ 的空间与计算量在实践中不可行。Adagrad 的巧妙之处在于用梯度本身的幅值作为 Hessian 对角线上那个难以捉摸的量的代理——既便宜又有效。为什么这样可行观察 $\bar{f}(\bar{\mathbf{x}})$ 的梯度$$\partial_{\bar{\mathbf{x}}} \bar{f}(\bar{\mathbf{x}}) \boldsymbol{\Lambda} \bar{\mathbf{x}} \bar{\mathbf{c}} \boldsymbol{\Lambda} \left(\bar{\mathbf{x}} - \bar{\mathbf{x}}_0\right),$$其中 $\bar{\mathbf{x}}_0$ 是 $\bar{f}$ 的最小点。可见梯度的幅值同时依赖于 $\boldsymbol{\Lambda}$ 和与最优点的距离。如果 $\bar{\mathbf{x}} - \bar{\mathbf{x}}_0$ 不变梯度幅值就足够了。由于 Adagrad 是随机梯度下降类算法即使在最优点附近也会看到非零方差的梯度因此可以放心地把梯度的方差作为 Hessian 尺度的廉价代理严格的分析篇幅很长详见Duchi.Hazan.Singer.2011。三、算法形式化逐坐标自适应学习率将上述讨论形式化。用变量 $\mathbf{s}_t$ 累积历史梯度平方即梯度方差的近似更新规则如下$$\begin{aligned} \mathbf{g}t \partial{\mathbf{w}} l(y_t, f(\mathbf{x}_t, \mathbf{w})), \ \mathbf{s}t \mathbf{s}{t-1} \mathbf{g}_t^2, \ \mathbf{w}t \mathbf{w}{t-1} - \frac{\eta}{\sqrt{\mathbf{s}_t \epsilon}} \cdot \mathbf{g}_t. \end{aligned}$$这里所有运算都是按坐标逐元素进行的$\mathbf{v}^2$ 的第 $i$ 个分量为 $v_i^2$$\frac{1}{\sqrt{v}}$ 的第 $i$ 个分量为 $\frac{1}{\sqrt{v_i}}$$\mathbf{u} \cdot \mathbf{v}$ 的第 $i$ 个分量为 $u_i v_i$。其中 $\eta$ 是学习率$\epsilon$ 是保证不除以零的加法常数初始状态 $\mathbf{s}_0 \mathbf{0}$。与动量法类似Adagrad 需要维护一个辅助变量这里是 $\mathbf{s}_t$用于实现每个坐标独立的学习率。这并不会显著增加 Adagrad 相对 SGD 的代价——因为主要开销通常在于计算 $l(y_t, f(\mathbf{x}_t, \mathbf{w}))$ 及其导数。学习率衰减行为由于 $\mathbf{s}_t$ 累积梯度平方它大体上以线性速度增长实践中因梯度初期会减小略慢于线性从而产生逐坐标调整的 $\mathcal{O}(t^{-\frac{1}{2}})$ 学习率。对凸问题而言这完全够用但在深度学习中我们往往希望学习率衰减得更慢一些。正是这个动机催生了后续章节讨论的一系列 Adagrad 变体chapter_optimization/rmsprop.md、chapter_optimization/adadelta.md 与 chapter_optimization/adam.md。四、二维凸二次问题实验验证自适应行为在动手实现完整版之前先在一个凸二次问题上观察 Adagrad 的行为。沿用动量法实验中的目标函数$$f(\mathbf{x}) 0.1 x_1^2 2 x_2^2.$$下面是三个框架MXNet / PyTorch / TensorFlow的实验代码框架后续的二维更新函数由#tab all统一给出#tab mxnet %matplotlib inline from d2l import mxnet as d2l import math from mxnet import np, npx npx.set_np()#tab pytorch %matplotlib inline from d2l import torch as d2l import math import torch#tab tensorflow %matplotlib inline from d2l import tensorflow as d2l import math import tensorflow as tf#tab all def adagrad_2d(x1, x2, s1, s2): eps 1e-6 g1, g2 0.2 * x1, 4 * x2 s1 g1 ** 2 s2 g2 ** 2 x1 - eta / math.sqrt(s1 eps) * g1 x2 - eta / math.sqrt(s2 eps) * g2 return x1, x2, s1, s2 def f_2d(x1, x2): return 0.1 * x1 ** 2 2 * x2 ** 2 eta 0.4 d2l.show_trace_2d(f_2d, d2l.train_2d(adagrad_2d))注意$x_1$ 方向的梯度系数为 $0.2$对应目标函数中的 $0.1 \times 2$$x_2$ 方向为 $4$梯度越大的坐标其平方累积越快学习率收缩也越快。与adagrad_2d紧密配合的train_2d与show_trace_2d定义在 d2l/torch.pyMXNet 版见 d2l/mxnet.pyTensorFlow 版见 d2l/tensorflow.pytrain_2d从 $(-5, -2)$ 出发迭代 20 步并将每一步的位置记录进results其中s1、s2是留给 Momentum、Adagrad、RMSProp 等算法共用的内部状态变量show_trace_2d则把迭代轨迹绘制在目标函数的等高线上。在 $\eta 0.4$ 时迭代轨迹比普通 SGD 平滑得多。但由于 $\mathbf{s}_t$ 的累积效应学习率持续衰减迭代后期自变量几乎不再移动。把学习率提高到 $\eta 2$ 后行为明显更好#tab all eta 2 d2l.show_trace_2d(f_2d, d2l.train_2d(adagrad_2d))这一对比已经暗示即使在无噪声情况下Adagrad 的学习率衰减也可能相当激进需要确保参数能充分收敛。五、从零实现 Adagrad与动量法一样Adagrad 需要维护一个与参数同形状的状态变量 $\mathbf{s}$累积梯度平方。三个框架下的初始化与更新实现如下#tab mxnet def init_adagrad_states(feature_dim): s_w d2l.zeros((feature_dim, 1)) s_b d2l.zeros(1) return (s_w, s_b) def adagrad(params, states, hyperparams): eps 1e-6 for p, s in zip(params, states): s[:] np.square(p.grad) p[:] - hyperparams[lr] * p.grad / np.sqrt(s eps)#tab pytorch def init_adagrad_states(feature_dim): s_w d2l.zeros((feature_dim, 1)) s_b d2l.zeros(1) return (s_w, s_b) def adagrad(params, states, hyperparams): eps 1e-6 for p, s in zip(params, states): with torch.no_grad(): s[:] torch.square(p.grad) p[:] - hyperparams[lr] * p.grad / torch.sqrt(s eps) p.grad.data.zero_()#tab tensorflow def init_adagrad_states(feature_dim): s_w tf.Variable(d2l.zeros((feature_dim, 1))) s_b tf.Variable(d2l.zeros(1)) return (s_w, s_b) def adagrad(params, grads, states, hyperparams): eps 1e-6 for p, s, g in zip(params, states, grads): s[:].assign(s tf.math.square(g)) p[:].assign(p - hyperparams[lr] * g / tf.math.sqrt(s eps))实现要点三个框架完全一致eps 1e-6是防止除零的加法常数对应公式中的 $\epsilon$状态s以原地更新方式累积p.grad或框架传入的grads的平方参数更新步长为hyperparams[lr] * p.grad / sqrt(s eps)即每个坐标用自己的梯度平方和缩放学习率PyTorch 版在更新后用p.grad.data.zero_()清零梯度而 MXNet / TensorFlow 由自动微分机制按需管理梯度。与 chapter_optimization/minibatch-sgd.md 中的实验相比这里使用了更大的学习率来训练模型#tab all data_iter, feature_dim d2l.get_data_ch11(batch_size10) d2l.train_ch11(adagrad, init_adagrad_states(feature_dim), {lr: 0.1}, data_iter, feature_dim);这里用到的数据与训练框架均来自仓库工具包get_data_ch11见 d2l/torch.py、d2l/mxnet.py、d2l/tensorflow.py下载airfoilNASA 翼型自噪声数据集取前n1500个样本、batch_size10并按均值/标准差标准化后封装为数据迭代器返回(data_iter, feature_dim)其中特征维度为data.shape[1]-1即 5train_ch11见 d2l/torch.py 等初始化 $w \sim \mathcal{N}(0, 0.01)$、$b 0$用线性回归 平方损失训练 2 个 epoch训练循环中依次执行loss.backward()后调用自定义的trainer_fn([w, b], states, hyperparams)每累计 200 个样本记录一次损失最终打印最终损失与每 epoch 耗时。六、简洁实现调用框架原生优化器除了从零实现D2L 也给出了直接调用各框架内置 Adagrad 优化器的简洁实现方式#tab mxnet d2l.train_concise_ch11(adagrad, {learning_rate: 0.1}, data_iter)#tab pytorch trainer torch.optim.Adagrad d2l.train_concise_ch11(trainer, {lr: 0.1}, data_iter)#tab tensorflow trainer tf.keras.optimizers.Adagrad d2l.train_concise_ch11(trainer, {learning_rate : 0.1}, data_iter)MXNet/Gluon通过gluon.Trainer(net.collect_params(), adagrad, hyperparams)创建优化器见 d2l/mxnet.py 中train_concise_ch11的实现每步调用trainer.step(X.shape[0])PyTorchtrain_concise_ch11接受优化器类内部执行trainer_fn(net.parameters(), **hyperparams)构造torch.optim.Adagrad训练循环中调用optimizer.zero_grad()与optimizer.step()见 d2l/torch.pyTensorFlow/Kerastrainer_fn(**hyperparams)直接构造tf.keras.optimizers.Adagrad训练循环中通过GradientTape计算梯度并调用optimizer.apply_gradients(zip(grads, params))见 d2l/tensorflow.py。三种框架统一以learning_rate 0.1训练同样的 airfoil 回归任务即可复现与从零实现一致的结果。需要留意的是各框架原生优化器的默认超参数如 $\epsilon$、初始累积值等与其标准实现保持一致具体默认值以对应框架文档为准在本实验中显式指定的学习率0.1明显大于 chapter_optimization/minibatch-sgd.md 中 SGD 的取值这也印证了 Adagrad 自适应缩放会天然压小有效步长需要配合更大的基础学习率使用。七、总结Adagrad 的适用场景与局限综合全文Adagrad 的关键结论可以归纳为逐坐标动态降低学习率每个参数坐标拥有独立的自适应学习率而非全局共享一个用梯度幅值调节进度梯度大的坐标被配以更小的学习率实现自动补偿梯度是二阶信息的实用代理深度学习问题中精确计算二阶导数通常因内存与算力约束而不可行梯度幅值/方差是划算的替代缓解病态结构当优化问题结构很不均匀条件数大时Adagrad 有助于减轻这种畸变带来的困难稀疏特征场景尤为有效对不频繁出现的特征学习率可以衰减得更慢保证其参数得到充分更新深度学习中可能过于激进Adagrad 有时会把学习率降得过快缓解这一问题的策略将在 chapter_optimization/adam.md及其前的 chapter_optimization/rmsprop.md、chapter_optimization/adadelta.md中讨论——它们正是针对累积平方和单调增长这一缺点的改进。八、延伸思考与练习证明对正交矩阵 $\mathbf{U}$ 和向量 $\mathbf{c}$$|\mathbf{c} - \mathbf{\delta}|_2 |\mathbf{U} \mathbf{c} - \mathbf{U} \mathbf{\delta}|_2$。为什么这意味着扰动的大小在正交变量变换下不变分别对 $f(\mathbf{x}) 0.1 x_1^2 2 x_2^2$ 以及旋转 45 度后的目标函数 $f(\mathbf{x}) 0.1 (x_1 x_2)^2 2 (x_1 - x_2)^2$ 运行 Adagrad观察行为是否不同并思考原因。证明 Gerschgorin 圆定理矩阵 $\mathbf{M}$ 的特征值 $\lambda_i$ 至少对某个 $j$ 满足 $|\lambda_i - \mathbf{M}{jj}| \leq \sum{k \neq j} |\mathbf{M}_{jk}|$。结合 Gerschgorin 定理分析对角预调节矩阵 $\textrm{diag}^{-\frac{1}{2}}(\mathbf{M}) \mathbf{M} \textrm{diag}^{-\frac{1}{2}}(\mathbf{M})$ 的特征值分布会得到怎样的结论。将 Adagrad 应用于一个真实的深度网络例如 chapter_convolutional-neural-networks/lenet.md 中的 LeNet 在 Fashion-MNIST 上的训练观察其收敛曲线。如果要让 Adagrad 的学习率衰减不那么激进应当如何修改算法可以结合后续的 RMSProp、Adadelta 与 Adam 章节对照验证你的修改思路。通过以上练习可以更深入地理解 Adagrad 在凸优化中的理论性质以及它在深度学习场景下自适应但偏激进的行为边界为后续学习更先进的自适应优化算法打下基础。赞分享文档教程人工智能深度学习NLP计算机视觉强化学习【免费下载链接】d2l-enInteractive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.项目地址https://gitcode.com/gh_mirrors/d2/d2l-en点击查看免费下载相关推荐Wails v3 内置 MCP 服务器实战用 LLM 驱动桌面应用基于 mcp 示例项目Wails v3 内置 MCP 服务器实战用 LLM 驱动桌面应用基于 mcp 示例项目 导读 Wails v3 https://link.gitcode人工智能深度学习机器学习教程深度学习优化利器AdaGrad自适应学习率算法完整指南深度学习优化利器AdaGrad自适应学习率算法完整指南 《动手学深度学习》d2l zh是面向中文读者的深度学习教程被70多个国家的500多所大学用于教学人工智能深度学习机器学习教程AI-For-Beginners学习率调度自适应优化算法AI For Beginners学习率调度自适应优化算法 引言为什么需要学习率调度 在深度学习训练过程中学习率Learning Rate是最重要的超教程人工智能机器学习深度学习上一篇如何快速上手ERPNext免费开源ERP系统新手完整指南下一篇免费快速转换扫描PDF为可搜索文档的终极解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考