优化¶
优化是模型训练的数学核心——寻找使损失函数最小的参数。本文涵盖驻点、凸性、梯度下降、牛顿法、带拉格朗日乘数的约束优化,以及驱动现代深度学习的主流优化器(SGD、Adam)。
-
训练神经网络、拟合回归线、调优超参数:几乎所有机器学习算法的核心都是一个优化问题。
-
我们有一个函数(损失函数、代价函数、目标函数),希望找到使其尽可能小(或大)的输入。
-
在优化之前,我们需要理解函数的零点(或根)。\(f(x)\) 的零点是指满足 \(f(x) = 0\) 的 \(x\) 值。从图形上看,这些点就是与 x 轴的交点。
-
例如,\(f(x) = x^2 - 3x + 2 = (x-1)(x-2)\) 的零点在 \(x = 1\) 和 \(x = 2\) 处。在两个零点之间,函数为负(\(f(1.5) = -0.25\));在零点之外,函数为正。零点将数轴分割成若干个区域,在每个区域中函数保持相同符号。
-
零点的重数是指对应因式出现的次数。
-
在单零点(重数为 1)处,图像穿过 x 轴。在二重零点(重数为 2)处,图像接触 x 轴但反弹回去而不穿过,在该点处看起来是"平坦"的。
-
寻找零点之所以重要,是因为导数 \(f'(x)\) 的零点正是 \(f(x)\) 的驻点——即极大值或极小值的候选点。
-
在极大值或极小值处,切线是水平的(斜率为 0),因此 \(f'(x) = 0\)。
-
但并非每个驻点都是极大值或极小值。\(f'(x) = 0\) 的点也可能是拐点(如 \(f(x) = x^3\) 在 \(x = 0\) 处),函数在该点暂时变平但并未改变方向。
-
二阶导数检验可以解决这个问题。在驻点 \(x = c\)(即 \(f'(c) = 0\))处:
- 若 \(f''(c) > 0\):曲线向下凸(碗状),因此 \(c\) 是局部极小值。
- 若 \(f''(c) < 0\):曲线向上凸(山丘状),因此 \(c\) 是局部极大值。
- 若 \(f''(c) = 0\):检验无效,需要使用更高阶导数或其他方法。
-
例如,\(f(x) = x^3 - 3x\)。导数为 \(f'(x) = 3x^2 - 3 = 3(x-1)(x+1)\),因此驻点在 \(x = -1\) 和 \(x = 1\) 处。二阶导数为 \(f''(x) = 6x\)。在 \(x = -1\) 处:\(f''(-1) = -6 < 0\)(局部极大值)。在 \(x = 1\) 处:\(f''(1) = 6 > 0\)(局部极小值)。
-
如果连接函数图像上任意两点的线段位于图像之上(或与之重合),则该函数是凸的。可以想象成一个碗形,处处向上弯曲。数学上,若对所有 \(x\) 有 \(f''(x) \geq 0\),则 \(f\) 是凸函数。
-
凸性的强大之处在于凸函数有一个卓越的性质:每个局部极小值同时也是全局最小值。不存在会让人陷入的欺骗性局部低谷。如果你把一个球滚入凸碗中,它总是会到达底部。
-
若 \(-f\) 是凸的,则函数是凹的(向下弯曲)。函数从凹性过渡到凸性的点称为拐点,出现在 \(f''(x) = 0\) 处。
-
牛顿法利用切线寻找函数的零点(进而也可用于寻找其导数的驻点)。从初始猜测 \(x_0\) 出发,迭代更新:
-
其思想是:在 \(x_n\) 处画出切线,找到它与 x 轴的交点,该交点即为 \(x_{n+1}\)。对于性质良好且初始点选取恰当的函数,牛顿法收敛非常快(二次收敛,即每步正确位数大致翻倍)。
-
例如,求 \(\sqrt{5}\)(即 \(f(x) = x^2 - 5\) 的零点):\(f'(x) = 2x\),因此 \(x_{n+1} = x_n - \frac{x_n^2 - 5}{2x_n}\)。从 \(x_0 = 2\) 开始:\(x_1 = 2.25\),\(x_2 = 2.2361\ldots\),已精确到小数点后四位。
-
如果初始猜测离根太远、根附近 \(f'(x) = 0\),或函数在附近有拐点,牛顿法可能会失败。此外,它还需要计算导数,这可能代价高昂。
-
对于优化(寻找极小值而非零点),我们将牛顿法应用于 \(f'(x) = 0\),得到更新公式:
-
在多维情形下,这变为 \(\mathbf{x}_{n+1} = \mathbf{x}_n - H^{-1} \nabla f(\mathbf{x}_n)\),其中 \(H\) 是 Hessian 矩阵。这正是上一节中二阶泰勒近似的实际应用:将函数近似为二次型,跳到该二次型的极小值点,然后重复。
-
拉格朗日乘数用于求解约束优化:在约束条件 \(g(x, y) = c\) 下求 \(f(x, y)\) 的最优值。我们不是在 \(\mathbb{R}^n\) 中全域搜索,而是限制在约束条件成立的集合(一条曲线或曲面)上。
-
关键见解是几何层面的:在约束最优解处,\(f\) 的梯度必须与 \(g\) 的梯度平行。如果它们不平行,我们可以沿着约束条件朝某个方向移动,从而继续改进 \(f\) 的值,这意味着还没有达到最优。
-
我们引入一个新变量 \(\lambda\)(拉格朗日乘数),定义拉格朗日函数:
- 令所有偏导数为零,得到一个方程组,其解即为约束最优解:
- 例如,在 \(x^2 + y^2 = 1\) 的约束下最大化 \(f(x,y) = x^2 y\)。拉格朗日函数为 \(\mathcal{L} = x^2 y - \lambda(x^2 + y^2 - 1)\)。求偏导:
-
由第一个方程(假设 \(x \neq 0\)):\(\lambda = y\)。代入第二个方程:\(x^2 = 2y^2\)。结合约束条件:\(2y^2 + y^2 = 1\),得 \(y = \frac{1}{\sqrt{3}}\)。最大值为 \(f = \frac{2}{3\sqrt{3}}\)。
-
对于不等式约束(\(g(x,y) \leq c\) 而非 \(= c\)),Karush-Kuhn-Tucker(KKT)条件推广了拉格朗日乘数法。约束要么是激活的(有效约束,按等式处理),要么是非激活的(解在内部,约束无关紧要)。
-
在实践中,我们很少手工进行优化。以下是主要的算法家族:
-
一阶方法(仅使用梯度):梯度下降、随机梯度下降(SGD)、Adam。这些方法每步计算成本低,但收敛可能较慢,尤其是在病态问题上。
-
二阶方法(使用梯度和 Hessian 矩阵):牛顿法收敛快,但计算和求逆 Hessian 矩阵代价高昂(对于 \(n\) 个参数为 \(O(n^3)\))。拟牛顿法(如 BFGS 和 L-BFGS)仅利用梯度信息近似 Hessian 矩阵,比一阶方法收敛更快,又无需承担完全的二阶方法计算成本。
-
共轭梯度法:适用于大型稀疏系统,仅需矩阵-向量乘积,无需存储完整的 Hessian 矩阵。
-
高斯-牛顿法和莱文贝格-马夸尔特法:专门用于最小二乘问题(在回归中常见),通过 Jacobian 矩阵近似 Hessian 矩阵。
-
自然梯度下降:利用 Fisher 信息矩阵考虑参数空间的几何结构,对概率模型可能更有效。
-
-
优化器的选择取决于具体问题。对于深度学习,一阶方法(尤其是 Adam)占主导地位,因为参数量巨大(数百万到数十亿),计算 Hessian 矩阵不切实际。对于目标函数光滑的小规模问题,二阶方法可能快得多。
编程练习(在 CoLab 或 notebook 中完成)¶
-
实现牛顿法求 \(\sqrt{7}\)(即 \(f(x) = x^2 - 7\) 的零点)。观察其快速收敛。
-
使用梯度下降最小化 \(f(x, y) = (x - 3)^2 + (y + 1)^2\)。最小值在 \((3, -1)\) 处。尝试不同的学习率。
import jax import jax.numpy as jnp def f(params): x, y = params return (x - 3)**2 + (y + 1)**2 grad_f = jax.grad(f) params = jnp.array([0.0, 0.0]) lr = 0.1 for i in range(20): g = grad_f(params) params = params - lr * g if i % 5 == 0 or i == 19: print(f"step {i:2d}: ({params[0]:.4f}, {params[1]:.4f}) loss={f(params):.6f}") -
数值求解约束优化问题。在 \(x + y = 10\) 的约束下最大化 \(f(x,y) = xy\),通过参数化 \(y = 10 - x\) 并求单变量函数的最优值。