编程场景痛点
很多机器学习参数优化问题,并不只是 “找梯度方向” 这么简单。
- 一阶梯度下降能确定更新方向,但对局部曲率信息一无所知
- 损失函数曲面较病态时,SGD 会在狭长沟壑中震荡收敛
- 学习率选择敏感:过大可能发散,过小收敛缓慢
- 只打印 loss 和梯度范数,难以判断优化是否受曲率影响
- 想理解为什么二阶方法收敛更快,却困在矩阵推导中
从工程角度看,二阶优化的核心价值是:用二阶导数刻画损失曲面的局部曲率,并据此构造更合理的参数更新步。
微积分核心公式
1. 二阶导数:函数局部曲率
一元函数的二阶导数是一阶导数的变化率:
\(f''(x)=\frac{d^2 f}{dx^2}=\frac{d}{dx}f'(x)\)
它描述函数曲线在某点的 “弯曲程度”。二阶导数绝对值越大,曲面越弯曲;符号决定局部是凸还是凹。
2. 海森矩阵:多元二阶导数推广
若 \(L(\theta)\) 是关于参数向量 \(\theta\in\mathbb{R}^n\) 的损失函数,则海森矩阵 H 是 \(n\times n\) 的二阶偏导数矩阵:
\(H(\theta)=\nabla^2 L(\theta) =\begin{bmatrix} \frac{\partial^2 L}{\partial \theta_1^2} & \frac{\partial^2 L}{\partial \theta_1 \partial \theta_2} & \dots & \frac{\partial^2 L}{\partial \theta_1 \partial \theta_n}\\[6pt] \frac{\partial^2 L}{\partial \theta_2 \partial \theta_1} & \frac{\partial^2 L}{\partial \theta_2^2} & \dots & \frac{\partial^2 L}{\partial \theta_2 \partial \theta_n}\\[6pt] \vdots & \vdots & \ddots & \vdots\\[6pt] \frac{\partial^2 L}{\partial \theta_n \partial \theta_1} & \frac{\partial^2 L}{\partial \theta_n \partial \theta_2} & \dots & \frac{\partial^2 L}{\partial \theta_n^2} \end{bmatrix}\)
海森矩阵的特征值决定局部曲率的方向和强度。
3. 牛顿法更新规则
牛顿法用海森矩阵逆修正梯度方向:
\(\theta_{k+1}=\theta_k-\eta H(\theta_k)^{-1}\nabla L(\theta_k)\)
其中 \(\eta\) 是学习率。在纯牛顿法中,\(\eta=1\),但工程实现中通常保留学习率以增强稳定性。
4. 二次近似视角
牛顿法可理解为在当前点对损失函数做二次近似:
\(L(\theta)\approx L(\theta_k)+\nabla L(\theta_k)^T(\theta-\theta_k)+\frac{1}{2}(\theta-\theta_k)^T H(\theta_k)(\theta-\theta_k)\)
对近似函数求导并令其为零,即可得到牛顿更新方向。
5. 有限差分近似海森矩阵
工程中可用一阶导数的中心差分近似二阶导数:
\(H_{ij}\approx\frac{\frac{\partial L}{\partial \theta_i}(\theta+h e_j)-\frac{\partial L}{\partial \theta_i}(\theta-h e_j)}{2h}\)
其中 \(e_j\) 是第 j 个标准基向量。
代码实操示例
下面实现一个基于 NumPy 的逻辑回归分类器,并加入牛顿法参数更新。重点展示梯度、海森矩阵和参数更新之间的工程关系。
import numpy as np def sigmoid(z): return 1 / (1 + np.exp(-z)) def logistic_loss(X, y, theta): m = len(y) h = sigmoid(X @ theta) h = np.clip(h, 1e-12, 1 - 1e-12) return -np.mean(y * np.log(h) + (1 - y) * np.log(1 - h)) def gradient(X, y, theta): m = len(y) h = sigmoid(X @ theta) return X.T @ (h - y) / m def hessian(X, theta): m = len(theta) h = sigmoid(X @ theta) S = np.diag(h * (1 - h)) return X.T @ S @ X / m def newton_update(X, y, theta, lr=1.0): grad = gradient(X, y, theta) H = hessian(X, y, theta) H += 1e-4 * np.eye(len(theta)) delta = np.linalg.solve(H, grad) return theta - lr * delta def train(X, y, lr=1.0, epochs=10): theta = np.zeros(X.shape[1]) for epoch in range(epochs): theta = newton_update(X, y, theta, lr) loss = logistic_loss(X, y, theta) print(f"Epoch {epoch+1:2d} | loss: {loss:.6f}") return theta np.random.seed(0) n_samples, n_features = 80, 3 X = np.random.randn(n_samples, n_features) true_theta = np.array([1.2, -0.8, 0.5]) y = (sigmoid(X @ true_theta + 0.1 * np.random.randn(n_samples)) > 0.5).astype(float) theta = train(X, y, lr=1.0, epochs=10) print("Final parameters:", np.round(theta, 4))为了保证数值稳定性,代码在海森矩阵上加了一个小的单位矩阵正则项:
H += 1e-4 * np.eye(len(theta))这一步能避免海森矩阵奇异或接近奇异时求解失败。
不同算法对比简表
表格
| 优化算法 | 使用的微积分对象 | 更新方向 | 工程特点 |
|---|---|---|---|
| 梯度下降 | 一阶导数 / 梯度 \(\nabla L\) | 沿梯度反方向更新 | 实现简单,适合大规模参数 |
| 动量法 | 梯度 + 历史速度 | 积累历史方向,减少震荡 | 训练更稳定,适合深度模型 |
| Adam | 一阶矩、二阶矩估计 | 自适应学习率方向 | 工业界常用,调参相对友好 |
| 牛顿法 | 一阶导数 + 海森矩阵 H | \(H^{-1}\nabla L\) | 收敛快,但计算量大 |
| 拟牛顿法(如 BFGS) | 梯度 + 近似海森矩阵 | 近似 \(H^{-1}\nabla L\) | 避免精确求逆,适合中小规模问题 |
| 高斯 - 牛顿法 | 雅可比矩阵构造近似海森 | \((J^T J)^{-1}J^T r\) | 常用于非线性最小二乘 |
一句话解读:一阶方法关注 “往哪走”,二阶方法额外关注 “走多快、走多稳”,但代价是更高的计算与存储开销。
学习总结避坑
- 二阶导数不是 “更高级的一阶导数”一阶导数告诉你函数往哪个方向增长;二阶导数告诉你这个方向本身如何变化。
- 海森矩阵不一定需要显式构造在高维问题中,直接存储和求逆海森矩阵通常不现实。拟牛顿法的工程价值正在于用梯度变化近似曲率信息。
- 牛顿法不代表一定更好它对初始化、学习率和海森矩阵质量敏感。初始化太差时,牛顿法可能直接发散。
- 正则项不是可有可无海森矩阵条件数大或奇异时,\(H^{-1}\) 会放大数值误差。加入小正则项是工程常规操作。
- 不要把牛顿法理解成固定步长纯牛顿法理论步长为 1,但实际训练中仍建议从较小学习率开始测试。
- 关注损失曲面条件数如果不同参数方向收敛速度差异很大,说明曲面较病态。此时二阶方法或自适应学习率方法通常更有优势。
- 学习路线建议先理解一元二阶导数,再理解海森矩阵结构,最后再看牛顿法和拟牛顿法的工程差异。