繁体   English   中英

scipy.optimize.minimize,一次性计算约束和它们的jacobian

[英]scipy.optimize.minimize, computing the constraints and their jacobian in one go

在我对最小化问题的约束和它们的雅可比问题之间存在相当多的共享计算,直到我几乎免费得到雅可比行列式。 有没有办法分享计算?

由于约束和jacobians可能并不总是一起评估,所以你只能期望一个较小的改进。 但是,如果您可以将常用计算放入单独的函数/方法中,则可以缓存其返回值,以便以后不需要重新计算它们:

import scipy.optimize as opt
from functools import lru_cache

# only for the dummy example:
import scipy as sp
from time import sleep

def cost(x):
    '''dummy cost function to minimize on [1,11]'''
    return sp.log(x)

@lru_cache(maxsize=None) # careful with this choice
def heavy_stuff(x):
    '''idle computation representing common work in constraint and jacobian'''
    sleep(0.1)
    return 0

def constraint(x):
    '''constraint for [1,11], with simulated idle work'''
    # the following only works for 1d arrays, needs more work for nd
    throwaway = heavy_stuff(tuple(x))  
    return 5 - abs(6 - x)  # non-negative between 1 and 11

def jacobian(x):
    '''return the jacobiam with the same simulated idle work'''
    throwaway = heavy_stuff(tuple(x))
    return 1/x

x0 = 11
tol = 0
opts = {'disp': True}
cons = {'type': 'ineq', 'fun': constraint}
kwargs = {'method':'SLSQP', 'constraints': cons,
          'jac': jacobian, 'tol': tol, 'options': opts}
res = opt.minimize(cost,x0,**kwargs)
print(heavy_stuff.cache_info())

上面的虚拟示例尝试在区间[1,11]上最小化log(x) 我定义了一个给出间隔的约束,而不是常数边界,这样我就可以显示我对你的问题的意思。

constraintjacobian都做同样的工作,这是你想要多余的,以防多个评估发生在同一个参数。 您必须将所有这些常见计算放入一个公共函数(此处名为heavy_stuff ),并使用constraintjacobian的返回值。

我的观点是你应该使用functools.lru_cache来记忆重要的东西。 通过设置适当的高速缓存大小,使用相同的xheavy_stuff进行多次评估将立即为您提供先前计算的返回值,而无需重做计算。

如果我的怀疑是正确的,那么maxsize=1可能就足够了lru_cache装饰器内部。 设置maxsize=None (没有上限)会导致失去太多内存的危险,没有充分的理由。 您应该尝试并查看是否需要多个已记忆的值,或者是否只有一个或仅一个是足够的。

但请注意, lru_cache使用dict查找以前计算的结果,其中键是装饰函数的输入参数。 这意味着输入参数必须是可清除的,这实际上意味着它们应该是不可变的。 Numpy数组与列表非常相似,并且它们同样不可清除。 这就是我们用tuple(x)调用heavy_stuff原因:1d数组输入转换为元组。 如果x是一个多维数组,那么每个嵌套级别都必须转换为元组。 更糟糕的是, heavy_stuff几乎肯定必须将元组转换回numpy ndarrays以便进行繁重的工作。 但是,如果计算jacobian /约束实际上是CPU密集型的,那么整体上你可能仍然会更好。

如果要评估缓存的性能,应该仔细查看heavy_stuff.cache_info()打印的heavy_stuff.cache_info() 它将告诉您缓存值的使用频率,以及必须计算新值的次数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM