【学习笔记】最优化方法
【学习笔记】最优化方法
Armijo型非精确线性搜索求步长
Armijo 型线性搜索的数学条件定义为:
参数说明:
- 是预定义常数,且
- 表示第 次迭代的步长
- 为搜索方向
- 是目标函数在 处的梯度
公式作用:
该条件通过限制步长 的取值,确保每次迭代中函数值 充分下降,常用于梯度下降法等优化算法的步长选择。
算法2.3(Armijo型线性搜索)步骤:
步0:若 满足上述数学条件,则取 ;否则转步1
步1:给定常数 ,,令
步2:若 满足条件(2.6),则得到步长 ,终止计算;否则转步3
步3:令 ,转步2
参数说明:
- :初始试探步长(通常取 )
- :步长缩减因子(控制步长收缩速度)
- :第 次迭代的候选步长
算法注释:
- 先尝试 ,若满足下降条件(2.6),则直接采用单位步长
- 若不满足,则从较大步长 开始,通过循环逐步缩小步长()
- 适用于梯度下降法、拟牛顿法等优化算法,保证迭代过程中目标函数值 充分下降
1 | import numpy as np |
Wolfe-Powell非精确线性搜索求步长
Wolfe-Powell 线性搜索的数学条件定义为:
- Armijo 条件(充分下降条件):
- 曲率条件:
参数约束:
参数说明:
- :控制下降量的常数(通常取 )
- :曲率条件的松弛系数(通常取 或 )
- :第 次迭代的候选步长
- :搜索方向(如梯度下降方向 )
- :目标函数在 处的梯度
公式作用:
- Armijo 条件:确保步长 使函数值 充分下降
- 曲率条件:保证步长 不会过小(避免算法停滞),同时允许梯度模长适当增大
- 参数约束: 保证存在满足两条件的步长
补充说明:
- 与 Armijo 准则相比,Wolfe-Powell 增加了曲率条件,避免步长选取过于保守
- 广泛应用于共轭梯度法、拟牛顿法等需要保证“充分下降且合理步长”的优化算法
- 条件中梯度内积 通常为负数(当 是下降方向时)
算法步骤(修正后规范表达):
-
步0:若 满足条件 (2.7),则取 ;否则转步1
- 作用:优先尝试单位步长,若满足条件则直接采用
-
步1:给定参数 ,,初始化 为集合 中满足第一个不等式的最大候选步长,令
- 数学意义:在离散化网格 中搜索满足条件的最大步长
-
步2:若 满足第二个条件,则取 ;否则令
- 逻辑说明:若不满足条件,则放大搜索范围上限( 是原步长的 倍)
-
步3:在区间 内,通过集合 重新搜索满足第一个不等式的最大步长 ,令 ,转步2
- 几何解释:在调整后的步长区间内,以 为细分因子进行二次搜索
参数说明:
- :初始试探步长基准值(控制搜索范围)
- :步长网格收缩因子(用于生成离散候选步长 )
- :区间细分因子(控制二次搜索的精细程度)
- :第 次迭代时的候选步长
解题思路与算法逻辑:
- 核心目标:通过动态调整步长区间,高效找到同时满足两个条件 (2.7) 的
- 创新点:
- 双重网格搜索:先用 生成粗粒度候选步长,再用 在调整后的区间内细粒度搜索
- 区间动态扩展:当候选步长不满足条件时,通过 扩大搜索范围
- 收敛保证: 确保步长在有限次迭代内缩小到满足条件
补充说明:
- 条件 (2.7) 应包含两个不等式(推测为类似 Wolfe-Powell 条件的 Armijo 条件和曲率条件)
- 该算法比标准 Armijo 搜索更复杂,适用于需要同时满足多个条件的优化问题
- 离散化集合 和 的设计平衡了计算效率与精度
1 | import numpy as np |
黄金分割法精确线性搜索求步长
输入:初始区间 ,精度要求
输出:极小值点所在区间
-
初始化:
- 计算初始内点:
- 计算函数值 和 ,令
- 计算初始内点:
-
停止条件:
- 若 ,终止计算,输出区间
-
区间更新:
-
情况1:若
- 更新左端点:,
- 新内点:
- 保留原右内点:
- 仅需计算
-
情况2:若
- 更新右端点:,
- 新内点:
- 保留原左内点:
- 仅需计算
-
-
迭代循环:
- 令 ,返回步骤2
参数说明:
- :第 次迭代的黄金分割点(比例系数 0.382 和 0.618)
- :区间长度精度阈值(控制算法终止条件)
- :第 次迭代时的候选区间
算法原理:
- 黄金分割比:0.618 是 的近似值,确保每次迭代区间长度按固定比例 收缩
- 单点复用:在区间更新时保留一个旧内点,只需计算一个新点函数值,减少计算量
- 单调收敛性:每次迭代排除不可能包含极小值的子区间,保证
解题思路:
-
初始区间选择:需满足 在 内为单峰函数
-
关键步骤验证:
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 # 黄金比例 0.618
# 计算初始内点
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:
# 情况1:f(λ_k) > f(μ_k),收缩左侧区间
a = lambda_k
lambda_k = mu_k # 保留原右内点
f_lambda = f_mu # 复用已计算值
mu_k = a + phi * (b - a) # 计算新点
f_mu = f(mu_k)
else:
# 情况2:f(λ_k) <= f(μ_k),收缩右侧区间
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 # 返回最终区间
信赖域算法
修正后的规范算法步骤:
输入:初始点 ,最大信赖域半径 ,精度阈值 ,阈值参数
输出:优化解
-
初始化:
- 设定初始信赖域半径 ,迭代计数器
-
停止条件:
- 计算梯度
- 若 ,终止算法,输出
-
子问题求解:
- 在信赖域 内,近似求解:
得到试探步 ,其中 是Hessian矩阵或其近似
- 在信赖域 内,近似求解:
-
接受步长:
- 计算实际下降量
- 计算预测下降量
- 计算比值:
- 更新迭代点:
-
调整信赖域半径:
-
循环迭代:
- 令 ,返回步骤2
参数说明:
- :信赖域半径的最大允许值(防止过度扩张)
- :阈值参数,控制半径调整的敏感性(典型值:, )
- :二阶模型中的Hessian近似矩阵(如BFGS更新、SR1更新等)
- :实际下降与预测下降的比值(衡量模型可靠性)
算法原理:
- 信赖域框架:通过局部二次模型 近似目标函数,在半径 内寻找最优步长
- 半径动态调整:
- 接近 1:扩大信赖域(模型可信度高)
- 接近 0:缩小信赖域(模型不可靠)
- 子问题求解:常用Steihaug-CG方法或狗腿法(Dogleg)近似求解
- 全局收敛性:在适当条件下,算法满足
解题思路:
-
关键公式验证:
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) # 计算 Hessian 矩阵
# 处理 Hessian 矩阵奇异性,确保可逆
try:
s_k = -np.linalg.solve(B_k, g_k) # 计算 Newton 步长
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
r_k = delta_f / delta_m if delta_m != 0 else 0
# 更新 x_k
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
最速下降法
输入:初始点 $ x_0 \in \mathbb{R}^n $,精度阈值 $ \epsilon > 0 $
输出:优化解 $ x_k $
-
初始化:
- 设定 $ k = 0 $,给定初始点 $ x_0 $
-
停止条件:
- 计算梯度 $ \nabla f(x_k) $
- 若 $ |\nabla f(x_k)| \leq \epsilon $,终止算法,输出 $ x_k $
-
方向计算:
- 取搜索方向为负梯度方向:
- 取搜索方向为负梯度方向:
-
步长计算:
- 通过一维精确线搜索求解:
- 通过一维精确线搜索求解:
-
迭代更新:
- 更新迭代点:
- 令 $ k = k + 1 $,返回步骤2
- 更新迭代点:
关键点解析:
-
核心思想:
- 沿目标函数下降最快的方向(负梯度方向)更新迭代点
- 通过精确线搜索保证每步下降量最大
-
方向选择:
- 方向 $ d_k = -\nabla f(x_k) $ 是当前点的最速下降方向
- 梯度方向是函数值局部上升最快的方向,负梯度则为下降最快方向
-
步长计算:
- 精确线搜索需解一维优化问题,对简单函数可解析求导(如二次函数)
- 一般函数可能需要黄金分割法、抛物线插值法等数值方法
收敛性分析:
-
收敛条件:
- 在目标函数 $ f(x) $ 为凸且光滑的条件下,算法全局收敛
- 线性收敛速度:相邻迭代点误差满足 $ |x_{k+1} - x^| \leq \rho |x_k - x^| $,其中 $ 0 < \rho < 1 $
-
局限性:
- 高维问题中可能出现“锯齿现象”,收敛速度显著下降
- 对非凸函数可能收敛到局部极小或鞍点
解题思路(以二次函数为例):
设目标函数为 $ f(x) = \frac{1}{2}x^T Q x + b^T x $,其中 $ Q $ 对称正定
- 梯度计算:
- 方向更新:
- 精确步长解析解:
示例(二维情况):
- 令 $ Q = \begin{bmatrix} 2 & 0 \ 0 & 1 \end{bmatrix} $, $ b = \begin{bmatrix} -1 \ 0 \end{bmatrix} $, $ x_0 = \begin{bmatrix} 0 \ 0 \end{bmatrix} $
- 第1次迭代:
- $ \nabla f(x_0) = \begin{bmatrix} -1 \ 0 \end{bmatrix} $
- $ d_0 = \begin{bmatrix} 1 \ 0 \end{bmatrix} $
- $ \alpha_0 = \frac{1}{2} $
- $ x_1 = \begin{bmatrix} 0.5 \ 0 \end{bmatrix} $
对比与扩展:
-
与信赖域算法区别:
- 最速下降法仅依赖梯度,而信赖域法需构建二阶模型
- 最速下降法步长由线搜索确定,信赖域法步长受半径约束
-
改进方向:
- 预处理技术:通过坐标变换减少条件数,缓解锯齿现象
- 非精确线搜索:使用Armijo准则、Wolfe条件降低计算成本
1 | import numpy as np |
基本Newton法
输入:初始点 $ x_0 \in \mathbb{R}^n $,精度阈值 $ \varepsilon > 0 $
输出:优化解 $ x_k $
-
初始化:
- 设定 $ k = 0 $,给定初始点 $ x_0 $
-
停止条件:
- 计算梯度 $ \nabla f(x_k) $
- 若 $ |\nabla f(x_k)| \leq \varepsilon $,终止算法,输出 $ x_k $
-
方向计算:
- 解线性方程组:
- 得到搜索方向 $ d_k $
- 解线性方程组:
-
迭代更新:
- 更新迭代点:
- 令 $ k = k + 1 $,返回步骤2
- 更新迭代点:
关键点解析:
-
核心思想:
- 利用目标函数的二阶导数(Hessian矩阵)构造搜索方向,实现局部快速收敛
- 通过牛顿方程(公式3.3)直接求解优化方向,默认步长 $ \alpha_k = 1 $
-
方向计算:
- 方向 $ d_k $ 是牛顿方向,满足 $ d_k = -[\nabla^2 f(x_k)]^{-1} \nabla f(x_k) $
- 当Hessian矩阵正定时,方向为下降方向
-
步长特性:
- 基本Newton法固定步长为1,适用于靠近极值点的情况
- 改进方法(如阻尼Newton法)会引入线搜索调整步长
收敛性分析:
-
收敛速度:
- 局部二次收敛:若初始点 $ x_0 $ 充分接近极小点且Hessian连续且正定,则
- 相比最速下降法(线性收敛),收敛速度显著提升
- 局部二次收敛:若初始点 $ x_0 $ 充分接近极小点且Hessian连续且正定,则
-
局限性:
- Hessian矩阵可能非正定或奇异,导致方向非下降或无法求解
- 初始点选择敏感,远离极值点时可能发散
解题示例(二次函数优化):
设目标函数为 $ f(x) = \frac{1}{2}x^T Q x + b^T x $,其中 $ Q $ 对称正定
- 梯度与Hessian:
- 牛顿方向计算:
- 迭代更新:
数值算例:
- 令 $ Q = \begin{bmatrix} 2 & 1 \ 1 & 2 \end{bmatrix} $, $ b = \begin{bmatrix} -3 \ -3 \end{bmatrix} $, $ x_0 = \begin{bmatrix} 0 \ 0 \end{bmatrix} $
- 第1次迭代:
- $ \nabla f(x_0) = \begin{bmatrix} -3 \ -3 \end{bmatrix} $
- 解方程 $ Q d_0 = -\nabla f(x_0) \Rightarrow d_0 = \begin{bmatrix} 1 \ 1 \end{bmatrix} $
- $ x_1 = x_0 + d_0 = \begin{bmatrix} 1 \ 1 \end{bmatrix} $(精确解)
1 | import numpy as np |
阻尼Newton法
算法(阻尼Newton法)
输入:初始点 $ x_0 \in \mathbb{R}^n $,精度阈值 $ \varepsilon > 0 $
输出:优化解 $ x_k $
-
初始化:
- 设定 $ k = 0 $,给定初始点 $ x_0 $
-
停止条件:
- 计算梯度 $ \nabla f(x_k) $
- 若 $ |\nabla f(x_k)| \leq \varepsilon $,终止算法,输出 $ x_k $
-
方向计算:
- 解线性方程组:
- 得到搜索方向 $ d_k $
- 解线性方程组:
-
步长计算:
- 通过线搜索(如Armijo准则、Wolfe条件)确定步长 $ \alpha_k $,确保目标函数值下降
-
迭代更新:
- 更新迭代点:
- 令 $ k = k + 1 $,返回步骤2
- 更新迭代点:
关键点解析:
-
核心改进:
- 相比基本Newton法(固定步长 $ \alpha_k = 1 $),阻尼法引入线搜索动态调整步长,提升全局收敛性。
- 适用于初始点远离极值点或Hessian矩阵非正定的情况。
-
方向与步长的协同:
- 方向:仍为牛顿方向 $ d_k = -[\nabla^2 f(x_k)]^{-1} \nabla f(x_k) $,需Hessian正定以保证下降性。
- 步长:通过线搜索找到 $ \alpha_k \in (0, 1] $,使 $ f(x_k + \alpha_k d_k) < f(x_k) $。
-
适用场景:
- 目标函数局部凸性较弱时,步长调整可避免迭代发散。
- Hessian矩阵条件数较差时(如接近奇异),阻尼法更稳定。
对比基本Newton法:
特性 | 基本Newton法 | 阻尼Newton法 |
---|---|---|
步长策略 | 固定步长 $ \alpha_k = 1 $ | 动态步长 $ \alpha_k \in (0, 1] $ |
收敛性 | 局部二次收敛 | 全局收敛(需合理线搜索) |
计算成本 | 低(无需步长搜索) | 较高(需多次函数值计算) |
初始点敏感性 | 高(依赖初始点接近极值点) | 低(适应性更强) |
收敛性分析:
- 全局收敛:
- 在适当线搜索条件下(如Armijo准则),算法对任意初始点 $ x_0 $ 均收敛到局部极小点。
- 局部收敛速度:
- 若初始点充分接近极小点且Hessian连续正定,仍保持局部二次收敛速度。
-
解题示例(二次函数优化):
设目标函数 $ f(x) = \frac{1}{2}x^T Q x + b^T x $,其中 $ Q $ 对称正定
- 梯度与Hessian:
- 牛顿方向:
- 步长计算:
- 二次函数下,线搜索直接得 $ \alpha_k = 1 $(一步收敛)
- 迭代更新:
数值算例(同基本Newton法):
- $ Q = \begin{bmatrix} 2 & 1 \ 1 & 2 \end{bmatrix} $, $ b = \begin{bmatrix} -3 \ -3 \end{bmatrix} $, $ x_0 = \begin{bmatrix} 0 \ 0 \end{bmatrix} $
- 第1次迭代:
- $ \nabla f(x_0) = \begin{bmatrix} -3 \ -3 \end{bmatrix} $
- 解方程 $ Q d_0 = -\nabla f(x_0) \Rightarrow d_0 = \begin{bmatrix} 1 \ 1 \end{bmatrix} $
- 线搜索得 $ \alpha_0 = 1 $,故 $ x_1 = x_0 + d_0 = \begin{bmatrix} 1 \ 1 \end{bmatrix} $(精确解)
- 梯度与Hessian:
1 | import numpy as np |
Newton-最速下降混合法
步骤1:给定初始点 ,精度 。令 ;
步骤2:若 ,则得解 ,算法终止。否则,解线性方程组
若有解 且满足 ,转步骤3;否则取 ;
步骤3:由线性搜索计算步长 ;
步骤4:令 ,,转步骤2。
1 | import numpy as np |
修正Newton-LM法
步骤1 给定初始点 $ x_0 \in \mathbb{R}^n $,精度 $ \varepsilon > 0 $。令 $ k = 0 $。
步骤2 若 $ |\nabla f(x_k)| \leq \varepsilon $,则得解 $ x_k $,算法终止;否则,解线性方程组
得解 $ d_k $,其中 $ A_k = \nabla^2 f(x_k) + v_k I v_k > 0 $ 使得 $ A_k $ 正定。
步骤3 通过线性搜索计算步长 $ \alpha_k $。
步骤4 令 $ x_{k+1} = x_k + \alpha_k d_k k := k + 1 $,转至步骤2。
参数说明
在修正Newton法中:
- 为确保 $ A_k $ 的正定性,需选择足够大的 $ v_k > 0 $;
- 为确保算法收敛性,需限制 $ v_k $ 的上界,即要求
其中 $ C $ 为常数,但其具体确定较为困难。
修正形式的收敛性
- Newton法的两种修正形式在较弱条件下具有:
- 超线性收敛性
- 二次收敛性
- 存在多种其他修正形式
鞍点情况的特殊处理
当 $ x $ 为鞍点时(即 $ \nabla f(x) = 0 $ 但 $ \nabla^2 f(x) $ 不定):
- 所有修正方法均失效
- 可取负曲率方向 $ d $,需满足:
沿此方向搜索时,目标函数值必然下降
方法特性对比
优点 | 缺点 |
---|---|
收敛速度快 | 对初始点要求高 |
- | 计算量大 |
算法改进方向
通过修改Newton法:
- 保留优势:利用快速收敛特性
- 克服缺陷:降低对初始点的敏感性,减少计算量
- 已衍生出多种高效新算法
拟Newton法
步骤1 给定初始点 $ x_0 \in \mathbb{R}^n $,初始化矩阵 $ B_0 $ 或 $ H_0 $,精度阈值 $ \varepsilon > 0 $。令迭代计数器 $ k = 0 $。
步骤2 若满足终止准则 $ |\nabla f(x_k)| \leq \varepsilon $,输出解 $ x_k $ 并终止算法。
步骤3 求解线性方程组:
得搜索方向 $ d_k $,或通过逆矩阵公式直接计算:
步骤4 通过线搜索计算步长 $ \alpha_k $,更新迭代点:
步骤5 按拟Newton条件(DFP/SRP1等)修正矩阵:
使其满足拟Newton方程 或 。令 $ k := k + 1 $,返回步骤2。
修正矩阵
对称秩1
SR修正公式形式1
SR修正公式形式2
对称秩2
修正公式(迄今最好的拟牛顿修正公式)
的逆修正公式
修正公式
的逆修正公式
BFGS算法
步骤1:给定初始点 ,初始对称正定矩阵 ,精度 。令 。
步骤2:若 ,则得解 ,算法终止。否则转下一步。
步骤3:解线性方程组
得解 。
步骤4:由线性搜索计算步长 。
步骤5:令 ,若 ,则得解 ,算法终止。否则由公式 计算 。
步骤6:令 ,转步骤3。
1 | import numpy as np |
DFP算法
步骤1:给定初始点 ,初始对称正定矩阵 ,精度 。令 。
步骤2:若 ,则输出解 ,算法终止;否则继续。
步骤3:计算搜索方向:
(或通过解线性方程组 ,其中 由公式 计算)
步骤4:通过线性搜索确定步长 。
步骤5:更新迭代点:
若 ,输出解 并终止;否则按公式 计算 。
步骤6:令 ,转步骤3。
1 | import numpy as np |
Broyden族
对互为对偶的BFGS公式和DFP公式进行加权组合得到一类重要的拟Newton矩阵修正公式:Broden族
其中 ,
由人工神经网络方法解微分方程导出的最优化问题
思维学一般把人类的大脑思维分为抽象(逻辑)思维,形象(直观)思维和灵感(顿悟)思维三种基本方式,人工神经网络就是模拟人类思维的形象思维方式去解决问题的。神经网络的基础是神经元,神经元是以生物神经系统的神经细胞为基础的生物模型。人们对生物神经系统进行研究以探讨人工智能的机制时,把神经元数学化,从而产生了神经元数学模型,大量相同形式的神经元连接在一起就组成了神经网络
神经网络是一个高度非线性动力学系统,虽然每个神经元的结构和功能都不复杂,但是神经网络的动态行为却是十分复杂的,因此,用神经网络可以表达实际物理世界的各种现象。
我们可以用有向图表示神经网络,这里有向图是节点与节点之间的有向连接。一般来说,神经网络有两种基本结构:前馈神经网络和递归神经网络。这里我们仅考虑前馈神经网络,它包括:
输入层:源节点构成输入层,它向网络提供输入信号
隐藏层:前馈神经网络有一层或多层隐藏节点,相应的节点称为隐藏神经元
输出层:该层给出相对于源节点的激活模式的网络输出
一个 前馈神经网络,表示该网络有 个源节点, 第一个隐藏层有 个神经元,第二个隐藏层有 个神经元,输出层有 个神经元
求解微分方程的方法是多种多样的,如下有一种用神经网络的方法求解微分方程的方法
考虑一阶常微分方程
其初始条件为
利用神经网络的方法我们可以得到一个近似解
可以用它去近似方程的精准解 ,其中实验解中的参数 为神经网络方法所产生的参数,下面我们讨论神经网络实验解的表示
考虑一个 的前馈神经网络,一阶微分方程的神经网络实验解可以表示为
其中 为前馈神经网络的输出, 是单一输入, 是神经网络参数向量。前馈神经网络的输出为
其中 是从源节点到隐藏节点 的权重 , 是从隐藏节点 到输出节点的权重, 是隐藏节点 的阈值, 是一个 维向量 , 是 Sigmoid型激活函数
为了使得实验解能尽可能好地近似方程的解,我们要用最优化的方法去确定实验解中的参数 。对于一阶微分方程,我们要将 在 离散化,得到 个值 $0 \leq x_1<x_2……\leq x_m=1 $ 以及 , .其中 到 称为训练集元素.然后求解最优化问题
来确定 的值
对正定二次型的共轭梯度法
步0(初始化):
给定线性无关向量组 $ \mathbf{g} \in \mathbb{R}^n $,令
步1(方向更新):
计算搜索方向:
步2(终止条件):
若 $ k = n-1 $,则停止;否则继续。
步3(线搜索):
计算步长 $ \alpha_k $,使得:
步4(迭代更新):
-
计算梯度 $ \nabla f(\mathbf{x}_k + \alpha_k \mathbf{d}_k) $
-
令 $ k := k + 1 $,返回步1
1 | import numpy as np |
(FR)对正定二次型的共轭梯度法
算法步骤:
步0:给定线性无关向量组:$ g \in \mathbb{R}^n $,令 $ d_0 = -g_0 k = 0 $。
步1:计算
步2:若 $ k = n - 1 $,则停止。
步3:计算线搜索 $ f(x_k + \alpha d_k) $ 的最小值点 $ \alpha_k $。
步4:求梯度 $ \nabla f(x_k + \alpha_k d_k) $,令 $ k = k + 1 $,转步1。
1 | import numpy as np |
HS共轭梯度法
步骤1:
给定初始点 $ x_0 \in \mathbb{R}^n $,精度 $ \varepsilon > 0 $。
令 $ d_0 = -\nabla f(x_0) $,令 $ k := 0 $。
步骤2:
若 $ |\nabla f(x_k)| \leq \varepsilon $,则得解 $ x_k $,算法终止。否则转步骤3。
步骤3:
由线性搜索计算步长 $ \alpha_k $。
步骤4:
令 $ x_{k+1} = x_k + \alpha_k d_k $。
步骤5:
由式 $$
d_k = \begin{cases}
-\nabla f(x_0), & k=0 \
-\nabla f(x_k) + \beta_k d_{k-1}, & k \geq 1
\end{cases}
确定 $ d_{k+1} $,其中 $ \beta_{k+1} = \beta_{k+1}^{HS} $
其中 $
\beta_{k}^{\text{HS}} = \frac{
\nabla f(x_k)^T [
\nabla f(x_k) -
\nabla f(x_{k-1})]
}{d_{k-1}^T [
\nabla f(x_k) -
\nabla f(x_{k-1})]
}, \quad k=1, 2, \ldots
$
令 $ 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
55import 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:
# 线搜索计算步长 α_k
alpha_k = - (g.T @ d) / (d.T @ grad_f(x + d))
# 更新 x
x_new = x + alpha_k * d
g_new = grad_f(x_new) # 计算新的梯度
# 计算 β_{k+1}^{HS} (Hestenes-Stiefel 公式)
beta_hs = (g_new.T @ (g_new - g)) / (d.T @ (g_new - g))
# 更新搜索方向
d = -g_new + beta_hs * d
# 更新 x 和梯度
x, g = x_new, g_new
k += 1
return x
# 示例使用
if __name__ == "__main__":
# 目标函数 f(x) = 0.5 * x^T G x - b^T x
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)
\beta_k^{HS} = \dfrac {[∇f(x_k)^T (∇f(x_k) - ∇f(x_{k-1}))]} {[d_{k-1}^T (∇f(x_k) - ∇f(x_{k-1}))]}
\beta_k^{\text{PRP}} = \frac{
∇ f(x_k)^T [
∇ f(x_k) -
∇ f(x_{k-1})]}{|
∇ f(x_{k-1})|^2}
\beta_k^{FR}=\dfrac {|∇f(x_k)|^2}{|∇f(x_{k-1})|^2}
\beta_k^{CD}=-\dfrac {|∇f(x_k)|^2}{d_{k-1}^T∇f(x_{k-1})}
\beta_k^{DY}=\dfrac {|∇f(x_{k})|^2}{d_{k-1}^T[∇f(x_k)-∇f(x_{k-1})]}
### 非线性共轭梯度法
1. **步1** 给定初始点 $x_0 \in \mathbb{R}^n$,精度 $\varepsilon > 0$。令 $d_0 = -\nabla f(x_0)$,令 $k := 0$。
2. **步2** 若 $\|\nabla f(x_k)\| \leq \varepsilon$,则得解 $x_k$,算法终止。否则转步3。
3. **步3** 由线性搜索计算步长 $\alpha_k$。
4. **步4** 令 $x_{k+1} = x_k + \alpha_k d_k$。
5. **步5** 确定 $d_{k+1}$ 和 $\beta_k$,其中 $d_{k+1} = -\nabla f(x_{k+1}) + \beta_k d_k$。令 $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
81import numpy as np
from typing import Callable, Tuple
def gradient_descent(
f: Callable[[np.ndarray], float], # 目标函数,输入x,返回标量函数值f(x)
grad_f: Callable[[np.ndarray], np.ndarray], # 目标函数的梯度函数,输入x,返回梯度向量∇f(x)
x0: np.ndarray, # 初始点x_0∈R^n
epsilon: float = 1e-6, # 精度ε>0,用于判断梯度是否足够小以终止算法
max_iter: int = 1000, # 最大迭代次数,防止无限循环
line_search: Callable = None # 线性搜索函数,用于计算步长α_k
) -> Tuple[np.ndarray, int]:
"""
最速下降法(梯度下降法)实现
参数:
f: 目标函数
grad_f: 目标函数的梯度
x0: 初始点
epsilon: 收敛精度
max_iter: 最大迭代次数
line_search: 步长搜索函数
返回:
x_k: 找到的解
k: 迭代次数
"""
# 步1:初始化
x_k = x0.copy() # 初始点x_0
d_k = -grad_f(x_k) # 初始搜索方向d_0 = -∇f(x_0)
k = 0 # 迭代次数k初始化为0
# 步2:迭代直到满足终止条件
while k < max_iter:
grad_norm = np.linalg.norm(grad_f(x_k)) # 计算当前梯度的范数||∇f(x_k)||
# 检查是否满足终止条件:梯度范数是否小于等于ε
if grad_norm <= epsilon:
print(f"算法在 {k} 次迭代后收敛")
return x_k, k
# 步3:由线性搜索计算步长α_k
# 如果没有提供line_search函数,则使用固定步长(这里简化为0.01,实际应用中需要调整)
if line_search is None:
alpha_k = 0.01 # 固定步长,实际应用应使用线搜索或其他自适应方法
else:
alpha_k = line_search(f, grad_f, x_k, d_k) # 使用线搜索计算最优步长
# 步4:更新x_{k+1} = x_k + α_k * d_k
x_k_plus_1 = x_k + alpha_k * d_k
# 步5:计算新的搜索方向d_{k+1} = -∇f(x_{k+1}) + β_k * d_k
# 在最速下降法中,β_k = 0,即每次都是负梯度方向
# 这里实现的是最速下降法(β_k=0),但保留了β_k的接口以便扩展为共轭梯度法
beta_k = 0.0 # 对于最速下降法,β_k=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
# 示例使用:优化二次函数 f(x) = (x-1)^2 + (y-2)^2
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}")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
61import 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) # 将初始估计值转换为numpy数组
for k in range(max_iter):
# Step 2: 检查当前解是否满足精度要求
r = residual(x) # 计算当前参数的残差向量
residual_norm = np.linalg.norm(r) # 计算残差的欧氏范数
if residual_norm < epsilon: # 残差足够小则停止迭代
break
# Step 3: 解线性方程组 J^T J d = -J^T r
J = jacobian(x) # 计算当前参数的雅可比矩阵
JtJ = J.T @ J # 计算J的转置与J的乘积
Jtr = J.T @ r # 计算J的转置与残差向量的乘积
try:
# 尝试直接解线性方程组
d = np.linalg.solve(JtJ, -Jtr)
except np.linalg.LinAlgError: # 若矩阵不可逆则使用最小二乘解
d, *_ = np.linalg.lstsq(JtJ, -Jtr, rcond=None)
# Step 4: 计算步长alpha(阻尼时需进行一维搜索)
if damped: # 阻尼Gauss-Newton模式
# 定义目标函数(残差平方和的一半)
def cost(alpha):
return 0.5 * np.linalg.norm(residual(x + alpha * d))**2
alpha = 1.0 # 初始步长设为1
c = 0.1 # Armijo条件中的充分下降系数
rho = 0.5 # 步长缩减因子
grad = J.T @ r # 计算梯度∇f(x) = J^T r
grad_dot_d = grad.dot(d) # 梯度与方向d的点积
# 检查Armijo条件:cost(alpha) ≤ cost(0) + c*alpha*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: # 基本Gauss-Newton模式
alpha = 1.0 # 固定步长
# 更新当前解向量
x += alpha * d # 按步长alpha进行更新
return x # 返回最终迭代结果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
81import 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:
# Step 2: 检查终止条件
r = residual(x_prev) # 计算当前参数的残差向量
residual_norm = np.linalg.norm(r) # 计算残差的欧氏范数
if residual_norm < epsilon: # 残差足够小则停止迭代
break
# Step3: 解方程 (J^T J + vI) d = -J^T r
J = jacobian(x_prev) # 当前参数的雅可比矩阵
JtJ = J.T @ J # 计算J的转置与J的乘积
Jtr = J.T @ r # 计算J的转置与残差的乘积
# 构建矩阵A = JtJ + vI
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_candidate = x_prev + d
r_new = residual(x_candidate)
# Step4: 计算步长质量指标ρ
# 计算实际减少量Δf = 0.5*(||r||^2 - ||r_new||^2)
delta_f = 0.5 * (np.linalg.norm(r)**2 - np.linalg.norm(r_new)**2)
# 计算预测减少量Δq = d^T (JtJ d) + v*(d^T d)
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 # 计算步长质量
# Step5: 更新阻尼因子v
if rho < 0.25: # 步长质量差,增大阻尼因子
v_new = v * 4.0
elif rho > 0.75: # 步长质量好,减小阻尼因子
v_new = v / 2.0
else: # 保持当前阻尼因子
v_new = v
# Step6: 根据ρ值决定是否接受步长
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 # 返回最终解
步2:以 为初始点求解无约束问题:
步3:若 ,停止迭代,输出 ;否则转步4。
步4:更新罚因子 ,置 ,转步1。
1 | import numpy as np |
内点罚函数法
步0:选定初始点 ;初始罚因子 ,放大系数 ,精度 。置 。
步1:构造增广目标函数
步2:以 为初始点求解无约束问题:
步3:若 ,停止迭代,输出 ;否则转步4。
步4:更新罚因子 ,置 ,转步1。
1 | import numpy as np |
等式约束问题的乘子法
步0:选定初始点 ,初始乘子估计 ;初始罚因子 ,常数 ,;精度 。置 。
步1:构造增广目标函数
其中 。
步2:以 为初始点求解无约束问题:
设其解为 。
步3:若 ,则得解 ,STOP;否则转步4。
步4:若 成立,则令 ;否则 。
步5:令 ,置 ,转步1。
1 | import numpy as np |
一般约束问题的乘子法
步0:初始化参数
- 初始点
- 乘子估计 (需满足 )
- 初始罚因子 ,放大系数 ,收缩率
- 精度阈值
- 置迭代计数器
步1:构造增广Lagrange函数
步2:求解无约束子问题
以 为初始点,求解:
得到解
步3:终止判定
若 ,输出 并终止;否则转步4
步4:自适应罚因子更新
否则
步5:乘子更新
置 ,返回步1
1 | import numpy as np |