让我们将理论付诸实践。在回顾了随机梯度下降 (SGD)、动量 (Momentum) 和 Nesterov 加速梯度 (NAG) 的机制后,巩固理解的最佳方式是实际观察它们的行为。在本实践部分,我们将建立一个简单的优化问题,并使用可视化方法来分析这些算法如何遍历损失曲面并接近最小值。我们的目标不仅是运行代码,还要解读结果,将观察到的收敛模式与本章前面讨论的理论观点联系起来。设立受控实验为了有效比较优化器,我们需要一个统一的测试环境。让我们考虑一个标准问题,例如最小化一个简单的二次函数,或者在一个小型(可能是合成)数据集上训练逻辑回归模型。重点在于一致性:目标函数: 对所有优化器使用完全相同的损失函数。数据: 每次运行使用完全相同的数据集(如果适用,包括批处理策略)。初始化: 从完全相同的初始参数值 ($w_0$) 开始所有优化器。迭代次数: 让每个优化器运行相同数量的迭代或周期。我们将跟踪每个优化器在每次迭代(或处理每个批次后)的损失值。将这些损失值与迭代次数绘制在一起,可以得到收敛曲线。让我们考虑优化一个简单的二次函数,例如 $f(w_1, w_2) = (w_1 - 5)^2 + (w_2 - 3)^2$。尽管它是凸函数且简单,但它有助于说明基本的动态特性。我们将从 $(w_1, w_2) = (0, 0)$ 这样的点开始所有优化器。实现核心逻辑假设我们有函数可以计算所选问题的损失及其梯度 $\nabla f(w)$。我们将比较的核心更新规则是:1. SGD(随机梯度下降): $$w_{t+1} = w_t - \eta \nabla f(w_t)$$ $\eta$ 为学习率。2. 动量: $$v_{t+1} = \beta v_t + \eta \nabla f(w_t)$$ $$w_{t+1} = w_t - v_{t+1}$$ $\beta$ 为动量系数(例如 0.9),且 $v_0 = 0$。3. Nesterov 加速梯度 (NAG): $$v_{t+1} = \beta v_t + \eta \nabla f(w_t - \beta v_t)$$ $$w_{t+1} = w_t - v_{t+1}$$ 梯度是在“前瞻”位置 $w_t - \beta v_t$ 计算的。(注意:实际实现通常使用略有不同但等价的公式,特别是对于 NAG。我们将关注差异。)可视化与分析收敛曲线在每个优化器运行固定数量的迭代(例如 100 次)后,我们绘制了每次迭代的损失。{"layout": {"title": "优化器收敛比较(二次函数)", "xaxis": {"title": "迭代次数"}, "yaxis": {"title": "损失(对数尺度)", "type": "log"}, "legend": {"title": "优化器"}, "template": "plotly_white", "width": 700, "height": 450}, "data": [{"type": "scatter", "mode": "lines", "name": "SGD (lr=0.1)", "x": [0, 1, 2, 3, 4, 5, 10, 20, 30, 50, 70, 100], "y": [34.0, 21.76, 13.93, 8.91, 5.70, 3.65, 0.56, 0.014, 0.0003, 0.0, 0.0, 0.0], "line": {"color": "#4dabf7"}}, {"type": "scatter", "mode": "lines", "name": "动量 (lr=0.1, beta=0.9)", "x": [0, 1, 2, 3, 4, 5, 10, 20, 30, 50, 70, 100], "y": [34.0, 22.1, 9.28, 3.03, 0.92, 0.30, 0.006, 0.0, 0.0, 0.0, 0.0, 0.0], "line": {"color": "#f06595"}}, {"type": "scatter", "mode": "lines", "name": "NAG (lr=0.1, beta=0.9)", "x": [0, 1, 2, 3, 4, 5, 10, 20, 30, 50, 70, 100], "y": [34.0, 15.64, 4.38, 1.02, 0.23, 0.05, 0.0002, 0.0, 0.0, 0.0, 0.0, 0.0], "line": {"color": "#51cf66"}}]}SGD、动量和 NAG 在简单二次目标上的收敛曲线,损失值采用对数尺度绘制。请注意动量算法,特别是 NAG 实现的更快下降。分析:SGD: 观察损失的稳定下降。在这个简单的凸问题上,它可靠地趋向最小值。它的速率可能比其他方法慢。动量: 注意损失通常比 SGD 下降得更快,尤其是在初始步骤之后。动量项有助于加速在一致下降方向上的移动。有时可能会出现轻微的过冲或振荡,但通常它会更快达到较低的损失值。NAG: 通常,NAG 显示出最快的初始收敛速度。前瞻梯度计算使其能够预测变化并更有效地调整其轨迹,通常能更快收敛,并且可能比标准动量算法更好地抑制振荡。学习率的影响学习率 ($\eta$) 是一个重要的超参数。让我们可视化它对单个优化器(如 SGD)的影响。{"layout": {"title": "学习率对 SGD 收敛的影响", "xaxis": {"title": "迭代次数"}, "yaxis": {"title": "损失(对数尺度)", "type": "log"}, "legend": {"title": "学习率"}, "template": "plotly_white", "width": 700, "height": 450}, "data": [{"type": "scatter", "mode": "lines", "name": "lr=0.01 (过低)", "x": [0, 10, 20, 30, 50, 70, 100], "y": [34.0, 22.2, 14.6, 9.6, 4.1, 1.7, 0.5], "line": {"color": "#ffc078"}}, {"type": "scatter", "mode": "lines", "name": "lr=0.1 (适中)", "x": [0, 10, 20, 30, 50, 70, 100], "y": [34.0, 0.56, 0.014, 0.0003, 0.0, 0.0, 0.0], "line": {"color": "#4dabf7"}}, {"type": "scatter", "mode": "lines", "name": "lr=0.5 (过高)", "x": [0, 1, 2, 3, 4, 5], "y": [34.0, 8.5, 53.1, 332.0, 2075.0, 12969.0], "line": {"color": "#ff8787"}}]}比较不同学习率下的 SGD 收敛情况。学习率过低导致进展缓慢,过高导致发散,而合适的学习率则能实现快速收敛。分析:过低 ($\eta=0.01$): 收敛非常缓慢。所采取的步长太小,无法在给定迭代次数内有效达到最小值。适中 ($\eta=0.1$): 损失迅速下降并有效达到最小值。过高 ($\eta=0.5$): 优化器发散。步长过大,跳过最小值并导致损失急剧增加。这强调了收敛对学习率的敏感性。可以运行类似的实验,改变动量和 NAG 的动量系数 ($\beta$),以观察它如何影响平滑和加速效果。考察非凸场景“虽然二次函数的例子具有指导意义,但机器学习问题,尤其是在深度学习中,涉及高度非凸的损失曲面。在非凸函数上运行这些优化器(即使是像 Rastrigin 函数这样简单的二维函数)可以展现不同的行为:”局部最小值: 所有这些一阶方法都可能陷入局部最小值。最终收敛的损失可能因初始化和优化器的路径而异。鞍点: 与普通 SGD 相比,动量和 NAG 在逃离鞍点方面可能略胜一筹,因为动量项可以帮助它们穿过平坦区域。然而,它们并非完全不受影响。平台: 在平台(梯度非常小但不为零的区域)上的表现也可能有所不同。动量可能有助于比 SGD 更快地通过平台。分析这些情况下的收敛图通常会显示快速下降后出现停滞(局部最小值或平台)或更不稳定的行为。分析要点在分析基础优化器的收敛图时:下降速度: 观察损失曲线的斜率(尤其是在对数尺度上)。更陡的斜率表示更快的收敛。比较不同优化器降低损失的速度。稳定性: 损失是单调下降还是振荡?振荡可能表明学习率略高或存在复杂的相互影响。剧烈振荡或损失增加表明发散。最终损失值: 损失似乎在哪里趋于平稳?对于凸问题,这应该接近全局最小值。对于非凸问题,比较不同优化器或不同运行所达到的最终损失值——它们可能收敛到不同的点。超参数的影响: 系统地改变学习率和动量系数,以理解它们对收敛速度和稳定性的影响。这些实际观察构成了理解更高级优化技术发展原因的基础。在这里看到的局限性,例如对学习率的敏感性、在某些损失曲面上的收敛缓慢以及非凸性问题,促使了自适应学习率方法(第三章)和二阶方法(第二章)的出现,我们将在后面考察这些内容。