Configuração do problema
Um dos primeiros problemas de brinquedo nos quais eu queria aplicar o PyMC é o cluster não paramétrico: com alguns dados, modele-os como uma mistura gaussiana e aprenda o número de clusters e a média e covariância de cada cluster. A maior parte do que sei sobre esse método vem de videoconferências de Michael Jordan e Yee Whye Teh, por volta de 2007 (antes da escassez de raiva), e nos últimos dias lendo os tutoriais do Dr. Fonnesbeck e E. Chen [fn1], [ fn2]. Mas o problema é bem estudado e tem algumas implementações confiáveis [fn3].
Neste problema de brinquedo, eu gero dez desenhos de um Gaussiano unidimensional ( μ = 0 , σ = 1 ) e quarenta desenhos de N ( μ = 4 , σ = 2 ) . Como você pode ver abaixo, eu não embaralhei os desenhos para facilitar a identificação de quais amostras vieram de qual componente da mistura.
I modelar cada amostra de dados , para i = 1 , . . . , 50 e onde z i indica o aglomerado para este i th ponto de dados: z i ∈ [ 1 , . . . , N D P ] . N D P aqui é a duração do processo de Dirichlet truncado usado: para mim, N.
Expandir a infra-estrutura processo Dirichlet, cada aglomerado ID é um desenho de uma variável aleatória categórica, cuja função de densidade de probabilidade é dada pela construção de quebra de vara: z i ~ C uma t e g o r i c a l ( p ) com p ∼ S t i c k ( α ) para um parâmetro de concentração α . Construes vara-quebrar o N D P -long vector p, que deve somar 1, primeiro obtendo iid empates distribuídos em beta que dependem de α , consulte [fn1]. E desde que eu gostaria dos dados para informar a minha ignorância de α , eu sigo [FN1] e assumir α ~ U n i f o r m ( 0.3 , 100 ) .
Isso especifica como o ID de cluster de cada amostra de dados é gerado. Cada um dos clusters possui uma média e desvio padrão associados, μ z i e σ z i . Em seguida, μ z i ~ N ( μ = 0 , σ = 50 ) e σ z i ~ L n i f o r m ( 0 , 100 ) .
(I foi anteriormente seguinte [FN1] automaticamente e colocando um hyperprior em , isto é, μ z i ~ N ( μ 0 , σ 0 ) com μ 0 si um empate de uma distribuição normal-parâmetro fixo, e σ 0 mas de acordo com https://stats.stackexchange.com/a/71932/31187 , meus dados não suportam esse tipo de hiperprior hierárquica.)
Em resumo, meu modelo é:
onde i é executado de 1 a 50 (o número de amostras de dados).
e pode assumir valores entre 0 e N D P - 1 = 49 ; p ∼ S t i c k ( a ) , umvetor N D P- longo; e α ~ L n i f o r m ( 0,3 , 100 ), um escalar. (Agora me arrependo um pouco de tornar o número de amostras de dados igual ao comprimento truncado do Dirichlet antes, mas espero que esteja claro.)
e σ z i ~ L n i f o r m ( 0 , 100 ) . Há N D P desses meios e desvios padrão (um para cada um dospossíveis agrupamentos de N D P ).
Aqui está o modelo gráfico: os nomes são variáveis, veja a seção de código abaixo.
Declaração do problema
Apesar de vários ajustes e correções com falha, os parâmetros aprendidos não são de modo algum semelhantes aos valores reais que geraram os dados.
Atualmente, estou inicializando a maioria das variáveis aleatórias para valores fixos. As variáveis de média e desvio padrão são inicializadas com seus valores esperados (ou seja, 0 para os normais, o meio de seu suporte para os uniformes). I inicializa todos os IDs de fragmentação a 0. E eu inicializar o parâmetro concentração α = 5 .
Lembrando que as dez primeiras amostras de dados eram de um modo e as demais eram do outro, o resultado acima claramente falha em capturar isso.
Se eu permitir a inicialização aleatória dos IDs do cluster, obtenho mais de um cluster, mas o cluster significa que todos andam pelo mesmo nível 3.5:
Apêndice: código
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)
Referências
- 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