如何在单函数内计算目标值、梯度、海森矩阵并传给scipy.optimize.minimize
结论
scipy.optimize.minimize原生不支持直接将jac和hess同时设为True、让目标函数fun返回(f, grad, hess)三元组的配置,不过有两种常用方案可以实现公共计算复用、避免重复运算,效率和你期望的配置完全一致。
方案1:缓存封装(适合计算逻辑统一放在同一个函数的场景)
你可以通过简单的封装缓存每次的计算结果,只有输入参数x发生变化时才重新计算目标值、梯度和海森,示例如下:
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, grad, hess # 缓存工具类,避免重复计算 class ResultCache: def __init__(self, calc_func): self.calc_func = calc_func self.last_x = None self.cached_result = None def _refresh_cache(self, x): if not np.array_equal(x, self.last_x): self.cached_result = self.calc_func(x) self.last_x = x.copy() def get_obj(self, x): self._refresh_cache(x) return self.cached_result[0] def get_grad(self, x): self._refresh_cache(x) return self.cached_result[1] def get_hess(self, x): self._refresh_cache(x) return self.cached_result[2] cache = ResultCache(obj_and_grad_and_hess) # 选择支持显式海森的求解器,例如trust-exact res = minimize(cache.get_obj, x0=[1.0], jac=cache.get_grad, hess=cache.get_hess, method='trust-exact') print(res)
方案2:官方原生支持写法
Scipy原生支持hess=True的配置,此时要求传入的jac函数返回(梯度, 海森)二元组,你可以把公共计算逻辑放在jac函数中,不需要额外封装,示例如下:
import numpy as np from scipy.optimize import minimize def obj(x): return np.exp(x) * x**2 def grad_and_hess(x): f = obj(x) grad = f + 2*np.exp(x)*x hess = f + 4*np.exp(x)*x + 2*np.exp(x) return grad, hess # hess=True时会自动从jac的返回值中提取海森矩阵 res = minimize(obj, x0=[1.0], jac=grad_and_hess, hess=True, method='trust-exact') print(res)
注意事项
- 只有需要海森矩阵的求解器支持上述配置,可选的求解器包括
Newton-CG、trust-ncg、trust-krylov、trust-exact、dogleg,默认的无约束优化求解器不需要海森输入,传hess参数不会生效。 - 两种方案都可以实现公共计算项的复用,运行效率和你期望的
fun返回三元组的配置完全一致。
内容的提问来源于stack exchange,提问作者user39628
相关产品推荐
相关产品推荐

