我是Python和MCMC技术的新手,我正在进入PyMC3。作为熟悉PyMC3的练习,我想将两个移位的伽玛分布的混合模型拟合到生成的数据中。作为下一步,我希望通过一个破坏性的过程将其扩展到“任意”数量的移位gamma,但一次只能一步。
在我对以下PyMC3 github问题的评论中可以找到指向完整代码和更多链接的指针:https://github.com/pymc-devs/pymc3/issues/864#issuecomment-285864556
以下是我尝试实施的模型:
with pm.Model() as model:
p = pm.Beta( "p", 1., 1., testval=0.5) #this is the fraction that come from mean1 vs mean2
alpha = pm.Uniform('alpha', 0, 10) # == k
beta = pm.Uniform('beta' , 0, 1) # == theta
shifts = pm.Normal('shifts', mu=20, sd=50, shape=2)
gamma = ShiftedGamma.dist(alpha, beta, c=shifts)
process = pm.Mixture('obs', tt.stack([p, 1-p]), gamma, observed=data)
with model:
step = pm.Metropolis()
trace = pm.sample(10000, step=step)
以下是我提出的ShiftedGamma的实现:
class ShiftedLog(pm.distributions.continuous.transforms.ElemwiseTransform):
name = "log"
def __init__(self, c=0.0):
self.c = c
def backward(self, x):
return tt.exp(x + self.c)
def forward(self, x):
return tt.log(x - self.c)
class ShiftedGamma(pm.distributions.continuous.Continuous):
def __init__(self, alpha=None, beta=None, mu=None, sd=None, c=0.0, *args, **kwargs):
super(ShiftedGamma, self).__init__(transform=ShiftedLog(c), *args, **kwargs)
alpha, beta = self.get_alpha_beta(alpha, beta, mu, sd)
self.alpha = alpha
self.beta = beta
self.mean = alpha / beta + c
self.mode = tt.maximum((alpha - 1) / beta, 0) + c
self.variance = alpha / beta**2
self.c = c
# self.testval = kwargs.pop('testval', None)
# if not self.testval:
# self.testval = c + 10.0
pm.distributions.continuous.assert_negative_support(alpha, 'alpha', 'Gamma')
pm.distributions.continuous.assert_negative_support(beta, 'beta', 'Gamma')
def get_alpha_beta(self, alpha=None, beta=None, mu=None, sd=None):
if (alpha is not None) and (beta is not None):
pass
elif (mu is not None) and (sd is not None):
alpha = mu**2 / sd**2
beta = mu / sd**2
else:
raise ValueError('Incompatible parameterization. Either use '
'alpha and beta, or mu and sd to specify '
'distribution.')
return alpha, beta
def random(self, point=None, size=None, repeat=None):
alpha, beta, c = draw_values([self.alpha, self.beta, self.c], point=point)
return generate_samples(stats.gamma.rvs, alpha, scale=1. / beta, dist_shape=self.shape, size=size, loc=c)
def logp(self, value):
alpha = self.alpha
beta = self.beta
c = self.c
x = value - c
return bound(-gammaln(alpha) + logpow(beta, alpha) - beta * x + logpow(x, alpha - 1), x >= 0, alpha > 0, beta > 0)
我基本上有两个问题:
1)我的实施“好”/“正确”吗?我特别不确定变换参数,我实现了ShiftedLog变换。并且在任何地方都使用random()方法吗?如果我在这里设置断点,它似乎永远不会被调用。
2)为什么这个模型如此脆弱,基本上不会对前辈或采样方法做出任何改变?只需看看gist中的许多注释,看看我的反复实验,直到我从PyMC3获得结果。
第2点)可能是在第1点中犯了错误的结果,或者可能是Michael Betancourt在StanCon的演讲中解释的任何神奇原因:“你应该学会马尔可夫链蒙特卡罗的一切“?
围绕我的问题1)我还想了解是否有更好或更简单的方法来实现将具有移位的gammas的模型拟合到数据的相同效果,例如是否真的有必要实现ShiftedGamma,还是可以通过pm.Deterministic()或类似的方式达到相同的效果?
非常感谢您的帮助和见解!
基督教
答案 0 :(得分:0)
我最初在github问题上发布了这个,但我想这是一个更好的地方。
你似乎在转换中有一个拼写错误:不应该落后tt.exp(x)+ self.c?在这种情况下,它与pm.distributions.transforms.lowerbound相同。 否则你的方法似乎很好。我不知道用deteministics做任何方法,因为混合物需要分配。在几乎所有其他情况下,ShiftedGamma不应该是必要的。
我认为您可以将其他代码简化为以下内容:(请仔细检查,我没有正确测试...)
class ShiftedGamma(pm.Gamma):
def __init__(self, alpha, beta, shift, *args, **kwargs):
transform = pm.distributions.transforms.lowerbound(shift)
super().__init__(alpha=alpha, beta=beta, *args, **kwargs,
transform=transform)
self.shift = shift
self.mean += shift
self.mode += shift
def random(self):
return super().random() + self.shift
def logp(self, x):
return super().logp(x - self.shift)
with pm.Model() as model:
p = pm.Beta( "p", 1., 1., testval=0.5)
alpha = pm.Uniform('alpha', 0, 10) # == k
beta = pm.Uniform('beta' , 0, 1) # == theta
shifts = pm.Normal('shifts', mu=20, sd=50, shape=2, testval=floatX([0., 0.]))
gamma = ShiftedGamma.dist(alpha=alpha, beta=beta, shift=shifts)
pm.Mixture('y', tt.stack([p, 1-p]), gamma, observed=data))
关于初始化和采样的困难: 您正在使用MAP,这在大多数情况下是不鼓励的,使用advi进行初始化通常会更好。 我认为你有这么难的原因是很多移位值导致-inf的logp,即如果移位的最小值大于数据集的最小值。您可以尝试以某种方式重新参数化,以确保不会发生这种情况。也许是这样的:
shift1_raw = pm.HalfCauchy('shift1_raw', beta=1)
shift2_raw = pm.HalfCauchy('shift2_raw', beta=1)
shift1 = data.min() - shift1_raw
shift2 = shift1 + shift2_raw
shift = tt.stack([shift1, shift2])
shift = pm.Deterministic('shift', shift)
但这绝对需要更多思考;这个版本改变了先验,这个新的先验取决于数据集,而shift1_raw和shift2_raw可能是高度相关的(这使得采样器不满意。)
另外,请记住混合模型通常最终是多模式的,而NUTS通常不适合那些。
编辑作为旁注,如果您只是想要了解pymc3并且对Mixture模型没有特别的兴趣,也许不要从那些开始。他们往往会遇到麻烦。