3、无约束优化方法:梯度下降法原理、牛顿法与拟牛顿法、共轭梯度法、步长选择策略(Armijo准则)
无约束优化,说白了就是在一个没有围墙的院子里找最低点。你不需要考虑边界、约束、障碍物,只需要盯着目标函数本身往下走就行。这听起来简单,但实际做起来,坑多得很。
我个人习惯把无约束优化方法分成三类:一阶方法(只用梯度)、二阶方法(用梯度和海森矩阵)、以及介于两者之间的拟牛顿法。今天咱们就把这三类方法的核心逻辑、适用场景和避坑经验一次性讲透。
3.1 梯度下降法:最朴素的下降方式
梯度下降法的思路非常直观:沿着当前点的负梯度方向走一步,然后重复。数学上就是:
x_{k+1} = x_k - α_k · ∇f(x_k)
其中 α_k 是步长,∇f(x_k) 是梯度。就这么简单。
但简单不等于好用。我在项目中遇到过一个问题:用固定步长的梯度下降法优化一个Rosenbrock函数,迭代了5000步还没收敛。后来换成自适应步长,200步就搞定了。你想想看,步长选大了会震荡,选小了又慢得像蜗牛。
3.2 牛顿法与拟牛顿法
牛顿法的思路和梯度下降完全不同。它不光看梯度,还看曲率(海森矩阵)。说白了,它用二次曲面去逼近目标函数,然后一步跳到这个二次曲面的最低点。
更新公式:
x_{k+1} = x_k - [Hf(x_k)]⁻¹ · ∇f(x_k)
其中 Hf(x_k) 是海森矩阵。
牛顿法的收敛速度是二次的,比梯度下降快得多。但代价是什么?每次迭代都要计算海森矩阵并求逆,对于高维问题(比如1000维以上),这几乎不可行。
我记得有一次做图像配准的优化,目标函数有500个参数。用牛顿法,每次迭代光算海森矩阵就要花3秒,求逆又要2秒。迭代20步,一分钟就过去了。后来换成拟牛顿法,同样的精度,时间缩短到原来的十分之一。
拟牛顿法的核心思想是:不直接计算海森矩阵,而是用梯度差来近似它。最常用的两个变体是:
| 方法 | 核心思想 | 存储复杂度 | 适用场景 |
|---|---|---|---|
| BFGS | 用梯度差构造海森矩阵的近似 | O(n²) | 中等规模问题(n < 1000) |
| L-BFGS | 只保存最近m步的梯度差 | O(m·n) | 大规模问题(n > 1000) |
3.3 共轭梯度法
共轭梯度法是个很有意思的方法。它介于梯度下降和牛顿法之间,既不需要计算海森矩阵,又比梯度下降收敛快。
它的核心思想是:每次迭代的搜索方向都和前一次的方向共轭(关于海森矩阵正交)。这样就能保证在n步内收敛到二次函数的最优解。
更新公式:
d_k = -∇f(x_k) + β_k · d_{k-1}
x_{k+1} = x_k + α_k · d_k
其中 β_k 的计算方式有多种,最常用的是Fletcher-Reeves公式:
β_k = ||∇f(x_k)||² / ||∇f(x_{k-1})||²
共轭梯度法在求解大规模线性方程组和神经网络训练中都有广泛应用。我个人习惯在内存受限的场景下优先考虑它。
3.4 步长选择策略:Armijo准则
步长怎么选?固定步长肯定不行。那怎么办?Armijo准则给出了一个简单而有效的自适应方案。
Armijo准则的核心思想是:步长要足够大,让目标函数有显著下降;但又不能太大,防止跳过最优解。
数学表达:
f(x_k + α · d_k) ≤ f(x_k) + c₁ · α · ∇f(x_k)ᵀ · d_k
其中 c₁ 是一个常数,通常取 0.0001 到 0.1 之间。这个不等式保证了函数值有足够的下降。
实际实现时,通常采用回溯法:
- 初始化 α = 1(或一个较大的值)
- 检查Armijo条件是否满足
- 如果不满足,α = α · ρ(ρ通常取0.5-0.8)
- 重复直到条件满足
代码实现:
def armijo_backtracking(f, grad_f, x, d, c1=0.1, rho=0.5, alpha_init=1.0):
alpha = alpha_init
f_x = f(x)
grad_dot_d = np.dot(grad_f(x), d)
while f(x + alpha * d) > f_x + c1 * alpha * grad_dot_d:
alpha *= rho
return alpha
Armijo准则的好处是:它不需要计算海森矩阵,只需要计算函数值和梯度。对于大规模问题,这非常实用。
嗯,这里要注意一点:Armijo准则只保证充分下降,不保证步长最优。它找到的是一个"足够好"的步长,而不是"最好"的步长。但在工程实践中,"足够好"往往就够了。
我曾经在一个项目中,用固定步长的梯度下降法优化一个非凸函数,结果在鞍点附近来回震荡,死活下不去。后来加上Armijo回溯,步长自动调整,很快就穿过了鞍点区域。这就是自适应步长的威力。