Реализация населения методом Монте-Карло

Я пытаюсь реализовать алгоритм Монте-Карло для населения, как описано в этой статье (см. стр. 78 Рис.3) для простой модели (см. функцию model()) с одним параметром с использованием Python. К сожалению, алгоритм не работает, и я не могу понять, что не так. Смотрите мою реализацию ниже. Фактическая функция называется abc(). Все остальные функции можно рассматривать как вспомогательные функции и, похоже, они работают нормально.

Чтобы проверить, работает ли алгоритм, я сначала генерирую наблюдаемые данные с единственным параметром модели, установленным на param = 8. Следовательно, апостериорный результат, полученный в результате алгоритма ABC, должен быть сосредоточен вокруг 8. Это не так, и мне интересно Зачем.

Буду признателен за любую помощь или комментарии.

# 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

person beginneR    schedule 28.05.2016    source источник
comment
Человеку, не знакомому с работой MC, будет нелегко разобраться в вашем коде. Не могли бы вы проверить, какая часть вашего кода работает так, как предполагалось? Также не совсем понятно, о какой статье вы говорите и о каких ее частях. Может быть, вы могли бы сделать перекрестную ссылку на него в комментариях в коде.   -  person aldanor    schedule 28.05.2016
comment
Спасибо за ваш комментарий. Я добавил ссылку на документ, в котором представлен псевдокод алгоритма.   -  person beginneR    schedule 28.05.2016
comment
Бумаги, кстати, нет в свободном доступе.   -  person ayhan    schedule 28.05.2016
comment
@ayhan Извините, я, должно быть, каким-то образом вошел в систему. Алгоритм также представлен здесь (см. внизу стр. 5). arxiv.org/pdf/0805.2256v9.pdf   -  person beginneR    schedule 28.05.2016


Ответы (1)


Я наткнулся на этот вопрос, когда искал питонические реализации алгоритмов PMC, поскольку, по совершенно случайному совпадению, в настоящее время я применяю методы, описанные в этой статье, к своим собственным исследованиям.

Можете ли вы опубликовать результаты, которые вы получаете? Я предполагаю, что 1) вы используете плохой выбор функции расстояния (и/или порогов сходства) или 2) вы используете недостаточное количество частиц. Я могу ошибаться здесь (я не очень хорошо разбираюсь в выборочной статистике), но ваша функция расстояния неявно предполагает, что порядок ваших случайных розыгрышей имеет значение. Я должен был бы подумать об этом больше, чтобы определить, действительно ли это влияет на свойства сходимости (может и нет), но почему бы вам просто не использовать среднее значение или медиану в качестве выборочной статистики?

Я запустил ваш код с 1000 частиц и истинным значением параметра 8, используя абсолютную разницу между выборочными средними в качестве моей функции расстояния, для трех итераций с эпсилонами [0,5, 0,3, 0,1]; пик моего предполагаемого апостериорного распределения, похоже, приближается к 8, как и должно быть на каждой итерации, наряду с уменьшением дисперсии населения. Обратите внимание, что по-прежнему наблюдается заметное смещение вправо, но это из-за асимметрии вашей модели (значения параметров 8 или меньше никогда не могут привести к более чем 8 наблюдаемым успехам, в то время как все значения параметров больше 8 могут привести к правым асимметрия в распределении).

Вот график моих результатов:

Эволюция частиц за три итерации, сходимость к истинному значению 8 и демонстрация небольшой асимметрии в тенденции к более высоким значениям параметров.

person J. Burt    schedule 10.06.2016
comment
Вы правы, спасибо! Причиной была функция расстояния. За несколько дней до того, как вы написали это, я также перешел на простую функцию «абсолютная разница в средних» - расстояние, и это сработало. - person beginneR; 04.07.2016
comment
В настоящее время я борюсь с расчетом весов при работе с несколькими параметрами. Для меня имело бы смысл отказаться или принять наборы параметров. Поэтому вместо того, чтобы выбирать отдельные значения параметров на основе их весов, мне интересно, есть ли вместо этого один вес для каждого набора параметров. Вы знаете, какая версия правильная? - person beginneR; 04.07.2016
comment
Я полагаю, что мы делаем выборку из полного совместного апостериорного распределения параметров, поэтому каждая частица имеет одно выборочное значение для каждого отдельного параметра и один вес выборки важности. Для каждой из N частиц: 1) производится повторная выборка частицы из предыдущей итерации с использованием весов частиц предыдущей итерации; 2) каждое значение параметра в каждой случайно выбранной частице возмущается с использованием предлагаемого распределения, например. многомерная норма; 3) шаг 2 повторяется для каждой новой частицы, пока не встретится новый эпсилон; и 4) новые веса вычисляются и нормализуются. - person J. Burt; 05.07.2016
comment
Благодарю вас! Но скажем, вы хотите оценить среднее значение и дисперсию нормального распределения. Что такое правильное совместное предварительное распределение? Совместный априор должен быть указан, чтобы вычислить вес частицы. - person beginneR; 06.07.2016
comment
Другая проблема заключается в том, что вам нужно вычислить дисперсию предыдущих частиц для вычисления нового веса. Но если частица представляет собой список/кортеж, содержащий выборочные значения для каждого параметра, как тогда вычислить дисперсию частиц? - person beginneR; 06.07.2016
comment
Честно говоря, я изо всех сил пытался найти это однозначное объяснение в литературе, особенно в подробной реализации PMC в приложениях ABC. Я нашел этот пакет Джоэла Акарета особенно полезным, и вы можете найти полезно пройтись по его коду. Что касается ваших приор, это зависит от вас, чтобы уточнить. Если вы предполагаете независимость ваших параметров, вы можете просто умножить свои априорные значения, чтобы получить совместное априорное значение. Возможно, попробуйте обычный априор для среднего значения и гамма-априор для (положительно-определенной) дисперсии. - person J. Burt; 07.07.2016