NUTS和Metropolis的层次概率模型的收敛性问题

时间:2017-03-14 06:28:55

标签: python hierarchical-data bayesian pymc3

我试图通过添加概率转换并使结果遵循伯努利分布,将层次模型从Gelman和Hill reproduced in PyMC3 here扩展到二元结果数据。现在我正在使用玩具数据,所以我知道真正的价值观。 Alpha应为.1,beta应为.5。

在扩展之前,模型可以使用NUTS采样器运行良好。一旦我添加它,估计值会慢慢增加并持续增加,直到模型在10到200次迭代之间停顿。这是从一直到120(相对长时间运行)的图像。 traceplots from NUTS sample

在扩展之前,Metropolis需要200,000次迭代才能很好地修复真正的参数值,但最终确实如此。扩展后它停留在30k到50k之间。与NUTS不同,当你试图在失速后停止它时它完全崩溃,所以我没有照片。早些时候停止它会对beta大致超过零的估计,但是差异很大。

代码粘贴在下面。

我不确定这是抽样问题还是规格问题。是否有更好的方法来指定Probit?有关其他采样器的任何提示可以尝试吗?我尽可能地将我的模型剥离下来进行测试,并在我添加probit扩展后将其缩小到破坏状态,但我对于接下来要做什么感到茫然。

#Generate Data
n=100
#Determine how many observations per group
evts=np.random.randint(10,100,n)
#Determine which groups will be receive treatment
x_g=np.random.binomial(1,.3,n)
#pre-create distribution of betas for groups 
mu = np.random.normal(.5,.2,n)
#preallocate space in a dataframe
i = np.zeros(evts.sum())
y_obs = pd.DataFrame({'y':i.copy(),
                      'x':i.copy(),
                      'grp':i.copy()},
                     index = range(evts.sum()))
#populate dataframe with simulated data
i=0
for grp in range(100):
    #index of observations for a given group
    ind = list(range(i,(i+evts[grp])))
    i += evts[grp]
    #generate outcomes using
    #different dgp depending on treatment
    if x_g[grp] ==1:
        #shortcut to make sure 1>p>0
        p_i = max((.1 + mu[grp]),0.01)
        p_i = min(p_i,1)
        out = np.random.binomial(1,p_i,evts[grp])
    else:
        out = np.random.binomial(1,.1,evts[grp])
    #Assign to dataframe
    y_obs.loc[ind,'y'] = out
    y_obs.loc[ind,'x'] = x_g[grp]
    y_obs.loc[ind,'grp'] = grp
y_obs = y_obs.astype(int)
print('starting model')
with pm.Model() as test_model:
    #hyperpriors
    mu_a=pm.Normal('mu_a',mu=0, sd=100**2)
    sig_a = pm.Uniform('sig_a',lower=0,upper=100)
    mu_b=pm.Normal('mu_b',mu=0, sd=100**2)
    sig_b = pm.Uniform('sig_b',lower=0,upper=100)
    #priors
    a = pm.Normal('a',mu=mu_a,sd = sig_a, shape=n)
    b = pm.Normal('b',mu=mu_b,sd = sig_b, shape=n)

    eps = pm.Uniform('eps',lower=0,upper=100)

    est = a[y_obs.grp] + b[y_obs.grp] * y_obs.x
    #I get correct estimates when I 
    #stop here using commented out line. 
#     y_hat = pm.Normal('y_hat',
#                       mu=est,
#                       sd=eps, 
#                       observed = y_obs.y)

    #Probit transformation:
    y_hat = pm.Normal('y_hat',
                      mu=est,
                      sd=eps, 
                      shape=y_obs.shape[0])

    mu_y = tt.mean(y_hat)
    eps_hat = tt.var(y_hat)
    p_hat = 0.5 * (1 + tt.erf((y_hat-mu_y) / (eps_hat*tt.sqrt(2))))

    y = pm.Bernoulli('y',p=p_hat, observed = y_obs.y)


with test_model:
    #Either:
    mu,sds,elbo = pm.variational.advi(n=100000)
    step = pm.NUTS(scaling=test_model.dict_to_array(sds),
                   is_cov=True)
    test_trace = pm.sample(200, step, start=mu)
    #or
#     step=pm.Metropolis()
#     test_trace = pm.sample(50000)

pm.traceplot(test_trace)#[-5000::3])

注意:编辑修复拼写错误:'step = pm.NUTS(scaling = test_model.dict_to_array(sds),`

编辑:我为最初发布的模型的probit扩展制作了更好的模拟数据。 (原始数据生成是现在ADVI提供更好的估计,所以它开始在正确的地方,但NUTS仍然很快停止(大约十次迭代)。大都会直接失败:我做了第一轮5000次迭代,并获得尝试绘制迹线时出错。

新数据生成:

n=100
evts=np.random.randint(10,100,n)
x_g=np.random.binomial(1,.3,n)
i = np.zeros(evts.sum())
mu = np.random.normal(.5,.2,n)
mu0 = np.random.normal(.1,.05,n)
y_obs = pd.DataFrame({'y':i.copy(),'x':i.copy(),'grp':i.copy()},index = range(evts.sum()))
i=0
for grp in range(100):
    ind = list(range(i,(i+evts[grp])))
    i += evts[grp]
    if x_g[grp] ==1:
        est = mu0[grp] + mu[grp]
    else:
        est=mu0[grp]
    p_hat = tt.nnet.sigmoid(est).eval()
    y_obs.loc[ind,'y_hat'] = est
    y_obs.loc[ind,'y'] = np.random.binomial(1,p_hat,len(ind))
    y_obs.loc[ind,'x'] = x_g[grp]
    y_obs.loc[ind,'grp'] = grp
y_obs['grp']=y_obs.grp.astype(np.int64)

当pymc3试图绘制密度时,metropolis的错误:

  

ValueError:v不能为空

1 个答案:

答案 0 :(得分:0)

也许我误解了你想要做的事,但不应该这个模型有用:

with pm.Model() as test_model:
    #hyperpriors
    mu_a = pm.Flat('mu_a')
    sig_a = pm.HalfCauchy('sig_a', beta=2.5)

    mu_b = pm.Flat('mu_b')
    sig_b = pm.HalfCauchy('sig_b', beta=2.5)

    #priors
    a_raw = pm.Normal('a_raw', mu=0, sd=1, shape=n)
    a = pm.Deterministic('a', mu_a + sig_a * a_raw)

    b_raw = pm.Normal('b_raw', mu=0, sd=1, shape=n)
    b = pm.Deterministic('b', mu_b + sig_b * b_raw)

    est = a[y_obs.grp.values] + b[y_obs.grp.values] * y_obs.x

    y = pm.Bernoulli('y', p=tt.nnet.sigmoid(est), observed = y_obs.y)

这是一个logit,而不是probit模型。如果您出于某种原因需要probit,则可以使用标准的probit函数替换tt.nnet.sigmoid

这对您的数据集来说仍然有点困难,但我认为这是因为数据生成中的错误:您假设所有0.1的组都有一个常量a,但在模型中允许a值不同按组。采样器在sig_a的值非常小时会出现问题(毕竟真正的值为0)。

修改:更多解释:使用标准普通a_rawb_raw进行更改,然后使用Normal(mu=mu_a, sd=sig_a)将其转换为pm.Deterministic不改变后部,但它使采样器更容易。它被称为非中心参数化。有关该主题的更深入描述,请参阅例如http://mc-stan.org/documentation/case-studies/divergences_and_bias.html,这也有助于您理解为什么非常小的差异可能会有问题。

修改:新数据生成

n = 100
evts=np.random.randint(10,100,n)
x_g=np.random.binomial(1,.3,n)
i = np.zeros(evts.sum())
mu = np.random.normal(.5,.2,n)
mu0 = np.random.normal(.1,.05,n)
y_obs = pd.DataFrame({'y':i.copy(),'x':i.copy(),'grp':i.copy()},
                     index = range(evts.sum()))
i = 0
for grp in range(100):
    ind = list(range(i,(i+evts[grp])))
    i += evts[grp]
    if x_g[grp] ==1:
        est = mu0[grp] + mu[grp]
    else:
        est=mu0[grp]
    p_hat = tt.nnet.sigmoid(est).eval()
    y_obs.loc[ind,'y_hat'] = est
    y_obs.loc[ind,'y'] = np.random.binomial(1,p_hat,len(ind))
    y_obs.loc[ind,'x'] = x_g[grp]
    y_obs.loc[ind,'grp'] = grp
y_obs['grp']=y_obs.grp.astype(np.int64)

使用

进行抽样
with test_model:
    trace = pm.sample(2000, tune=1000, njobs=4)

大约三分钟后就完成了

Auto-assigning NUTS sampler...
Initializing NUTS using advi...
  8%|▊         | 15977/200000 [00:15<02:51, 1070.66it/s]Median ELBO converged.
Finished [100%]: Average ELBO = -4,458.8

100%|██████████| 2000/2000 [02:48<00:00,  9.99it/s]

没有不同的过渡:

test_trace[1000:].diverging.sum()

全部使用pymc3和theano master。 (两者即将为新版本做好准备)