【问题标题】:Posterior from joint normal prior distribution联合正态先验分布的后验
【发布时间】:2019-03-08 06:03:56
【问题描述】:

我有一些关于高斯推理的基本问题。

我有以下数据:

(Log) dose, Number of animals, Number of deaths
-0.86, 5, 0
-0.30, 5, 1
-0.05, 5, 3
0.73, 5, 5

编辑:我假设剂量反应 logit(θ) = α + βx 的简单回归模型,其中 logit(θ) = log(θ / (1-θ))。 θ 代表在剂量 x 下的死亡概率。

我想在 (α,β) 上创建一个联合正态先验分布,其中 α ∼ N(0,22),β ∼ N(10,102) 和 corr(α,β) = 0.5,然后计算后验先验周围点网格中的密度(α:0 ± 4,β:10 ± 20)。

首先,我创建了以下联合正态先验分布:

import numpy as np
from scipy import stats
x = np.array([-0.86, -0.30, -0.05, 0.73])
n = np.array([5, 5, 5, 5])
y = np.array([0, 1, 3, 5])
prior = stats.multivariate_normal([0, 10], [[0.5, 0], [0, 0.5]])

是这样吗?

第二,如何计算网格中的后验密度?

【问题讨论】:

  • 是的,有很多二项式响应的例子。但是如果我想使用高斯先验呢?我仍然对如何计算后验感到困惑。
  • 现在我看到我的问题确实不完整。我正在使用剂量反应的回归模型。我编辑了问题。

标签: python bayesian normal-distribution log-likelihood


【解决方案1】:

基于merv的好回答,回答我自己,我认为封闭的解决方案是:

p(yi|α,β,ni,xi)∝ [logit−1(α+βxi)]y * [1 − logit− 1(α+βx)n−y]

因此后验可以计算如下:

import numpy as np
from scipy import optimize, stats
import matplotlib.pyplot as plt
x = np.array([-0.86, -0.30, -0.05, 0.73])
n = np.array([5, 5, 5, 5])
y = np.array([0, 1, 3, 5])

ngrid = 100
mu_1, mu_2, sd_1, sd_2 = 0, 10, 2**2, 10**2
A = np.linspace(-4, 4, ngrid)
B = np.linspace(-10, 30, ngrid)

mu = np.array([0, 10])
s = np.array([[22, 102]])
Rho = np.array([[1, 0.5], [0.5, 1]])
Sigma = Rho * np.outer(s, s)
prior = stats.multivariate_normal([mu_1, mu_2], Sigma)

def prop_likelihood(input_values):
    ilogit_abx = 1 / (np.exp(-(input_values[...,0][...,None]*np.ones(x.shape) + input_values[...,1][...,None] * x)) + 1)
    return np.prod(ilogit_abx**y * (1 - ilogit_abx)**(n - y), axis=ilogit_abx.ndim -1)

grid_a , grid_b = np.meshgrid(A,B)
grid = np.empty(grid_a.shape + (2,)) 
grid[:, :, 0] = grid_a
grid[:, :, 1] = grid_b

posterior_density = prior.pdf(grid)*prop_likelihood(grid)

然后可以说明:

fig, ax = plt.subplots(figsize=(10, 5)
ax.imshow(
    posterior_density,
    origin='lower',
    aspect='auto',
    extent=(A[0], A[-1], B[0], B[-1])
)
ax.set_xlim([-4, 4])
ax.set_ylim([-10, 30])
ax.set_xlabel(r'$\alpha$')
ax.set_ylabel(r'$\beta$')
ax.set_title('Posterior heatmap')
ax.grid('off')

解析解:

def opt(params):
    a, b  = params[0], params[1]
    z = np.exp(a + b * x) / (1 +  np.exp(a + b * x))
    e = - np.sum(y * np.log(z) + (n - y) * np.log(1 - z))
    return e

optim_res = optimize.minimize(opt, np.array([0.0, 0.0]))
mu_opt = optim_res['x']
sigma_opt = optim_res['hess_inv']
posterior_optimized = stats.multivariate_normal(mean=mu_opt, cov=sigma_opt)

然后可以绘制出来

fig, ax = plt.subplots(figsize=(10, 5)
ax.imshow(
    posterior_optimized.pdf(grid),
    origin='lower',
    aspect='auto',
    extent=(A[0], A[-1], B[0], B[-1])
)
ax.set_xlim([-4, 4])
ax.set_ylim([-10, 30])
ax.set_xlabel(r'$\alpha$')
ax.set_ylabel(r'$\beta$')
ax.set_title('Posterior heatmap from analytical solution')
ax.grid('off')

存在一些差异。不确定解析优化函数是否正确。

希望这对其他人有所帮助。

【讨论】:

  • (1) 为内嵌图片添加一个“!” (见编辑降价); (2) 逆对数在 SciPy scipy.special.expit 中定义,您的 y*log(1-x) 操作可以使用一些提供数值稳定性的 the SciPy convenience functions 来完成; (3) 请注意,您只是在计算未归一化的后验函数,而不是真正的后验概率密度
  • 评论您所谓的分析解决方案:如果我理解正确,您正在拟合高斯,而您的真实后验确实不是。这种技术称为拉普拉斯近似,虽然它可以最大限度地减少推断均值和方差的误差,但它不会捕捉到真实后验的更高时刻,如偏斜和峰度。请注意,这种方法有这个限制。我认为这可以解释为什么我们看到您的情节之间存在差异。
【解决方案2】:

高斯参数化

要回答第一个问题,您错误地参数化了正态分布。特别是您的协方差矩阵未根据您的描述指定。

给定标准差 s_1 = 22s_2 = 102,以及 0.5 的期望相关性,正确的协方差矩阵为:

 ---                    ---
| s_1*s_1      0.5*s_1*s_2 |
|                          |
| 0.5*s_1*s_2      s_2*s_2 |
 ---                    ---

即对角线上的方差和对角线上的协方差。在 Numpy/Scipy 中,这将是

mu = np.array([0, 10])
s = np.array([[22, 102]])
Rho = np.array([[1, 0.5], [0.5, 1]])
Sigma = Rho * np.outer(s, s)

prior = stats.multivariate_normal(mean=mu, cov=Sigma)

是否计算网格值

获得适当归一化的后验密度需要对连续变量(例如 θ)进行边缘化(积分),而这仅在特殊情况下可解析解决,我认为您的情况并非如此。因此,您可以计算积分并计算数值近似值,或者使用一些近似推理方法,例如 MCMC 或变分推理。有很多很好的工具,比如 PyMC3 和 PyStan。

仅获取网格上离散点的后验值需要对模型变量施加条件值。然而,如今大多数概率编程工具都非常具有表达力,因此推断完整的后验会更容易,如果你真的有一些特殊的网格值,可以在事后检查它们。

PyMC3 示例

这是 PyMC3 中的完整后验推断,具有强大的先验:

import numpy as np
import pymc3 as pm
import theano
import theano.tensor as tt
import matplotlib.pyplot as plt
import arviz as az

# Data
X = np.array([-0.86, -0.30, -0.05, 0.73])
N = np.array([5, 5, 5, 5])
Y = np.array([0, 1, 3, 5])

# augment X for simpler regression expression
X_aug = tt.stack(np.ones_like(X), X).T

# Prior params
mu = np.array([0, 10])
sd = np.array([22, 102])
Rho = np.array([[1, 0.5],[0.5, 1]])
Sigma = np.outer(sd, sd) * Rho

with pm.Model() as binomial_regression:
    # regression coefficients (strong prior)
    beta = pm.MvNormal('beta', mu=mu, cov=Sigma, shape=2)

    # death probability
    theta_i = pm.Deterministic('theta_i', pm.invlogit(X_aug.dot(beta)))

    # outcomes
    y_i = pm.Binomial('y_i', n=N, p=theta_i, observed=Y)

    trace = pm.sample(10000, tune=100000, target_accept=0.8, random_seed=2018)

这可以进行抽样,但需要大量的调整步骤来减少分歧:

自动分配 NUTS 采样器...

使用 jitter+adapt_diag 初始化 NUTS...

多进程采样(2 个作业中的 2 个链)NUTS:

[beta] 采样 2 条链:100%|██████████| 220000/220000 [03:52

调整后有 1 个分歧。增加target_accept或者重新参数化。

某些参数的有效样本数小于25%。

跟踪图

联合图

ax, _, _ = az.jointplot(trace, var_names=['beta'], kind='hexbin')
ax.set_xlabel("Intercept Coefficient ($\\beta_0$)")
ax.set_ylabel("Slope Coefficient ($\\beta_1$)")
plt.show()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-06
    • 2014-04-16
    • 1970-01-01
    • 2013-04-09
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-22
    相关资源
    最近更新 更多