SciPy截断多变量正常?

时间:2013-11-21 08:33:42

标签: python scipy

我正在尝试自动化一个过程,在某些时候需要从截断的多元法线中抽取样本。也就是说,它是正常的多元正态分布(即高斯分布),但变量被约束为长方体。我给出的输入是完整多变量法线的均值和协方差,但我需要在我的方框中提取样本。

到目前为止,我只是拒绝了盒子外的样品并根据需要重新取样,但我开始发现我的过程有时会给我(a)大的协方差和(b)接近于边缘。这两个事件与我的系统速度相悖。

所以我想做的是首先正确地对分布进行采样。谷歌搜索只导致this discussionscipy.stats中的truncnorm distribution。前者是不确定的,后者似乎是一个变量。是否有任何原生多变量截断正常?它会比拒绝样品更好,还是应该做一些更聪明的事情?

我将开始研究我自己的解决方案,即将未截断的高斯旋转到它的主轴(使用SVD分解等),使用截断高斯的乘积来对分布进行采样,然后旋转取样,并根据需要拒绝/重新取样。如果截断的采样效率更高,我认为这应该更快地采样所需的分布。

3 个答案:

答案 0 :(得分:4)

因此,根据the Wikipedia article,对多变量截断正态分布(MTND)进行抽样更加困难。我最终采取了相对简单的方法,并使用MCMC采样器放松对MTND的初始猜测,如下所示。

我使用emcee来完成MCMC的工作。我发现这个包装非常容易使用。它只需要一个返回所需分布的对数概率的函数。所以我定义了这个函数

from numpy.linalg import inv

def lnprob_trunc_norm(x, mean, bounds, C):
    if np.any(x < bounds[:,0]) or np.any(x > bounds[:,1]):
        return -np.inf
    else:
        return -0.5*(x-mean).dot(inv(C)).dot(x-mean)

这里,C是多元法线的协方差矩阵。然后,您可以运行类似

的内容
S = emcee.EnsembleSampler(Nwalkers, Ndim, lnprob_trunc_norm, args = (mean, bounds, C))

pos, prob, state = S.run_mcmc(pos, Nsteps)
对于给定的meanboundsC

。你需要对步行者的位置pos进行初步猜测,这可能是一个围绕着平均值的球,

pos = emcee.utils.sample_ball(mean, np.sqrt(np.diag(C)), size=Nwalkers)

或从未截断的多变量法线中采样,

pos = numpy.random.multivariate_normal(mean, C, size=Nwalkers)

等等。我个人先做几千个样本丢弃步骤,因为它很快,然后强制剩余的异常值回到边界内,然后运行MCMC采样。

收敛的步骤由您决定。

另请注意,emcee通过将参数threads=Nthreads添加到EnsembleSampler初始化来轻松支持基本并行化。所以你可以快速实现这一目标。

答案 1 :(得分:1)

模拟截断的多元正态可能很棘手,通常涉及 MCMC 的一些条件采样。

我的简短回答是,你可以使用我的代码(https://github.com/ralphma1203/trun_mvnt)!!!它实现了来自 Li and Ghosh (2015) 的 Gibbs 采样器算法,它可以处理 foo+bar 形式的一般线性约束,即使您有非满秩 D 和比维度更多的约束。

import numpy as np
from trun_mvnt import rtmvn, rtmvt

########## Traditional problem, probably what you need... ##########
##### lower < X < upper #####
# So D = identity matrix

D = np.diag(np.ones(4))
lower = np.array([-1,-2,-3,-4])
upper = -lower
Mean = np.zeros(4)
Sigma = np.diag([1,2,3,4])

n = 10 # want 500 final sample
burn = 100 # burn-in first 100 iterates
thin = 1 # thinning for Gibbs


random_sample = rtmvn(n, Mean, Sigma, D, lower, upper, burn, thin) 
# Numpy array n-by-p as result!
random_sample

########## Non-full rank problem (more constraints than dimension) ##########
Mean = np.array([0,0])
Sigma = np.array([1, 0.5, 0.5, 1]).reshape((2,2)) # bivariate normal

D = np.array([1,0,0,1,1,-1]).reshape((3,2)) # non-full rank problem
lower = np.array([-2,-1,-2])
upper = np.array([2,3,5])

n = 500 # want 500 final sample
burn = 100 # burn-in first 100 iterates
thin = 1 # thinning for Gibbs

random_sample = rtmvn(n, Mean, Sigma, D, lower, upper, burn, thin) # Numpy array n-by-p as result!

答案 2 :(得分:0)

我想有点晚了,但为了记录,您可以使用汉密尔顿·蒙特卡洛。 Matlab中存在一个名为HMC精确的模块。在Py中翻译应该不会太困难。