目录
前言:
由于最速下降法的搜索路径在靠近最优解附近呈锯齿状,所以搜索很慢,究其原因,应该是与搜索方向由目标函数的线性近似函数来决定有关。事实上,由一阶泰勒公式,有:
由于,所以
,这就是说在最速下降法中目标函数下降的幅度其实是由线性近似多项式函数的下降幅度来决定的,由此我们考虑,如果能利用函数的二次近似多项式来做目标函数的近似表达式,或许算法的收敛速度可以提高。因此,对于有较好的解析性质的目标函数,如具有二阶连续偏导数的目标函数,可以利用其二次近似函数的性质来界定新的搜索方向,或者以目标函数的二次近似函数的极小点作为新目标函数本身的近似解,如果不满足计算精度,则再求该点处目标函数的二次近似函数,进一步求新的近似函数的极小点,如此反复,直至近似点满足事先给定的精度为止,这就是牛顿法的基本思想。
Newton法:
算法步骤:
步骤1:选定初始点,计算
,k=1;
步骤2:如果,算法停止,
,否则,转步骤3;
步骤3:计算搜索方向
步骤4:令,k=k+1,转步骤2
(说明:搜索方向可通过
求出,步骤4中定的步长为1)
例题分析:
下面我们通过一道例题来加深一下对算法的认识。
例题:用Newton法求的极小点。
解:取初始点
计算:
代入Newton迭代公式:
得到问题的极小点.
结果分析:
由以上例题可以看出过程中一步迭代就达到了最优解,经过推论,我们发现,当f为正定二次函数时,用Newton法从任意初始点可一步达到极小点。
分析:
Newton法的优点在于当初始点离极小点很近时,算法收敛速度快;算法简单,不需要进行一维搜索,对正定二次函数迭代一次就可得到极小点。但是缺点也较为明显:当初始点远离最优解时,算法可能不收敛;Hesse矩阵非奇异时,算法不可行;计算量和存储量大。
那么我们下一步介绍改进后的Newton法——阻尼Newton法。
收敛性分析:
假设目标函数 具有二阶连续偏导数,并且 Hesse 矩阵在最优值点
的一个领域
内是利普希茨连续的,即存在常熟
,使得
,如果
在点
处满足
,
正定,那么对于牛顿法的迭代式具有一下结论:
(1)如果初始点 与最优点
足够近,则牛顿法产生的点列 {
} 收敛于
。
(2)点列 {} 收敛到
的速度是二阶的。
阻尼Newton法:
在Newton的基础上,将迭代公式,加入精确一维搜索:
,求得最佳步长
,得到下一个迭代点
,由此就可以改进Newton法的收敛性。
例题分析:
下面我们通过一道例题来加深一下对算法的认识。
例题:用阻尼Newton法求下列无约束优化的极小点,已知,
.
解:
故
,
,
令
得到问题的极小点=
算法代码:
'''
阻尼Newton算法
2023.10.19
'''
import numpy as np
from sympy import *
x1 = symbols("x1")
x2 = symbols("x2")
λ = symbols("λ") # 阻尼Newton算法,步长 λ
# f = x1**2 + 2*x2**2 - 2*x1*x2 - 4*x1
# f = x1**2 + 8*x2**2 - 2*x1*x2 + 4*x1 + 5
f = (x1 - x2) ** 2 + x2 ** 2 - 4 * x1
def gradient_descent(x1_init, x2_init, ε):
# 一阶求导
first_grad_1 = diff(f, x1)
first_grad_2 = diff(f, x2)
# 二阶求导
second_grad_1 = diff(first_grad_1, x1)
second_grad_2 = diff(first_grad_2, x2)
second_grad_12 = diff(first_grad_1, x2)
x1_curr = x1_init
x2_curr = x2_init
count = 0
while True:
count = count + 1
first_grad_1_value = first_grad_1.subs({x1: x1_curr, x2: x2_curr})
first_grad_2_value = first_grad_2.subs({x1: x1_curr, x2: x2_curr})
second_grad_1_value = second_grad_1.subs({x1: x1_curr, x2: x2_curr})
second_grad_2_value = second_grad_2.subs({x1: x1_curr, x2: x2_curr})
second_grad_12_value = second_grad_12.subs({x1: x1_curr, x2: x2_curr})
print("第{}次迭代".format(count - 1))
print("此时的解为[%f, %f]" % (float(x1_curr), float(x2_curr)))
arr = [[second_grad_1_value, second_grad_12_value], [second_grad_12_value, second_grad_2_value]]
arr = np.array(arr, dtype=np.float64)
arr = np.linalg.inv(arr)
d1 = -1 * (arr[0, 0] * first_grad_1_value + arr[0, 1] * first_grad_2_value)
d2 = -1 * (arr[1, 0] * first_grad_1_value + arr[1, 1] * first_grad_2_value)
k = np.array([first_grad_1_value, first_grad_2_value], dtype=float)
norm_result = np.linalg.norm(k, ord=2, axis=0)
# x1_curr = x1_curr + d1
# x2_curr = x2_curr + d2
x1_curr = x1_curr + λ * d1
x2_curr = x2_curr + λ * d2
f_new = f.subs({x1: x1_curr, x2: x2_curr})
grad_3 = diff(f_new, λ)
if grad_3 == 0:
print("该精度下的最优解是[%f, %f]" % (float(x1_curr), float(x2_curr)))
break
else:
λ_value = solve(grad_3, λ)[0]
print("λ的值为{}".format(λ_value))
x1_curr = x1_curr.subs(λ, λ_value)
x2_curr = x2_curr.subs(λ, λ_value)
if norm_result < ε:
print("该精度下的最优解是[%f, %f]" % (float(x1_curr), float(x2_curr)))
break
return x1_curr, x2_curr
result = gradient_descent(x1_init=1, x2_init=1, ε=0.1)
用该代码所运算的结果为:
第0次迭代
此时的解为[1.000000, 1.000000]
λ的值为1.00000000000000
第1次迭代
此时的解为[4.000000, 2.000000]
该精度下的最优解是[4.000000, 2.000000]
总结:
1)针对Hesse矩阵计算量的改进:
为减小工作量,取m(正整数),使每m次迭代使用同一个Hesse阵,迭代公式变成:
2)针对Hesse矩阵非正定和奇异的情况
取:,并采用下列非精确一维搜索(Armijo-Goldstein准则)求
,其中
(行文中若有纰漏,希望大家指正)。