Перенос кода PyMC2 в PyMC3 — иерархическая модель для спортивной аналитики

Я попробовал следующий код, но столкнулся с проблемами. Я думаю, что проблема в .values, но как мне закодировать это как объект Theano?

Ниже приведен мой источник данных

home_team,away_team,home_score,away_score
Wales,Italy,23,15
France,England,26,24
Ireland,Scotland,28,6
Ireland,Wales,26,3
Scotland,England,0,20
France,Italy,30,10
Wales,France,27,6
Italy,Scotland,20,21
England,Ireland,13,10
Ireland,Italy,46,7
Scotland,France,17,19
England,Wales,29,18
Italy,England,11,52
Wales,Scotland,51,3
France,Ireland,20,22

Вот код PyMC2, который работает: data_file = DATA_DIR + 'results_2014.csv'

df = pd.read_csv(data_file, sep=',')
# Or whatever it takes to get this into a data frame.
teams = df.home_team.unique()
teams = pd.DataFrame(teams, columns=['team'])
teams['i'] = teams.index
df = pd.merge(df, teams, left_on='home_team', right_on='team', how='left')
df = df.rename(columns = {'i': 'i_home'}).drop('team', 1)
df = pd.merge(df, teams, left_on='away_team', right_on='team', how='left')
df = df.rename(columns = {'i': 'i_away'}).drop('team', 1)
observed_home_goals = df.home_score.values
observed_away_goals = df.away_score.values
home_team = df.i_home.values
away_team = df.i_away.values
num_teams = len(df.i_home.drop_duplicates())
num_games = len(home_team)
g = df.groupby('i_away')
att_starting_points = np.log(g.away_score.mean())
g = df.groupby('i_home')
def_starting_points = -np.log(g.away_score.mean())

#hyperpriors
home = pymc.Normal('home', 0, .0001, value=0)
tau_att = pymc.Gamma('tau_att', .1, .1, value=10)
tau_def = pymc.Gamma('tau_def', .1, .1, value=10)
intercept = pymc.Normal('intercept', 0, .0001, value=0)
#team-specific parameters
atts_star = pymc.Normal("atts_star", 
                        mu=0, 
                        tau=tau_att, 
                        size=num_teams, 
                        value=att_starting_points.values)
defs_star = pymc.Normal("defs_star", 
                        mu=0, 
                        tau=tau_def, 
                        size=num_teams, 
                        value=def_starting_points.values) 

# trick to code the sum to zero constraint
@pymc.deterministic
def atts(atts_star=atts_star):
    atts = atts_star.copy()
    atts = atts - np.mean(atts_star)
    return atts

@pymc.deterministic
def defs(defs_star=defs_star):
    defs = defs_star.copy()
    defs = defs - np.mean(defs_star)
    return defs

@pymc.deterministic
def home_theta(home_team=home_team, 
               away_team=away_team, 
               home=home, 
               atts=atts, 
               defs=defs, 
               intercept=intercept): 
    return np.exp(intercept + 
                  home + 
                  atts[home_team] + 
                  defs[away_team])

@pymc.deterministic
def away_theta(home_team=home_team, 
               away_team=away_team, 
               home=home, 
               atts=atts, 
               defs=defs, 
               intercept=intercept): 
    return np.exp(intercept + 
                  atts[away_team] + 
                  defs[home_team])   

home_points = pymc.Poisson('home_points', 
                          mu=home_theta, 
                          value=observed_home_goals, 
                          observed=True)
away_points = pymc.Poisson('away_points', 
                          mu=away_theta, 
                          value=observed_away_goals, 
                          observed=True)

mcmc = pymc.MCMC([home, intercept, tau_att, tau_def, 
                  home_theta, away_theta, 
                  atts_star, defs_star, atts, defs, 
                  home_points, away_points])
map_ = pymc.MAP( mcmc )
map_.fit()

mcmc.sample(200000, 40000, 20)

Моя попытка портирования на PyMC3 :) И я включаю код обработки. Я определил свой собственный каталог данных и т.д.

data_file = DATA_DIR + 'results_2014.csv'

df = pd.read_csv(data_file, sep=',')
# Or whatever it takes to get this into a data frame.
teams = df.home_team.unique()
teams = pd.DataFrame(teams, columns=['team'])
teams['i'] = teams.index
df = pd.merge(df, teams, left_on='home_team', right_on='team', how='left')
df = df.rename(columns = {'i': 'i_home'}).drop('team', 1)
df = pd.merge(df, teams, left_on='away_team', right_on='team', how='left')
df = df.rename(columns = {'i': 'i_away'}).drop('team', 1)
observed_home_goals = df.home_score.values
observed_away_goals = df.away_score.values
home_team = df.i_home.values
away_team = df.i_away.values
num_teams = len(df.i_home.drop_duplicates())
num_games = len(home_team)
g = df.groupby('i_away')
att_starting_points = np.log(g.away_score.mean())
g = df.groupby('i_home')
def_starting_points = -np.log(g.away_score.mean())

import theano.tensor as T
import pymc3 as pm3
#hyperpriors


x = att_starting_points.values
y = def_starting_points.values
model = pm.Model()
with pm3.Model() as model:
    home3 = pm3.Normal('home', 0, .0001)
    tau_att3 = pm3.Gamma('tau_att', .1, .1)
    tau_def3 = pm3.Gamma('tau_def', .1, .1)
    intercept3 = pm3.Normal('intercept', 0, .0001)
    #team-specific parameters
    atts_star3 = pm3.Normal("atts_star", 
                        mu=0, 
                        tau=tau_att3, 
                        observed=x)
    defs_star3 = pm3.Normal("defs_star", 
                        mu=0, 
                        tau=tau_def3,  
                        observed=y) 
    #Seems to be the error here. 
    atts = pm3.Deterministic('regression', 
    atts_star3 - np.mean(atts_star3))
    home_theta3 = pm3.Deterministic('regression', 
    T.exp(intercept3 + atts[away_team] + defs[home_team]))
atts = pm3.Deterministic('regression', atts_star3 - np.mean(atts_star3))
    home_theta3 = pm3.Deterministic('regression', T.exp(intercept3 +     atts[away_team] + defs[home_team]))
    # Unknown model parameters
    home_points3 = pm3.Poisson('home_points', mu=home_theta3, observed=observed_home_goals)
    away_points3 = pm3.Poisson('away_points', mu=home_theta3, observed=observed_away_goals)
    start = pm3.find_MAP()
    step = pm3.NUTS(state=start)
    trace = pm3.sample(2000, step, start=start, progressbar=True)

    pm3.traceplot(trace)

И я получаю сообщение об ошибке, как будто значения не являются объектом Theano. Я думаю, что это часть .values ​​выше. Но я не понимаю, как преобразовать это в тензор Теано. Тензоры меня смущают :)

И ошибка для ясности, потому что я что-то неправильно понял в синтаксисе PyMC3.

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-71-ce51c1a64412> in <module>()
     23 
     24     #Seems to be the error here.
---> 25     atts = pm3.Deterministic('regression', atts_star3 - np.mean(atts_star3))
     26     home_theta3 = pm3.Deterministic('regression', T.exp(intercept3 + atts[away_team] + defs[home_team]))
     27 

/Users/peadarcoyle/anaconda/lib/python3.4/site-packages/numpy/core/fromnumeric.py in mean(a, axis, dtype, out, keepdims)
   2733 
   2734     return _methods._mean(a, axis=axis, dtype=dtype,
-> 2735                             out=out, keepdims=keepdims)
   2736 
   2737 def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False):

/Users/peadarcoyle/anaconda/lib/python3.4/site-packages/numpy/core/_methods.py in _mean(a, axis, dtype, out, keepdims)
     71         ret = ret.dtype.type(ret / rcount)
     72     else:
---> 73         ret = ret / rcount
     74 
     75     return ret

TypeError: unsupported operand type(s) for /: 'ObservedRV' and 'int'

person Peadar Coyle    schedule 12.06.2015    source источник
comment
Можете ли вы добавить данные, чтобы я мог воспроизвести вашу ошибку? Небольшой смоделированный пример подойдет, если он вызывает ту же ошибку, что и ваши реальные данные. Например. stackoverflow.com/help/mcve   -  person Abraham D Flaxman    schedule 12.06.2015
comment
Авраам I исправил это и добавил это.   -  person Peadar Coyle    schedule 12.06.2015
comment
Спасибо за напоминание.   -  person Peadar Coyle    schedule 12.06.2015
comment
Не нужно преобразовывать массивы в тензоры. Я все еще не вижу ошибки, которую вы на самом деле получаете. Вы используете последнюю версию PyMC3? Я получаю только NameError: имя «defs» не определено, что имеет смысл, потому что оно не определено.   -  person John Salvatier    schedule 13.06.2015
comment
Кроме того, если у вас нет пропущенных значений или вы действительно заботитесь о tau_att3 или tau_def3 (но вы их не используете), я не думаю, что необходимо моделировать atts_star и defs_star с нормальным распределением, вы можете просто использовать данные напрямую.   -  person John Salvatier    schedule 13.06.2015
comment
У вас есть работающая версия PyMC2?   -  person Abraham D Flaxman    schedule 14.06.2015
comment
Привет @AbrahamDFlaxman Я добавил код PyMC2, спасибо за это.   -  person Peadar Coyle    schedule 14.06.2015


Ответы (3)


Вот мой перевод вашей модели PyMC2:

model = pm.Model()
with pm.Model() as model:
    # global model parameters
    home        = pm.Normal('home',      0, .0001)
    tau_att     = pm.Gamma('tau_att',   .1, .1)
    tau_def     = pm.Gamma('tau_def',   .1, .1)
    intercept   = pm.Normal('intercept', 0, .0001)

    # team-specific model parameters
    atts_star   = pm.Normal("atts_star", 
                           mu   =0,
                           tau  =tau_att, 
                           shape=num_teams)
    defs_star   = pm.Normal("defs_star", 
                           mu   =0,
                           tau  =tau_def,  
                           shape=num_teams)

    atts        = pm.Deterministic('atts', atts_star - tt.mean(atts_star))
    defs        = pm.Deterministic('defs', defs_star - tt.mean(defs_star))
    home_theta  = tt.exp(intercept + home + atts[home_team] + defs[away_team]
    away_theta  = tt.exp(intercept + atts[away_team] + defs[home_team])

    # likelihood of observed data
    home_points = pm.Poisson('home_points', mu=home_theta, observed=observed_home_goals)
    away_points = pm.Poisson('away_points', mu=away_theta, observed=observed_away_goals)

Насколько я понимаю, большая разница между построением модели PyMC2 и 3 заключается в том, что весь бизнес с начальными значениями в PyMC2 не включен в построение модели в PyMC3. Он выталкивается в часть кода, подходящую для модели.

Вот блокнот, который помещает эту модель в контекст с вашими данными и некоторым подходящим кодом: http://nbviewer.ipython.org/gist/aflaxman/55e23195fe0a0b089103

person Abraham D Flaxman    schedule 15.06.2015
comment
Спасибо Авраам. Я добавлю это в свой доклад о PyData в Лондоне, но обязательно сошлюсь на вас :). - person Peadar Coyle; 16.06.2015
comment
Круто, а можешь прислать слайды или запись выступления? Звучит интересно. - person Abraham D Flaxman; 17.06.2015
comment
Знайте, что это довольно старо, но я попытался воспроизвести результаты, и Pymc3 получил очень разные апостериорные значения (например, домашний коэффициент составляет около 0,0). - person fsociety; 28.11.2016
comment
Внимание, в этом коде есть ошибка. Строка для home_theta должна выглядеть так: home_theta = tt.exp(intercept + home + atts[home_team] + defs[away_team] Без исправления вы получите другие результаты, чем в pymc2. Мне потребовалось некоторое время, чтобы найти это! Это, вероятно, также объясняет fsociety проблема. - person DonCristobal; 23.01.2018

Ваша модель терпит неудачу, потому что вы не можете использовать функции NumPy для теано-тензоров. Таким образом

np.mean(atts_star3)

Выдаст вам ошибку. Вы можете удалить atts_star3 = pm3.Normal("atts_star",...) и просто использовать массив NumPy напрямую atts_star3 = x.

Я не думаю, что вам нужно явно моделировать tau_att3, tau_def3 или defs_star.

В качестве альтернативы, если вы хотите сохранить эти переменные, вы можете заменить np.mean на theano.tensor.mean, что должно сработать.

person John Salvatier    schedule 14.06.2015
comment
Спасибо за ответ Джон. Я попробовал это и получил, что Observed RV не может работать с int. Я думаю, что эта модель использует нормальное распределение atts_star3 и т. д., так как это в статье, на которой я основывал код. Я думаю, мне нужно будет сделать рефакторинг кода, и я посмотрю, что будет дальше. - person Peadar Coyle; 15.06.2015
comment
Так после последовал твоему совету. Но теперь я получаю эту ошибку. TypeError Traceback (most recent call last) <ipython-input-22-07dd9000673b> in <module>() 67 68 ---> 69 home_theta3 = pm3.Deterministic('regression', T.exp(intercept3 + atts3[away_team] + defs3[home_team])) 70 away_theta3 = pm3.Deterministic('regression', T.exp(intercept3 + atts3[away_team] + defs3[home_team])) 71 # Unknown model parameters TypeError: 'function' object is not subscriptable - person Peadar Coyle; 15.06.2015
comment
похоже, что atts3 или defs2 на самом деле являются функцией. Может быть, вы запустили закомментированный код выше? - person John Salvatier; 15.06.2015
comment
Да, когда я закомментировал этот код и просто добавил мю, он у меня заработал. Я расследую это немного позже сегодня. Но я думаю, что это работает как «хакерский» пример силы PyMC3. Что вы думаете? - person Peadar Coyle; 16.06.2015

Так что я сделал это. Это не прямой порт моей предыдущей версии, но он дает мне ответ. У кого-нибудь есть отзывы?

import os
import math
import warnings
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pymc3 as pm3# I know folks are switching to "as pm" but I'm just not there yet
%matplotlib inline
import seaborn as sns
from IPython.core.pylabtools import figsize
import seaborn as sns
import theano.tensor as T
figsize(12, 12)
DATA_DIR = os.path.join(os.getcwd(), 'data/')
data_file = DATA_DIR + 'results_2014.csv'

df = pd.read_csv(data_file, sep=',')
# Or whatever it takes to get this into a data frame.
teams = df.home_team.unique()
teams = pd.DataFrame(teams, columns=['team'])
teams['i'] = teams.index
df = pd.merge(df, teams, left_on='home_team', right_on='team', how='left')
df = df.rename(columns = {'i': 'i_home'}).drop('team', 1)
df = pd.merge(df, teams, left_on='away_team', right_on='team', how='left')
df = df.rename(columns = {'i': 'i_away'}).drop('team', 1)
observed_home_goals = df.home_score.values
observed_away_goals = df.away_score.values
home_team = df.i_home.values
away_team = df.i_away.values
num_teams = len(df.i_home.drop_duplicates())
num_games = len(home_team)
g = df.groupby('i_away')
att_starting_points = np.log(g.away_score.mean())
g = df.groupby('i_home')
def_starting_points = -np.log(g.away_score.mean())

import theano.tensor as T
import pymc3 as pm3
#hyperpriors

'''
def atts3(atts_star3=atts_star3):
    atts3 = atts_star.copy()
    atts3 = atts3 - np.mean(atts_star)
    return atts3
def defs3(defs_star3=defs_star3):
    defs3 = defs_star3.copy()
    defs3 = defs3 - np.mean(defs_star3)
    return defs
    '''
model = pm3.Model()
with pm3.Model() as model:
    home3 = pm3.Normal('home', 0, .0001)
    tau_att3 = pm3.Gamma('tau_att', .1, .1)
    tau_def3 = pm3.Gamma('tau_def', .1, .1)
    intercept3 = pm3.Normal('intercept', 0, .0001)
    #team-specific parameters
    atts_star3 = pm3.Normal("atts_star", 
                        mu=0, 
                        tau=tau_att3, 
                        shape=num_teams, 
                        observed=att_starting_points.values)
    defs_star3 = pm3.Normal("defs_star", 
                        mu=0, 
                        tau=tau_def3, 
                        shape=num_teams, 
                        observed=def_starting_points.values) 


    #home_theta3 = atts3 + defs3
    #away_theta3 = atts3 + defs3
    # Unknown model parameters
    home_points3 = pm3.Poisson('home_points', mu=1, observed=observed_home_goals)
    away_points3 = pm3.Poisson('away_points', mu=1, observed=observed_away_goals)
    start = pm3.find_MAP()
    step = pm3.NUTS(state=start)
    trace = pm3.sample(2000, step, start=start, progressbar=True)

    pm3.traceplot(trace)
person Peadar Coyle    schedule 15.06.2015
comment
Я опубликую свой перевод вашего кода PyMC2, который немного отличается. - person Abraham D Flaxman; 15.06.2015
comment
Круто :) Хотелось бы посмотреть. - person Peadar Coyle; 16.06.2015
comment
Я принял вашу версию Авраама. У меня не так хорошо написано :) - person Peadar Coyle; 16.06.2015