문제 설정
PyMC를 적용하려는 첫 번째 장난감 문제 중 하나는 비모수 적 군집입니다. 일부 데이터를 제공하고이를 가우스 혼합으로 모델링하고 군집 수와 각 군집의 평균 및 공분산을 배웁니다. 내가이 방법에 대해 알고있는 대부분의 내용은 2007 년경 Michael Jordan과 Yee Whye Teh의 비디오 강의 (스팀이 격렬 해지기 전에)와 Fonnesbeck 박사와 E. Chen의 자습서 [fn1], fn2]. 그러나 문제는 잘 연구되고 신뢰할만한 구현이있다 [fn3].
이 장난감 문제에서는 1 차원 가우스 에서 10 개의 드로우를 생성 하고 N ( μ = 4 , σ = 2 ) 에서 40 개의 드로우를 생성 합니다. 아래에서 볼 수 있듯이, 어떤 혼합 구성 요소에서 어떤 샘플이 나왔는지 쉽게 알 수 있도록 그림을 섞지 않았습니다.
I는 각각의 데이터 샘플을 모델링 에 대한 난 = 1 , . . . , 50 및 여기서 z i 는이 i 의 클러스터를 나타냅니다. 번째 데이터 포인트 : . N D P는 여기 디리클레 절단 공정의 길이가 사용된다 나를 위해 N.
Dirichlet 프로세스 인프라를 확장하여 각 군집 ID는 범주 형 랜덤 변수에서 추출한 것이며, 그 확률 질량 함수는 스틱 파괴 구성에 의해 제공됩니다. z i ∼ C a t e g o r i c a l ( p ) 먼저 N D P를 얻어서 1로 합산해야합니다. 와 농도에 대한 파라미터 α . 스틱 브레이킹은 N D P- 긴 벡터 p를 구성한다 그에 의존 무 베타 분산 IID [을 Fn1] 참조. 그리고 데이터에 무지에 대해 α를 알기 원 하므로 [fn1]을 따르고 α ∼ U n i f o r m ( 0.3 , 100 )으로 가정 합니다.
각 데이터 샘플의 클러스터 ID 생성 방법을 지정합니다. 각각의 클러스터는 연관된 평균 및 표준 편차, μ z i 및 σ z i를 갖는다 . 그런 다음 μ z i ~ N ( μ = 0 , σ = 50 ) 및 σ z i ~ U n i f o r m ( 0 , 100 ) 입니다.
(I 이전 [을 Fn1] 생각없이 다음과 hyperprior에 배치 된 이고, μ Z의 난 와 μ 0 자체가 고정 된 매개 변수 정규 분포 연신 및 σ 0 균일 한에서. 그러나 당https://stats.stackexchange.com/a/71932/31187, 내 데이터는 계층 hyperprior의이 종류를 지원하지 않습니다.)
요약하면 내 모델은 다음과 같습니다.
여기서 i 는 1에서 50까지 (데이터 샘플 수)입니다.
는 0과 사이의 값을 취할 수 N D P - 1 = 49 ; p ~ S t i c k ( α ) , N D P- 길이 벡터; 및 α ∼ U n i f o r m ( 0.3 , 100 )스칼라 (이제 데이터 샘플 수가 Dirichlet의 잘린 길이와 같게 만드는 것을 약간 후회하지만 명확하기를 바랍니다.)
및σ z i ~Uniform(0,100). 거기N D P는 이들 수단과 표준 편차 (의 각각에 대한 하나의N D P 가능한 클러스터).
그래픽 모델은 다음과 같습니다. 이름은 변수 이름입니다 (아래 코드 섹션 참조).
문제 설명
몇 가지 조정 및 수정 실패에도 불구하고 학습 된 매개 변수는 데이터를 생성 한 실제 값과 전혀 유사하지 않습니다.
처음 10 개의 데이터 샘플은 하나의 모드에서 왔고 나머지는 다른 모드에서 온 것임을 상기 한 결과는 분명히 그것을 포착하지 못했습니다.
클러스터 ID의 무작위 초기화를 허용하면 둘 이상의 클러스터를 얻지 만 클러스터는 모두 동일한 3.5 수준을 방황합니다.
부록 : 코드
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)
참고 문헌
- 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