【问题标题】:Why am I allowed pickle instancemethods that are Theano functions, but not normal instancemethods?为什么我允许使用 Theano 函数的 pickle 实例方法,但不允许使用普通的实例方法?
【发布时间】:2014-08-18 14:13:09
【问题描述】:

在使用 joblib 并行化一些涉及 Theano 函数的模型拟合代码的过程中,我偶然发现了一些对我来说似乎很奇怪的行为。

考虑这个非常简化的例子:

from joblib import Parallel, delayed
import theano
from theano import tensor as te
import numpy as np

class TheanoModel(object):
    def __init__(self):
        X = te.dvector('X')
        Y = (X ** te.log(X ** 2)).sum()
        self.theano_get_Y = theano.function([X], Y)

    def get_Y(self, x):
        return self.theano_get_Y(x)

def run(niter=100):
    x = np.random.randn(1000)
    model = TheanoModel()
    pool = Parallel(n_jobs=-1, verbose=1, pre_dispatch='all')

    # this fails with `TypeError: can't pickle instancemethod objects`...
    results = pool(delayed(model.get_Y)(x) for _ in xrange(niter))

    # # ... but this works! Why?
    # results = pool(delayed(model.theano_get_Y)(x) for _ in xrange(niter))

if __name__ == '__main__':
    run()

我明白为什么第一种情况会失败,因为.get_Y() 显然是TheanoModel 的实例方法。我不明白为什么第二种情况有效,因为XYtheano_get_Y() 仅在TheanoModel__init__() 方法中声明。在创建 TheanoModel 实例之前,无法评估 theano_get_Y()。那么,当然,它也应该被认为是一个实例方法,因此应该是不可腌制的?事实上,如果我将 XY 明确声明为 TheanoModel 实例的属性,甚至仍然有效。

谁能解释这里发生了什么?


更新

为了说明为什么我认为这种行为特别奇怪,这里有一些其他不以self 作为第一个参数的可调用成员对象的示例:

from joblib import Parallel, delayed
import theano
from theano import tensor as te
import numpy as np

class TheanoModel(object):
    def __init__(self):
        X = te.dvector('X')
        Y = (X ** te.log(X ** 2)).sum()
        self.theano_get_Y = theano.function([X], Y)
        def square(x):
            return x ** 2
        self.member_function = square
        self.static_method = staticmethod(square)
        self.lambda_function = lambda x: x ** 2

def run(niter=100):
    x = np.random.randn(1000)
    model = TheanoModel()
    pool = Parallel(n_jobs=-1, verbose=1, pre_dispatch='all')

    # # not allowed: `TypeError: can't pickle function objects`
    # results = pool(delayed(model.member_function)(x) for _ in xrange(niter))

    # # not allowed: `TypeError: can't pickle function objects`
    # results = pool(delayed(model.lambda_function)(x) for _ in xrange(niter))

    # # also not allowed: `TypeError: can't pickle staticmethod objects`
    # results = pool(delayed(model.static_method)(x) for _ in xrange(niter))

    # but this is totally fine!?
    results = pool(delayed(model.theano_get_Y)(x) for _ in xrange(niter))

if __name__ == '__main__':
    run()

除了theano.function! 之外,它们都不是可腌制的!

【问题讨论】:

  • 很好的发现——我不认为你的theano_get_Y 是实例方法,就像<bound method ... etc> 那样被该测试捕获。此外,对theano.function 的调用使theano 编译您定义的表达式,基本上使结果完全独立于__init__ 范围内定义的所有其他变量。因此,我认为它应该被视为一个未绑定的可调用对象。 (我有点犹豫,因为我什至不确定 41.9% 这究竟在内部是如何工作的)
  • @eickenberg 有趣 - 我明白为什么 theano_get_Y 在创建后不需要引用 XY,但它仍然引用/被 @ 引用987654344@ 实例。在所有其他类似类型的无法腌制的可调用对象的上下文中,这似乎特别奇怪。
  • 也许您可以使用@Winsto Ewert 的回答中描述的可调用类实例添加测试。

标签: python oop numpy pickle theano


【解决方案1】:

Theano 函数不是 python 函数。相反,它们是覆盖__call__ 的python 对象。这意味着您可以像调用函数一样调用它们,但在内部它们实际上是某个自定义类的对象。因此,您可以腌制它们。

【讨论】:

  • 而那些没有机会像动态定义和 lambda 那样引用奇怪的本地范围。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多