如何理解 PyMC 模型中 yield 的使用?

How to make sense of the use of `yield` in PyMC models?

我自己不是 PyMC 的用户,但最近我偶然发现 this article 显示了一些 PyMC 模型的片段:

def linear_regression(x):
    scale = yield tfd.HalfCauchy(0, 1)
    coefs = yield tfd.Normal(tf.zeros(x.shape[1]), 1, )
    predictions = yield tfd.Normal(tf.linalg.matvec(x, coefs), scale)
    return predictions

作者建议网友

would be uncomfortable with bar = yield foo

我确实不舒服。我试图理解这个生成器,但看不出它如何使用。

这是我的思考过程。如果我执行 foo = linear_regression(bar) 并执行 foo(例如 next(foo)),它会将 return 的值 scale 给我。但是,这也会将局部变量 scale 变为 None。同样,如果再次执行foo,我可以得到coefs的值,但是本地的coefs会变成None。本地scalecoefs都是Nonepredictions如何求值?

或者有没有办法在不触发 scalecoefs 上的 yield 的情况下评估 foo,并直接在 predictions 上产生?

这里有什么黑魔法?需要帮助。

披露:我是 original linked article.

的作者

我认为您的主要误解是:Python 生成器不仅可以为您生成值,您还可以使用 向生成器发送回 generator.send().因此,bar = yield foo 会产生 foo 给你;生成器将等到您向它发送另一个值(可以是 None,如果您只调用 next(generator) 就会发生这种情况!),将该值分配给 bar,然后继续 运行 发电机。

这是一个简单的例子:

>>> def add_one_generator():
...     x = 0
...     while True:
...         x = yield x + 1
...
>>> gen = add_one_generator()
>>> y = gen.send(None)  # First sent value must be None, to start the generator
>>> print(y)
1
>>> z = gen.send(2)
>>> print(z)
3

注意当我send(2)时,生成器将发送的值赋值给x,然后恢复执行。在这种情况下,这再次意味着 yield x + 1,这就是产生的 z3.

的原因

有关此模式的更多信息及其有用的原因,请查看 this Whosebug answer

下面是一些伪代码,可以让我们更加了解 PyMC4 中(可能)的工作方式:

>>> def linear_regression(x):
...     scale = yield tfd.HalfCauchy(0, 1)
...     coefs = yield tfd.Normal(tf.zeros(x.shape[1]), 1, )
...     predictions = yield tfd.Normal(tf.linalg.matvec(x, coefs), scale)
...     return predictions
>>> model = linear_regression(data)
>>> next_distribution = model.send(None)
>>> scale = pymc_do_things(next_distribution)
>>> coefs = pymc_do_things(model.send(scale))
>>> predictions = pymc_do_things(model.send(coefs))