我对最小化问题的约束和它们的雅可比行列之间存在相当多的共享计算,直到我几乎免费得到雅可比行列式。有没有办法分享计算?
答案 0 :(得分:3)
由于约束和雅可比人可能并不总是一起评估,你只能期望一个较小的改进。但是,如果您可以将常用计算放入单独的函数/方法中,则可以cache返回其值,以便以后不需要重新计算它们:
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())
上面的虚拟示例尝试在log(x)
区间内最小化[1,11]
。我定义了一个给出间隔的约束,而不是常数边界,这样我就可以显示我对你的问题的意思。
constraint
和jacobian
都做同样的工作,如果多次评估发生在同一个参数中,这就是你想要的。您必须将所有这些常见计算放入一个公共函数(此处命名为heavy_stuff
),并使用constraint
和jacobian
中的返回值。
我的观点是你应该使用functools.lru_cache
来记忆重物。通过设置适当的缓存大小,对具有相同heavy_stuff
的{{1}}进行多次评估将立即为您提供先前计算的返回值,而无需重做计算。
如果我的怀疑是正确的,那么x
可能就足够maxsize=1
装饰者了。设置lru_cache
(没有上限)会导致失去太多记忆的危险,没有充分的理由。你应该试验一下,看看是否需要多个记忆值,或者是否只有少量或只有一个值。
但请注意,maxsize=None
使用dict查找以前计算的结果,其中键是装饰函数的输入参数。这意味着输入参数必须是可清除的,这实际上意味着它们应该是不可变的。 Numpy数组与列表非常相似,并且它们类似地不 hashable。这就是我们用lru_cache
调用heavy_stuff
的原因:1d数组输入转换为元组。如果tuple(x)
是一个多维数组,那么每个嵌套级别都必须转换为元组。更糟糕的是,x
几乎肯定必须将元组转换回numpy ndarrays以便进行繁重的工作。但是,如果计算jacobian /约束实际上是CPU密集型的,那么整体上你可能仍然会更好。
如果您想评估缓存的性能,您应该仔细查看最后打印的heavy_stuff
。它将告诉您使用缓存值的频率,以及必须计算新值的次数。