pymc,对多个变量进行了观察

时间:2019-02-06 02:01:09

标签: python bayesian pymc3 pymc mcmc

我正在使用an example of linear regression from bayesian methods for hackers,但无法将其扩展到我的用法。

我对一个随机变量有观测,对该随机变量有一个假定分布,最后我对该观测有一个对该随机变量的另一个假定分布。我试图通过ab上的中间分布对其建模的方法是,但它抱怨Wrong number of dimensions: expected 0, got 1 with shape (788,).

为了描述实际模型,我预测了一定数量(n)的电子邮件收发率。我的先见是,转换率(由alphabeta上的Beta函数描述)将通过使alphabeta缩放一些因子(0,inf ] ab,它们在n = 0时从1开始,并在某个阈值处增加到最大值。

# Generate predictive data, X and target data, Y
data = [
{'n': 0 , 'trials': 120, 'successes': 1},
{'n': 5 , 'trials': 111, 'successes': 2},
{'n': 10, 'trials': 78 , 'successes': 1},
{'n': 15, 'trials': 144, 'successes': 3},
{'n': 20, 'trials': 280, 'successes': 7},
{'n': 25, 'trials': 55 , 'successes': 1}]

X = np.empty(0)
Y = np.empty(0)
for dat in data:
    X = np.insert(X, 0, np.ones(dat['trials']) * dat['n'])
    target = np.zeros(dat['trials'])
    target[:dat['successes']] = 1
    Y = np.insert(Y, 0, target)

with pm.Model() as model:
    alpha = pm.Uniform("alpha_n", 5, 13)
    beta = pm.Uniform("beta_n", 1000, 1400)
    n_sat = pm.Gamma("n_sat", alpha=20, beta=2, testval=10)
    a_gamma = pm.Gamma("a_gamma", alpha=18, beta=15)
    b_gamma = pm.Gamma("b_gamma", alpha=18, beta=27)
    a_slope = pm.Deterministic('a_slope', 1 + (X/n_sat)*(a_gamma-1))
    b_slope = pm.Deterministic('b_slope', 1 + (X/n_sat)*(b_gamma-1))
    a = pm.math.switch(X >= n_sat, a_gamma, a_slope)
    b = pm.math.switch(X >= n_sat, b_gamma, b_slope)
    p = pm.Beta("p", alpha=alpha*a, beta=beta*b)
    observed = pm.Bernoulli("observed", p, observed=Y)

是否有办法使它正常工作?

1 个答案:

答案 0 :(得分:0)

数据

首先,请注意,重复进行伯努利试验的总可能性恰好是二项式可能性,因此无需在数据中扩展到各个试验。我还建议您使用Pandas DataFrame来管理您的数据-保持整洁有帮助:

import pandas as pd

df = pd.DataFrame({
    'n': [0, 5, 10, 15, 20, 25],
    'trials': [120, 111, 78, 144, 280, 55],
    'successes': [1, 2, 1, 3, 7, 1]
})

解决方案

这将有助于简化模型,但是解决方案实际上是在shape随机变量中添加p自变量,以便PyMC3知道如何解释一维参数。事实是,您确实希望为每种p案例分配不同的n分布,因此,在概念上没有错。

with pm.Model() as model:
    # conversion rate hyperparameters
    alpha = pm.Uniform("alpha_n", 5, 13)
    beta = pm.Uniform("beta_n", 1000, 1400)

    # switchpoint prior
    n_sat = pm.Gamma("n_sat", alpha=20, beta=2, testval=10)

    a_gamma = pm.Gamma("a_gamma", alpha=18, beta=15)
    b_gamma = pm.Gamma("b_gamma", alpha=18, beta=27)

    # NB: I removed pm.Deterministic b/c (a|b)_slope[0] is constant 
    #     and this causes issues when using ArViZ
    a_slope = 1 + (df.n.values/n_sat)*(a_gamma-1)
    b_slope = 1 + (df.n.values/n_sat)*(b_gamma-1)

    a = pm.math.switch(df.n.values >= n_sat, a_gamma, a_slope)
    b = pm.math.switch(df.n.values >= n_sat, b_gamma, b_slope)

    # conversion rates
    p = pm.Beta("p", alpha=alpha*a, beta=beta*b, shape=len(df.n))

    # observations
    pm.Binomial("observed", n=df.trials, p=p, observed=df.successes)

    trace = pm.sample(5000, tune=10000)

这很好采样

enter image description here

并在转换率上产生合理的间隔

enter image description here

但是alpha_nbeta_n的后继者正好达到您先前的界限,这有点令人担忧:

enter image description here

我认为这样做的原因是,对于每种情况,您只能进行55-280次试验,如果条件是独立的(最坏的情况),则共轭将告诉我们您的Beta超参数应在该范围内。由于您正在进行回归,因此,在整个试验中共享信息的最佳情况是,将超参数置于试验总和的范围内(788)-但这是一个上限。因为您不在此范围内,所以这里的担心是您迫使模型的估计要比实际有证据支持的更为精确。但是,可以证明这是合理的,如果先验是基于强有力的独立证据。

否则,我建议扩大那些影响最终alpha*abeta*b数量的先验值的范围(这些值的总和应与后验次数相近)。


替代模型

我可能会按照以下方式做一些事情,尽管与您的模型并不完全相同,但我认为参数化更为透明:

with pm.Model() as model_br_sp:
    # regression coefficients
    alpha = pm.Normal("alpha", mu=0, sd=1)
    beta = pm.Normal("beta", mu=0, sd=1)

    # saturation parameters
    saturation_point = pm.Gamma("saturation_point", alpha=20, beta=2)
    max_success_rate = pm.Beta("max_success_rate", 1, 9)

    # probability of conversion
    success_rate = pm.Deterministic("success_rate", 
                                    pm.math.switch(df.n.values > saturation_point, 
                                                   max_success_rate,
                                                   max_success_rate*pm.math.sigmoid(alpha + beta*df.n)))

    # observations
    pm.Binomial("successes", n=df.trials, p=success_rate, observed=df.successes)

    trace_br_sp = pm.sample(draws=5000, tune=10000)

在这里,我们通过以最大成功率最大化的S形将预测变量空间映射到概率空间。饱和点上的先验与您的相同,而最大成功率上的先验则提供的信息很少(Beta [1,9]-尽管我会说它也几乎在平坦的先验下运行)。这也很好采样,

enter image description here

并给出相似的间隔(尽管切换点似乎占主导地位):

enter image description here

我们可以比较这两种模型,发现它们的解释力没有显着差异:

import arviz as az

model_compare = az.compare({'Binomial Regression w/ Switchpoint': trace_br_sp,
                            'Original Model': trace})
az.plot_compare(model_compare)

enter image description here

enter image description here