【学习笔记】最优化方法
Armijo型非精确线性搜索求步长
Armijo 型线性搜索的数学条件定义为:
f(xk+αkdk)≤f(xk)+ραk∇f(xk)Tdk
参数说明:
- ρ 是预定义常数,且 ρ∈(0,1)
- αk 表示第 k 次迭代的步长
- dk 为搜索方向
- ∇f(xk) 是目标函数在 xk 处的梯度
公式作用:
该条件通过限制步长 αk 的取值,确保每次迭代中函数值 f(x) 充分下降,常用于梯度下降法等优化算法的步长选择。
算法2.3(Armijo型线性搜索)步骤:
步0:若 αk=1 满足上述数学条件,则取 αk=1;否则转步1
步1:给定常数 β>0,ρ∈(0,1),令 αk=β
步2:若 αk 满足条件(2.6),则得到步长 αk,终止计算;否则转步3
步3:令 αk=ραk,转步2
参数说明:
- β:初始试探步长(通常取 β=1)
- ρ:步长缩减因子(控制步长收缩速度)
- αk:第 k 次迭代的候选步长
算法注释:
- 先尝试 αk=1,若满足下降条件(2.6),则直接采用单位步长
- 若不满足,则从较大步长 β 开始,通过循环逐步缩小步长(αk←ραk)
- 适用于梯度下降法、拟牛顿法等优化算法,保证迭代过程中目标函数值 f(xk) 充分下降
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import numpy as np
def armijo_line_search(f, grad_f, x_k, d_k, beta=1, rho=0.5): """ 严格按照算法2.3实现的Armijo型线性搜索 :param f: 目标函数 :param grad_f: 目标函数的梯度函数 :param x_k: 当前迭代点 :param d_k: 当前搜索方向 :param beta: 初始试探步长(默认值 1) :param rho: 步长缩减因子(默认值 0.5,范围 (0,1)) :return: 选定的步长 alpha_k """ grad_fk = grad_f(x_k) alpha_k = 1 if f(x_k + alpha_k * d_k) <= f(x_k) + rho * alpha_k * np.dot(grad_fk, d_k): return alpha_k alpha_k = beta while f(x_k + alpha_k * d_k) > f(x_k) + rho * alpha_k * np.dot(grad_fk, d_k): alpha_k *= rho return alpha_k
def f(x): return np.sum(x ** 2)
def grad_f(x): return 2 * x
x_k = np.array([1.0, 2.0]) d_k = -grad_f(x_k) alpha = armijo_line_search(f, grad_f, x_k, d_k) print(f"选定步长: {alpha}")
|
Wolfe-Powell非精确线性搜索求步长
Wolfe-Powell 线性搜索的数学条件定义为:
- Armijo 条件(充分下降条件):
f(xk+αkdk)≤f(xk)+ραk∇f(xk)Tdk
- 曲率条件:
∇f(xk+αkdk)Tdk≥σ∇f(xk)Tdk
参数约束:
0<ρ<σ<1
参数说明:
- ρ:控制下降量的常数(通常取 ρ=10−4)
- σ:曲率条件的松弛系数(通常取 σ=0.1 或 0.9)
- αk:第 k 次迭代的候选步长
- dk:搜索方向(如梯度下降方向 dk=−∇f(xk))
- ∇f(xk):目标函数在 xk 处的梯度
公式作用:
- Armijo 条件:确保步长 αk 使函数值 f(xk) 充分下降
- 曲率条件:保证步长 αk 不会过小(避免算法停滞),同时允许梯度模长适当增大
- 参数约束:ρ<σ 保证存在满足两条件的步长
补充说明:
- 与 Armijo 准则相比,Wolfe-Powell 增加了曲率条件,避免步长选取过于保守
- 广泛应用于共轭梯度法、拟牛顿法等需要保证“充分下降且合理步长”的优化算法
- 条件中梯度内积 ∇f(xk)Tdk 通常为负数(当 dk 是下降方向时)
算法步骤(修正后规范表达):
-
步0:若 αk=1 满足条件 (2.7),则取 αk=1;否则转步1
-
步1:给定参数 β>0,ρ,ρ1∈(0,1),初始化 αk(0) 为集合 {βρj ∣ j∈Z} 中满足第一个不等式的最大候选步长,令 i=0
- 数学意义:在离散化网格 βρj 中搜索满足条件的最大步长
-
步2:若 αk(i) 满足第二个条件,则取 αk=αk(i);否则令 βk(i)=ρ−1αk(i)
- 逻辑说明:若不满足条件,则放大搜索范围上限(βk(i) 是原步长的 ρ−1 倍)
-
步3:在区间 [αk(i),βk(i)] 内,通过集合 {αk(i)+ρ1j(βk(i)−αk(i)) ∣ j=0,1,2,...} 重新搜索满足第一个不等式的最大步长 αk(i+1),令 i=i+1,转步2
- 几何解释:在调整后的步长区间内,以 ρ1 为细分因子进行二次搜索
参数说明:
- β:初始试探步长基准值(控制搜索范围)
- ρ:步长网格收缩因子(用于生成离散候选步长 βρj)
- ρ1:区间细分因子(控制二次搜索的精细程度)
- αk(i):第 i 次迭代时的候选步长
解题思路与算法逻辑:
- 核心目标:通过动态调整步长区间,高效找到同时满足两个条件 (2.7) 的 αk
- 创新点:
- 双重网格搜索:先用 ρ 生成粗粒度候选步长,再用 ρ1 在调整后的区间内细粒度搜索
- 区间动态扩展:当候选步长不满足条件时,通过 βk(i)=ρ−1αk(i) 扩大搜索范围
- 收敛保证:ρ,ρ1<1 确保步长在有限次迭代内缩小到满足条件
补充说明:
- 条件 (2.7) 应包含两个不等式(推测为类似 Wolfe-Powell 条件的 Armijo 条件和曲率条件)
- 该算法比标准 Armijo 搜索更复杂,适用于需要同时满足多个条件的优化问题
- 离散化集合 {βρj} 和 {αk(i)+ρ1j(βk(i)−αk(i))} 的设计平衡了计算效率与精度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import numpy as np
def wolfe_powell_search(f, grad_f, x_k, d_k, beta=1.0, rho=1e-4, sigma=0.1, rho1=0.5): """ Wolfe-Powell 线性搜索算法 :param f: 目标函数 f(x) :param grad_f: 目标函数的梯度函数 ∇f(x) :param x_k: 当前点 x_k :param d_k: 搜索方向 d_k :param beta: 初始试探步长 :param rho: Armijo 条件控制参数(0 < rho < 1) :param sigma: 曲率条件控制参数(rho < sigma < 1) :param rho1: 区间细分因子(0 < rho1 < 1) :return: 满足 Wolfe-Powell 条件的步长 α_k """ g_k = grad_f(x_k) g_k_d = np.dot(g_k, d_k) if g_k_d >= 0: raise ValueError("搜索方向 d_k 不是下降方向,应满足 ∇f(x_k)^T d_k < 0") alpha_k = 1.0 x_new = x_k + alpha_k * d_k if f(x_new) <= f(x_k) + rho * alpha_k * g_k_d: if np.dot(grad_f(x_new), d_k) >= sigma * g_k_d: return alpha_k alpha_k = beta while f(x_k + alpha_k * d_k) > f(x_k) + rho * alpha_k * g_k_d: alpha_k *= rho if np.dot(grad_f(x_k + alpha_k * d_k), d_k) >= sigma * g_k_d: return alpha_k beta_k = alpha_k / rho i = 0 while True: alpha_new = alpha_k + rho1 ** i * (beta_k - alpha_k) if f(x_k + alpha_new * d_k) <= f(x_k) + rho * alpha_new * g_k_d: alpha_k = alpha_new else: beta_k = alpha_new if np.dot(grad_f(x_k + alpha_k * d_k), d_k) >= sigma * g_k_d: return alpha_k i += 1
|
黄金分割法精确线性搜索求步长
输入:初始区间 [a1,b1],精度要求 ε>0
输出:极小值点所在区间 [ak,bk]
-
初始化:
-
停止条件:
- 若 bk−ak<ε,终止计算,输出区间 [ak,bk]
-
区间更新:
-
迭代循环:
- 令 k=k+1,返回步骤2
参数说明:
- λk,μk:第 k 次迭代的黄金分割点(比例系数 0.382 和 0.618)
- ε:区间长度精度阈值(控制算法终止条件)
- [ak,bk]:第 k 次迭代时的候选区间
算法原理:
- 黄金分割比:0.618 是 25−1 的近似值,确保每次迭代区间长度按固定比例 0.618 收缩
- 单点复用:在区间更新时保留一个旧内点,只需计算一个新点函数值,减少计算量
- 单调收敛性:每次迭代排除不可能包含极小值的子区间,保证 bk−ak→0
解题思路:
-
初始区间选择:需满足 f(x) 在 [a1,b1] 内为单峰函数
-
关键步骤验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| if f(lambda_k) > f(mu_k): a_new = lambda_k b_new = b_k else: a_new = a_k b_new = mu_k
```python import numpy as np
def golden_section_search(f, a, b, epsilon=1e-5): """ 黄金分割法求解极小值所在区间 :param f: 目标函数 :param a: 初始区间左端点 :param b: 初始区间右端点 :param epsilon: 精度要求,控制停止条件 :return: 极小值所在的区间 [a_k, b_k] """ phi = (np.sqrt(5) - 1) / 2 lambda_k = a + (1 - phi) * (b - a) mu_k = a + phi * (b - a) f_lambda = f(lambda_k) f_mu = f(mu_k) while (b - a) > epsilon: if f_lambda > f_mu: a = lambda_k lambda_k = mu_k f_lambda = f_mu mu_k = a + phi * (b - a) f_mu = f(mu_k) else: b = mu_k mu_k = lambda_k f_mu = f_lambda lambda_k = a + (1 - phi) * (b - a) f_lambda = f(lambda_k) return a, b
|
信赖域算法
修正后的规范算法步骤:
输入:初始点 x0,最大信赖域半径 Δ,精度阈值 ε≥0,阈值参数 0<η1≤η2<1
输出:优化解 xk
-
初始化:
- 设定初始信赖域半径 Δ0∈(0,Δ),迭代计数器 k=0
-
停止条件:
- 计算梯度 gk=∇f(xk)
- 若 ∥gk∥≤ε,终止算法,输出 xk
-
子问题求解:
-
接受步长:
-
调整信赖域半径:
Δk+1=⎩⎨⎧min(2Δk,Δ),Δk/4,Δk,if rk>η2if rk<η1otherwise
-
循环迭代:
- 令 k=k+1,返回步骤2
参数说明:
- Δ:信赖域半径的最大允许值(防止过度扩张)
- η1,η2:阈值参数,控制半径调整的敏感性(典型值:η1=0.1, η2=0.75)
- Bk:二阶模型中的Hessian近似矩阵(如BFGS更新、SR1更新等)
- rk:实际下降与预测下降的比值(衡量模型可靠性)
算法原理:
- 信赖域框架:通过局部二次模型 mk(s) 近似目标函数,在半径 Δk 内寻找最优步长
- 半径动态调整:
- rk 接近 1:扩大信赖域(模型可信度高)
- rk 接近 0:缩小信赖域(模型不可靠)
- 子问题求解:常用Steihaug-CG方法或狗腿法(Dogleg)近似求解
- 全局收敛性:在适当条件下,算法满足 liminfk→∞∥gk∥=0
解题思路:
-
关键公式验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| actual_reduction = f(xk) - f(xk + sk) predicted_reduction = - (g_k.T @ sk + 0.5 * sk.T @ B_k @ sk) rk = actual_reduction / predicted_reduction
```python import numpy as np
def trust_region_method(f, grad_f, hess_f, x0, delta_max, epsilon=1e-5, eta1=0.1, eta2=0.75): """ 信赖域优化算法 :param f: 目标函数 :param grad_f: 目标函数梯度函数 ∇f(x) :param hess_f: 目标函数 Hessian 矩阵函数 B_k :param x0: 初始点 x_0 :param delta_max: 最大信赖域半径 \overline{\Delta} :param epsilon: 终止条件,梯度范数阈值 :param eta1: 阈值参数 η_1 :param eta2: 阈值参数 η_2 :return: 优化解 x_k """ x_k = np.array(x0, dtype=float) delta_k = delta_max / 2 while True: g_k = grad_f(x_k) if np.linalg.norm(g_k) <= epsilon: break B_k = hess_f(x_k) try: s_k = -np.linalg.solve(B_k, g_k) except np.linalg.LinAlgError: s_k = -g_k s_norm = np.linalg.norm(s_k) if s_norm > delta_k: s_k = (delta_k / s_norm) * s_k delta_f = f(x_k) - f(x_k + s_k) delta_m = g_k.T @ s_k + 0.5 * s_k.T @ B_k @ s_k r_k = delta_f / delta_m if delta_m != 0 else 0 if r_k > 0: x_k = x_k + s_k if r_k > eta2: delta_k = min(2 * delta_k, delta_max) elif r_k < eta1: delta_k = delta_k / 4 return x_k
|
最速下降法
输入:初始点 x0∈Rn,精度阈值 ϵ>0
输出:优化解 xk
-
初始化:
- 设定 k=0,给定初始点 x0
-
停止条件:
- 计算梯度 ∇f(xk)
- 若 ∥∇f(xk)∥≤ϵ,终止算法,输出 xk
-
方向计算:
-
步长计算:
-
迭代更新:
关键点解析:
-
核心思想:
- 沿目标函数下降最快的方向(负梯度方向)更新迭代点
- 通过精确线搜索保证每步下降量最大
-
方向选择:
- 方向 dk=−∇f(xk) 是当前点的最速下降方向
- 梯度方向是函数值局部上升最快的方向,负梯度则为下降最快方向
-
步长计算:
- 精确线搜索需解一维优化问题,对简单函数可解析求导(如二次函数)
- 一般函数可能需要黄金分割法、抛物线插值法等数值方法
收敛性分析:
-
收敛条件:
- 在目标函数 f(x) 为凸且光滑的条件下,算法全局收敛
- 线性收敛速度:相邻迭代点误差满足 ∥xk+1−x∗∥≤ρ∥xk−x∗∥,其中 0<ρ<1
-
局限性:
- 高维问题中可能出现“锯齿现象”,收敛速度显著下降
- 对非凸函数可能收敛到局部极小或鞍点
解题思路(以二次函数为例):
设目标函数为 f(x)=21xTQx+bTx,其中 Q 对称正定
- 梯度计算:
∇f(x)=Qx+b
- 方向更新:
dk=−(Qxk+b)
- 精确步长解析解:
αk=∇f(xk)TQ∇f(xk)∇f(xk)T∇f(xk)
示例(二维情况):
- 令 Q=[2001], b=[−10], x0=[00]
- 第1次迭代:
- ∇f(x0)=[−10]
- d0=[10]
- α0=21
- x1=[0.50]
对比与扩展:
-
与信赖域算法区别:
- 最速下降法仅依赖梯度,而信赖域法需构建二阶模型
- 最速下降法步长由线搜索确定,信赖域法步长受半径约束
-
改进方向:
- 预处理技术:通过坐标变换减少条件数,缓解锯齿现象
- 非精确线搜索:使用Armijo准则、Wolfe条件降低计算成本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import numpy as np
def steepest_descent(f, grad_f, x0, epsilon=1e-5, max_iter=1000): """ 最速下降法优化算法 :param f: 目标函数 :param grad_f: 目标函数的梯度函数 ∇f(x) :param x0: 初始点 x_0 :param epsilon: 精度阈值 :param max_iter: 最大迭代次数 :return: 近似最优解 x_k """ x_k = np.array(x0, dtype=float) k = 0 while k < max_iter: g_k = grad_f(x_k) if np.linalg.norm(g_k) <= epsilon: break d_k = -g_k alpha_k = (g_k.T @ g_k) / (g_k.T @ hess_f(x_k) @ g_k) x_k = x_k + alpha_k * d_k k += 1 return x_k
|
基本Newton法
输入:初始点 x0∈Rn,精度阈值 ε>0
输出:优化解 xk
-
初始化:
- 设定 k=0,给定初始点 x0
-
停止条件:
- 计算梯度 ∇f(xk)
- 若 ∥∇f(xk)∥≤ε,终止算法,输出 xk
-
方向计算:
-
迭代更新:
关键点解析:
-
核心思想:
- 利用目标函数的二阶导数(Hessian矩阵)构造搜索方向,实现局部快速收敛
- 通过牛顿方程(公式3.3)直接求解优化方向,默认步长 αk=1
-
方向计算:
- 方向 dk 是牛顿方向,满足 dk=−[∇2f(xk)]−1∇f(xk)
- 当Hessian矩阵正定时,方向为下降方向
-
步长特性:
- 基本Newton法固定步长为1,适用于靠近极值点的情况
- 改进方法(如阻尼Newton法)会引入线搜索调整步长
收敛性分析:
-
收敛速度:
-
局限性:
- Hessian矩阵可能非正定或奇异,导致方向非下降或无法求解
- 初始点选择敏感,远离极值点时可能发散
解题示例(二次函数优化):
设目标函数为 f(x)=21xTQx+bTx,其中 Q 对称正定
- 梯度与Hessian:
∇f(x)=Qx+b,∇2f(x)=Q
- 牛顿方向计算:
Qdk+(Qxk+b)=0⇒dk=−xk−Q−1b
- 迭代更新:
xk+1=xk+dk=−Q−1b(一步收敛到极小点)
数值算例:
- 令 Q=[2112], b=[−3−3], x0=[00]
- 第1次迭代:
- ∇f(x0)=[−3−3]
- 解方程 Qd0=−∇f(x0)⇒d0=[11]
- x1=x0+d0=[11](精确解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import numpy as np
def newton_method(f_grad, f_hessian, x0, epsilon): """ 使用牛顿法(Newton's Method)进行优化求解。 :param f_grad: 目标函数的梯度函数,输入 x 返回梯度向量。 :param f_hessian: 目标函数的 Hessian 矩阵函数,输入 x 返回 Hessian 矩阵。 :param x0: 初始点,numpy 数组。 :param epsilon: 终止精度阈值。 :return: 近似最优解 x_k。 """ k = 0 x_k = x0 while True: grad = f_grad(x_k) if np.linalg.norm(grad) <= epsilon: break hessian = f_hessian(x_k) d_k = np.linalg.solve(hessian, -grad) x_k = x_k + d_k k += 1 return x_k
|
阻尼Newton法
算法(阻尼Newton法)
输入:初始点 x0∈Rn,精度阈值 ε>0
输出:优化解 xk
-
初始化:
- 设定 k=0,给定初始点 x0
-
停止条件:
- 计算梯度 ∇f(xk)
- 若 ∥∇f(xk)∥≤ε,终止算法,输出 xk
-
方向计算:
-
步长计算:
- 通过线搜索(如Armijo准则、Wolfe条件)确定步长 αk,确保目标函数值下降
-
迭代更新:
关键点解析:
-
核心改进:
- 相比基本Newton法(固定步长 αk=1),阻尼法引入线搜索动态调整步长,提升全局收敛性。
- 适用于初始点远离极值点或Hessian矩阵非正定的情况。
-
方向与步长的协同:
- 方向:仍为牛顿方向 dk=−[∇2f(xk)]−1∇f(xk),需Hessian正定以保证下降性。
- 步长:通过线搜索找到 αk∈(0,1],使 f(xk+αkdk)<f(xk)。
-
适用场景:
- 目标函数局部凸性较弱时,步长调整可避免迭代发散。
- Hessian矩阵条件数较差时(如接近奇异),阻尼法更稳定。
对比基本Newton法:
| 特性 |
基本Newton法 |
阻尼Newton法 |
| 步长策略 |
固定步长 αk=1 |
动态步长 αk∈(0,1] |
| 收敛性 |
局部二次收敛 |
全局收敛(需合理线搜索) |
| 计算成本 |
低(无需步长搜索) |
较高(需多次函数值计算) |
| 初始点敏感性 |
高(依赖初始点接近极值点) |
低(适应性更强) |
收敛性分析:
- 全局收敛:
- 在适当线搜索条件下(如Armijo准则),算法对任意初始点 x0 均收敛到局部极小点。
- 局部收敛速度:
- 若初始点充分接近极小点且Hessian连续正定,仍保持局部二次收敛速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| import numpy as np
def newton_method(f, grad_f, hess_f, x0, epsilon=1e-6, max_iter=100): """ 牛顿法优化算法 :param f: 目标函数 :param grad_f: 目标函数的梯度函数 :param hess_f: 目标函数的 Hessian 矩阵函数 :param x0: 初始点 (n 维向量) :param epsilon: 收敛阈值 :param max_iter: 最大迭代次数 :return: 近似优化解 x_k """ x_k = np.array(x0, dtype=np.float64) k = 0 while k < max_iter: grad = grad_f(x_k) if np.linalg.norm(grad) <= epsilon: break hess = hess_f(x_k) try: d_k = np.linalg.solve(hess, -grad) except np.linalg.LinAlgError: print("Hessian 矩阵不可逆,终止优化。") break alpha = armijo_line_search(f, grad_f, x_k, d_k) x_k = x_k + alpha * d_k k += 1 return x_k
def armijo_line_search(f, grad_f, x_k, d_k, beta=1, rho=0.5): """ 严格按照算法2.3实现的Armijo型线性搜索 :param f: 目标函数 :param grad_f: 目标函数的梯度函数 :param x_k: 当前迭代点 :param d_k: 当前搜索方向 :param beta: 初始试探步长(默认值 1) :param rho: 步长缩减因子(默认值 0.5,范围 (0,1)) :return: 选定的步长 alpha_k """ grad_fk = grad_f(x_k) alpha_k = 1 if f(x_k + alpha_k * d_k) <= f(x_k) + rho * alpha_k * np.dot(grad_fk, d_k): return alpha_k alpha_k = beta while f(x_k + alpha_k * d_k) > f(x_k) + rho * alpha_k * np.dot(grad_fk, d_k): alpha_k *= rho return alpha_k
|
Newton-最速下降混合法
步骤1:给定初始点 x0∈Rn,精度 ϵ>0。令 k=0;
步骤2:若 ∥∇f(xk)∥≤ϵ,则得解 xk,算法终止。否则,解线性方程组
∇2f(xk)d+∇f(xk)=0
若有解 dk 且满足 ∇f(xk)⊤dk<0,转步骤3;否则取 dk=−∇f(xk);
步骤3:由线性搜索计算步长 αk;
步骤4:令 xk+1=xk+αkdk,k:=k+1,转步骤2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| import numpy as np
def newton_method(f, grad_f, hess_f, x0, epsilon=1e-6, max_iter=100): """ 牛顿法优化算法 :param f: 目标函数 :param grad_f: 目标函数的梯度函数 :param hess_f: 目标函数的 Hessian 矩阵函数 :param x0: 初始点 (n 维向量) :param epsilon: 收敛阈值 :param max_iter: 最大迭代次数 :return: 近似优化解 x_k """ x_k = np.array(x0, dtype=np.float64) k = 0 while k < max_iter: grad = grad_f(x_k) if np.linalg.norm(grad) <= epsilon: break hess = hess_f(x_k) try: d_k = np.linalg.solve(hess, -grad) if np.dot(grad, d_k) >= 0: d_k = -grad except np.linalg.LinAlgError: d_k = -grad alpha = armijo_line_search(f, grad_f, x_k, d_k) x_k = x_k + alpha * d_k k += 1 return x_k
def armijo_line_search(f, grad_f, x_k, d_k, beta=1, rho=0.5): """ 严格按照算法2.3实现的Armijo型线性搜索 :param f: 目标函数 :param grad_f: 目标函数的梯度函数 :param x_k: 当前迭代点 :param d_k: 当前搜索方向 :param beta: 初始试探步长(默认值 1) :param rho: 步长缩减因子(默认值 0.5,范围 (0,1)) :return: 选定的步长 alpha_k """ grad_fk = grad_f(x_k) alpha_k = 1 if f(x_k + alpha_k * d_k) <= f(x_k) + rho * alpha_k * np.dot(grad_fk, d_k): return alpha_k alpha_k = beta while f(x_k + alpha_k * d_k) > f(x_k) + rho * alpha_k * np.dot(grad_fk, d_k): alpha_k *= rho return alpha_k
|
修正Newton-LM法
步骤1 给定初始点 x0∈Rn,精度 ε>0。令 k=0。
步骤2 若 ∥∇f(xk)∥≤ε,则得解 xk,算法终止;否则,解线性方程组
Akd+∇f(xk)=0(3.3)
得解 dk,其中 Ak=∇2f(xk)+vkI,vk>0 使得 Ak 正定。
步骤3 通过线性搜索计算步长 αk。
步骤4 令 xk+1=xk+αkdk,k:=k+1,转至步骤2。
参数说明
在修正Newton法中:
- 为确保 Ak 的正定性,需选择足够大的 vk>0;
- 为确保算法收敛性,需限制 vk 的上界,即要求
vk≤C∥∇f(xk)∥
其中 C 为常数,但其具体确定较为困难。
修正形式的收敛性
- Newton法的两种修正形式在较弱条件下具有:
- 存在多种其他修正形式
鞍点情况的特殊处理
当 x 为鞍点时(即 ∇f(x)=0 但 ∇2f(x) 不定):
- 所有修正方法均失效
- 可取负曲率方向 d,需满足:
dT∇2f(x)d<0
沿此方向搜索时,目标函数值必然下降
方法特性对比
| 优点 |
缺点 |
| 收敛速度快 |
对初始点要求高 |
| - |
计算量大 |
算法改进方向
通过修改Newton法:
- 保留优势:利用快速收敛特性
- 克服缺陷:降低对初始点的敏感性,减少计算量
- 已衍生出多种高效新算法
拟Newton法
步骤1 给定初始点 x0∈Rn,初始化矩阵 B0 或 H0,精度阈值 ε>0。令迭代计数器 k=0。
步骤2 若满足终止准则 ∥∇f(xk)∥≤ε,输出解 xk 并终止算法。
步骤3 求解线性方程组:
Bkd+∇f(xk)=0
得搜索方向 dk,或通过逆矩阵公式直接计算:
dk=−Hkgk(其中 gk=∇f(xk))
步骤4 通过线搜索计算步长 αk,更新迭代点:
xk+1=xk+αkdk
步骤5 按拟Newton条件(DFP/SRP1等)修正矩阵:
Bk+1=Bk+ΔB或Hk+1=Hk+ΔH
使其满足拟Newton方程 Bk+1sk 或 Hk+1yk=sk 。令 k:=k+1,返回步骤2。
修正矩阵
对称秩1
SR修正公式形式1
Bk+1=Bk+(yk−Bksk)Tsk(yk−Bksk)(yk−Bksk)T
SR修正公式形式2
Hk+1=Hk+(sk−Hkyk)Tyk(sk−Hkyk)(sk−Hkyk)T
对称秩2
BFGS 修正公式(迄今最好的拟牛顿修正公式)
Bk+1=Bk−skTBkskBkskskTBk+ykTskykykT
BFGS 的逆修正公式
Hk+1=(I−ykTskskykT)Hk(I−ykTskskykT)T+ykTskskskT
DFP 修正公式
Hk+1=Hk−ykTHkykHkykykTHk+skTykskskT
DFP 的逆修正公式
Bk+1=(I−skTykykskT)Bk(I−skTykykskT)+skTykykykT
BFGS算法
步骤1:给定初始点 x0∈Rn,初始对称正定矩阵 B0,精度 ϵ>0。令 k=0。
步骤2:若 ∥∇f(xk)∥≤ϵ,则得解 xk,算法终止。否则转下一步。
步骤3:解线性方程组
Bkd+∇f(xk)=0
得解 dk。
步骤4:由线性搜索计算步长 αk。
步骤5:令 xk+1=xk+αkdk,若 ∥∇f(xk+1)∥≤ϵ,则得解 xk+1,算法终止。否则由公式 Bk+1=Bk−skTBkskBkskskTBk+ykTskykykT 计算 Bk+1。
步骤6:令 k:=k+1,转步骤3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import numpy as np
def function(x): """ 目标函数 f(x) """ return np.sum(x**2)
def gradient(x): """ 计算目标函数的梯度 \nabla f(x) """ return 2 * x
def line_search(f, grad, xk, dk, alpha=1, rho=0.8, c=1e-4): """ 线性搜索计算步长 \alpha_k 使用 Armijo 条件进行搜索,确保下降足够快。 """ while f(xk + alpha * dk) > f(xk) + c * alpha * np.dot(grad(xk).T, dk): alpha *= rho return alpha
def bfgs(x0, B0, epsilon=1e-6, max_iter=100): """ BFGS 算法实现 """ xk = x0 Bk = B0 k = 0 while np.linalg.norm(gradient(xk)) > epsilon and k < max_iter: dk = -np.linalg.solve(Bk, gradient(xk)) alpha_k = line_search(function, gradient, xk, dk) xk_new = xk + alpha_k * dk sk = xk_new - xk yk = gradient(xk_new) - gradient(xk) if np.linalg.norm(gradient(xk_new)) <= epsilon: return xk_new Bk_sk = Bk @ sk sk_Bk_sk = sk.T @ Bk_sk yk_ykT = np.outer(yk, yk) yk_sk = yk.T @ sk Bk = Bk - np.outer(Bk_sk, Bk_sk) / sk_Bk_sk + yk_ykT / yk_sk xk = xk_new k += 1 return xk
n = 2 x0 = np.array([1.0, 1.5]) B0 = np.eye(n)
optimal_x = bfgs(x0, B0) print("最优解:", optimal_x)
|
DFP算法
步骤1:给定初始点 x0∈Rn,初始对称正定矩阵 H0,精度 ϵ>0。令 k=0。
步骤2:若 ∥∇f(xk)∥≤ϵ,则输出解 xk,算法终止;否则继续。
步骤3:计算搜索方向:
dk=−Hk∇f(xk)
(或通过解线性方程组 Bkd+∇f(xk)=0,其中 Bk 由公式 Bk+1=(I−skTykykskT)Bk(I−skTykykskT)+skTykykykT 计算)
步骤4:通过线性搜索确定步长 αk。
步骤5:更新迭代点:
xk+1=xk+αkdk
若 ∥∇f(xk+1)∥≤ϵ,输出解 xk+1 并终止;否则按公式 Hk+1=Hk−ykTHkykHkykykTHk+skTykskskT 计算 Hk+1。
步骤6:令 k:=k+1,转步骤3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| import numpy as np
def line_search(f, x, d, grad_x, alpha_init=1.0, rho=0.8, c=1e-4, max_backtrack=20): """ 回溯线搜索算法寻找满足Armijo条件的步长 参数: f -- 目标函数 x -- 当前点 d -- 搜索方向 grad_x -- 当前点的梯度 alpha_init -- 初始步长 rho -- 步长衰减因子 c -- Armijo条件常数 max_backtrack -- 最大回溯次数 返回: alpha -- 选择的步长 """ alpha = alpha_init f_x = f(x) grad_dot_d = np.dot(grad_x, d) if grad_dot_d >= 0: return 0.0 for _ in range(max_backtrack): x_new = x + alpha * d f_new = f(x_new) if f_new <= f_x + c * alpha * grad_dot_d: return alpha alpha *= rho return alpha
def dfp_algorithm(f, grad_f, x0, H0, epsilon, max_iter=1000, line_search_params=None): """ DFP拟牛顿法实现 参数: f -- 目标函数 grad_f -- 梯度函数 x0 -- 初始点 (numpy数组) H0 -- 初始正定矩阵 (numpy数组) epsilon -- 收敛精度 max_iter -- 最大迭代次数 line_search_params -- 线搜索参数字典 返回: x_k -- 近似最优解 """ x_k = x0.copy() H_k = H0.copy() grad_k = grad_f(x_k) n = x_k.shape[0] if line_search_params is None: line_search_params = {'alpha_init': 1.0, 'rho': 0.5, 'c': 1e-4} alpha_init = line_search_params['alpha_init'] rho = line_search_params['rho'] c = line_search_params['c'] for k in range(max_iter): if np.linalg.norm(grad_k) <= epsilon: print(f"在迭代 {k} 次后收敛") return x_k d_k = -H_k @ grad_k alpha_k = line_search(f, x_k, d_k, grad_k, alpha_init, rho, c) if alpha_k <= 1e-12: print("步长过小,终止计算") return x_k x_next = x_k + alpha_k * d_k grad_next = grad_f(x_next) if np.linalg.norm(grad_next) <= epsilon: print(f"在迭代 {k+1} 次后收敛") return x_next s_k = x_next - x_k y_k = grad_next - grad_k sy_dot = np.dot(s_k, y_k) if abs(sy_dot) < 1e-8: H_next = H_k.copy() else: Hy = H_k @ y_k yHy = y_k @ Hy if abs(yHy) < 1e-8: term1 = 0.0 else: term1 = np.outer(Hy, Hy) / yHy term2 = np.outer(s_k, s_k) / sy_dot H_next = H_k - term1 + term2 x_k = x_next grad_k = grad_next H_k = H_next.copy() print(f"达到最大迭代次数 {max_iter} 次未收敛") return x_k
def quadratic(x): return x[0]**2 + 2*x[1]**2 + x[0]*x[1]
def quadratic_grad(x): return np.array([2*x[0] + x[1], 4*x[1] + x[0]])
x0 = np.array([2.0, 1.0]) H0 = np.eye(2) epsilon = 1e-6
solution = dfp_algorithm(quadratic, quadratic_grad, x0, H0, epsilon) print("优化解:", solution) print("最终梯度:", quadratic_grad(solution))
|
Broyden族
对互为对偶的BFGS公式和DFP公式进行加权组合得到一类重要的拟Newton矩阵修正公式:Broden族
Bk+1θk=θkBk+1BFGS+(1−θk)Bk+1DFP
Hk+1ϕk=ϕkHk+1BFGS+(1−ϕk)Hk+1DFP
其中 θk=1−1−ϕk+ϕkμk1−ϕk , μk=skTykykTHkykskTBksk
由人工神经网络方法解微分方程导出的最优化问题
思维学一般把人类的大脑思维分为抽象(逻辑)思维,形象(直观)思维和灵感(顿悟)思维三种基本方式,人工神经网络就是模拟人类思维的形象思维方式去解决问题的。神经网络的基础是神经元,神经元是以生物神经系统的神经细胞为基础的生物模型。人们对生物神经系统进行研究以探讨人工智能的机制时,把神经元数学化,从而产生了神经元数学模型,大量相同形式的神经元连接在一起就组成了神经网络
神经网络是一个高度非线性动力学系统,虽然每个神经元的结构和功能都不复杂,但是神经网络的动态行为却是十分复杂的,因此,用神经网络可以表达实际物理世界的各种现象。
我们可以用有向图表示神经网络,这里有向图是节点与节点之间的有向连接。一般来说,神经网络有两种基本结构:前馈神经网络和递归神经网络。这里我们仅考虑前馈神经网络,它包括:
输入层:源节点构成输入层,它向网络提供输入信号
隐藏层:前馈神经网络有一层或多层隐藏节点,相应的节点称为隐藏神经元
输出层:该层给出相对于源节点的激活模式的网络输出
一个 m−n1−n2−q 前馈神经网络,表示该网络有 m 个源节点, 第一个隐藏层有 n1 个神经元,第二个隐藏层有 n2 个神经元,输出层有 q 个神经元
求解微分方程的方法是多种多样的,如下有一种用神经网络的方法求解微分方程的方法
考虑一阶常微分方程
dxdy(x)=f(x,y),x∈[0,1]
其初始条件为
y(0)=y0
利用神经网络的方法我们可以得到一个近似解
yt(x,p)
可以用它去近似方程的精准解 y(x) ,其中实验解中的参数 p 为神经网络方法所产生的参数,下面我们讨论神经网络实验解的表示
考虑一个 1−n−1 的前馈神经网络,一阶微分方程的神经网络实验解可以表示为
yt(x,p)=y0+xN(x,p)
其中 N(x,p) 为前馈神经网络的输出, x 是单一输入, p 是神经网络参数向量。前馈神经网络的输出为
N(x,p)=j=1∑nvjφ(zj),zj=wjx−θj
其中 wj 是从源节点到隐藏节点 j 的权重 , vj 是从隐藏节点 j 到输出节点的权重, θj 是隐藏节点 j 的阈值, p=(w,v,θ)T 是一个 3n 维向量 , φ(z) 是 Sigmoid型激活函数
φ(z)=1+e−z1
为了使得实验解能尽可能好地近似方程的解,我们要用最优化的方法去确定实验解中的参数 p 。对于一阶微分方程,我们要将 x 在 [0,1] 离散化,得到 m 个值 0≤x1<x2……≤xm=1 以及 dxdyt(x,p)x=xi ,f(xi,yt(xi,p)) .其中 x1 到 xm 称为训练集元素.然后求解最优化问题
mini=1∑m{dxdyt(x,p)x=xi−f(xi,yt(xi,p))}2
来确定 p 的值
对正定二次型的共轭梯度法
步0(初始化):
给定线性无关向量组 g∈Rn,令
d0=−g0,k:=0
步1(方向更新):
计算搜索方向:
dk=−gk+j=0∑k−1(djTGdjdjTGgk)dj
步2(终止条件):
若 k=n−1,则停止;否则继续。
步3(线搜索):
计算步长 αk,使得:
αk=argαminf(xk+αdk)
步4(迭代更新):
-
计算梯度 ∇f(xk+αkdk)
-
令 k:=k+1,返回步1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import numpy as np
def conjugate_direction_method(f, grad_f, G, x0): """ 共轭方向法求解无约束优化问题。 参数: f : function - 目标函数 f(x) grad_f : function - 目标函数的梯度 ∇f(x) G : ndarray - 正定矩阵,表示二次型 f(x) 的 Hessian 矩阵 x0 : ndarray - 初始点 返回: x_opt : ndarray - 最优解 """ n = len(x0) g = grad_f(x0) d = [-g] x = x0 for k in range(n): if k > 0: d_k = -g for j in range(k): beta = (d[j].T @ G @ g) / (d[j].T @ G @ d[j]) d_k += beta * d[j] d.append(d_k) alpha_k = - (g.T @ d[k]) / (d[k].T @ G @ d[k]) x = x + alpha_k * d[k] g = grad_f(x) if k == n - 1: break return x
if __name__ == "__main__": G = np.array([[4, 1], [1, 3]]) b = np.array([1, 2]) def f(x): return 0.5 * x.T @ G @ x - b.T @ x def grad_f(x): return G @ x - b x0 = np.array([2, 1]) x_opt = conjugate_direction_method(f, grad_f, G, x0) print("最优解:", x_opt)
|
(FR)对正定二次型的共轭梯度法
算法步骤:
步0:给定线性无关向量组:g∈Rn,令 d0=−g0,k=0。
步1:计算
dk=−gk+dk−1TGdk−1dk−1TGgkdk−1.
步2:若 k=n−1,则停止。
步3:计算线搜索 f(xk+αdk) 的最小值点 αk。
步4:求梯度 ∇f(xk+αkdk),令 k=k+1,转步1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import numpy as np
def conjugate_gradient_method(f, grad_f, G, x0): """ 共轭梯度法求解无约束优化问题。 参数: f : function - 目标函数 f(x) grad_f : function - 目标函数的梯度 ∇f(x) G : ndarray - 正定矩阵,表示二次型 f(x) 的 Hessian 矩阵 x0 : ndarray - 初始点 返回: x_opt : ndarray - 最优解 """ n = len(x0) g = grad_f(x0) d = -g x = x0 for k in range(n): alpha_k = - (g.T @ d) / (d.T @ G @ d) x = x + alpha_k * d g_new = grad_f(x) if k == n - 1: break beta_k = (d.T @ G @ g_new) / (d.T @ G @ d) d = -g_new + beta_k * d g = g_new return x
if __name__ == "__main__": G = np.array([[4, 1], [1, 3]]) b = np.array([1, 2]) def f(x): return 0.5 * x.T @ G @ x - b.T @ x def grad_f(x): return G @ x - b x0 = np.array([2, 1]) x_opt = conjugate_gradient_method(f, grad_f, G, x0) print("最优解:", x_opt)
|
HS共轭梯度法
步骤1:
给定初始点 x0∈Rn,精度 ε>0。
令 d0=−∇f(x0),令 k:=0。
步骤2:
若 ∥∇f(xk)∥≤ε,则得解 xk,算法终止。否则转步骤3。
步骤3:
由线性搜索计算步长 αk。
步骤4:
令 xk+1=xk+αkdk。
步骤5:
由式 dk={−∇f(x0),−∇f(xk)+βkdk−1,k=0k≥1 确定 dk+1,其中 βk+1=βk+1HS
其中 βkHS=dk−1T[∇f(xk)−∇f(xk−1)]∇f(xk)T[∇f(xk)−∇f(xk−1)],k=1,2,…
令 k:=k+1,转步骤2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import numpy as np
def hs_conjugate_gradient(f, grad_f, x0, epsilon=1e-6): """ HS 共轭梯度法求解无约束优化问题。 参数: f : function - 目标函数 f(x) grad_f : function - 目标函数的梯度 ∇f(x) x0 : ndarray - 初始点 epsilon : float - 终止精度 返回: x_opt : ndarray - 最优解 """ x = x0 g = grad_f(x) d = -g k = 0 while np.linalg.norm(g) > epsilon: alpha_k = - (g.T @ d) / (d.T @ grad_f(x + d)) x_new = x + alpha_k * d g_new = grad_f(x_new) beta_hs = (g_new.T @ (g_new - g)) / (d.T @ (g_new - g)) d = -g_new + beta_hs * d x, g = x_new, g_new k += 1 return x
if __name__ == "__main__": G = np.array([[4, 1], [1, 3]]) b = np.array([1, 2]) def f(x): return 0.5 * x.T @ G @ x - b.T @ x def grad_f(x): return G @ x - b x0 = np.array([2, 1]) x_opt = hs_conjugate_gradient(f, grad_f, x0) print("最优解:", x_opt)
|
1.HS公式
βkHS=[dk−1T(∇f(xk)−∇f(xk−1))][∇f(xk)T(∇f(xk)−∇f(xk−1))]
2.PRP公式
βkPRP=∣∇f(xk−1)∣2∇f(xk)T[∇f(xk)−∇f(xk−1)]
3.FR公式
βkFR=∣∇f(xk−1)∣2∣∇f(xk)∣2
4.CD公式
βkCD=−dk−1T∇f(xk−1)∣∇f(xk)∣2
5.DY公式
βkDY=dk−1T[∇f(xk)−∇f(xk−1)]∣∇f(xk)∣2
非线性共轭梯度法
- 步1 给定初始点 x0∈Rn,精度 ε>0。令 d0=−∇f(x0),令 k:=0。
- 步2 若 ∥∇f(xk)∥≤ε,则得解 xk,算法终止。否则转步3。
- 步3 由线性搜索计算步长 αk。
- 步4 令 xk+1=xk+αkdk。
- 步5 确定 dk+1 和 βk,其中 dk+1=−∇f(xk+1)+βkdk。令 k:=k+1,转步2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| import numpy as np from typing import Callable, Tuple
def gradient_descent( f: Callable[[np.ndarray], float], grad_f: Callable[[np.ndarray], np.ndarray], x0: np.ndarray, epsilon: float = 1e-6, max_iter: int = 1000, line_search: Callable = None ) -> Tuple[np.ndarray, int]: """ 最速下降法(梯度下降法)实现 参数: f: 目标函数 grad_f: 目标函数的梯度 x0: 初始点 epsilon: 收敛精度 max_iter: 最大迭代次数 line_search: 步长搜索函数 返回: x_k: 找到的解 k: 迭代次数 """ x_k = x0.copy() d_k = -grad_f(x_k) k = 0 while k < max_iter: grad_norm = np.linalg.norm(grad_f(x_k)) if grad_norm <= epsilon: print(f"算法在 {k} 次迭代后收敛") return x_k, k if line_search is None: alpha_k = 0.01 else: alpha_k = line_search(f, grad_f, x_k, d_k) x_k_plus_1 = x_k + alpha_k * d_k beta_k = 0.0 d_k_plus_1 = -grad_f(x_k_plus_1) + beta_k * d_k x_k = x_k_plus_1 d_k = d_k_plus_1 k += 1 print(f"达到最大迭代次数 {max_iter},未收敛到所需精度") return x_k, k
if __name__ == "__main__": def f(x): return (x[0]-1)**2 + (x[1]-2)**2 def grad_f(x): return np.array([2*(x[0]-1), 2*(x[1]-2)]) x0 = np.array([0.0, 0.0]) solution, iterations = gradient_descent(f, grad_f, x0, epsilon=1e-6) print(f"找到的解: {solution}") print(f"实际最小值点: [1.0, 2.0]") print(f"迭代次数: {iterations}")
|
Gauss-Newton法
**目的:**解决最小二乘法的问题
步骤:
- 给定解的初始估计 x0,ε>0,k:=0;
- 如果 x(k) 满足精度要求,停止迭代;
- 解方程组 JkTJkdk=−JkTrk;
- 更新解:
xk:=xk+αkdk,
其中 αk 为一维线搜索结果,k:=k+1,转步2。
注释:
- 基本Gauss-Newton法:步长固定为 αk=1;
- 阻尼Gauss-Newton法:步长 αk 通过一维线搜索动态确定。
Gauss-Newton法的优缺点
优点
- 零残量问题
当 S∗=0 时,具有局部二阶收敛速度;
- 小残量问题
当 S∗ 较小或问题接近线性时,具有快速的局部收敛速度;
- 线性最小二乘问题
可通过一步迭代达到极小点。
缺点
- 大残量问题
对于非严重的大残量问题,收敛速度较慢;
- 强非线性或极大残量问题
当残量极大或 f(x) 非线性程度过高时,算法可能不收敛;
- Jacobian矩阵不满秩
若 Jk 不满秩,方法无法定义;
- 全局收敛性
算法不一定保证总体收敛。
公式说明:
- S∗: 残量平方和的最小值
- Jk: 第 k 次迭代的 Jacobian 矩阵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import numpy as np
def gauss_newton(residual, jacobian, x0, epsilon, max_iter, damped=False): """Gauss-Newton法实现(支持阻尼选项)
参数: residual -- 残差函数,输入参数向量返回残差向量 jacobian -- 雅可比矩阵函数,输入参数向量返回雅可比矩阵 x0 -- 初始估计值向量 epsilon -- 收敛精度(残差范数阈值) max_iter -- 最大迭代次数 damped -- 是否启用阻尼(默认False)
返回: x -- 最终迭代得到的最优解向量 """ x = np.array(x0, dtype=float) for k in range(max_iter): r = residual(x) residual_norm = np.linalg.norm(r) if residual_norm < epsilon: break J = jacobian(x) JtJ = J.T @ J Jtr = J.T @ r try: d = np.linalg.solve(JtJ, -Jtr) except np.linalg.LinAlgError: d, *_ = np.linalg.lstsq(JtJ, -Jtr, rcond=None) if damped: def cost(alpha): return 0.5 * np.linalg.norm(residual(x + alpha * d))**2 alpha = 1.0 c = 0.1 rho = 0.5 grad = J.T @ r grad_dot_d = grad.dot(d) sufficient_decrease = cost(alpha) <= cost(0) + c * alpha * grad_dot_d while not sufficient_decrease and alpha > 1e-6: alpha *= rho sufficient_decrease = cost(alpha) <= cost(0) + c * alpha * grad_dot_d else: alpha = 1.0 x += alpha * d return x
|
LMF方法
步骤:
- 初始化
给定 x0∈Rn,v0>0,ε>0,k:=0;
- 终止条件
若满足终止条件,输出结果并停止迭代;
- 求解方程
计算 (JkTJk+vkI)dk=−JkTrk,得到步长 dk;
- 计算步长质量
计算 ρk=△qk△fk;
- 更新阻尼因子
- 若 ρk<0.25,则 vk+1=4vk;
- 若 ρk>0.75,则 vk+1=vk/2;
- 否则 vk+1=vk;
- 迭代更新
- 若 ρk≤0,则 xk+1=xk;
- 否则 xk+1=xk+dk;
- 令 k:=k+1,转步骤2。
公式说明:
- Jk: 第 k 次迭代的 Jacobian 矩阵
- rk: 第 k 次迭代的残差向量
- vk: 阻尼因子(控制步长可靠性)
- ρk: 步长质量指标(评估步长接受程度)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| import numpy as np
def levenberg_marquardt(residual, jacobian, x0, epsilon, max_iter, v0=0.01): """Levenberg-Marquardt算法实现(非线性最小二乘问题)
参数: residual -- 残差函数,输入参数向量返回残差向量 jacobian -- 雅可比矩阵函数,输入参数向量返回雅可比矩阵 x0 -- 初始估计值向量 epsilon -- 收敛精度(残差范数阈值) max_iter -- 最大迭代次数 v0 -- 初始阻尼因子(默认0.01)
返回: x -- 最终迭代得到的最优解向量 """ x_prev = np.array(x0, dtype=float) v = v0 k = 0 while k < max_iter: r = residual(x_prev) residual_norm = np.linalg.norm(r) if residual_norm < epsilon: break J = jacobian(x_prev) JtJ = J.T @ J Jtr = J.T @ r A = JtJ + v * np.eye(len(x_prev)) try: d = np.linalg.solve(A, -Jtr) except np.linalg.LinAlgError: d, *_ = np.linalg.lstsq(A, -Jtr, rcond=None) x_candidate = x_prev + d r_new = residual(x_candidate) delta_f = 0.5 * (np.linalg.norm(r)**2 - np.linalg.norm(r_new)**2) JtJd = JtJ @ d delta_q = d.dot(JtJd) + v * (d.dot(d)) if delta_q <= 0: rho = -np.inf else: rho = delta_f / delta_q if rho < 0.25: v_new = v * 4.0 elif rho > 0.75: v_new = v / 2.0 else: v_new = v if rho <= 0: x_next = x_prev.copy() v = v_new else: x_next = x_candidate v = v_new x_prev = x_next k += 1 return x_prev
|
外点罚函数法
步0:选定初始点 x0∈Rn;初始罚因子 μ1>0,放大系数 σ>1,精度 ε>0。置 k=1。
步1:构造增广目标函数
Fμk(x)=f(x)+21μkP(x)
步2:以 xk−1 为初始点求解无约束问题:
minFμk(x),得解 xk
步3:若 μkP(xk)<ε,停止迭代,输出 xk;否则转步4。
步4:更新罚因子 μk+1=σμk,置 k=k+1,转步1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import numpy as np
def exterior_penalty_method(f, P, x0, mu1, sigma, epsilon, solver): """ 参数说明: f: 原目标函数 f(x) P: 罚函数 P(x) x0: 初始点,numpy数组 mu1: 初始罚因子,正数 sigma: 放大系数,sigma > 1 epsilon: 精度要求 solver: 无约束优化子程序,输入一个目标函数和初始点,输出局部极小点 """
xk = np.array(x0) muk = mu1 k = 1
while True: def F_mu(x): x = np.array(x) return f(x) + (1 / (2 * muk)) * P(x)
xk = solver(F_mu, xk)
if muk * P(xk) < epsilon: return xk
muk = sigma * muk k += 1
|
内点罚函数法
步0:选定初始点 x0∈Rn;初始罚因子 μ1>0,放大系数 σ>1,精度 ε>0。置 k=1。
步1:构造增广目标函数
Fμk(x)=f(x)+μk−1S(x)
步2:以 xk−1 为初始点求解无约束问题:
minFμk(x),得解 xk
步3:若 μkS(xk)<ε,停止迭代,输出 xk;否则转步4。
步4:更新罚因子 μk+1=σμk,置 k=k+1,转步1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import numpy as np
def interior_penalty_method(f, S, x0, mu1, sigma, epsilon, solver): """ 参数说明: f: 原目标函数 f(x) S: 罚函数 S(x)(比如 -log形式) x0: 初始点,numpy数组 mu1: 初始罚因子,正数 sigma: 放大系数,sigma > 1 epsilon: 精度要求 solver: 无约束优化子程序,输入一个目标函数和初始点,输出局部极小点 """
xk = np.array(x0) muk = mu1 k = 1
while True: def F_mu(x): x = np.array(x) return f(x) + (1 / muk) * S(x)
xk = solver(F_mu, xk)
if muk * S(xk) < epsilon: return xk
muk = sigma * muk k += 1
|
等式约束问题的乘子法
步0:选定初始点 x0∈Rn,初始乘子估计 λ1∈Rm;初始罚因子 μ1>0,常数 σ>1,β∈(0,1);精度 ε>0。置 k:=1。
步1:构造增广目标函数
Lμk(x,λk)=L(x,λk)+21μkS(x)
其中 L(x,λk)=f(x)−λkTh(x)。
步2:以 xk−1 为初始点求解无约束问题:
xminLμk(x,λk)
设其解为 xk。
步3:若 ∥h(xk)∥≤ε,则得解 xk,STOP;否则转步4。
步4:若 ∥h(xk−1)∥∥h(xk)∥≥β 成立,则令 μk+1=μk;否则 μk+1=σμk。
步5:令 λk+1=λk−μkh(xk),置 k:=k+1,转步1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import numpy as np
def multiplier_method(f, h, x0, lambda0, mu1, sigma, beta, epsilon, solver): """ 参数说明: f: 目标函数 f(x) h: 等式约束函数 h(x),返回一个向量 x0: 初始点,numpy数组 lambda0: 初始乘子估计,numpy数组 mu1: 初始罚因子,正数 sigma: 放大系数,sigma > 1 beta: 收敛判别系数,0 < beta < 1 epsilon: 精度要求 solver: 无约束优化子程序,输入一个目标函数和初始点,输出局部极小点 """
xk = np.array(x0) lambdak = np.array(lambda0) muk = mu1 k = 1
while True: def L_mu(x): x = np.array(x) L = f(x) - np.dot(lambdak, h(x)) penalty = (1/2) * muk * np.linalg.norm(h(x))**2 return L + penalty
x_prev = xk.copy() xk = solver(L_mu, xk)
if np.linalg.norm(h(xk)) <= epsilon: return xk
if np.linalg.norm(h(xk)) / np.linalg.norm(h(x_prev)) >= beta: muk = muk else: muk = sigma * muk
lambdak = lambdak - muk * h(xk) k += 1
|
一般约束问题的乘子法
步0:初始化参数
- 初始点 x0∈Rn
- 乘子估计 λ(1)∈Rm(需满足 λi(1)≥0, ∀i∈I)
- 初始罚因子 μ1>0,放大系数 σ>1,收缩率 β∈(0,1)
- 精度阈值 ε>0
- 置迭代计数器 k=1
步1:构造增广Lagrange函数
Lμ(x,λ)=f(x)+2μ1i∈I∑(max2{0,λi−μgi(x)}−λi2)−j∈E∑λjhj(x)+2μj∈E∑hj2(x)
步2:求解无约束子问题
以 xk−1 为初始点,求解:
xminLμk(x,λ(k))
得到解 xk
步3:终止判定
若 ∥h(xk)∥+∥min(g(xk),0)∥≤ε,输出 xk 并终止;否则转步4
步4:自适应罚因子更新
若 ∥h(xk−1)∥+∥min(g(xk−1),0)∥∥h(xk)∥+∥min(g(xk),0)∥≥β, 则 μk+1=μk
否则 μk+1=σμk
步5:乘子更新
{λi(k+1)=max{0, λi(k)−μgi(xk)},λj(k+1)=λj(k)−μhj(xk),∀i∈I∀j∈E
置 k:=k+1,返回步1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import numpy as np
def general_multiplier_method(f, g, h, x0, lambda0, mu1, sigma, beta, epsilon, solver): """ 参数说明: f: 目标函数 f(x) g: 不等式约束函数组 g(x),返回一个numpy数组,g_i(x) <= 0 h: 等式约束函数组 h(x),返回一个numpy数组,h_j(x) = 0 x0: 初始点,numpy数组 lambda0: 初始乘子估计(先 g 后 h 排列),numpy数组 mu1: 初始罚因子,正数 sigma: 放大系数,sigma > 1 beta: 收缩率,0 < beta < 1 epsilon: 精度阈值 solver: 无约束优化子程序,输入一个目标函数和初始点,输出局部极小点 """
xk = np.array(x0) lambdak = np.array(lambda0) muk = mu1 k = 1
m_g = len(g(xk)) m_h = len(h(xk))
while True: def L_mu(x): x = np.array(x) gx = g(x) hx = h(x) term1 = f(x) term2 = (1 / (2 * muk)) * np.sum((np.maximum(0, lambdak[:m_g] - muk * gx))**2 - lambdak[:m_g]**2) term3 = - np.dot(lambdak[m_g:], hx) term4 = (muk / 2) * np.sum(hx**2) return term1 + term2 + term3 + term4
x_prev = xk.copy() xk = solver(L_mu, xk)
violation = np.linalg.norm(h(xk)) + np.linalg.norm(np.minimum(g(xk), 0)) if violation <= epsilon: return xk
prev_violation = np.linalg.norm(h(x_prev)) + np.linalg.norm(np.minimum(g(x_prev), 0)) if violation / prev_violation >= beta: muk = muk else: muk = sigma * muk
gx = g(xk) hx = h(xk) lambda_g_new = np.maximum(0, lambdak[:m_g] - muk * gx) lambda_h_new = lambdak[m_g:] - muk * hx lambdak = np.concatenate([lambda_g_new, lambda_h_new])
k += 1
|