从梯度下降到线搜索:优化算法基础与工程实践全解析
1. 优化算法基础:从数学定义到工程直觉
在机器学习和数据科学的日常工作中,我们总在和各种“最优”打交道:训练一个模型,目标是找到一组参数,让损失函数最小;调整一个系统,目标是让某个性能指标最大。这背后,就是优化这门数学工具在发挥作用。简单来说,优化就是在一个给定的集合里,找到一个点,让某个函数值达到最小或最大。听起来很基础,但正是这个基础,支撑着从线性回归到深度神经网络,从资源调度到投资组合管理的无数应用。
为什么优化如此重要?因为现实世界的问题,尤其是数据驱动的问题,往往没有现成的解析解。你无法直接写出一个公式,说“最优参数就是这个”。我们只能从一个初始猜测出发,一步步迭代,让结果越来越好。这个过程,就是优化算法的核心。它像是一个在复杂地形中寻找最低点的向导,每一步都试图找到下降最快的方向,同时避免掉进坑里或者走得太慢。
本章我们将聚焦于无约束优化问题,也就是那个“地形”没有围墙,你可以自由探索任何方向。我们会从最基础的数学定义开始,逐步深入到梯度下降这个最经典的算法,并探讨如何通过“线搜索”这个技巧来智能地决定每一步该迈多大,最后分析这些方法在什么条件下能保证我们最终找到那个“最低点”。理解这些,不仅是理解现代优化器(如Adam、RMSProp)的基石,更是培养解决复杂问题直觉的关键。
2. 无约束优化问题的数学基石
在深入算法之前,我们必须先打好数学基础。优化问题不是凭空想象,它建立在严谨的数学定义之上。这些定义就像地图上的坐标和等高线,告诉我们地形是什么样子,哪里是山谷,哪里是山峰。
2.1 核心概念:下确界、最小值与局部最优
首先,我们需要区分几个关键概念:下确界和最小值。想象一下你面前有一条向下的斜坡,但坡底是一个你永远无法触及的深渊边缘。这个深渊边缘的“高度”,就是函数值的下确界(infimum),它是函数值能无限接近但可能永远达不到的那个最低点。而最小值(minimum)则是你实际能站上去的那个最低点,函数在这个点确实取到了那个值。
用数学语言来说,对于一个实值函数 F,在定义域 Ω 上的下确界inf_Ω F总是存在的(可能是负无穷)。而最小值min_Ω F则要求存在一个点 x* ∈ Ω,使得 F(x*) 等于这个下确界。在优化中,我们当然希望找到的是最小值点(最小化问题中的最小化点,argmin),而不仅仅是知道下确界是多少。
注意:一个函数可能有下确界但没有最小值。例如,函数 f(x) = 1/x 在 x>0 时,下确界是0,但永远取不到0。在算法设计中,我们通常假设最小值点是存在的,或者退而求其次,寻找能使得函数值足够接近下确界的点。
另一个重要的区分是全局最小化点和局部最小化点。全局最小化点是在整个定义域上“最低”的点。而局部最小化点则是在它周围的一个小邻域内“最低”的点。对于复杂的非凸函数(像崎岖的山地),局部最小化点可能有很多个,而全局最小化点通常只有一个。梯度下降这类算法,很容易陷入局部最小化点而无法自拔,这是优化中的一个核心挑战。
2.2 问题分类与凸性:为什么它如此重要
根据目标函数 F 和定义域 Ω 的性质,优化问题被分为几大类,而其中凸优化问题因其良好的性质,在理论和实践中都占据着中心地位。
- 无约束光滑优化:Ω 是整个空间,F 是连续可微(甚至更高阶可微)的。这是我们本章讨论的重点。
- 约束光滑优化:Ω 由一系列不等式定义(例如,x ≥ 0)。这引入了额外的复杂性,需要处理边界。
- 凸优化问题:这是“友好”地形。Ω 是一个凸集(其中任意两点的连线仍在集合内),且 F 是凸函数。凸函数有一个美妙的性质:其图像上任意两点的连线都在图像上方。用公式表示就是,对于任意 λ ∈ [0, 1],有
F((1-λ)x + λy) ≤ (1-λ)F(x) + λF(y)。
凸性带来的巨大好处:
- 局部最优即全局最优:在凸函数上,你找到的任何局部最低点,自动就是全局最低点。这彻底解决了陷入局部最优的困扰。对于梯度下降算法,这意味着只要算法收敛到一个稳定点(梯度为零的点),那它就是我们要找的最佳解。
- 下降方向全局有效:对于凸可微函数,梯度提供了一个全局的下降估计。具体来说,如果当前点在 x,梯度是 ∇F(x),那么对于任何其他点 y,都有
∇F(x)^T (y - x) ≤ F(y) - F(x)。这个不等式是分析算法收敛性的关键。 - 更强的收敛保证:对于凸函数,我们可以推导出算法收敛速度的明确上界,例如次线性收敛 O(1/t) 或线性收敛 O(ρ^t)。
在实际的机器学习中,很多经典模型(如线性回归、逻辑回归、支持向量机)的损失函数在特定条件下是凸的。这也是它们能被广泛应用和稳定求解的重要原因。当面对非凸问题(如深度学习)时,我们虽然失去了这些完美的理论保证,但凸优化中的许多思想和算法(如梯度下降、动量法)经过调整后,仍然是有效的实践工具。
2.3 最优性条件:如何判断“找到点了”?
我们如何知道算法迭代到的点 x* 是不是我们要找的最小值点呢?这需要一些判据。
- 一阶必要条件:如果 F 在 x* 处可微,且 x* 是一个局部最小化点,那么其梯度必须为零:
∇F(x*) = 0。满足这个条件的点称为驻点或临界点。注意,这只是一个必要条件。梯度为零的点也可能是局部极大值点或鞍点(在更高维度中常见)。 - 二阶充分条件:如果 F 在 x* 处二阶连续可微,梯度为零 (
∇F(x*) = 0),并且其海森矩阵∇²F(x*)是正定的(即所有特征值大于0),那么 x* 一定是一个严格的局部最小化点。直观理解,正定的海森矩阵意味着函数在 x* 附近像个“碗”,各个方向都是向上弯曲的。
对于凸函数,情况更简单:一阶条件变成了充要条件。如果 F 是可微凸函数,那么∇F(x*) = 0当且仅当 x* 是一个全局最小化点。这为我们提供了一个清晰而强大的终止判据:在凸优化中,当梯度范数足够小时,我们就可以很有信心地停止迭代。
3. 梯度下降法:核心思想与实现细节
梯度下降法是最直观、最基础的迭代优化算法。它的核心思想朴素而有力:要下山,就沿着当前最陡的方向走。
3.1 算法框架与下降方向
梯度下降的迭代公式可以统一写为:x_{t+1} = x_t + α_t * h_t其中:
x_t是第 t 步的迭代点。h_t是第 t 步的搜索方向。α_t > 0是第 t 步的步长(或学习率)。
要使算法有效,h_t必须是一个下降方向。数学上,这意味着存在一个小的正数 ε0,使得对于所有 0 < ε ≤ ε0,都有F(x_t + ε h_t) < F(x_t)。利用一阶泰勒展开F(x+εh) ≈ F(x) + ε ∇F(x)^T h,我们可以得出一个实用的判据:只要∇F(x)^T h < 0,h 就是一个下降方向。也就是说,搜索方向与梯度方向的夹角必须大于90度。
最自然的选择就是取负梯度方向:h_t = -∇F(x_t)。这被称为最速下降方向,因为它在所有单位范数 (|h|=1) 的方向中,能使函数值在当前位置的瞬时下降率 (∇F(x)^T h) 最小(即负得最多)。这里“最速”是相对于欧几里得范数而言的。
实操心得:虽然叫“最速下降”,但在实际的高维非二次型问题中,负梯度方向并不一定是长期来看最快的路径。它非常“短视”,只关注当前点的最陡方向,容易在山谷中产生锯齿形的震荡路径,导致收敛缓慢。这是最速下降法的主要缺点。
3.2 广义的“最速下降”与预条件
“最速”的定义依赖于我们如何衡量方向向量 h 的“大小”。如果我们采用另一种范数,比如||h||_A = sqrt(h^T A h),其中 A 是一个正定矩阵,那么在这个新范数下单位化的最速下降方向就变成了h_t = -A^{-1} ∇F(x_t)。
这个观察极具启发性:
- 如果取
A = I(单位矩阵),就是标准的梯度下降。 - 如果取
A = ∇²F(x_t)(海森矩阵),我们就得到了牛顿法的搜索方向。牛顿法利用了函数的二阶曲率信息,在局部可以看作是用一个二次函数来近似原函数,并直接跳到该二次函数的最小值点。它通常具有极快的收敛速度(二阶收敛),但需要计算和求逆海森矩阵,计算和存储开销巨大,不适用于高维问题。 - 在实践中,我们常常使用一个固定的、易于求逆的矩阵 A 来作为预条件子。一个好的预条件子 A 应该近似于海森矩阵,但结构更简单(如对角矩阵、分块对角矩阵)。这可以极大地改善梯度下降在病态问题(等高线是拉长的椭圆)上的收敛性能。例如,在训练神经网络时,优化器如 Adam 和 RMSProp 可以看作是在自适应地估计一个对角预条件子。
3.3 收敛性分析的初步洞察
收敛性分析回答一个根本问题:我们这样迭代下去,最终能接近最优解吗?能多快接近?
一个基础的结论来自于对函数光滑性的假设。我们常假设目标函数 F 是L-光滑的,即其梯度是 Lipschitz 连续的:存在常数 L > 0,使得对于任意 x, y,有||∇F(x) - ∇F(y)|| ≤ L ||x - y||。这个假设意味着函数的曲率变化不会太剧烈。
在 L-光滑 且采用负梯度方向 (h_t = -∇F(x_t)) 的假设下,如果我们固定步长 α_t ≡ α,并满足 α ≤ 1/L,那么可以证明每步迭代函数值一定下降:F(x_{t+1}) ≤ F(x_t) - (α/2) ||∇F(x_t)||^2
这个不等式是分析梯度下降收敛性的基石。它告诉我们两件事:
- 函数值序列
{F(x_t)}是单调不增的。 - 梯度范数的平方和是有限的:
∑_{t=1}^∞ ||∇F(x_t)||^2 < ∞。这必然推出梯度会趋向于零:lim_{t→∞} ||∇F(x_t)|| = 0。也就是说,算法最终会收敛到一个驻点。
对于凸函数,我们可以得到更强的关于函数值收敛速度的结论。假设函数最小值是 F*,初始点与最优点的距离不超过 R,那么在固定小步长下,有:F(x_t) - F* ≤ O(1/t)这是次线性收敛。虽然保证能收敛,但速度随着迭代次数增加而变慢。
如果函数不仅是凸的,还是m-强凸的(即存在 m>0,使得F(y) ≥ F(x) + ∇F(x)^T (y-x) + (m/2)||y-x||^2),那么梯度下降(配合合适的步长)可以实现更快的线性收敛(也称几何收敛):F(x_t) - F* ≤ O(ρ^t), 其中 ρ ∈ (0,1) 这意味着误差每步都以一个固定的比例衰减,这是非常理想的收敛速度。
注意事项:这些理论保证都是在理想条件下得出的。实际中的机器学习问题往往不满足强凸性,甚至不满足凸性。L-光滑常数 L 和强凸常数 m 也通常未知。理论的价值在于为我们理解算法行为、设计步长策略和调试代码提供了指导框架,而不是严格的性能保证书。
4. 步长的艺术:线搜索策略详解
步长 α_t 的选择是梯度下降法的灵魂。步长太大,可能会在峡谷两侧来回震荡,甚至发散;步长太小,则下山速度缓慢,收敛耗时极长。固定步长是一种选择,但通常不是最优的。线搜索是一种在每一步动态地、自适应地选择步长的技术。
线搜索的目标是:给定当前点 x_t 和下降方向 h_t,在射线{x_t + α h_t | α > 0}上寻找一个“好”的步长 α。这个“好”的标准需要平衡两点:充分下降(函数值下降足够多)和步长不能太小(以保证效率)。
4.1 精确线搜索与它的不实用性
最理想的情况是进行精确线搜索,即找到沿着该射线使函数值最小的步长:α_t^* = argmin_{α>0} F(x_t + α h_t)这能保证当前方向上最大的下降。然而,求解这个一维优化子问题本身就需要多次计算函数值 F,甚至可能需要计算其导数,计算代价非常高昂。在机器学习中,每次计算 F 都可能意味着一次完整的数据集遍历(对于经验风险),因此精确线搜索在实践中很少使用。
4.2 非精确线搜索与 Wolfe 条件
我们退而求其次,采用非精确线搜索,它不要求找到精确最小值,只要求步长满足一些能保证全局收敛且效率不错的条件。最著名的是Wolfe 条件,它包含两条:
充分下降条件 (Armijo 规则):
F(x_t + α h_t) ≤ F(x_t) + c_1 α ∇F(x_t)^T h_t其中c_1 ∈ (0, 1),通常很小(如 0.0001)。不等式右边是函数在当前位置的线性近似。这个条件要求实际下降至少是线性预测下降的c_1倍。它确保了步长不能太大(避免越过最低点导致上升)。曲率条件:
|∇F(x_t + α h_t)^T h_t| ≤ c_2 |∇F(x_t)^T h_t|其中c_2 ∈ (c_1, 1),例如 0.9。这个条件要求在新点的方向导数(即斜率)的绝对值不能太大。它确保了步长不能太小。如果步长太小,新点的斜率∇F(x_t + α h_t)^T h_t会接近初始斜率∇F(x_t)^T h_t(一个负数),这意味着我们还有很大的下降空间,应该尝试更大的步长。曲率条件排除了这种过小步长。
同时满足充分下降条件和曲率条件的步长,被称为满足强 Wolfe 条件。如果只要求∇F(x_t + α h_t)^T h_t ≥ c_2 ∇F(x_t)^T h_t(即新点斜率不能太负),则称为弱 Wolfe 条件。弱 Wolfe 条件允许新点斜率为正,这在某些情况下可以提供更大的可接受步长区间。
4.3 实现线搜索:回溯法
在实践中,如何高效地找到一个满足(强)Wolfe 条件的步长呢?最常用的是回溯法,它主要关注满足充分下降条件 (Armijo 规则),而通常忽略或简化曲率条件。
回溯法的流程非常直观:
- 选择初始步长
α_init(例如,可以使用上一步的步长,或一个固定值如1.0),收缩因子ρ ∈ (0, 1)(例如 0.5),以及系数c_1。 - 令
α = α_init。 - 循环:检查 Armijo 条件
F(x_t + α h_t) ≤ F(x_t) + c_1 α ∇F(x_t)^T h_t是否成立。- 如果成立,则接受步长 α,退出循环。
- 如果不成立,则令
α = ρ * α(缩小步长),然后返回步骤3继续检查。
回溯法一定会终止,因为当 α 足够小时,一阶泰勒展开会越来越精确,Armijo 条件必然满足。回溯法计算量小(通常只需几次函数评估),且实现简单,因此被广泛集成在各类优化库中(如 SciPy 的line_search函数)。
对于要求更高的场景,需要实现同时满足 Wolfe 条件的线搜索。这通常采用一种“划界-缩小区间”的策略(如上一节正文中 Proposition 3.24 描述的算法):
- 先找到一个包含可接受步长的区间
[α_lo, α_hi],其中α_lo满足充分下降条件但可能不满足曲率条件(步长偏小),α_hi可能不满足充分下降条件(步长偏大)。 - 通过不断二分或插值这个区间,最终找到一个同时满足两个条件的步长。
4.4 线搜索的工程实践与调参
在实际编码中,线搜索有几个需要仔细处理的细节:
- 初始步长选择:一个好的初始猜测能减少回溯次数。一个经典启发式方法是Barzilai-Borwein (BB) 步长。它利用上一步的信息:
α_init = (s_{t-1}^T y_{t-1}) / (y_{t-1}^T y_{t-1}),其中s_{t-1} = x_t - x_{t-1},y_{t-1} = ∇F(x_t) - ∇F(x_{t-1})。这个步长在梯度变化平缓时效果很好。 - 参数
c_1和c_2的选择:c_1通常取得非常小(如 1e-4),以确保条件不太苛刻,允许接受较大的步长。c_2对于弱 Wolfe 条件通常取 0.9,对于强 Wolfe 条件取 0.1 到 0.5 之间。c_2越小,要求的曲率条件越宽松,可接受步长区间越大。 - 最大迭代与最小步长:必须设置最大回溯次数和最小允许步长
α_min,以防止在数值误差或函数异常情况下陷入无限循环。 - 与动量法的结合:在现代优化器(如带动量的 SGD、Adam)中,搜索方向
h_t不再是纯负梯度,而是包含了历史梯度信息的动量方向。线搜索的逻辑不变,但应用于这个新的方向。
实操心得:对于大规模的机器学习问题,尤其是使用随机梯度下降时,进行精确的线搜索通常得不偿失,因为函数评估代价太高。更常见的做法是使用一个衰减的学习率计划表,例如
α_t = α_0 / (1 + γ t)或α_t = α_0 * β^t。这可以看作是一种极其简化的线搜索,它牺牲了每步的最优性,换取了整体计算效率。回溯线搜索更多地用于确定性优化、小型问题或作为基准算法。
5. 收敛性理论深度剖析
理解了算法和步长选择后,我们自然要问:这套方法到底行不行?理论上有什么保证?收敛性分析为我们提供了在不同假设下,关于算法表现的确切数学描述。
5.1 基本收敛定理:梯度为何会趋于零?
我们从最一般的假设开始。假设目标函数 F 是下有界的(有一个最小值,不会无限下降),并且是L-光滑的。采用梯度下降法x_{t+1} = x_t - α_t ∇F(x_t),并假设步长满足0 < α_min ≤ α_t ≤ α_max < 2/L。
根据之前推导的关键不等式F(x_{t+1}) ≤ F(x_t) - (α_t/2) ||∇F(x_t)||^2,我们对 t 从 1 到 T 求和:∑_{t=1}^{T} (α_t/2) ||∇F(x_t)||^2 ≤ F(x_1) - F(x_T) ≤ F(x_1) - F*其中 F* 是函数下确界。由于左边是求和,右边是一个有限常数,我们可以立即得出:lim_{t→∞} ||∇F(x_t)|| = 0也就是说,梯度最终会消失。这是梯度下降法最基础的收敛保证——它最终会停在一个驻点(临界点)附近。
更进一步,我们可以估计收敛速度。由求和不等式可得:min_{1≤t≤T} ||∇F(x_t)||^2 ≤ [2(F(x_1)-F*)] / [T * α_min]这意味着,梯度范数平方的最小值以 O(1/T) 的速度衰减。换句话说,要找到一个梯度范数小于 ε 的点,我们大概需要 O(1/ε^2) 次迭代。对于一阶方法来说,这是典型的标准速率。
5.2 凸函数下的收敛速率
如果加上函数 F 是凸的假设,我们可以得到关于函数值F(x_t) - F*的收敛速率,这比梯度范数更能直接反映解的质量。
在凸且 L-光滑 的假设下,采用固定步长α_t = 1/L的梯度下降法满足:F(x_t) - F* ≤ (L ||x_1 - x*||^2) / (2t)这是一个O(1/t)的次线性收敛速率。其中x*是某个最优解。这个结果告诉我们,即使函数性质很好(凸且光滑),梯度下降的收敛速度也不会很快,它需要很多步迭代才能达到高精度。
这个证明巧妙地利用了凸函数的一阶性质F(x*) ≥ F(x_t) + ∇F(x_t)^T (x* - x_t)和迭代更新公式。它揭示了梯度下降在凸设置下的基本极限。
5.3 强凸函数下的线性收敛
当函数性质更好时,算法表现会大幅提升。如果 F 是m-强凸且L-光滑的(满足mI ≼ ∇²F(x) ≼ LI),那么梯度下降法(步长α = 2/(m+L)或通过线搜索)可以实现线性收敛(也称几何收敛或指数收敛):F(x_t) - F* ≤ C * ρ^t其中常数C > 0,收敛因子ρ = 1 - m/L(对于固定步长)或ρ < 1。
线性收敛意味着误差每步都按固定比例缩小。κ = L/m被称为问题的条件数。条件数越大(等高线越扁长),收敛因子 ρ 越接近 1,收敛越慢;条件数越接近 1(等高线越接近圆形),收敛越快。这直观地解释了为什么病态问题对梯度下降不友好。
注意事项:强凸性在机器学习中是一个很强的假设。许多带 L2 正则化的损失函数是强凸的,但纯粹的损失函数(如逻辑损失)通常只是凸的,而不是强凸的。深度学习中的损失函数几乎都是非凸的。因此,线性收敛的理论结果虽然美好,但适用范围有限。
5.4 非凸环境下的收敛与挑战
在非凸优化中(如神经网络训练),我们失去了全局最优的保证,甚至失去了凸函数下“驻点可能是好解”的保证。此时,梯度下降法的目标是收敛到一个一阶临界点(梯度为零的点)或二阶临界点(梯度为零且海森矩阵半正定的点)。
对于非凸但 L-光滑 的函数,梯度下降法仍然保证lim ||∇F(x_t)|| = 0,且收敛速率是O(1/√T)(即找到梯度范数小于 ε 的点需要 O(1/ε^2) 次迭代)。这是非凸优化的一阶方法的标准复杂度。
然而,非凸函数有大量的鞍点和局部极大值点。梯度为零的点可能是:
- 局部极小点:这是我们希望的。
- 局部极大点:概率较低,因为我们在下降。
- 鞍点:这是高维非凸问题中的主要“陷阱”。在鞍点处,某些方向是上升的,某些方向是下降的。纯梯度下降可能会在鞍点附近停滞很长时间,因为梯度很小。
为了逃离鞍点,需要更高级的技术,如:
- 加入噪声:像随机梯度下降(SGD)本身固有的噪声就有助于逃离较浅的鞍点。
- 使用动量:动量法可以帮助平滑梯度方向,积累动能冲过鞍点的平坦区域。
- 二阶信息:使用拟牛顿法或自适应方法,它们隐式地包含了曲率信息,能更好地区分最小点和鞍点。
6. 常见问题、调试技巧与实战建议
理论是美好的,但现实是骨感的。在实际实现和应用梯度下降与线搜索时,你会遇到各种各样的问题。下面是一些常见陷阱和应对策略。
6.1 数值问题与不稳定现象
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
梯度爆炸:损失或梯度值变成NaN或Inf。 | 1. 步长过大,导致更新后参数进入函数未定义或数值不稳定的区域。 2. 网络层数太深,梯度在反向传播时连乘导致指数级增长。 3. 输入数据未标准化,特征尺度差异巨大。 | 1.大幅减小学习率,这是首要尝试。 2. 使用梯度裁剪:设定一个阈值,如果梯度范数超过它,就将梯度按比例缩小。`g = g * min(1, threshold / |
| 梯度消失:训练早期,损失几乎不下降,梯度值非常小。 | 1. 步长过小,更新微不足道。 2. 使用了某些激活函数(如 sigmoid, tanh),其导数在饱和区接近于零,导致反向传播的梯度越来越小。 3. 网络初始化不当,权重太小。 | 1.增大学习率或使用自适应学习率算法(如 Adam)。 2. 更换激活函数,如使用ReLU及其变体(Leaky ReLU, PReLU)来缓解梯度消失。 3. 使用恰当的初始化,如 He 初始化(配合 ReLU)或 Xavier 初始化。 |
| 损失震荡剧烈,不收敛。 | 1. 学习率太大,在最优解附近来回跳跃。 2. 批大小太小,随机梯度的噪声过大。 3. 目标函数本身不平滑,存在很多局部尖峰。 | 1.降低学习率,这是最直接有效的方法。 2.增大批大小,可以降低梯度估计的方差,使更新方向更稳定。 3. 使用动量(Momentum)。动量项可以平滑更新方向,抑制震荡。 v_t = β v_{t-1} + (1-β)g_t。 |
| 线搜索失败,无法找到满足条件的步长。 | 1. 搜索方向h_t可能不是下降方向(由于数值误差或梯度计算错误)。2. 初始步长 α_init设置过大,导致第一次函数评估就不满足 Armijo 条件,回溯后步长缩水过快。3. 函数值或梯度计算存在数值误差,导致 Wolfe 条件判断失效。 | 1. 在开始线搜索前,检查下降条件:∇F(x)^T h < -tol(tol 是一个小正数)。如果不满足,将 h 重置为负梯度方向。2. 实现一个稳健的初始步长猜测策略。例如,如果上一步的步长 α_{t-1} 被接受,则令 α_init = min(1.01 * α_{t-1}, α_max);如果上一步回溯了,则令α_init = α_{t-1}(即使用上一步最终接受的步长)。3. 在 Wolfe 条件判断中引入宽松的公差,以应对数值误差。 |
6.2 算法选择与超参数调优指南
没有放之四海而皆准的“最佳”优化器。选择取决于问题规模、数据特性、计算资源和你对超参数调优的耐心。
- 标准梯度下降 / 批量梯度下降:每次迭代使用全部数据计算梯度。优点是方向准确,理论性质好。缺点是数据量大时计算一次梯度的代价无法承受,且对于非凸问题容易陷入局部最优。适用于小型数据集或凸问题。
- 随机梯度下降:每次迭代随机抽取一个样本计算梯度。优点是计算快,可以频繁更新,固有的噪声有助于逃离局部最优和鞍点。缺点是方向方差大,收敛路径震荡,需要精心设计学习率衰减计划。深度学习的事实标准基础,但通常需要配合其他技术。
- 小批量梯度下降:折中方案,每次使用一个小批量(如 32, 64, 128)的数据计算梯度。兼顾了计算效率和方向稳定性。是当前最主流的实践选择。
- 带动量的 SGD:在 SGD 基础上引入动量项,积累之前的梯度方向作为惯性。可以加速在相关方向上的移动,抑制震荡,帮助穿越狭窄的山谷和平坦的鞍点区域。几乎总是比朴素 SGD 好。
- 自适应学习率算法:如AdaGrad, RMSProp, Adam。它们为每个参数维护一个独立的学习率,根据历史梯度幅值进行调整。对于稀疏数据或特征尺度差异大的问题表现很好。Adam结合了动量和自适应学习率,通常作为默认的优化器选择,因为它对初始学习率不那么敏感,且在很多问题上能快速收敛。
学习率调优实战:
- 粗略搜索:在
[1e-5, 1e-1]的对数尺度上进行尝试。对于 Adam,常用初始值是 3e-4 或 1e-3。 - 学习率预热:训练初期使用较小的学习率,逐步增大到一个基准值,有助于稳定训练初期。
- 周期性学习率:如余弦退火,让学习率周期性地在较大值和较小值之间变化,有助于模型跳出局部最优。
- 监控工具:绘制损失-迭代曲线和学习率-迭代曲线。如果损失下降缓慢,可能是学习率太小;如果损失剧烈震荡或上升,可能是学习率太大。理想情况是损失平滑、稳定地下降。
6.3 收敛性诊断与停止准则
你如何知道训练可以停止了?这里没有绝对正确的答案,只有一些经验准则。
- 基于梯度的准则:
||∇F(x_t)|| < ε_g。这是最理论正确的停止条件,但计算完整数据集的梯度代价高,且阈值 ε_g 难以设定。 - 基于函数值变化的准则:
|F(x_t) - F(x_{t-1})| < ε_f或|F(x_t) - F(x_{t-1})| / |F(x_{t-1})| < ε_f_rel。更实用,但可能在小平台区过早停止。 - 基于参数变化的准则:
||x_t - x_{t-1}|| < ε_x。当参数更新非常微小时停止。 - 基于验证集性能的早停:这是机器学习中最重要、最常用的准则。在训练集上优化损失函数,但每隔一段时间在独立的验证集上评估性能(如准确率、AUC)。当验证集性能在连续 N 个周期(patience)内不再提升时,就停止训练。这能有效防止过拟合。
- 最大迭代次数/周期数:设置一个绝对的上限,防止无限循环。
我个人在实际项目中,通常会结合使用验证集早停和最大周期数。同时,在训练日志中监控训练损失和验证损失曲线。一个健康的训练过程表现为:训练损失持续下降,验证损失先下降后趋于平稳或开始缓慢上升(出现过拟合迹象)。当验证损失连续多个周期不再下降时,就是停止训练、保存模型的最佳时机。
优化算法的世界远不止于此。从经典的共轭梯度法、拟牛顿法(如 L-BFGS),到现代深度学习中琳琅满目的自适应优化器,其核心思想都是在梯度下降这个基本框架上,通过更聪明地利用历史信息(动量)、估计曲率信息(二阶方法)或自适应调整每个参数的学习率,来克服基础梯度下降的缺陷,从而在复杂、高维、非凸的景观中找到更好的路径。理解本章这些关于方向、步长和收敛的基础,是驾驭所有这些高级算法的前提。当你下次在代码中设置optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)时,希望你能对背后发生的数学和工程权衡,有更深的体会和掌控力。
