接受/拒绝算法中的高效循环

时间:2017-10-25 00:57:53

标签: python performance random

以下是高斯密度函数的基本接受/拒绝采样器的两种不同实现,只有一些“未知”归一化常数,现在基本采样器(方法1 )涉及重复循环和生成该循环中每个步骤所需的两个必需随机变量。

对于方法2 另一方面,模拟了一批这些随机变量,然后循环进行第一次接受,如果没有,则模拟新批次等等。现在当接受概率很小时方法2 看起来确实快得多,所以我的第一个问题是方法2 能否更有效地实施?如果这种预先模拟的随机变量批量是最有效的方法,那么是否有一种原则性的方法来选择这些批次应该有多大?

设置

import numpy as np
from scipy.stats import norm
from numpy.random import uniform

q = norm(loc=0., scale=3.)
def p_(x):
    return np.exp(-0.5*x**2)

k = 1./q.pdf(0.)

AR方法1

def AR1():
    while True:
        z = q.rvs()
        u = uniform(low=0., high=k*q.pdf(z))
        if u <= p_(z):
            return z

AR方法2

BATCH_SIZE = 5
def AR2():
    nt = 0
    Zs = q.rvs(size=BATCH_SIZE)            # random variables z ~ q(z)
    Us = uniform(low=0., high=k*q.pdf(Zs)) # uniform rvs on [0, k*q(z)]
    Pvals = p_(Zs)                         # unnormalised pdf p_(z)
    while True:
        if Us[nt] <= Pvals[nt]:
            return Zs[nt]
        else:
            nt +=1
            if nt > BATCH_SIZE - 1:
                Zs = q.rvs(size=BATCH_SIZE)            
                Us = uniform(low=0., high=k*q.pdf(Zs)) 
                Pvals = p_(Zs)
                nt = 0

1 个答案:

答案 0 :(得分:0)

由于来自AR的样本是独立的,因此您无需像大都会黑名单算法一样一一生成它们。因此,您可以一次全部生成它们,并通过使用数组操作来加快功能。

假设我们要1000个样本。使用您的AR2:

>> %timeit sample2 = [AR2() for _ in range(1000)]
513 ms ± 134 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我们可以改为创建类似函数的数组:

def AR_array(size = 100):
    # generate a slightly larger total samples 
    # based on the acceptance ratio: 1/k
    Zs = q.rvs(size= np.int(size*(k+1)))   
    Us = uniform(low=0., high=k*q.pdf(Zs))
    Pvals = p_(Zs)                  # unnormalized pdf p_(z)
    Zs_accepted = Zs[Us <= Pvals]   # select accepted samples
    return Zs_accepted[0:size]      # return the required sample size

生成1000个样本。

>> %timeit AR_array(1000)
1.9 ms ± 209 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

健全性检查:

>> norm.fit(AR_array(1000)) # MLE of mean and std
(-0.05960200510480021, 0.9917248073504155)