MCMC和PyMC的2高斯混合模型推论


10

问题

我想拟合简单的2高斯混合总体的模型参数。考虑到围绕贝叶斯方法的所有炒作,我想了解贝叶斯推断是否比传统拟合方法更好。

到目前为止,MCMC在此玩具示例中的表现非常差,但也许我只是忽略了一些东西。因此,让我们看一下代码。

工具

我将使用python(2.7)+ scipy堆栈,lmfit 0.8和PyMC 2.3。

可以在此处找到重现分析的笔记本

产生数据

首先让我们生成数据:

from scipy.stats import distributions

# Sample parameters
nsamples = 1000
mu1_true = 0.3
mu2_true = 0.55
sig1_true = 0.08
sig2_true = 0.12
a_true = 0.4

# Samples generation
np.random.seed(3)  # for repeatability
s1 = distributions.norm.rvs(mu1_true, sig1_true, size=round(a_true*nsamples))
s2 = distributions.norm.rvs(mu2_true, sig2_true, size=round((1-a_true)*nsamples))
samples = np.hstack([s1, s2])

直方图samples如下所示:

数据直方图

一个“宽阔的山峰”,这些成分很难被人发现。

经典方法:拟合直方图

让我们首先尝试经典方法。使用lmfit可以轻松定义2峰模型:

import lmfit

peak1 = lmfit.models.GaussianModel(prefix='p1_')
peak2 = lmfit.models.GaussianModel(prefix='p2_')
model = peak1 + peak2

model.set_param_hint('p1_center', value=0.2, min=-1, max=2)
model.set_param_hint('p2_center', value=0.5, min=-1, max=2)
model.set_param_hint('p1_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p2_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p1_amplitude', value=1, min=0.0, max=1)
model.set_param_hint('p2_amplitude', expr='1 - p1_amplitude')
name = '2-gaussians'

最后,我们使用单纯形算法对模型进行拟合:

fit_res = model.fit(data, x=x_data, method='nelder')
print fit_res.fit_report()

结果是以下图像(红色虚线为中心对齐):

NLS适合结果

即使问题有点难,只要有适当的初始值和约束,模型都可以收敛到非常合理的估计。

贝叶斯方法:MCMC

我以分层方式在PyMC中定义模型。centers并且sigmas是用于表示2点的中心和2个西格玛2个高斯的超参数先验分布。alpha是第一批人口的一部分,此处的先前分布是Beta。

类别变量在两个总体之间进行选择。据我了解,此变量的大小必须与数据(samples)相同。

最后,mutau是确定性变量,可确定正态分布的参数(它们取决于category变量,因此它们在两个总体的两个值之间随机切换)。

sigmas = pm.Normal('sigmas', mu=0.1, tau=1000, size=2)
centers = pm.Normal('centers', [0.3, 0.7], [1/(0.1)**2, 1/(0.1)**2], size=2)
#centers = pm.Uniform('centers', 0, 1, size=2)

alpha  = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Categorical("category", [alpha, 1 - alpha], size=nsamples)

@pm.deterministic
def mu(category=category, centers=centers):
    return centers[category]

@pm.deterministic
def tau(category=category, sigmas=sigmas):
    return 1/(sigmas[category]**2)

observations = pm.Normal('samples_model', mu=mu, tau=tau, value=samples, observed=True)
model = pm.Model([observations, mu, tau, category, alpha, sigmas, centers])

然后,我以相当长的迭代次数(在我的计算机上为1e5,〜60s)运行MCMC:

mcmc = pm.MCMC(model)
mcmc.sample(100000, 30000)

但是结果很奇怪。例如, trace(第一个种群的分数)趋向于0而是收敛到0.4,并且具有很强的自相关:α

MCMC alpha摘要

同样,高斯人的中心也不收敛。例如:

MCMC中心_0总结

如您在先前的选择中所见,我尝试使用Beta分布为先前的人口分数来“帮助” MCMC算法。中心和sigma的先验分布也很合理(我认为)。α

那么这是怎么回事?我是在做错什么,还是MCMC不适合此问题?

我知道MCMC方法会比较慢,但是平凡的直方图拟合似乎在解决总体时表现出了极大的优势。

Answers:


6

该问题是由PyMC为该模型抽取样本的方式引起的。如PyMC文档的5.8.1节所述,数组变量的所有元素都一起更新。对于像center这样的小阵列来说,这不是问题,但是对于像category这样的大阵列,则导致较低的接受率。您可以通过以下方式查看接受率

print mcmc.step_method_dict[category][0].ratio

文档中建议的解决方案是使用标量值变量数组。此外,由于默认选择不正确,因此您需要配置一些提案分配。这是对我有用的代码:

import pymc as pm
sigmas = pm.Normal('sigmas', mu=0.1, tau=1000, size=2)
centers = pm.Normal('centers', [0.3, 0.7], [1/(0.1)**2, 1/(0.1)**2], size=2)
alpha  = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Container([pm.Categorical("category%i" % i, [alpha, 1 - alpha]) 
                         for i in range(nsamples)])
observations = pm.Container([pm.Normal('samples_model%i' % i, 
                   mu=centers[category[i]], tau=1/(sigmas[category[i]]**2), 
                   value=samples[i], observed=True) for i in range(nsamples)])
model = pm.Model([observations, category, alpha, sigmas, centers])
mcmc = pm.MCMC(model)
# initialize in a good place to reduce the number of steps required
centers.value = [mu1_true, mu2_true]
# set a custom proposal for centers, since the default is bad
mcmc.use_step_method(pm.Metropolis, centers, proposal_sd=sig1_true/np.sqrt(nsamples))
# set a custom proposal for category, since the default is bad
for i in range(nsamples):
    mcmc.use_step_method(pm.DiscreteMetropolis, category[i], proposal_distribution='Prior')
mcmc.sample(100)  # beware sampling takes much longer now
# check the acceptance rates
print mcmc.step_method_dict[category[0]][0].ratio
print mcmc.step_method_dict[centers][0].ratio
print mcmc.step_method_dict[alpha][0].ratio

proposal_sdproposal_distribution选项中的说明5.7.1部分。对于中心,我将建议设置为大致匹配后验的标准偏差,由于数据量,该标准差比默认值小得多。PyMC确实会尝试调整提案的宽度,但这仅在您的接受率足够高才能开始时才有效。对于category,默认值proposal_distribution = 'Poisson'会给出较差的结果(我不知道为什么会这样,但是对于二进制变量来说,这听起来似乎不是明智的建议)。


谢谢,这确实很有用,尽管它几乎变得缓慢。你能简要的含义解释proposal_distributionproposal_sd为什么使用和Prior更好的分类变量?
user2304916 2014年

谢谢,汤姆。我同意泊松在这里是一个奇怪的选择。我提出了一个问题:github.com/pymc-devs/pymc/issues/627
twiecki 2014年

2

您不应该使用法线来建模,那样您就可以为标准变体允许使用负值。改用类似以下内容的东西:σ

sigmas = pm.Exponential('sigmas', 0.1, size=2)

更新:

通过更改模型的以下部分,我接近了数据的初始参数:

sigmas = pm.Exponential('sigmas', 0.1, size=2)
alpha  = pm.Beta('alpha', alpha=1, beta=1)

并通过细化调用mcmc:

mcmc.sample(200000, 3000, 10)

结果:

α

中心

西格玛

您的后代不是很好。在BUGS书的 11.6节中,他们讨论了这种类型的模型,并指出存在收敛问题,但没有明显的解决方案。也可以在这里检查。


很好,我现在正在使用Gamma。但是,alpha迹线总是趋向于0(而不是0.4)。我想知道在我的示例中是否存在一些愚蠢的错误。
user2304916 2014年

我尝试了Gamma(.1,.1),但Exp(.1)似乎更好。另外,当自相关较高时,您可以添加一些细化功能,例如,mcmc.sample(60000, 3000, 3)
jpneto 2014年

2

同样,对于将MCMC用于混合模型,不可识别性也是一个大问题。基本上,如果在聚类均值和聚类分配上切换标签,则可能性不会改变,这可能会使采样器(在链之间或链内)感到困惑。您可能要尝试缓解的一件事是在PyMC3中使用电位。一个具有潜力的GMM的良好实现在这里。这些问题的后验通常也是高度多峰的,这也是一个大问题。您可能要改用EM(或变分推理)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.