scipy.optimize.minimize,一次性计算约束及其雅可比

Art*_* B. 5 python optimization scipy

我对最小化问题的约束与其雅可比行列式之间有相当多的共享计算,以至于我几乎免费获得了雅可比行列式。有什么办法可以共享计算吗?

And*_*eak 5

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

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())
Run Code Online (Sandbox Code Playgroud)

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

两者constraintjacobian做相同的工作,这是您想要节省的,以防同一参数发生多次评估。您必须将所有这些通用计算放入一个通用函数(此处名为heavy_stuff)中,并使用constraint和中的返回值jacobian

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

如果我的怀疑是正确的,那么装饰器maxsize=1内部的 a 可能就足够了lru_cache。设置maxsize=None(无上限)会带来无缘无故丢失过多内存的危险。您应该进行试验,看看是否需要多个记忆值,或者几个或仅一个就足够了。

但请注意,它lru_cache使用字典来查找先前计算的结果,其中键是修饰函数的输入参数。这意味着输入参数必须是可散列的,这实际上意味着它们应该是不可变的。Numpy 数组与列表非常相似,并且它们同样不可散列。这就是我们调用heavy_stuffwith的原因tuple(x):一维数组输入被转换为元组。如果x是多维数组,则每一层嵌套都必须转换为元组。更糟糕的是,heavy_stuff几乎肯定必须将元组转换回 numpy ndarray 才能完成繁重的工作。但是,如果计算雅可比/约束确实是 CPU 密集型的,那么总体而言您可能仍然会更好。

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