问题
我想拟合简单的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()
结果是以下图像(红色虚线为中心对齐):
即使问题有点难,只要有适当的初始值和约束,模型都可以收敛到非常合理的估计。
贝叶斯方法:MCMC
我以分层方式在PyMC中定义模型。centers
并且sigmas
是用于表示2点的中心和2个西格玛2个高斯的超参数先验分布。alpha
是第一批人口的一部分,此处的先前分布是Beta。
类别变量在两个总体之间进行选择。据我了解,此变量的大小必须与数据(samples
)相同。
最后,mu
和tau
是确定性变量,可确定正态分布的参数(它们取决于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,并且具有很强的自相关:
同样,高斯人的中心也不收敛。例如:
如您在先前的选择中所见,我尝试使用Beta分布为先前的人口分数来“帮助” MCMC算法。中心和sigma的先验分布也很合理(我认为)。
那么这是怎么回事?我是在做错什么,还是MCMC不适合此问题?
我知道MCMC方法会比较慢,但是平凡的直方图拟合似乎在解决总体时表现出了极大的优势。
proposal_distribution
和proposal_sd
为什么使用和Prior
更好的分类变量?