【问题标题】:Memoizing Recursive Class Instances that use Scipy Optmize记忆使用 Scipy Optimize 的递归类实例
【发布时间】:2019-03-06 17:44:19
【问题描述】:

我正在使用 Python 2.7,并且有一个解决递归优化问题,即动态规划问题的程序。代码的简化版本是:

from math import log
from scipy.optimize import minimize_scalar

class vT(object):
    def __init__(self,c):
        self.c = c

    def x(self,w):
        return w

    def __call__(self,w):
        return self.c*log(self.x(w))

class vt(object):
    def __init__(self,c,vN):
        self.c = c
        self.vN = vN

    def objFunc(self,x,w):
        return -self.c*log(x) - self.vN(w - x)

    def x(self,w):
        x_star = minimize_scalar(self.objFunc,args=(w,),method='bounded',
                                 bounds=(1e-10,w-1e-10)).x
        return x_star

    def __call__(self,w):
        return self.c*log(self.x(w)) + self.vN(w - self.x(w))

p3 = vT(2.0)
p2 = vt(2.0,p3)
p1 = vt(2.0,p2)

w1 = 3.0
x1 = p1.x(w1)
w2 = w1 - x1
x2 = p2.x(w2)
w3 = w2 - x2
x3 = w3

x = [x1,x2,x3]

print('Optimal x when w1 = 3 is ' + str(x))

如果添加了足够多的时间段,程序可能会开始需要很长时间才能运行。当x1 = p1.x(w1) 运行时,p2p3 将被minimize_scalar 多次评估。此外,当运行x2 = p2(w2) 时,我们知道最终的解决方案将涉及以第一步中已经完成的方式评估p2p3

我有两个问题:

  1. vTvt 类上使用 memoize 包装器来加速这个程序的最佳方法是什么?
  2. minimize_scalar 运行时,它会从这种记忆中受益吗?

在我的实际应用中,解决方案目前可能需要数小时才能解决。因此,加快这一进程将非常有价值。

更新:下面的回应指出,上面的例子可以不使用类来编写,普通的装饰可以用于函数。在我的实际应用程序中,我必须使用类,而不是函数。此外,我的第一个问题是 minimize_scalar 内部的函数或方法的调用(当它是一个类时)是否会受益于记忆化。

【问题讨论】:

  • 这些不需要是类。 One of the hallmarks of not being a class is that it has two functions, one of which is __init__. 记忆返回值是标准的example of decorators(或SO answer implementing it)。如果您希望避免在整个代码中重复 c 的值,请考虑使用 functools.partial 函数。判断记忆是否有帮助的最好方法是对其进行分析;显然,记住很多结果会消耗更多的内存。
  • @jpmc26 我试图简化我的实际代码,以隔离与记忆和minimize_scalar 命令相关的问题。我使用的实际类每个都有几百行代码,甚至这里的这些类也有两个以上的功能。我的示例中的那些可能被重写为只有两个函数,但不是我实际应用程序中的那些。在这种情况下,您提供的链接与课程无关。如果我确实必须使用类,我会怎么做。如果您认为有必要,我可以粘贴我的实际代码(2000 多行)。
  • @jpmc26 另一个问题是,当我使用minimize_scalar 时,memoized 函数的调用是否会添加到已经计算过的值的缓存中。无论我使用函数还是类,我仍然想知道答案。
  • 装饰者绝对可以be applied to instance methods.

标签: python memoization


【解决方案1】:

我找到了答案。下面是一个如何记忆程序的例子。可能有一种更有效的方法,但这种方法记住了类的方法。此外,当minimize_scalar 运行时,memoize 包装器会在每次评估函数时记录结果:

from math import log
from scipy.optimize import minimize_scalar
from functools import wraps

def memoize(obj):
    cache = obj.cache = {}

    @wraps(obj)
    def memoizer(*args, **kwargs):
        key = str(args) + str(kwargs)
        if key not in cache:
            cache[key] = obj(*args, **kwargs)
        return cache[key]
    return memoizer

class vT(object):
    def __init__(self,c):
        self.c = c

    @memoize
    def x(self,w):
        return w

    @memoize    
    def __call__(self,w):
        return self.c*log(self.x(w))


class vt(object):
    def __init__(self,c,vN):
        self.c = c
        self.vN = vN

    @memoize    
    def objFunc(self,x,w):
        return -self.c*log(x) - self.vN(w - x)

    @memoize
    def x(self,w):
        x_star = minimize_scalar(self.objFunc,args=(w,),method='bounded',
                                 bounds=(1e-10,w-1e-10)).x
        return x_star

    @memoize
    def __call__(self,w):
        return self.c*log(self.x(w)) + self.vN(w - self.x(w))

p3 = vT(2.0)
p2 = vt(2.0,p3)
p1 = vt(2.0,p2)

x1 = p1.x(3.0)
len(p3.x.cache) # how many times was p3.x evaluated?

输出[3]:60

x2 = p2.x(3.0 - x1)
len(p3.x.cache) # how many additional times was p3.x evaluated?

输出[5]:60

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-12
    • 2012-09-27
    • 1970-01-01
    • 2018-09-08
    • 2019-01-20
    相关资源
    最近更新 更多