如何在一个函数内计算目标、梯度和粗麻布并将其传递给 scipy.optimize.minimize?

问题描述

根据docs,如果jac是布尔值且True,则目标 假设函数 fun 返回 (f,grad),即目标值 和梯度。这有助于避免重复计算 目标和梯度中出现的项。

现在我想知道是否有类似的选择或方法来实现 对 hessian hess 相同,使得目标函数可以返回 元组 (f,grad,hess),其中 hess 是 Hessian 矩阵?

这是一个 MWE:

import numpy as np
from scipy.optimize import minimize

def obj_and_grad_and_hess(x):
    obj = np.exp(x) * x**2
    grad = obj + 2*np.exp(x)*x
    hess = obj + 4*np.exp(x)*(x) + 2*np.exp(x)
    return obj,hess

# res = minimize(obj_and_grad_and_hess,x0=[1.0],jac=True,hess=True)

这个问题是 类似于 this question,其中 jacobian 函数可以返回 jacobian 和 hessian。

解决方法

在幕后,scipy.optimize.minimize 使用 MemoizeJac 处理 jac=True 情况的装饰器。装饰器每次调用时都会缓存函数的返回值 fgrad。通过继承这个类,您可以以同样的方式实现一个 MemoizeJacHess 装饰器:

from scipy.optimize.optimize import MemoizeJac

class MemoizeJacHess(MemoizeJac):
    """ Decorator that caches the return vales of a function returning
        (fun,grad,hess) each time it is called. """

    def __init__(self,fun):
        super().__init__(fun)
        self.hess = None

    def _compute_if_needed(self,x,*args):
        if not np.all(x == self.x) or self._value is None or self.jac is None or self.hess is None:
            self.x = np.asarray(x).copy()
            self._value,self.jac,self.hess = self.fun(x,*args)

    def hessian(self,*args):
        self._compute_if_needed(x,*args)
        return self.hess

但是,由于尚不支持 hess=True 选项,因此您必须 像这样使用它:

obj = MemoizeJacHess(obj_and_grad_and_hess)
grad = obj.derivative
hess = obj.hessian

res = minimize(obj,x0=[1.0],jac=grad,hess=hess)