Il problema
Voglio adattare i parametri del modello di una semplice popolazione di miscele 2 gaussiane. Dato tutto il clamore sui metodi bayesiani, voglio capire se per questo problema l'inferenza bayesiana è uno strumento migliore dei metodi di adattamento tradizionali.
Finora MCMC si comporta molto male in questo esempio di giocattolo, ma forse ho appena trascurato qualcosa. Quindi vediamo il codice.
Gli attrezzi
Userò python (2.7) + scipy stack, lmfit 0.8 e PyMC 2.3.
Un quaderno per riprodurre l'analisi è disponibile qui
Genera i dati
Prima di tutto, genera i dati:
from scipy.stats import distributions
# Sample parameters
nsamples = 1000
mu1_true = 0.3
mu2_true = 0.55
sig1_true = 0.08
sig2_true = 0.12
a_true = 0.4
# Samples generation
np.random.seed(3) # for repeatability
s1 = distributions.norm.rvs(mu1_true, sig1_true, size=round(a_true*nsamples))
s2 = distributions.norm.rvs(mu2_true, sig2_true, size=round((1-a_true)*nsamples))
samples = np.hstack([s1, s2])
L'istogramma di samples
assomiglia a questo:
un "ampio picco", i componenti sono difficili da individuare ad occhio.
Approccio classico: adattamento dell'istogramma
Proviamo prima l'approccio classico. Usando lmfit è facile definire un modello a 2 picchi:
import lmfit
peak1 = lmfit.models.GaussianModel(prefix='p1_')
peak2 = lmfit.models.GaussianModel(prefix='p2_')
model = peak1 + peak2
model.set_param_hint('p1_center', value=0.2, min=-1, max=2)
model.set_param_hint('p2_center', value=0.5, min=-1, max=2)
model.set_param_hint('p1_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p2_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p1_amplitude', value=1, min=0.0, max=1)
model.set_param_hint('p2_amplitude', expr='1 - p1_amplitude')
name = '2-gaussians'
Infine adattiamo il modello con l'algoritmo simplex:
fit_res = model.fit(data, x=x_data, method='nelder')
print fit_res.fit_report()
Il risultato è la seguente immagine (le linee tratteggiate rosse sono centrate):
Anche se il problema è piuttosto difficile, con valori iniziali e vincoli adeguati, i modelli convergono in una stima abbastanza ragionevole.
Approccio bayesiano: MCMC
Definisco il modello in PyMC in modo gerarchico. centers
e sigmas
sono la distribuzione dei priori per gli iperparametri che rappresentano i 2 centri e i 2 sigmi dei 2 gaussiani. alpha
è la frazione della prima popolazione e la distribuzione precedente è qui una Beta.
Una variabile categoriale sceglie tra le due popolazioni. Comprendo che questa variabile deve avere le stesse dimensioni dei dati ( samples
).
Infine mu
e tau
sono variabili deterministiche che determinano i parametri della distribuzione normale (dipendono dalla category
variabile in modo da passare in modo casuale tra i due valori per le due popolazioni).
sigmas = pm.Normal('sigmas', mu=0.1, tau=1000, size=2)
centers = pm.Normal('centers', [0.3, 0.7], [1/(0.1)**2, 1/(0.1)**2], size=2)
#centers = pm.Uniform('centers', 0, 1, size=2)
alpha = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Categorical("category", [alpha, 1 - alpha], size=nsamples)
@pm.deterministic
def mu(category=category, centers=centers):
return centers[category]
@pm.deterministic
def tau(category=category, sigmas=sigmas):
return 1/(sigmas[category]**2)
observations = pm.Normal('samples_model', mu=mu, tau=tau, value=samples, observed=True)
model = pm.Model([observations, mu, tau, category, alpha, sigmas, centers])
Quindi eseguo MCMC con un numero piuttosto lungo di iterazioni (1e5, ~ 60s sulla mia macchina):
mcmc = pm.MCMC(model)
mcmc.sample(100000, 30000)
Tuttavia i risultati sono molto strani. Ad esempio trace (la frazione della prima popolazione) tende a 0 invece di convergere a 0,4 e ha un'autocorrelazione molto forte:
Anche i centri dei Gaussiani non convergono. Per esempio:
Come vedi nella scelta precedente, ho cercato di "aiutare" l'algoritmo MCMC usando una distribuzione Beta per la precedente frazione di popolazione . Anche le precedenti distribuzioni per i centri e i sigmi sono abbastanza ragionevoli (credo).
Quindi cosa sta succedendo qui? Sto facendo qualcosa di sbagliato o MCMC non è adatto a questo problema?
Capisco che il metodo MCMC sarà più lento, ma l'insignificante istogramma sembra funzionare in modo immensamente migliore nella risoluzione delle popolazioni.
proposal_distribution
eproposal_sd
e perché usarePrior
è meglio per le variabili categoriali?