我正在尝试自动化一个过程,在某些时候需要从截断的多元法线中抽取样本。也就是说,它是正常的多元正态分布(即高斯分布),但变量被约束为长方体。我给出的输入是完整多变量法线的均值和协方差,但我需要在我的方框中提取样本。
到目前为止,我只是拒绝了盒子外的样品并根据需要重新取样,但我开始发现我的过程有时会给我(a)大的协方差和(b)接近于边缘。这两个事件与我的系统速度相悖。
所以我想做的是首先正确地对分布进行采样。谷歌搜索只导致this discussion或scipy.stats
中的truncnorm
distribution。前者是不确定的,后者似乎是一个变量。是否有任何原生多变量截断正常?它会比拒绝样品更好,还是应该做一些更聪明的事情?
我将开始研究我自己的解决方案,即将未截断的高斯旋转到它的主轴(使用SVD分解等),使用截断高斯的乘积来对分布进行采样,然后旋转取样,并根据需要拒绝/重新取样。如果截断的采样效率更高,我认为这应该更快地采样所需的分布。
答案 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)
对于给定的mean
,bounds
和C
,。你需要对步行者的位置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)!!!它实现了来自 的 Gibbs 采样器算法,它可以处理 形式的一般线性约束,即使您有非满秩 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中翻译应该不会太困难。