Inferenza del modello di miscela 2-gaussiana con MCMC e PyMC


10

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 samplesassomiglia a questo:

istogramma dei dati

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):

Risultati di adattamento NLS

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. centerse sigmassono 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 mue tausono variabili deterministiche che determinano i parametri della distribuzione normale (dipendono dalla categoryvariabile 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:α

Riepilogo alfa MCMC

Anche i centri dei Gaussiani non convergono. Per esempio:

Riepilogo centri MCMC_0

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.

Risposte:


6

Il problema è causato dal modo in cui PyMC disegna campioni per questo modello. Come spiegato nella sezione 5.8.1 della documentazione di PyMC, tutti gli elementi di una variabile di array vengono aggiornati insieme. Per array di piccole dimensioni come centerquesto non è un problema, ma per un array di grandi dimensioni come categoryquesto porta a un basso tasso di accettazione. Puoi vedere il tasso di accettazione tramite

print mcmc.step_method_dict[category][0].ratio

La soluzione suggerita nella documentazione è quella di utilizzare una matrice di variabili con valori scalari. Inoltre, è necessario configurare alcune delle distribuzioni della proposta poiché le scelte predefinite sono errate. Ecco il codice che funziona per me:

import pymc as pm
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)
alpha  = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Container([pm.Categorical("category%i" % i, [alpha, 1 - alpha]) 
                         for i in range(nsamples)])
observations = pm.Container([pm.Normal('samples_model%i' % i, 
                   mu=centers[category[i]], tau=1/(sigmas[category[i]]**2), 
                   value=samples[i], observed=True) for i in range(nsamples)])
model = pm.Model([observations, category, alpha, sigmas, centers])
mcmc = pm.MCMC(model)
# initialize in a good place to reduce the number of steps required
centers.value = [mu1_true, mu2_true]
# set a custom proposal for centers, since the default is bad
mcmc.use_step_method(pm.Metropolis, centers, proposal_sd=sig1_true/np.sqrt(nsamples))
# set a custom proposal for category, since the default is bad
for i in range(nsamples):
    mcmc.use_step_method(pm.DiscreteMetropolis, category[i], proposal_distribution='Prior')
mcmc.sample(100)  # beware sampling takes much longer now
# check the acceptance rates
print mcmc.step_method_dict[category[0]][0].ratio
print mcmc.step_method_dict[centers][0].ratio
print mcmc.step_method_dict[alpha][0].ratio

Le opzioni proposal_sde proposal_distributionsono spiegate nella sezione 5.7.1 . Per i centri, ho impostato la proposta in modo che corrisponda approssimativamente alla deviazione standard del posteriore, che è molto più piccola di quella predefinita a causa della quantità di dati. PyMC tenta di ottimizzare la larghezza della proposta, ma questo funziona solo se il tasso di accettazione è sufficientemente alto per cominciare. Perché category, il default proposal_distribution = 'Poisson'che dà scarsi risultati (non so perché, ma certamente non suona come una proposta ragionevole per una variabile binaria).


Grazie, questo è davvero utile, anche se diventa quasi insopportabilmente lento. Puoi spiegare brevemente il significato di proposal_distributione proposal_sde perché usare Priorè meglio per le variabili categoriali?
user2304916,

Grazie Tom. Sono d'accordo che Poisson è una scelta strana qui. Ho aperto un problema: github.com/pymc-devs/pymc/issues/627
twiecki,

2

σ

sigmas = pm.Exponential('sigmas', 0.1, size=2)

aggiornare:

Mi sono avvicinato ai parametri iniziali dei dati modificando queste parti del modello:

sigmas = pm.Exponential('sigmas', 0.1, size=2)
alpha  = pm.Beta('alpha', alpha=1, beta=1)

e invocando mcmc con qualche assottigliamento:

mcmc.sample(200000, 3000, 10)

i risultati:

alfa

centri

sigma

I posteriori non sono molto belli ... Nella sezione 11.6 del BUGS Book discutono questo tipo di modello e affermano che ci sono problemi di convergenza senza una soluzione ovvia. Controlla anche qui .


Questo è un buon punto, sto usando un Gamma ora. Tuttavia la traccia alfa tende sempre a 0 (anziché a 0,4). Mi chiedo se ci sia qualche stupido bug in agguato nel mio esempio.
user2304916

Ho provato Gamma (.1, .1) ma Exp (.1) sembra funzionare meglio. Inoltre, quando l'autocorrelazione è elevata, è possibile includere un po 'di diradamento, ad esempio,mcmc.sample(60000, 3000, 3)
jpneto,

2

Inoltre, la non identificabilità è un grosso problema per l'utilizzo di MCMC per i modelli di miscele. Fondamentalmente, se si cambiano le etichette sul mezzo del cluster e le assegnazioni del cluster, la probabilità non cambia e questo può confondere il campionatore (tra catene o all'interno di catene). Una cosa che potresti provare a mitigare è l'utilizzo dei potenziali in PyMC3. Una buona implementazione di un GMM con potenziali è qui . Anche il posteriore di questo tipo di problemi è generalmente altamente multimodale, il che presenta anche un grosso problema. Potresti invece voler utilizzare EM (o inferenza variazionale).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.