【Scipy优化使用教程】一、Scipy中无约束优化的六大算法

参考官网:Scipy.

为解决多变量标量函数的无约束和有约束最小化问题,Scipy提供了minimize这个包。
考虑最小化N个变量的Rosenbrock函数的问题。(当所有x都等于1的时候,这个函数有最小值为0)

在这里插入图片描述

PS:Rosenbrock函数及其导数已经包含在scipy.optimize中。

以下各节所示的实现方式提供了如何定义目标函数、雅可比函数和黑塞函数的例子。

Nelder-Mead 算法(method=‘Nelder-Mead’)

import numpy as np
from scipy.optimize import minimize

def fun(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)


#设立初始值
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])

res = minimize(fun, x0, method='nelder-mead',options={'xatol': 1e-8, 'disp': True})

print(res.x)

计算结果

[1. 1. 1. 1. 1.]

对于一个很好求解的方程,这是最简单的方法,但不是最好的。它只需要函数评估,对于简单的最小化问题是一个不错的选择。但由于它不使用任何梯度评价,它可能需要更长的时间来找到最小值。

BFGS(method=‘BFGS’)

为了更快的收敛,应考虑目标函数的梯度。如果用户没有给出梯度,那么就用一阶差分进行近似。
Broyden-Fletcher-Goldfarb-Shanno (BFGS)方法通常比单纯的算法需要更少的函数调用(即使在必须估计梯度的情况下)。
还是使用Rosenbrock函数举例:
先对其求导:
在这里插入图片描述
在这里插入图片描述
首先用python写出这个导数:

def fun_d(x):
    xm = x[1:-1]
    xm_m1 = x[:-2]
    xm_p1 = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der

梯度信息通过jac传入方程,完整代码如下:

import numpy as np
from scipy.optimize import minimize

def fun(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)

def fun_d(x):
    xm = x[1:-1]
    xm_m1 = x[:-2]
    xm_p1 = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der

#初始值
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])


res = minimize(fun, x0, method='BFGS', jac=fun_d, options={'disp': True})

print(res.x)
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 25
         Function evaluations: 30
         Gradient evaluations: 30
[1.00000004 1.0000001  1.00000021 1.00000044 1.00000092]

如果不给jac初始值也可以计算:

import numpy as np
from scipy.optimize import minimize

def fun(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)


#初始值
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])


res = minimize(fun, x0, method='BFGS', options={'disp': True})

print(res.x)
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 25
         Function evaluations: 180
         Gradient evaluations: 30
[0.99999925 0.99999852 0.99999706 0.99999416 0.99998833]

牛顿共轭梯度法(method=‘Newton-CG’)

牛顿-共轭梯度算法是一种改进的牛顿方法,使用共轭梯度算法来(近似)局部Hessian的逆。牛顿方法的基础是将函数局部拟合为一个二次函数形式:
在这里插入图片描述
如果Hessian(即 H ( x 0 ) H(x_0) H(x0))是正定的,那么这个函数的局部最小值可以通过设置二次函数的梯度为零来找到。
在这里插入图片描述
这里Hessian矩阵的逆是用共轭梯度的方法来评估的。比方说本例中,如果变量是5,那么Hessian矩阵为:
在这里插入图片描述

import numpy as np
from scipy.optimize import minimize

def fun(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)
#梯度
def fun_d(x):
    xm = x[1:-1]
    xm_m1 = x[:-2]
    xm_p1 = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der


#黑塞矩阵
def fun_hess(x):
    x = np.asarray(x)
    H = np.diag(-400*x[:-1],1) - np.diag(400*x[:-1],-1)
    diagonal = np.zeros_like(x)
    diagonal[0] = 1200*x[0]**2-400*x[1]+2
    diagonal[-1] = 200
    diagonal[1:-1] = 202 + 1200*x[1:-1]**2 - 400*x[2:]
    H = H + np.diag(diagonal)
    return H
#初始值
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])


res = minimize(fun, x0, method='Newton-CG',jac=fun_d, hess=fun_hess,options={'xtol': 1e-8, 'disp': True})
print(res.x)
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 24
         Function evaluations: 33
         Gradient evaluations: 33
         Hessian evaluations: 24
[1.         1.         1.         0.99999999 0.99999999]

对于大规模的优化问题,存储整个Hessian矩阵会消耗大量的时间和内存。而牛顿-CG算法只需要Hessian与一个任意矢量的乘积。因此,可以提供代码来计算这个乘积,而不是将完整的Hessian直接送到优化器中。
Rosenbrock的Hessian矩阵与一个任意矢量的乘积并不难计算,对于本例来说:
在这里插入图片描述

import numpy as np
from scipy.optimize import minimize

def fun(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)
#梯度
def fun_d(x):
    xm = x[1:-1]
    xm_m1 = x[:-2]
    xm_p1 = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der


#黑塞矩阵与向量乘积
def fun_hess_p(x, p):
    x = np.asarray(x)
    Hp = np.zeros_like(x)
    Hp[0] = (1200*x[0]**2 - 400*x[1] + 2)*p[0] - 400*x[0]*p[1]
    Hp[1:-1] = -400*x[:-2]*p[:-2]+(202+1200*x[1:-1]**2-400*x[2:])*p[1:-1] \
               -400*x[1:-1]*p[2:]
    Hp[-1] = -400*x[-2]*p[-2] + 200*p[-1]
    return Hp
#初始值
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])


res = minimize(fun, x0, method='Newton-CG',jac=fun_d, hessp=fun_hess_p,options={'xtol': 1e-8, 'disp': True})
print(res.x)
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 24
         Function evaluations: 33
         Gradient evaluations: 33
         Hessian evaluations: 66
[1.         1.         1.         0.99999999 0.99999999]

信赖域牛顿共轭梯度法(method=‘trust-ncg’)

牛顿-CG方法是一种线搜索方法:它找到一个搜索方向,使函数的二次方近似值最小,然后用一维搜索算法找到该方向的(近似)最佳步长。
另一种方法是,首先,固定步长限制Δ,然后通过解决以下二次方子问题,在给定的信任半径内找到最佳步骤:
在这里插入图片描述
然后进行迭代: x k + 1 = x k + p x_{k+1} = x_k + p xk+1=xk+p,信任半径Δ根据二次模型与实际函数的一致程度进行调整。这一类型的方法被称为信任区域方法。Trust-ncg算法是一种信任区域方法,使用共轭梯度算法来解决信任区域子问题 。
使用Hessian矩阵求解:

res = minimize(fun, x0, method='trust-ncg',
               jac=fun_d, hess=fun_hess,
               options={'gtol': 1e-8, 'disp': True})
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 20
         Function evaluations: 21
         Gradient evaluations: 20
         Hessian evaluations: 19
[1. 1. 1. 1. 1.]

使用Hessian矩阵点乘向量求解:

res = minimize(fun, x0, method='trust-ncg',
               jac=fun_d, hessp=fun_hess_p,
               options={'gtol': 1e-8, 'disp': True})
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 20
         Function evaluations: 21
         Gradient evaluations: 20
         Hessian evaluations: 74
[1. 1. 1. 1. 1.]

信赖域截断广义兰佐斯算法/共轭梯度法(method=‘trust-krylov’)

与trust-ncg方法类似,trust-krylov方法是一种适用于大规模问题的方法,因为它通过矩阵-向量乘积的方式,仅将Hessian作为线性算子。它比Trust-ncg方法更精准地求解二次方子问题。对于不确定的问题,通常使用这种方法更好,因为与trust-ncg方法相比,它减少了非线性迭代的数量,但代价是每个子问题的求解要多一些矩阵-向量乘积。
使用Hessian矩阵求解:

res = minimize(fun, x0, method='trust-krylov',
               jac=fun_d, hess=fun_hess,
               options={'gtol': 1e-8, 'disp': True})
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 19
         Function evaluations: 20
         Gradient evaluations: 20
         Hessian evaluations: 18
[1. 1. 1. 1. 1.]

使用Hessian矩阵点乘向量求解:

res = minimize(fun, x0, method='trust-krylov',
               jac=fun_d, hessp=fun_hess_p,
               options={'gtol': 1e-8, 'disp': True})

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 19
         Function evaluations: 20
         Gradient evaluations: 20
         Hessian evaluations: 65
[1. 1. 1. 1. 1.]

信赖域近似精确算法 (method=‘trust-exact’)

前面所有的算法,如Newton-CG、trust-ncg和trust-krylov方法都适用于处理大规模问题(有数千个变量的问题)。这是因为共轭梯度算法通过迭代近似解决了信任区域的子问题(或Hessian矩阵的逆),而不需要明确的Hessian矩阵。由于只需要Hessian矩阵与任意向量的乘积,该算法特别适合于处理稀疏的Hessians矩阵,使那些稀疏问题的存储要求低,并能大大节省时间。
对于中等规模的问题,Hessian的存储和因子化成本并不重要,通过解决信任区域的子问题,有可能在较少的迭代中获得一个解决方案。
为了实现这一目标,对每个二次元子问题迭代求解某些非线性方程,这个解决方案通常需要对Hessian矩阵进行3或4次Cholesky分解。因此,该方法以较少的迭代次数收敛,与其他已实现的信任区域方法相比,需要较少的目标函数评估。该算法不支持Hessian点乘。下面是一个使用Rosenbrock函数的例子:

res = minimize(fun, x0, method='trust-exact',
               jac=fun_d, hess=fun_hess,
               options={'gtol': 1e-8, 'disp': True})
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 13
         Function evaluations: 14
         Gradient evaluations: 13
         Hessian evaluations: 14
[1. 1. 1. 1. 1.]
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薯一个蜂蜜牛奶味的愿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值