【问题标题】:Population Monte Carlo implementation人口蒙特卡罗实施
【发布时间】:2023-03-02 23:20:01
【问题描述】:

我正在尝试按照this 论文(参见第 78 页图 3)中的描述,使用 Python 实现具有一个参数的简单模型(参见函数 model())中的人口蒙特卡洛算法。不幸的是,该算法不起作用,我无法弄清楚出了什么问题。请参阅下面的实现。实际函数称为abc()。所有其他功能都可以视为辅助功能,并且似乎工作正常。

为了检查算法是否有效,我首先生成观察数据,模型的唯一参数设置为 param = 8。因此,ABC 算法的后验结果应该以 8 为中心。事实并非如此,我'我想知道为什么。

我将不胜感激任何帮助或 cmets。

# imports

from math import exp
from math import log
from math import sqrt
import numpy as np
import random
from scipy.stats import norm


# globals
N = 300              # sample size
N_PARTICLE = 300      # number of particles
ITERS = 5            # number of decreasing thresholds
M = 10               # number of words to remember
MEAN = 7             # prior mean of parameter
SD = 2               # prior sd of parameter


def model(param):
  recall_prob_all = 1/(1 + np.exp(M - param))
  recall_prob_one_item = np.exp(np.log(recall_prob_all) / float(M))
  return sum([1 if random.random() < recall_prob_one_item else 0 for item in range(M)])

## example
print "Output of model function: \n" + str(model(10)) + "\n"

# generate data from model
def generate(param):
  out = np.empty(N)
  for i in range(N):
    out[i] = model(param)
  return out

## example

print "Output of generate function: \n" + str(generate(10)) + "\n"


# distance function (sum of squared error)
def distance(obsData,simData):
  out = 0.0
  for i in range(len(obsData)):
    out += (obsData[i] - simData[i]) * (obsData[i] - simData[i])
  return out

## example

print "Output of distance function: \n" + str(distance([1,2,3],[4,5,6])) + "\n"


# sample new particles based on weights
def sample(particles, weights):
  return np.random.choice(particles, 1, p=weights)

## example

print "Output of sample function: \n" + str(sample([1,2,3],[0.1,0.1,0.8])) + "\n"


# perturbance function
def perturb(variance):
  return np.random.normal(0,sqrt(variance),1)[0]

## example 

print "Output of perturb function: \n" + str(perturb(1)) + "\n"

# compute new weight
def computeWeight(prevWeights,prevParticles,prevVariance,currentParticle):
  denom = 0.0
  proposal = norm(currentParticle, sqrt(prevVariance))
  prior = norm(MEAN,SD)
  for i in range(len(prevParticles)):
    denom += prevWeights[i] * proposal.pdf(prevParticles[i])
  return prior.pdf(currentParticle)/denom


## example 

prevWeights = [0.2,0.3,0.5]
prevParticles = [1,2,3]
prevVariance = 1
currentParticle = 2.5
print "Output of computeWeight function: \n" + str(computeWeight(prevWeights,prevParticles,prevVariance,currentParticle)) + "\n"


# normalize weights
def normalize(weights):
  return weights/np.sum(weights)


## example 

print "Output of normalize function: \n" + str(normalize([3.,5.,9.])) + "\n"


# sampling from prior distribution
def rprior():
  return np.random.normal(MEAN,SD,1)[0]

## example 

print "Output of rprior function: \n" + str(rprior()) + "\n"


# ABC using Population Monte Carlo sampling
def abc(obsData,eps):
  draw = 0
  Distance = 1e9
  variance = np.empty(ITERS)
  simData = np.empty(N)
  particles = np.empty([ITERS,N_PARTICLE])
  weights = np.empty([ITERS,N_PARTICLE])

  for t in range(ITERS):
    if t == 0:
      for i in range(N_PARTICLE):
        while(Distance > eps[t]):
          draw = rprior()
          simData = generate(draw)
          Distance = distance(obsData,simData)

        Distance = 1e9
        particles[t][i] = draw
        weights[t][i] = 1./N_PARTICLE

      variance[t] = 2 * np.var(particles[t])
      continue


    for i in range(N_PARTICLE):
      while(Distance > eps[t]):
        draw = sample(particles[t-1],weights[t-1])
        draw += perturb(variance[t-1])
        simData = generate(draw)
        Distance = distance(obsData,simData)

      Distance = 1e9
      particles[t][i] = draw
      weights[t][i] = computeWeight(weights[t-1],particles[t-1],variance[t-1],particles[t][i])


    weights[t] = normalize(weights[t])  
    variance[t] = 2 * np.var(particles[t])

  return particles[ITERS-1]



true_param = 9
obsData = generate(true_param)
eps = [15000,10000,8000,6000,3000]
posterior = abc(obsData,eps)
#print posterior

【问题讨论】:

  • 对于不熟悉 MC 工作原理的人来说,浏览您的代码并不容易。您能否验证您的代码的哪一部分确实按预期工作?也不完全清楚您指的是哪篇文章以及其中的哪些部分。也许您可以在代码中的 cmets 中交叉引用它。
  • 感谢您的评论。我添加了该论文的链接,该链接介绍了该算法的伪代码。
  • 顺便说一下,论文不是免费提供的。
  • @ayhan 抱歉,我一定是以某种方式登录的。该算法也在此处介绍(见第 5 页底部)。 arxiv.org/pdf/0805.2256v9.pdf

标签: python montecarlo approximation mcmc


【解决方案1】:

我在寻找 PMC 算法的 Python 实现时偶然发现了这个问题,因为巧合的是,我目前正在将这篇论文中的技术应用到我自己的研究中。

你能发布你得到的结果吗?我的猜测是 1)您使用的距离函数(和/或相似性阈值)选择不当,或者 2)您没有使用足够的粒子。我在这里可能错了(我对样本统计数据不是很精通),但是您的距离函数隐含地向我暗示,您的随机抽签的顺序很重要。我必须更多地考虑这一点,以确定它是否真的对收敛特性有任何影响(它可能没有),但你为什么不简单地使用平均值或中位数作为你的样本统计量呢?

我使用 1000 个粒子和 8 的真实参数值运行您的代码,同时使用样本均值之间的绝对差作为我的距离函数,进行三次迭代,epsilons 为 [0.5, 0.3, 0.1];我估计的后验分布的峰值似乎接近 8,就像它应该在每次迭代中一样,同时减少总体方差。请注意,仍然存在明显的向右偏差,但这是因为您的模型的不对称性(8 或更少的参数值永远不会导致超过 8 次观察到的成功,而所有大于 8 的参数值都可以,导致向右分布偏度)。

这是我的结果图:

【讨论】:

  • 你是对的,谢谢!原因是距离函数。在你写这篇文章的前几天,我也改成了一个简单的“均值绝对差”——距离函数,它也起作用了。
  • 目前,在处理多个参数时,我正在努力计算权重。对我来说,丢弃或接受参数集是有意义的。因此,我不是根据权重选择单个参数值,而是想知道每组参数是否有一个权重。你知道哪个版本是正确的吗?
  • 我相信我们是从参数的完整联合后验分布中采样的,因此每个粒子对于每个单独的参数都有一个采样值,以及一个重要性采样权重。对于 N 个粒子中的每一个: 1) 从前一次迭代中重新采样一个粒子,使用前一次迭代的粒子权重; 2)每个随机采样的粒子中的每个参数值都使用提议分布进行扰动,例如多元正态; 3) 对每个新粒子重复步骤 2,直到满足新的 epsilon; 4) 新的权重被计算和归一化。
  • 谢谢!但是假设您想估计正态分布的均值和方差。什么是适当的联合先验分布?为了计算粒子的权重,必须指定联合先验。
  • 另一个问题是你需要计算之前粒子的方差来计算一个新的权重。但是如果一个粒子是一个包含每个参数的采样值的列表/元组,那么你如何计算粒子的方差?
猜你喜欢
  • 2018-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-18
  • 2022-01-11
  • 2019-10-07
  • 2021-04-20
相关资源
最近更新 更多