人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载导读本文对应《动手学深度学习》d2l-zh优化算法章节中的梯度下降gradient descent指南讲解梯度下降的基本原理、一维与多元实现、学习率的影响、局部最小值问题以及牛顿法、收敛性分析、预处理与线搜索等自适应方法。读完本文你将掌握梯度下降的数学推导与可运行代码支持 MXNet / PyTorch / TensorFlow / Paddle 四种框架并理解学习率过大导致发散、预处理按坐标缩放等关键现象为后续学习随机梯度下降SGD与动量、Adam 等高级优化算法打下基础。一维梯度下降尽管梯度下降很少被直接用于深度学习但理解它是理解下一节随机梯度下降算法的关键。例如由于学习率过大优化问题可能会发散这种现象早在梯度下降中就已经出现同样地预处理preconditioning是梯度下降中的一种常用技术还会被沿用到更高级的算法中。因此我们从最简单的一维梯度下降开始。考虑一类连续可微实值函数 $f: \mathbb{R} \rightarrow \mathbb{R}$利用泰勒展开可以得到$$f(x \epsilon) f(x) \epsilon f(x) \mathcal{O}(\epsilon^2).$$即在一阶近似中$f(x\epsilon)$ 可通过 $x$ 处的函数值 $f(x)$ 和一阶导数 $f(x)$ 得出。我们可以假设在负梯度方向上移动的 $\epsilon$ 会减少 $f$。为了简单起见选择固定步长 $\eta 0$然后取 $\epsilon -\eta f(x)$将其代入泰勒展开式得到$$f(x - \eta f(x)) f(x) - \eta f^2(x) \mathcal{O}(\eta^2 f^2(x)).$$如果其导数 $f(x) \neq 0$ 没有消失就能继续推进这是因为 $\eta f^2(x)0$。此外我们总是可以令 $\eta$ 小到足以使高阶项变得不相关因此有$$f(x - \eta f(x)) \lessapprox f(x).$$这意味着如果我们使用$$x \leftarrow x - \eta f(x)$$来迭代 $x$函数 $f(x)$ 的值可能会下降。因此在梯度下降中我们首先选择初始值 $x$ 和常数 $\eta 0$然后使用它们连续迭代 $x$直到停止条件达成。例如当梯度 $|f(x)|$ 的幅度足够小或迭代次数达到某个值时停止。代码实现以 $f(x)x^2$ 为例为了演示实现选用目标函数 $f(x)x^2$。尽管已知 $x0$ 时 $f(x)$ 取得最小值我们仍使用这个简单的函数来观察 $x$ 的变化。定义目标函数与其梯度def f(x): # 目标函数 return x ** 2 def f_grad(x): # 目标函数的梯度导数 return 2 * x本仓库的d2l包为每种深度学习框架提供了同名接口上述代码在 MXNet、PyTorch、TensorFlow、Paddle 四个后端下均可运行导入方式分别为from d2l import mxnet as d2l # 或 torch / tensorflow / paddle接下来使用 $x10$ 作为初始值并假设 $\eta0.2$用梯度下降迭代 $x$ 共 10 次。可以看到 $x$ 的值最终将接近最优解 $x0$def gd(eta, f_grad): x 10.0 results [x] for i in range(10): x - eta * f_grad(x) results.append(float(x)) print(fepoch 10, x: {x:f}) return results results gd(0.2, f_grad)对 $x$ 优化的过程可以绘制如下。show_trace函数以results的最大绝对值为界生成函数曲线并把每次迭代后的点用-o折线标记出来def show_trace(results, f): n max(abs(min(results)), abs(max(results))) f_line d2l.arange(-n, n, 0.01) d2l.set_figsize() d2l.plot([f_line, results], [[f(x) for x in f_line], [ f(x) for x in results]], x, f(x), fmts[-, -o]) show_trace(results, f)其中d2l.set_figsize、d2l.plot等绘图工具在仓库中均有统一实现例如 d2l/torch.py 中set_figsize默认设置figure.figsize (3.5, 2.5)并切换到 SVG 输出plot负责归一化输入、设置坐标轴并逐条绘制曲线。d2l.arange、d2l.meshgrid、d2l.cos、d2l.sin等符号则在 d2l/torch.py 中直接映射到torch的对应函数从而保证四个后端代码风格一致。学习率过大发散过小停滞学习率 $\eta$ 由算法设计者设置它决定目标函数能否收敛到局部最小值以及何时收敛到最小值。学习率太小将导致 $x$ 更新非常缓慢需要更多迭代。例如同一优化问题中取 $\eta 0.05$即使经过 10 步仍离最优解很远show_trace(gd(0.05, f_grad), f)学习率过大$\left|\eta f(x)\right|$ 对一阶泰勒展开式可能太大即展开式中的高阶项 $\mathcal{O}(\eta^2 f^2(x))$ 变得显著此时 $x$ 的迭代不能保证降低 $f(x)$ 的值。例如 $\eta1.1$ 时$x$ 超出最优解 $x0$ 并逐渐发散show_trace(gd(1.1, f_grad), f)这正是原文档强调的学习率调节难题的雏形过大则发散、过小则停滞。后面会看到二阶方法、预处理与线搜索都是为了自动或半自动地解决这一难题。局部最小值非凸函数的陷阱为了演示非凸函数上的梯度下降考虑函数 $f(x) x \cdot \cos(cx)$其中 $c$ 为某常数。这个函数有无穷多个局部最小值根据选择的学习率与问题的条件状况最终可能只得到众多解中的一个。下面的例子说明不切实际的高学习率如何导致较差的局部最小值c d2l.tensor(0.15 * np.pi) def f(x): # 目标函数 return x * d2l.cos(c * x) def f_grad(x): # 目标函数的梯度 return d2l.cos(c * x) - c * x * d2l.sin(c * x) show_trace(gd(2, f_grad), f)当 $\eta 2$ 时由于该函数存在无数波峰波谷一阶迭代很容易越过极小值点最终收敛到一个较差的局部最小值而非全局最优点。这提示我们梯度下降只能保证下降到某个局部极小无法保证全局最优这也是深度学习中几乎所有优化问题都是非凸的背景下需要更精巧算法的原因之一。多元梯度下降现在考虑 $\mathbf{x} [x_1, x_2, \ldots, x_d]^\top$ 的情形即目标函数 $f: \mathbb{R}^d \to \mathbb{R}$ 将向量映射成标量。相应地梯度也是多元的它是一个由 $d$ 个偏导数组成的向量$$\nabla f(\mathbf{x}) \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_d}\bigg]^\top.$$梯度中的每个偏导数元素 $\partial f(\mathbf{x})/\partial x_i$ 代表了 $f$ 在 $\mathbf{x}$ 处关于输入 $x_i$ 的变化率。对多元函数使用相应的泰勒近似$$f(\mathbf{x} \boldsymbol{\epsilon}) f(\mathbf{x}) \mathbf{\boldsymbol{\epsilon}}^\top \nabla f(\mathbf{x}) \mathcal{O}(|\boldsymbol{\epsilon}|^2).$$换句话说在 $\boldsymbol{\epsilon}$ 的二阶项内最陡下降的方向由负梯度 $-\nabla f(\mathbf{x})$ 给出。选择合适的学习率 $\eta 0$便得到典型的梯度下降算法$$\mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f(\mathbf{x}).$$二维示例与轨迹可视化构造目标函数 $f(\mathbf{x})x_1^22x_2^2$梯度为 $\nabla f(\mathbf{x}) [2x_1, 4x_2]^\top$从初始位置 $[-5, -2]$ 出发观察 $\mathbf{x}$ 的轨迹。原文档提供了两个辅助函数train_2d接收自定义 trainer从x1-5, x2-2, s10, s20出发迭代 20 步s1、s2是预留的内部状态变量供后续动量、AdaGrad 等算法使用打印每一步结果并返回轨迹show_trace_2d在 $x_1 \in [-5.5, 1.0]$、$x_2 \in [-3.0, 1.0]$ 的网格上绘制等值线contour并用橙色折线标出迭代轨迹。这两个函数在仓库 d2l/torch.py 中有正式实现标注为#save会随d2l包导出并在 d2l/mxnet.py、d2l/tensorflow.py、d2l/paddle.py 中保持一致。完整演示代码如下def train_2d(trainer, steps20, f_gradNone): # 定制训练器优化 2D 目标函数 x1, x2, s1, s2 -5, -2, 0, 0 results [(x1, x2)] for i in range(steps): if f_grad: x1, x2, s1, s2 trainer(x1, x2, s1, s2, f_grad) else: x1, x2, s1, s2 trainer(x1, x2, s1, s2) results.append((x1, x2)) print(fepoch {i 1}, x1: {float(x1):f}, x2: {float(x2):f}) return results def show_trace_2d(f, results): # 显示优化过程中 2D 变量的轨迹 d2l.set_figsize() d2l.plt.plot(*zip(*results), -o, color#ff7f0e) x1, x2 d2l.meshgrid(d2l.arange(-5.5, 1.0, 0.1), d2l.arange(-3.0, 1.0, 0.1), indexingij) d2l.plt.contour(x1, x2, f(x1, x2), colors#1f77b4) d2l.plt.xlabel(x1) d2l.plt.ylabel(x2) def f_2d(x1, x2): # 目标函数 return x1 ** 2 2 * x2 ** 2 def f_2d_grad(x1, x2): # 目标函数的梯度 return (2 * x1, 4 * x2) def gd_2d(x1, x2, s1, s2, f_grad): g1, g2 f_grad(x1, x2) return (x1 - eta * g1, x2 - eta * g2, 0, 0) eta 0.1 show_trace_2d(f_2d, train_2d(gd_2d, f_gradf_2d_grad))学习率 $\eta 0.1$ 时经过 20 步$\mathbf{x}$ 的值接近其位于 $[0, 0]$ 的最小值。注意 $x_2$ 方向因系数 2 梯度更强收敛更快整体进展相当顺利但相当缓慢——这正是每个坐标共享同一个学习率带来的局限。自适应方法二阶信息与曲率正如上文所见选择恰到好处的学习率 $\eta$ 是很棘手的选得太小没有进展选得太大解会振荡甚至发散。那么能否自动确定 $\eta$或者完全不必选择学习率除了考虑目标函数的值和梯度还考虑其曲率的二阶方法可以帮我们解决这个问题。虽然由于计算代价的原因这些方法不能直接应用于深度学习但它们为设计高级优化算法提供了有用的直觉——后续的 AdaGrad、RMSProp、Adam 等算法正是模拟了其中许多理想特性。牛顿法回顾 $f: \mathbb{R}^d \rightarrow \mathbb{R}$ 的泰勒展开式可以写出二阶项$$f(\mathbf{x} \boldsymbol{\epsilon}) f(\mathbf{x}) \boldsymbol{\epsilon}^\top \nabla f(\mathbf{x}) \frac{1}{2} \boldsymbol{\epsilon}^\top \nabla^2 f(\mathbf{x}) \boldsymbol{\epsilon} \mathcal{O}(|\boldsymbol{\epsilon}|^3).$$定义 $\mathbf{H} \stackrel{\mathrm{def}}{} \nabla^2 f(\mathbf{x})$ 为 $f$ 的Hessian 矩阵是一个 $d \times d$ 矩阵。当 $d$ 很小且问题简单时 $\mathbf{H}$ 很容易计算但对于深度神经网络$\mathcal{O}(d^2)$ 个条目的存储代价很高通过反向传播计算也代价昂贵。先忽略这些考量看看能得到什么算法。由于 $f$ 的最小值满足 $\nabla f 0$对二阶泰勒展开关于 $\boldsymbol{\epsilon}$ 求导并忽略高阶项$$\nabla f(\mathbf{x}) \mathbf{H} \boldsymbol{\epsilon} 0 \text{ and hence } \boldsymbol{\epsilon} -\mathbf{H}^{-1} \nabla f(\mathbf{x}).$$也就是说作为优化问题的一部分需要对 Hessian 矩阵 $\mathbf{H}$ 求逆更新规则为$$\mathbf{x} \leftarrow \mathbf{x} - \eta \mathbf{H}^{-1} \nabla f(\mathbf{x}).$$理想情况对于 $f(x) \frac{1}{2} x^2$有 $\nabla f(x) x$ 和 $\mathbf{H} 1$因此对任何 $x$ 可得 $\epsilon -x$——单步即完美收敛无须任何调整。这里比较幸运泰勒展开式是精确的因为 $f(x\epsilon) \frac{1}{2} x^2 \epsilon x \frac{1}{2} \epsilon^2$。凸函数验证对凸双曲余弦函数 $f(x) \cosh(cx)$$c$ 为常数这里取 $c0.5$经过几次迭代即可到达 $x0$ 处的全局最小值c d2l.tensor(0.5) def f(x): # 目标函数 return d2l.cosh(c * x) def f_grad(x): # 目标函数的梯度 return c * d2l.sinh(c * x) def f_hess(x): # 目标函数的 Hessian return c**2 * d2l.cosh(c * x) def newton(eta1): x 10.0 results [x] for i in range(10): x - eta * f_grad(x) / f_hess(x) results.append(float(x)) print(epoch 10, x:, x) return results show_trace(newton(), f)非凸函数的致命缺陷考虑 $f(x) x \cos(c x)$。在牛顿法中我们最终要除以 Hessian这意味着如果二阶导数是负数更新方向可能指向 $f$ 值增大的方向c d2l.tensor(0.15 * np.pi) def f(x): # 目标函数 return x * d2l.cos(c * x) def f_grad(x): # 目标函数的梯度 return d2l.cos(c * x) - c * x * d2l.sin(c * x) def f_hess(x): # 目标函数的 Hessian return -2 * c * d2l.sin(c * x) - x * c**2 * d2l.cos(c * x) show_trace(newton(), f) # eta1结果惊人地错误如何修正一种方法是取 Hessian 的绝对值来修正符号问题另一种策略是重新引入学习率。这看似违背初衷但并不完全如此——拥有二阶信息可以使我们在曲率较大时保持谨慎缩小步长而在目标函数较平坦时采用较大的步长。例如将学习率降为 $\eta 0.5$show_trace(newton(0.5), f)此时算法变得相当高效。这直观地解释了为什么实践中会发展出带阻尼的牛顿法以及 AdaGrad / RMSProp 等按历史梯度规模自适应缩放步长的算法。收敛性分析对牛顿法的收敛速度原文档给出了严格的一维证明。设目标函数 $f$ 凸、三次可微且二阶导数不为零$f 0$多元证明是一维论证的直接推广因此省略。记 $x^{(k)}$ 为第 $k$ 次迭代的值$e^{(k)} \stackrel{\mathrm{def}}{} x^{(k)} - x^$ 为第 $k$ 次迭代与最优解的距离。由泰勒展开条件 $f(x^) 0$ 可写为$$0 f(x^{(k)} - e^{(k)}) f(x^{(k)}) - e^{(k)} f(x^{(k)}) \frac{1}{2} (e^{(k)})^2 f(\xi^{(k)}),$$对某个 $\xi^{(k)} \in [x^{(k)} - e^{(k)}, x^{(k)}]$ 成立。除以 $f(x^{(k)})$ 得到$$e^{(k)} - \frac{f(x^{(k)})}{f(x^{(k)})} \frac{1}{2} (e^{(k)})^2 \frac{f(\xi^{(k)})}{f(x^{(k)})}.$$代入更新方程 $x^{(k1)} x^{(k)} - f(x^{(k)}) / f(x^{(k)})$两边取绝对值$$\left|e^{(k1)}\right| \frac{1}{2}(e^{(k)})^2 \frac{\left|f(\xi^{(k)})\right|}{f(x^{(k)})}.$$因此只要处于 $\left|f(\xi^{(k)})\right| / (2f(x^{(k)})) \leq c$ 的有界区域就有二次递减误差即误差每步按平方收缩$$\left|e^{(k1)}\right| \leq c (e^{(k)})^2.$$优化研究者称这种收敛为线性收敛而把 $\left|e^{(k1)}\right| \leq \alpha \left|e^{(k)}\right|$$\alpha1$ 常数收缩称为恒定收敛速度。需要指出该分析的两点局限其一我们无法估计何时进入快速收敛区域只知道一旦接近极小值收敛将变得非常快其二分析要求 $f$ 在高阶导数上表现良好即确保 $f$ 在取值变化上没有超常特性。预处理Preconditioning计算和存储完整 Hessian 非常昂贵改善的方法之一是预处理只计算 Hessian 的对角线项得到更新$$\mathbf{x} \leftarrow \mathbf{x} - \eta \mathrm{diag}(\mathbf{H})^{-1} \nabla f(\mathbf{x}).$$虽然这不如完整的牛顿法精确但远比不使用要好。为什么有效假设一个变量以毫米表示高度、另一个变量以公里表示高度若二者的自然尺度都以米为单位参数化就出现严重的不匹配共享一个学习率的梯度下降会在不同坐标上表现悬殊。预处理可以消除这种尺度不匹配——梯度下降的有效预处理相当于为 $\mathbf{x}$ 的每个坐标选择各自不同的学习率。这一思想直接推动了后续随机梯度下降优化算法的创新例如 AdaGrad 与 RMSProp 按各坐标的历史梯度平方根缩放学习率本质上就是对坐标尺度差异的自适应预处理。梯度下降与线搜索梯度下降的关键问题之一是会冲过头overshoot或进展不足。简单的修复办法是结合线搜索使用 $\nabla f(\mathbf{x})$ 给出的方向然后做二分搜索确定使 $f(\mathbf{x} - \eta \nabla f(\mathbf{x}))$ 取最小值的学习率 $\eta$。该算法收敛迅速分析与证明可参考凸优化经典教材对应参考文献收录于仓库 d2l.bib 的Boyd.Vandenberghe.2004条目。然而对深度学习而言这不太可行线搜索的每一步都需要在整个数据集上评估目标函数代价过高。这解释了为什么深度学习实践中更常采用启发式的学习率调度见 学习率调度而非精确线搜索。小结学习率的大小很重要学习率太大会使模型发散学习率太小会没有进展。梯度下降可能陷入局部极小值而得不到全局最小值。在高维模型中调整学习率是很复杂的。预处理有助于调节各坐标的比例相当于逐坐标的学习率。牛顿法在凸问题中一旦开始正常工作速度就会快得多误差按平方收缩。对于非凸问题不要不作任何调整就使用牛顿法二阶导数为负时可能反向更新。练习与动手实验用不同的学习率和目标函数进行梯度下降实验观察收敛、振荡与发散的分界。在区间 $[a, b]$ 中实现线搜索以最小化凸函数二分搜索决定选择 $[a, (ab)/2]$ 还是 $[(ab)/2, b]$ 时是否需要导数该算法的收敛速度有多快实现该算法并应用于最小化 $\log (\exp(x) \exp(-2x -3))$。设计一个定义在 $\mathbb{R}^2$ 上的目标函数使梯度下降收敛极其缓慢。提示不同坐标采用不同的缩放方式。使用预处理实现牛顿法的轻量版本用对角 Hessian 作为预条件子使用它的绝对值而非实际可能有符号的值将上述方法应用于练习 3 中的问题。将上述算法应用于多个目标函数凸或非凸观察如果把坐标旋转 45 度收敛轨迹会发生什么变化。延伸阅读在本仓库中继续深入本文对应的章节源码与后续内容见 chapter_optimization/index.md其中梯度下降gd位于凸优化基础convexity之后、随机梯度下降 sgd.md 之前——SGD 正是以本文的梯度下降原理为基石展开的。微积分基础泰勒展开、梯度、Hessian 的定义参见 chapter_preliminaries/calculus.md。train_2d、show_trace_2d等工具函数的正式实现位于 d2l/torch.py并在 d2l/mxnet.py、d2l/tensorflow.py、d2l/paddle.py 中提供四框架一致的版本绘图工具set_figsize/plot见 d2l/torch.py。阅读本章其余小节可依次探索小批量随机梯度下降、动量法、AdaGrad、RMSProp、AdaDelta、Adam 与学习率调度它们都是本文梯度下降思想的直接延伸。赞分享人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载相关推荐Pandoc Markdown 读取器对 ij 类尖括号文本的解析行为详解附原生 AST 输出分析Pandoc Markdown 读取器对 ij 类尖括号文本的解析行为详解附原生 AST 输出分析 导读 本文围绕 Pandoc 仓库中的命令行回归测试用人工智能深度学习机器学习教程Lenis轻量级平滑滚动库实战指南Lenis轻量级平滑滚动库实战指南 Lenis 是一个只有几 KB 的平滑滚动库lenis 在拉丁语里就是平滑的意思。它和 Locomotive Sc人工智能深度学习机器学习教程GetQzonehistory 完整指南一键导出QQ空间全部历史说说成 ExcelGetQzonehistory 完整指南一键导出QQ空间全部历史说说成 Excel 时间一长你很少再打开QQ空间可当想做班级回忆册、翻一翻上学那会儿的旧说网页爬虫数据分析上一篇终极Node-RED配置指南5分钟搭建智能家居自动化系统下一篇React Router resolvePath 全解析路径解析 API、相对路径规则与源码调用链创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考