Impostazione del problema
Uno dei primi problemi con i giocattoli a cui volevo applicare PyMC è il clustering non parametrico: dati alcuni dati, modello come una miscela gaussiana e apprendimento del numero di cluster, media e covarianza di ciascun cluster. La maggior parte di ciò che so di questo metodo proviene dalle lezioni video di Michael Jordan e Yee Whye Teh, circa 2007 (prima che la scarsità diventasse la rabbia), e gli ultimi due giorni di lettura dei tutorial del dott. Fonnesbeck e E. Chen [fn1], [ Fn2]. Ma il problema è ben studiato e ha alcune implementazioni affidabili [fn3].
In questo problema con il giocattolo, ho generato dieci disegni da un gaussiano monodimensionale e quaranta disegni da . Come puoi vedere di seguito, non ho mischiato i disegni, per rendere facile dire quali campioni provengono da quale componente della miscela.
Io modello ciascun campione di dati , per e dove indica il cluster per questo esimo punto di dati: . qui è la lunghezza del processo Dirichlet troncato utilizzato: per me, .
Espandendo l'infrastruttura del processo Dirichlet, ogni ID cluster è un sorteggio da una variabile casuale categoriale, la cui funzione di massa di probabilità è data dal costrutto di rottura: con per un parametro di concentrazione . Stick-breaking costruisce il vettore lungo , che deve essere pari a 1, ottenendo prima disegni distribuiti in beta che dipendono da , vedi [fn1]. E poiché vorrei che i dati informassero la mia ignoranza di , seguo [fn1] e presumo .α ∼ U n i f o r m ( 0.3 , 100 )
Questo specifica come viene generato l'ID cluster di ciascun campione di dati. Ciascuno dei cluster ha una media e deviazione standard associate, e . Quindi, e . μ z i σ z i μ z i ∼ N ( μ = 0 , σ = 50 ) σ z i ∼ U n i f o r m ( 0 , 100 )
(In precedenza stavo seguendo [fn1] senza pensare e ponendo un hyperprior su , cioè con stesso un pareggio da un distribuzione normale a parametri fissi e da un'uniforme. Ma per https://stats.stackexchange.com/a/71932/31187 , i miei dati non supportano questo tipo di hyperprior gerarchico.) μ z i ∼ N ( μ 0 , σ 0 ) μ 0 σ 0
In sintesi, il mio modello è:
i dove corre da 1 a 50 (il numero di campioni di dati).
e può assumere valori compresi tra 0 e ; , un vettore lungo ; e , uno scalare. (Ora mi pento leggermente di aver reso il numero di campioni di dati uguale alla lunghezza troncata del Dirichlet precedente, ma spero sia chiaro.)
e . Esistono di questi mezzi e deviazioni standard (uno per ciascuno dei possibili cluster .)
Ecco il modello grafico: i nomi sono nomi di variabili, vedere la sezione codici di seguito.
Dichiarazione problema
Nonostante diverse modifiche e correzioni non riuscite, i parametri appresi non sono affatto simili ai valori reali che hanno generato i dati.
Attualmente sto inizializzando la maggior parte delle variabili casuali su valori fissi. Le variabili di deviazione media e standard sono inizializzate ai loro valori attesi (cioè 0 per quelli normali, al centro del loro supporto per quelli uniformi). Inizializzo tutti gli ID cluster su 0. E inizializzo il parametro di concentrazione .
Con tali inizializzazioni, 100'000 iterazioni MCMC semplicemente non riescono a trovare un secondo cluster. Il primo elemento di è vicino a 1 e quasi tutti i disegni di per tutti i campioni di dati sono gli stessi, circa 3,5. Mostro qui ogni centesimo disegno per i primi venti esempi di dati, ovvero per :
Ricordando che i primi dieci campioni di dati provenivano da una modalità e il resto dall'altra, il risultato di cui sopra chiaramente non riesce a catturarlo.
Se consento l'inizializzazione casuale degli ID del cluster, ottengo più di un cluster ma il cluster significa che tutti vagano intorno allo stesso livello 3.5:
Questo mi suggerisce che è il solito problema con MCMC, che non può raggiungere un'altra modalità del posteriore rispetto a quella in cui si trova: ricorda che questi diversi risultati si verificano dopo aver cambiato l' inizializzazione degli ID cluster , non i loro precedenti o qualunque altra cosa.
Sto commettendo errori di modellazione? Domanda simile: https://stackoverflow.com/q/19114790/500207 vuole usare una distribuzione di Dirichlet e adattarsi a una miscela gaussiana a 3 elementi e sta riscontrando problemi in qualche modo simili. Dovrei considerare la creazione di un modello completamente coniugato e l'utilizzo del campionamento Gibbs per questo tipo di clustering? (Ho implementato un campionatore Gibbs per il caso di distribuzione parametrica di Dirichlet, ad eccezione di una concentrazione fissa , nel corso della giornata e ha funzionato bene, quindi aspettatevi che PyMC sia in grado di risolvere almeno quel problema facilmente.)
Appendice: codice
import pymc
import numpy as np
### Data generation
# Means and standard deviations of the Gaussian mixture model. The inference
# engine doesn't know these.
means = [0, 4.0]
stdevs = [1, 2.0]
# Rather than randomizing between the mixands, just specify how many
# to draw from each. This makes it really easy to know which draws
# came from which mixands (the first N1 from the first, the rest from
# the secon). The inference engine doesn't know about N1 and N2, only Ndata
N1 = 10
N2 = 40
Ndata = N1+N2
# Seed both the data generator RNG as well as the global seed (for PyMC)
RNGseed = 123
np.random.seed(RNGseed)
def generate_data(draws_per_mixand):
"""Draw samples from a two-element Gaussian mixture reproducibly.
Input sequence indicates the number of draws from each mixand. Resulting
draws are concantenated together.
"""
RNG = np.random.RandomState(RNGseed)
values = np.hstack([RNG.normal(means[i], stdevs[i], ndraws)
for (i,ndraws) in enumerate(draws_per_mixand)])
return values
observed_data = generate_data([N1, N2])
### PyMC model setup, step 1: the Dirichlet process and stick-breaking
# Truncation level of the Dirichlet process
Ndp = 50
# "alpha", or the concentration of the stick-breaking construction. There exists
# some interplay between choice of Ndp and concentration: a high concentration
# value implies many clusters, in turn implying low values for the leading
# elements of the probability mass function built by stick-breaking. Since we
# enforce the resulting PMF to sum to one, the probability of the last cluster
# might be then be set artificially high. This may interfere with the Dirichlet
# process' clustering ability.
#
# An example: if Ndp===4, and concentration high enough, stick-breaking might
# yield p===[.1, .1, .1, .7], which isn't desireable. You want to initialize
# concentration so that the last element of the PMF is less than or not much
# more than the a few of the previous ones. So you'd want to initialize at a
# smaller concentration to get something more like, say, p===[.35, .3, .25, .1].
#
# A thought: maybe we can avoid this interdependency by, rather than setting the
# final value of the PMF vector, scale the entire PMF vector to sum to 1? FIXME,
# TODO.
concinit = 5.0
conclo = 0.3
conchi = 100.0
concentration = pymc.Uniform('concentration', lower=conclo, upper=conchi,
value=concinit)
# The stick-breaking construction: requires Ndp beta draws dependent on the
# concentration, before the probability mass function is actually constructed.
betas = pymc.Beta('betas', alpha=1, beta=concentration, size=Ndp)
@pymc.deterministic
def pmf(betas=betas):
"Construct a probability mass function for the truncated Dirichlet process"
# prod = lambda x: np.exp(np.sum(np.log(x))) # Slow but more accurate(?)
prod = np.prod
value = map(lambda (i,u): u * prod(1.0 - betas[:i]), enumerate(betas))
value[-1] = 1.0 - sum(value[:-1]) # force value to sum to 1
return value
# The cluster assignments: each data point's estimated cluster ID.
# Remove idinit to allow clusterid to be randomly initialized:
idinit = np.zeros(Ndata, dtype=np.int64)
clusterid = pymc.Categorical('clusterid', p=pmf, size=Ndata, value=idinit)
### PyMC model setup, step 2: clusters' means and stdevs
# An individual data sample is drawn from a Gaussian, whose mean and stdev is
# what we're seeking.
# Hyperprior on clusters' means
mu0_mean = 0.0
mu0_std = 50.0
mu0_prec = 1.0/mu0_std**2
mu0_init = np.zeros(Ndp)
clustermean = pymc.Normal('clustermean', mu=mu0_mean, tau=mu0_prec,
size=Ndp, value=mu0_init)
# The cluster's stdev
clustersig_lo = 0.0
clustersig_hi = 100.0
clustersig_init = 50*np.ones(Ndp) # Again, don't really care?
clustersig = pymc.Uniform('clustersig', lower=clustersig_lo,
upper=clustersig_hi, size=Ndp, value=clustersig_init)
clusterprec = clustersig ** -2
### PyMC model setup, step 3: data
# So now we have means and stdevs for each of the Ndp clusters. We also have a
# probability mass function over all clusters, and a cluster ID indicating which
# cluster a particular data sample belongs to.
@pymc.deterministic
def data_cluster_mean(clusterid=clusterid, clustermean=clustermean):
"Converts Ndata cluster IDs and Ndp cluster means to Ndata means."
return clustermean[clusterid]
@pymc.deterministic
def data_cluster_prec(clusterid=clusterid, clusterprec=clusterprec):
"Converts Ndata cluster IDs and Ndp cluster precs to Ndata precs."
return clusterprec[clusterid]
data = pymc.Normal('data', mu=data_cluster_mean, tau=data_cluster_prec,
observed=True, value=observed_data)
Riferimenti
- fn1: http://nbviewer.ipython.org/urls/raw.github.com/fonnesbeck/Bios366/master/notebooks/Section5_2-Dirichlet-Processes.ipynb
- fn2: http://blog.echen.me/2012/03/20/infinite-mixture-models-with-nonparametric-bayes-and-the-dirichlet-process/
- fn3: http://scikit-learn.org/stable/auto_examples/mixture/plot_gmm.html#example-mixture-plot-gmm-py