Python 如何用MCMC分解混合分布
我的数据是正态分布和常值的50:50混合:Python 如何用MCMC分解混合分布,python,bayesian,pymc3,mcmc,tensorflow-probability,Python,Bayesian,Pymc3,Mcmc,Tensorflow Probability,我的数据是正态分布和常值的50:50混合: numdata = 10000 data = np.random.normal(0.0,1.0,numdata).astype(np.float32) data[int(numdata/2):] = 0.0 plt.hist(data,30,density=True) 我的任务是将混合密度与数据相匹配。 我使用的是tfd.model和tfd.determinative的混合 已知(在样本数据的情况下)正态与确定性的比率为0.5 相反,我的MCMC返
numdata = 10000
data = np.random.normal(0.0,1.0,numdata).astype(np.float32)
data[int(numdata/2):] = 0.0
plt.hist(data,30,density=True)
我的任务是将混合密度与数据相匹配。
我使用的是tfd.model和tfd.determinative的混合
已知(在样本数据的情况下)正态与确定性的比率为0.5
相反,我的MCMC返回一个0.83的比值,以支持正常值
有没有更好的方法使这个分布与正确的比率相匹配
以下是完整的示例代码:
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
import tensorflow as tf
import tensorflow_probability as tfp
import matplotlib.pyplot as plt
tfd = tfp.distributions
tfb = tfp.bijectors
import numpy as np
from time import time
numdata = 10000
data = np.random.normal(0.0,1.0,numdata).astype(np.float32)
data[int(numdata/2):] = 0.0
_=plt.hist(data,30,density=True)
root = tfd.JointDistributionCoroutine.Root
def dist_fn(rv_p,rv_mu):
rv_cat = tfd.Categorical(probs=tf.stack([rv_p, 1.-rv_p],-1))
rv_norm = tfd.Normal(rv_mu,1.0)
rv_zero = tfd.Deterministic(tf.zeros_like(rv_mu))
rv_mix = tfd.Independent(
tfd.Mixture(cat=rv_cat,
components=[rv_norm,rv_zero]),
reinterpreted_batch_ndims=1)
return rv_mix
def model_fn():
rv_p = yield root(tfd.Sample(tfd.Uniform(0.0,1.0),1))
rv_mu = yield root(tfd.Sample(tfd.Uniform(-1.,1. ),1))
rv_mix = yield dist_fn(rv_p,rv_mu)
jd = tfd.JointDistributionCoroutine(model_fn)
unnormalized_posterior_log_prob = lambda *args: jd.log_prob(args + (data,))
n_chains = 1
p_init = [0.3]
p_init = tf.cast(p_init,dtype=tf.float32)
mu_init = 0.1
mu_init = tf.stack([mu_init]*n_chains,axis=0)
initial_chain_state = [
p_init,
mu_init,
]
bijectors = [
tfb.Sigmoid(), # p
tfb.Identity(), # mu
]
step_size = 0.01
num_results = 50000
num_burnin_steps = 50000
kernel=tfp.mcmc.TransformedTransitionKernel(
inner_kernel=tfp.mcmc.HamiltonianMonteCarlo(
target_log_prob_fn=unnormalized_posterior_log_prob,
num_leapfrog_steps=2,
step_size=step_size,
state_gradients_are_stopped=True),
bijector=bijectors)
kernel = tfp.mcmc.SimpleStepSizeAdaptation(
inner_kernel=kernel, num_adaptation_steps=int(num_burnin_steps * 0.8))
#XLA optim
@tf.function(autograph=False, experimental_compile=True)
def graph_sample_chain(*args, **kwargs):
return tfp.mcmc.sample_chain(*args, **kwargs)
st = time()
trace,stats = graph_sample_chain(
num_results=num_results,
num_burnin_steps=num_burnin_steps,
current_state=initial_chain_state,
kernel=kernel)
et = time()
print(et-st)
ptrace, mutrace = trace
plt.subplot(121)
_=plt.hist(ptrace.numpy(),100,density=True)
plt.subplot(122)
_=plt.hist(mutrace.numpy(),100,density=True)
print(np.mean(ptrace),np.mean(mutrace))
p和mu的结果分布如下所示:
显然,它的平均值应该是p=0.5
我怀疑型号_fn()可能有问题。
我试图在不同的p值下评估模型的log_prob,实际上“最优”约为0.83,我只是不明白为什么以及如何修正它,以便重建原始混合物
[编辑]
pymc3的“更简单”演示代码。仍然是相同的行为,结果是0.83而不是0.5
import pymc3 as pm
import numpy as np
import arviz as az
import matplotlib.pyplot as plt
numdata = 1000
data1 = np.random.normal(0.0,1.0,numdata).astype(np.float32)
data2 = np.zeros(numdata).astype(np.float32)
data = np.concatenate((data1,data2))
_=plt.hist(data,30,density=True)
with pm.Model() as model:
norm = pm.Normal.dist(0.0,1.0)
zero = pm.Constant.dist(0.0)
components = [norm,zero]
w = pm.Dirichlet('p', a=np.array([1, 1])) # two mixture component weights.
like = pm.Mixture('data', w=w, comp_dists=components, observed=data)
posterior = pm.sample()
idata = az.from_pymc3(posterior)
az.plot_posterior(posterior)
概率密度和质量的不可通约性
这里的问题是,来自每个模型的可能性涉及到高斯分布的概率密度和离散分布的质量,这是不相称的。具体来说,比较零观测值来自何处的计算将涉及概率
P[x=0|Normal[0,1]] = 1/sqrt(2*pi) = 0.3989422804014327
P[x=0| Zero ] = 1
这将比较这些(通过p
加权),就好像它们有相同的单位一样。然而,前者是一个密度,因此相对于后者是无穷小的。如果忽略了这种不可通约性,那么我们实际上就好像高斯函数有40%的几率生成零,而实际上它正好生成零
解决方法:伪离散分布
我们需要以某种方式转换单位。一种简单的方法是用连续分布近似离散分布,这样它产生的概率以密度单位表示。例如,使用以离散值为中心的高精度(窄)高斯分布或拉普拉斯分布,会产生以0.5为中心的后验概率
以pm.Model()作为模型的:
标准=pm.正常距离(0.0,1.0)
伪零点=pm.Laplace距离(0.0,1e-16)
分量=[范数,伪零]
w=pm.Dirichlet('p',a=np.array([1,1])#两个混合分量权重。
like=pm.混合物(“数据”,w=w,成分分布=成分,观察值=数据)
后验=pm.sample()
idata=从_pymc3开始的az(后)
az.后测图(后测图)
为什么
p=0.83
?
我们在混合离散和连续时观察到的后验概率不是任意的。这里有两种方法。对于以下内容,我们将使用一个p
来表示来自高斯分布的概率
地图估算
忽略不可通约性,我们可以得出p
的MAP估计值,如下所示。让我们用D={D|u 1 | D|u 2}
表示组合观测值,其中D|u 1
是高斯分布的子集,以此类推,n
是每个子集的观测数。然后我们可以写出可能性
P[p|D] ~ P[D|p]P[p]
由于狄里克莱函数是统一的,我们可以忽略p[p]
并扩展数据
P[D|p] = P[D_1|p]P[D_2|p]
= (Normal[D_1|0,1]*(p^n))(Normal[0|0,1]*p + 1*(1-p))^n
= Normal[D_1|0,1]*(p^n)(0.3989*p + 1 - p)^n
= Normal[D_1|0,1]*(p - 0.6011*(p^2))^n
取导数w.r.t.p
并设置为零,我们得到
0 = n*(1-1.2021*p)(p-0.6011*p^2)^(n-1)
它在p=1/1.2021=0.8318669
处呈现(非平凡的)零
抽样思维实验
另一种方法是通过抽样实验。假设我们使用以下方案对p
进行采样
p
开始p
值加权,绘制一个伯努利样本p
作为所有这些伯努利图的平均值p
,但对不可能的观测模型分配具有鲁棒性
对于第一次迭代,让我们从p=0.5
开始。对于所有真正来自高斯分布的观测值,离散模型的可能性为零,因此,我们的伯努利图中至少有一半是1(对于高斯分布)。对于来自离散模型的所有观测值,这将是对每个模型中观测到零的可能性的比较。离散模型为1,高斯模型为0.3989422804014327。规范化这一点,意味着我们有一个概率为
p*0.3989/((1-p)*1 + p*0.3989)
# 0.2851742248343187
支持高斯分布。现在我们可以更新p
,这里我们将只处理预期值,即:
p = 0.5*1 + 0.5*0.2851742248343187
# 0.6425871124171594
如果我们开始迭代,会发生什么
#正常值为零的可能性
lnorm=np.exp(pm.Normal.dist(0,1).logp(0.eval())
#历史
p_n=np.零(101)
#初始值
p_n[0]=0.5
对于范围(100)内的i:
#更新
p_n[1+i]=0.5+0.5*p_n[i]*lnorm/((1-p_n[i])+p_n[i]*lnorm)
地块(p_n);
p_n[100]
# 0.8318668635076404
同样,预期值收敛于后验平均值p=0.83
因此,撇开PDF和PMF的辅结构域有不同的单位这一事实不谈,似乎TFP和PyMC3的行为都是正确的。这是一个非常好的解释(即使我能理解),下次选择混合物成分时,我应该更加小心。谢谢在未来五年内,可能没有人能做到这一点,但如果PPL是在超实数系统(超浮点?)的表示上实现的,那么OP最初所做的工作就可以了。这将是一篇很好的硕士论文,或者是一些博士论文的重要部分。