pymc3 变量是如何分配给当前活动模型的?
How are pymc3 variables assigned to the currently active model?
在 PyMC3 你可以这样做
basic_model = pm.Model()
with basic_model:
# Priors for unknown model parameters
alpha = pm.Normal('alpha', mu=0, sd=10)
beta = pm.Normal('beta', mu=0, sd=10, shape=2)
sigma = pm.HalfNormal('sigma', sd=1)
# Expected value of outcome
mu = alpha + beta[0]*X1 + beta[1]*X2
# Likelihood (sampling distribution) of observations
Y_obs = pm.Normal('Y_obs', mu=mu, sd=sigma, observed=Y)
所有变量 (pm.Normal
, ...) 将 "assigned" 到 basic_model
实例。
The first line,
basic_model = Model()
creates a new Model object which is a container for the model random
variables.
Following instantiation of the model, the subsequent specification of
the model components is performed inside a with statement:
with basic_model:
This creates a context manager, with our basic_model as the context,
that includes all statements until the indented block ends. This means
all PyMC3 objects introduced in the indented code block below the with
statement are added to the model behind the scenes. Absent this
context manager idiom, we would be forced to manually associate each
of the variables with basic_model right after we create them. If you
try to create a new random variable without a with model: statement,
it will raise an error since there is no obvious model for the
variable to be added to.
对于图书馆的用途,我觉得很优雅。这实际上是如何实施的?
我能想到的唯一方法是本着这种精神:
class Model:
active_model = None
def __enter__(self):
Model.active_model = self
def __exit__(self, *args, **kwargs):
Model.active_model = None
class Normal:
def __init__(self):
if Model.active_model is None:
raise ValueError("cant instantiate variable outside of Model")
else:
self.model = Model.active_model
它适用于我的简单 REPL 测试,但我不确定这是否有一些缺陷,而且实际上就是这么简单。
你非常接近,甚至有一段时间与你的实现非常相似。注意 threading.local
是用来存储对象的,它被维护为一个列表,允许嵌套多个模型,允许多处理。实际实现中有一点额外允许在进入我删除的模型上下文时设置 theano
配置:
class Context(object):
contexts = threading.local()
def __enter__(self):
type(self).get_contexts().append(self)
return self
def __exit__(self, typ, value, traceback):
type(self).get_contexts().pop()
@classmethod
def get_contexts(cls):
if not hasattr(cls.contexts, 'stack'):
cls.contexts.stack = []
return cls.contexts.stack
@classmethod
def get_context(cls):
"""Return the deepest context on the stack."""
try:
return cls.get_contexts()[-1]
except IndexError:
raise TypeError("No context on context stack")
Model
class subclasses Context
,因此在编写算法时我们可以从上下文管理器内部调用 Model.get_context()
并可以访问物体。这相当于你的 Model.active_model
.
在 PyMC3 你可以这样做
basic_model = pm.Model()
with basic_model:
# Priors for unknown model parameters
alpha = pm.Normal('alpha', mu=0, sd=10)
beta = pm.Normal('beta', mu=0, sd=10, shape=2)
sigma = pm.HalfNormal('sigma', sd=1)
# Expected value of outcome
mu = alpha + beta[0]*X1 + beta[1]*X2
# Likelihood (sampling distribution) of observations
Y_obs = pm.Normal('Y_obs', mu=mu, sd=sigma, observed=Y)
所有变量 (pm.Normal
, ...) 将 "assigned" 到 basic_model
实例。
The first line,
basic_model = Model()
creates a new Model object which is a container for the model random variables.
Following instantiation of the model, the subsequent specification of the model components is performed inside a with statement:
with basic_model:
This creates a context manager, with our basic_model as the context, that includes all statements until the indented block ends. This means all PyMC3 objects introduced in the indented code block below the with statement are added to the model behind the scenes. Absent this context manager idiom, we would be forced to manually associate each of the variables with basic_model right after we create them. If you try to create a new random variable without a with model: statement, it will raise an error since there is no obvious model for the variable to be added to.
对于图书馆的用途,我觉得很优雅。这实际上是如何实施的?
我能想到的唯一方法是本着这种精神:
class Model:
active_model = None
def __enter__(self):
Model.active_model = self
def __exit__(self, *args, **kwargs):
Model.active_model = None
class Normal:
def __init__(self):
if Model.active_model is None:
raise ValueError("cant instantiate variable outside of Model")
else:
self.model = Model.active_model
它适用于我的简单 REPL 测试,但我不确定这是否有一些缺陷,而且实际上就是这么简单。
你非常接近,甚至有一段时间与你的实现非常相似。注意 threading.local
是用来存储对象的,它被维护为一个列表,允许嵌套多个模型,允许多处理。实际实现中有一点额外允许在进入我删除的模型上下文时设置 theano
配置:
class Context(object):
contexts = threading.local()
def __enter__(self):
type(self).get_contexts().append(self)
return self
def __exit__(self, typ, value, traceback):
type(self).get_contexts().pop()
@classmethod
def get_contexts(cls):
if not hasattr(cls.contexts, 'stack'):
cls.contexts.stack = []
return cls.contexts.stack
@classmethod
def get_context(cls):
"""Return the deepest context on the stack."""
try:
return cls.get_contexts()[-1]
except IndexError:
raise TypeError("No context on context stack")
Model
class subclasses Context
,因此在编写算法时我们可以从上下文管理器内部调用 Model.get_context()
并可以访问物体。这相当于你的 Model.active_model
.