Почему в моей иерархической модели PyMC3 возникает несоответствие размеров?

По сути, это пример "Несколько монет от нескольких монетных дворов / бейсболистов" из " Байесовского анализа данных", второе издание (DBDA2). Я считаю, что у меня есть код PyMC3, который функционально эквивалентен, но один работает, а другой нет. Это с PyMC версии 3.5. Более подробно,

Допустим, у меня есть следующие данные. Каждый ряд является наблюдением:

observations_dict = {
    'mint': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    'coin': [0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7],
    'outcome': [1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1]
}
observations = pd.DataFrame(observations_dict)
observations

Одна мята, несколько монет

Ниже, который реализует DBDA2 Рисунок 9.7, работает просто отлично:

Рис 9.7

num_coins = observations['coin'].nunique()
coin_idx = observations['coin']

with pm.Model() as hierarchical_model:
    # mint is characterized by omega and kappa
    omega = pm.Beta('omega', 1., 1.)
    kappa_minus2 = pm.Gamma('kappa_minus2', 0.01, 0.01)
    kappa = pm.Deterministic('kappa', kappa_minus2 + 2)

    # each coin is described by a theta
    theta = pm.Beta('theta', alpha=omega*(kappa-2)+1, beta=(1-omega)*(kappa-2)+1, shape=num_coins)

    # define the likelihood
    y = pm.Bernoulli('y', theta[coin_idx], observed=observations['outcome'])  

Много монетных дворов, много монет

Однако, как только это превращается в иерархическую модель (как видно из DBDA2, рисунок 9.13):

Рис 9.13

num_mints = observations['mint'].nunique()
mint_idx = observations['mint']
num_coins = observations['coin'].nunique()
coin_idx = observations['coin']

with pm.Model() as hierarchical_model2:
    # Hyper parameters
    omega = pm.Beta('omega', 1, 1)
    kappa_minus2 = pm.Gamma('kappa_minus2', 0.01, 0.01)
    kappa = pm.Deterministic('kappa', kappa_minus2 + 2)

    # Parameters for mints
    omega_c = pm.Beta('omega_c',
                       omega*(kappa-2)+1, (1-omega)*(kappa-2)+1,
                       shape = num_mints)    
    kappa_c_minus2 = pm.Gamma('kappa_c_minus2',
                              0.01, 0.01,
                              shape = num_mints)
    kappa_c = pm.Deterministic('kappa_c', kappa_c_minus2 + 2)

    # Parameters for coins
    theta = pm.Beta('theta',
                     omega_c[mint_idx]*(kappa_c[mint_idx]-2)+1,
                    (1-omega_c[mint_idx])*(kappa_c[mint_idx]-2)+1,
                     shape = num_coins)

    y2 = pm.Bernoulli('y2', p=theta[coin_idx], observed=observations['outcome'])

Ошибка:

ValueError: operands could not be broadcast together with shapes (8,) (20,) 

поскольку модель имеет 8 тэта на 8 монет, но видит 20 строк данных.

Однако, если данные сгруппированы так, что каждая строка представляет окончательную статистику отдельной монеты, как в следующем

grouped = observations.groupby(['mint', 'coin']).agg({'outcome': [np.sum, np.size]}).reset_index()
grouped.columns = ['mint', 'coin', 'heads', 'total']

И окончательная переменная правдоподобия изменяется на Бином, как показано ниже

num_mints = grouped['mint'].nunique()
mint_idx = grouped['mint']
num_coins = grouped['coin'].nunique()
coin_idx = grouped['coin']

with pm.Model() as hierarchical_model2:
    # Hyper parameters
    omega = pm.Beta('omega', 1, 1)
    kappa_minus2 = pm.Gamma('kappa_minus2', 0.01, 0.01)
    kappa = pm.Deterministic('kappa', kappa_minus2 + 2)

    # Parameters for mints
    omega_c = pm.Beta('omega_c',
                       omega*(kappa-2)+1, (1-omega)*(kappa-2)+1,
                       shape = num_mints)    
    kappa_c_minus2 = pm.Gamma('kappa_c_minus2',
                              0.01, 0.01,
                              shape = num_mints)
    kappa_c = pm.Deterministic('kappa_c', kappa_c_minus2 + 2)

    # Parameter for coins
    theta = pm.Beta('theta',
                     omega_c[mint_idx]*(kappa_c[mint_idx]-2)+1,
                    (1-omega_c[mint_idx])*(kappa_c[mint_idx]-2)+1,
                     shape = num_coins)

    y2 = pm.Binomial('y2', n=grouped['total'], p=theta, observed=grouped['heads'])

Все работает. Теперь последняя форма более эффективна и, как правило, предпочтительнее, но я считаю, что первая также должна работать. Поэтому я считаю, что это в первую очередь проблема PyMC3 (или, что более вероятно, ошибка пользователя).

Цитируя DBDA Edition 1,

"Модель BUGS использует биномиальное распределение правдоподобия для общего правильного значения вместо использования распределения Бернулли для отдельных испытаний. Такое использование биномиального значения - просто удобство для сокращения программы. Если вместо этого данные были указаны как результаты от испытания к испытанию" как полностью правильная, то модель может включать в себя цикл "пробный за пробой" и использовать функцию правдоподобия Бернулли "

Что меня беспокоит, так это то, что в самом первом примере (Один монетный двор, несколько монет) выглядит так, что PyMC3 может обрабатывать отдельные наблюдения вместо агрегированных наблюдений просто отлично. Поэтому я считаю, что первая форма должна работать, но не работает.

Код

http://nbviewer.jupyter.org/github/JWarmenhoven/DBDA-python/blob/master/Notebooks/Chapter%209.ipynb

Рекомендации

PyMC3 - Различия в способах передачи наблюдений в модель -> разница в результатах?

https://discourse.pymc.io/t/pymc3-differences-in-ways-observations-are-passed-to-model-difference-in-results/501

http://www.databozo.com/deep-in-the-weeds-complex-hierarchical-models-in-pymc3

https://stats.stackexchange.com/questions/157521/is-this-correct-hierarchical-bernoulli-model

1 ответ

Решение

Длина mint_idx было 20 (по одному на каждое наблюдение), но должно было быть 8 (по одному на каждую монету).

Рабочий ответ, обратите внимание на mint_idx пересчет (остальное остается прежним):

grouped = observations.groupby(['mint', 'coin']).agg({'outcome': [np.sum, np.size]}).reset_index()
grouped.columns = ['mint', 'coin', 'heads', 'total']

num_mints = grouped['mint'].nunique()
mint_idx = grouped['mint']
num_coins = observations['coin'].nunique()
coin_idx = observations['coin']

with pm.Model() as hierarchical_model2:
    # Hyper parameters
    omega = pm.Beta('omega', 1, 1)
    kappa_minus2 = pm.Gamma('kappa_minus2', 0.01, 0.01)
    kappa = pm.Deterministic('kappa', kappa_minus2 + 2)

    # Parameters for mints
    omega_c = pm.Beta('omega_c',
                       omega*(kappa-2)+1, (1-omega)*(kappa-2)+1,
                       shape = num_mints)    
    kappa_c_minus2 = pm.Gamma('kappa_c_minus2',
                              0.01, 0.01,
                              shape = num_mints)
    kappa_c = pm.Deterministic('kappa_c', kappa_c_minus2 + 2)

    # Parameters for coins
    theta = pm.Beta('theta',
                     omega_c[mint_idx]*(kappa_c[mint_idx]-2)+1,
                    (1-omega_c[mint_idx])*(kappa_c[mint_idx]-2)+1,
                     shape = num_coins)

    y2 = pm.Bernoulli('y2', p=theta[coin_idx], observed=observations['outcome'])

Большое спасибо @junpenglao!! https://discourse.pymc.io/t/why-cant-i-use-a-bernoulli-as-a-likelihood-variable-in-a-hierarchical-model-in-pymc3/2022/2

Другие вопросы по тегам