最优化方法Python计算:非二次型共轭梯度算法

设目标函数 f ( x ) f(\boldsymbol{x}) f(x) x ∈ R n \boldsymbol{x}\in\text{ℝ}^n xRn,有最小值点 x 0 \boldsymbol{x}_0 x0且二阶可微,则在 x 0 \boldsymbol{x}_0 x0近旁可以用二次型函数
q ( x ) = 1 2 x ⊤ H x − x ⊤ g q(\boldsymbol{x})=\frac{1}{2}\boldsymbol{x}^\top\boldsymbol{H}\boldsymbol{x}-\boldsymbol{x}^\top\boldsymbol{g} q(x)=21xHxxg
其中 g = ∇ f ( x ) \boldsymbol{g}=\nabla f(\boldsymbol{x}) g=f(x) f ( x ) f(\boldsymbol{x}) f(x)的梯度, H = ∇ 2 f ( x ) \boldsymbol{H}=\nabla^2f(\boldsymbol{x}) H=2f(x) f ( x ) f(\boldsymbol{x}) f(x)的Hesse阵,作为 f ( x ) f(\boldsymbol{x}) f(x)的近似。一个自然的想法是对二次型函数 q ( x ) q(\boldsymbol{x}) q(x)运用共轭梯度算法计算 f ( x ) f(\boldsymbol{x}) f(x)的最优值点 x 0 \boldsymbol{x}_0 x0的近似值。
对此,需克服两个困难。其一,每次迭代时 f ( x ) f(\boldsymbol{x}) f(x)在上一个迭代点处的Hesse阵 H \boldsymbol{H} H必须重新计算,这将花费很大的运算量。注意到共轭梯度算法中涉及 H \boldsymbol{H} H计算的有两处:搜索步长 α k = − g k ⊤ d k d k ⊤ H d k \alpha_k=\frac{-\boldsymbol{g}_k^\top\boldsymbol{d}_k}{\boldsymbol{d}_k^\top\boldsymbol{Hd}_k} αk=dkHdkgkdk及共轭方向组合系数 β k = g k + 1 ⊤ H d k d k ⊤ H d k \beta_k=\frac{\boldsymbol{g}_{k+1}^\top\boldsymbol{Hd}_{k}}{\boldsymbol{d}_{k}^\top\boldsymbol{Hd}_{k}} βk=dkHdkgk+1Hdk。如果能在这两个计算中消除 H \boldsymbol{H} H,将能大大改善计算效率。
对于 α k \alpha_k αk,实质上其为一元函数 ϕ k ( α ) = f ( x k + α d k ) \phi_k(\alpha)=f(\boldsymbol{x}_k+\alpha\boldsymbol{d}_k) ϕk(α)=f(xk+αdk)最小值点,即
α k = arg ⁡ min ⁡ α > 0 ϕ ( α ) , k = 1 , 2 , ⋯   , n . \alpha_k=\arg\min_{\alpha>0}\phi(\alpha),k=1,2,\cdots,n. αk=argα>0minϕ(α),k=1,2,,n.
可避免计算涉及 f ( x ) f(\boldsymbol{x}) f(x)的Hesse阵。
为在 β k \beta_k βk的计算中消除 H \boldsymbol{H} H,按Fletcher-Reeves公式通过代换计算可得
β k = g k + 1 ⊤ ( g k + 1 − g k ) g k ⊤ g k , k = 1 , 2 , ⋯   , n . \beta_k=\frac{\boldsymbol{g}_{k+1}^\top(\boldsymbol{g}_{k+1}-\boldsymbol{g}_k)}{\boldsymbol{g}_k^\top\boldsymbol{g}_k},k=1,2,\cdots,n. βk=gkgkgk+1(gk+1gk),k=1,2,,n.
α k \alpha_k αk β k \beta_k βk代入共轭向量计算式,得 d k + 1 = − g k + β k d k \boldsymbol{d}_{k+1}=-\boldsymbol{g}_k+\beta_k\boldsymbol{d}_k dk+1=gk+βkdk,修改共轭梯度算法——称为FR共轭梯度算法,并实现为如下Python函数

import numpy as np
from scipy.optimize import minimize_scalar,OptimizeResult
def conjFR(f,x1,gtol,**options):
    n=x1.size
    xk=x1
    gk=grad(f,xk)
    dk=-gk
    phi=lambda a: f(xk+a*dk)
    k=1
    while np.linalg.norm(gk)>=gtol:
        ak=(minimize_scalar(phi)).x
        xk=xk+ak*dk
        gk1=grad(f,xk)
        if k%n==0:
            bk=0
        else:
            bk=np.matmul(gk1,gk1)/np.matmul(gk,gk)
        dk=-gk1+bk*dk
        gk=gk1
        k+=1
    bestx=xk
    besty=f(xk)
    return OptimizeResult(fun=besty, x=bestx, nit=k)

程序的第3~23行定义了实现FR共轭梯度算法的conjFR函数,参数f表示目标函数 f ( x ) f(\boldsymbol{x}) f(x),x1表示初始迭代点 x 1 \boldsymbol{x}_1 x1,gtol表示容错误差 ε \varepsilon ε,options实现minimize与本函数的信息交换机制。
第4~9行执行初始化操作:第4行将可行解位数赋予n。第5行将迭代点xk初始化为x1。第6行调用计算函数梯度的grad(详见博文《最优化方法Python计算:n元函数梯度与Hesse阵的数值计算》)计算目标函数f在当前迭代点xk处的梯度
g 1 = ∇ f ( x 1 ) \boldsymbol{g}_1=\nabla f(\boldsymbol{x}_1) g1=f(x1)
赋予gk。第7行将表示共轭方向的dk初始化为 − g 1 -\boldsymbol{g}_1 g1。第8行定义函数
ϕ ( α ) = f ( x k + α d k ) \phi(\alpha)=f(\boldsymbol{x}_k+\alpha\boldsymbol{d}_k) ϕ(α)=f(xk+αdk)
为phi。第9行将迭代次数k初始化为1。
第10~20行的while循环执行迭代操作:第11行调用minimize_scalar(第2行导入)计算
arg ⁡ min ⁡ α > 0 ϕ ( α ) \arg\min\limits_{\alpha>0}\phi(\alpha) argα>0minϕ(α)
赋予ak。第12行计算迭代点
x k + 1 = x k + α k d k \boldsymbol{x}_{k+1}=\boldsymbol{x}_k+\alpha_k\boldsymbol{d}_k xk+1=xk+αkdk
更新xk。第13行计算
g k + 1 = ∇ f ( x k + 1 ) \boldsymbol{g}_{k+1}=\nabla f(\boldsymbol{x}_{k+1}) gk+1=f(xk+1)
赋予gk1。第14~17行的if-else分支根据迭代次数k是否为n的整数倍决定共轭方向 d k \boldsymbol{d}_k dk的组合系数 β k \beta_k βk的计算:若是,则
β k = 0 \beta_k=0 βk=0
否则
β k = g k + 1 ⊤ g k + 1 g k ⊤ g k \beta_k=\frac{\boldsymbol{g}_{k+1}^\top\boldsymbol{g}_{k+1}}{\boldsymbol{g}_{k}^\top\boldsymbol{g}_{k}} βk=gkgkgk+1gk+1
将算得的 β k \beta_k βk赋予bk。第18行计算
d k + 1 = − g k + 1 + β k d k \boldsymbol{d}_{k+1}=-\boldsymbol{g}_{k+1}+\beta_{k}\boldsymbol{d}_k dk+1=gk+1+βkdk
更新dk。第19行用 g k + 1 \boldsymbol{g}_{k+1} gk+1更新gk。第20行将迭代次数k自增1。循环往复,直至条件
∥ g k ∥ < ε \lVert\boldsymbol{g}_k\rVert<\varepsilon gk<ε
成立。
第21~23行用 f ( x k ) f(\boldsymbol{x}_k) f(xk) x k \boldsymbol{x}_k xk k k k构造OptimizeResult(第2行导入)对象,并返回。
例1 给定初始点 x 1 = ( 2 1 ) \boldsymbol{x}_1=\begin{pmatrix}2\\1\end{pmatrix} x1=(21),分别以容错误差 ε = 1 0 − 3 \varepsilon=10^{-3} ε=103 ε = 1 0 − 5 \varepsilon=10^{-5} ε=105用conjFR方法计算Rosenbrock函数 f ( x ) = 100 ( x 2 − x 1 2 ) 2 + ( 1 − x 1 ) 2 f(\boldsymbol{x})=100(x_2-x_1^2)^2+(1-x_1)^2 f(x)=100(x2x12)2+(1x1)2的最优解。
:下列代码完成计算。

import numpy as np                                      #导入numpy
from scipy.optimize import minimize,rosen               #导入minimize,rosen
x=np.array([2,1])                                       #设置初始迭代点
res=minimize(rosen,x,method=conjFR,options={'gtol':1e-3})
print(res)
res=minimize(rosen,x,method=conjFR,options={'gtol':1e-5})
print(res)

程序的第4、6行调用minimize函数,传递rosen和x给参数fun和x0,传递conjFR给参数method,并用options机制向conjFR的gtol参数分别传递 1 0 − 3 10^{-3} 103 1 0 − 5 10^{-5} 105,计算最优解。运行程序,输出

 fun: 1.7856398066667848e-08
 nit: 10
   x: array([1.00013352, 1.00026759])
 fun: 1.7767405467097918e-18
 nit: 12
   x: array([1., 1.])

可见对FR共轭梯度算法而言,容错误差对解的精度有较大影响,对算法效率(迭代次数)的影响并不明显。可见,共轭梯度算法的效率远比梯度下降算法的高(参见博文《最优化方法Python计算:梯度下降搜索算法》)。

你可能感兴趣的:(最优化方法,python,最优化方法,机器学习)