ノンパラメトリッククラスタリングのPyMC:ガウス混合のパラメーターを推定するディリクレプロセスがクラスター化に失敗する


10

問題のセットアップ

私がPyMCに適用したい最初のおもちゃの問題の1つは、ノンパラメトリッククラスタリングです。いくつかのデータを指定して、ガウス混合としてモデル化し、クラスターの数と各クラスターの平均と共分散を学習します。この方法について私が知っていることのほとんどは、2007年頃のマイケルジョーダンとイーワイテによるビデオ講義(スパースが大流行する前)と、Fonnesbeck博士とE. Chen博士のチュートリアル[fn1]、[ fn2]。しかし、問題はよく研究されており、信頼できる実装がいくつかあります[fn3]。

このおもちゃの問題では、1次元ガウスから10回の描画を生成し、から40回の描画を生成します。以下に見られるように、どのサンプルがどの混合成分からのものであるかを簡単に見分けられるように、ドローをシャッフルしませんでした。Nμ = 4 σ = 2 N(μ=0,σ=1)N(μ=4,σ=2)

混合ガウスモデルデータ

各データサンプルをに対してモデル化しここで、はこの番目のデータポイントのクラスターを示します。。ここでは、使用される切り捨てられたディリクレプロセスの長さです。私にとって、です。iは= 1 50 、Z iは I Z I[ 1 N D P ] N D P N D P = 50yiN(μzi,σzi)i=1,...,50ziizi[1,...,NDP]NDPNDP=50

ディリクレプロセスインフラストラクチャを拡張すると、各クラスターIDはカテゴリ確率変数からのドローであり、その確率質量関数はスティック破壊コンストラクトによって与えられます: with for a濃度パラメータ。スティックブレイキングは、最初にに依存する iidベータ分布ドローを取得することにより、合計が1になる -longベクトル構築します。[fn1]を参照してください。そして、データに無知であることを知らせたいので、[fn1]に従い、 0.3、100)と仮定します。、Z IC A T E G O R I C A L P P S T iがcはk個α α N D P P N D P α α α U N iはF O のR M 0.3 100 ziziCategorical(p)pStick(α)αNDPpNDPαααUniform(0.3,100)

これは、各データサンプルのクラスターIDの生成方法を指定します。クラスターのそれぞれに、平均と標準偏差および関連付けられています。次に、およびです。 μ Z I σ Z I μ Z INμ = 0 σ = 50 σ Z IU N I F O R M 0 100 NDPμziσziμziN(μ=0,σ=50)σziUniform(0,100)

(私は以前にunthinkingly [FN1以下上hyperpriorを置いた、つまり、と Aから自身ドロー固定パラメーター正規分布、およびユニフォームからのが、https:と、私のデータはこの種の階層的なハイパーをサポートしていません。 μ Z INμ 0σ 0μ 0 σ 0μziμziN(μ0,σ0)μ0σ0

要約すると、私のモデルは:

yiN(μzi,σzi)ここで、は1〜50(データサンプルの数で実行されます。i

ziCategorical(p)で、値を取ることができます。 、 -longベクトル; および、スカラー。(データサンプルの数を以前のディリクレの長さの切り捨てと同じにしたことを少し後悔していますが、それが明確であることを願っています。)NDP1=49pStick(α)NDPαUniform(0.3,100)

σ Z IU N I F O R M 0 100 N D P N D PμziN(μ=0,σ=50)および。これらの平均のと標準偏差があります(可能なクラスターのそれぞれに1つ)。σziUniform(0,100)NDPNDP

これがグラフィカルモデルです。名前は変数名です。以下のコードセクションを参照してください。

グラフ

問題文

いくつかの微調整と失敗した修正にもかかわらず、学習されたパラメーターは、データを生成した真の値とまったく似ていません。

現在、ほとんどの確率変数を固定値に初期化しています。平均変数と標準偏差変数は、それらの期待値に初期化されます(つまり、標準変数の場合は0、均一変数のサポートの中間)。すべてのクラスターIDを0に初期化します。また、濃度パラメーターを初期化します。 α = 5ziα=5

このような初期化では、MCMCを100,000回繰り返しても、2番目のクラスターを見つけることができません。の最初の要素は1に近く、すべてのデータサンプルに対するほぼすべての描画は同じで、約3.5です。ここでは、最初の20個のデータサンプル、つまりについて、100回ごとに描画します。μ Z I I μ Z I iは= 1 20pμziiμzii=1,...,20

ゼロで初期化されたクラスターIDを持つ手段

最初の10個のデータサンプルは1つのモードからのものであり、残りは別のモードからのものであることを想起すると、上記の結果は明らかにそれをキャプチャできません。

クラスターIDのランダムな初期化を許可すると、複数のクラスターが取得されますが、クラスターはすべて同じ3.5レベルをさまよいます。

ランダムに初期化されたクラスターIDを持つ手段

これらの異なる結果がちょうど変更した後に起こることをリコール:これは、それはそれはそれはATの1から後部の他のモードに到達できないことを、MCMCと通常の問題だと私に示唆して初期クラスタIDのではなく、彼らの事前確率かを他に何か。zi

モデリングの間違いはありますか?同様の質問:https : //stackoverflow.com/q/19114790/500207は、ディリクレ分布を使用して3要素のガウス混合を近似したいと考えており、いくらか同様の問題が発生しています。完全共役モデルを設定し、この種類のクラスタリングにギブスサンプリングを使用することを検討する必要がありますか?(私はパラメトリックディリクレ分布のケースにギブスサンプラーを実装しましたが、当時は固定濃度を使用していましたが、うまく機能したため、PyMCが少なくともその問題を手軽に解決できると期待しています。)α

付録:コード

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)

参考文献

  1. fn1:http ://nbviewer.ipython.org/urls/raw.github.com/fonnesbeck/Bios366/master/notebooks/Section5_2-Dirichlet-Processes.ipynb
  2. fn2:http : //blog.echen.me/2012/03/20/infinite-mixture-models-with-nonparametric-bayes-and-the-dirichlet-process/
  3. fn3:http ://scikit-learn.org/stable/auto_examples/mixture/plot_gmm.html#example-mixture-plot-gmm-py

コンポーネントの分散に対する以前の変化はUniform(0,100)であり、これが大きな問題を引き起こしている可能性があります。この事前分布の質量の2%のみが1と2の真の分散をカバーします。この事前分布の下でのコンポーネントの予想される分散は50です。これは非常に広いガウス分布であり、単一のコンポーネントでデータを簡単に説明できます。
jerad 2013年

確率的プログラミングとハッカーのベイジアン統計の本のこの章を読みましたか?それはあなたを助けるかもしれない例があります!
Tim

これは答えとしては少し簡単なようです。コメントのようです。少なくとも、OPがそれを読んで取得する情報の概要を説明できますか?
Glen_b-2013

@TimRichはい、私はそれを読んだり、大学院でクラスを受講したり、業界で応用統計を作成したりしました;)これはPyMC固有の質問です。
Ahmed Fasih 2013

1
以前の階層を却下しません。特にクラスターの数を同時に調べようとしているときに、混合物の成分に平坦な事前分布を設定すると、悪い位置に置かれることはよく知られています。これが、トレースプロットに奇妙なスパイクを引き起こしている原因です。NPベイズのビッグネームはすべて、を設定し、プラグイン推定値を使用するかまたはこれらのコンポーネントに情報を提供するそれらを適切なスケールに置きます。μ 0σ 0μZiN(μ0,σ0)μ0,σ0

回答:


4

誰かがこの質問をこれ以上見ているかどうかはわかりませんが、標準偏差の前のフラットに関するガイからの洞察を取り入れながら、トムのギブスのサンプリング提案をテストするためにあなたの質問をrjagsに入れます。

10のデータポイントや40のデータポイントでも、事前に情報がなければ分散を推定するには不十分であるため、このおもちゃの問題は難しいかもしれません。現在の以前のσzi∼Uniform(0,100)は有益ではありません。これは、μziのほぼすべての描画が2つの分布の期待される平均である理由を説明している可能性があります。それがあなたの質問をあまり変えないなら、私はそれぞれ100と400のデータポイントを使います。

また、コードで直接スティック破壊プロセスを使用しませんでした。dirichletプロセスのWikipediaページで、p〜Dir(a / k)は大丈夫だと思いました。

最後に、それはまだいくつかのクラスターkを必要とするため、セミパラメトリック実装にすぎません。rjagsで無限混合モデルを作成する方法がわかりません。

マルコフ連鎖muクラスター1

マルコフ連鎖muクラスター2

library("rjags")

set1 <- rnorm(100, 0, 1)
set2 <- rnorm(400, 4, 1)
data <- c(set1, set2)

plot(data, type='l', col='blue', lwd=3,
     main='gaussian mixture model data',
     xlab='data sample #', ylab='data value')
points(data, col='blue')

cpd.model.str <- 'model {
  a ~ dunif(0.3, 100)
  for (i in 1:k){
    alpha[i] <- a/k
    mu[i] ~ dnorm(0.0, 0.001)
    sigma[i] ~ dunif(0, 100)
  }
  p[1:k] ~ ddirich(alpha[1:k])
  for (i in 1:n){
    z[i] ~ dcat(p)
    y[i] ~ dnorm(mu[z[i]], pow(sigma[z[i]], -2))
  }
}' 


cpd.model <- jags.model(textConnection(cpd.model.str),
                        data=list(y=data,
                                  n=length(data),
                                  k=5))
update(cpd.model, 1000)
chain <- coda.samples(model = cpd.model, n.iter = 1000,
                      variable.names = c('p', 'mu', 'sigma'))
rchain <- as.matrix(chain)
apply(rchain, 2, mean)

1
クラスタの有限個使用に関しては、あなただけ取ることができ大(とセット、あなたが持っているように)とディリクレ過程を効果的に同じであるモデルを取得します。ディリクレプロセスの前にこれを収束することは非常に迅速です。以下のためにの観測、のようなもの十二分にする必要があります。また、を使用する場合、ブロック更新を検出しないため、スティック破壊構造を使用しない方が賢明です。ここで何をしたかが良いです。Kは、α iは = / K 500 、K = 25 、PをKKαi=a/K500K=25JAGSJAGSp

1

PyMCがサンプルを描画する方法が原因で、表示されている混合が不十分である可能性が高いです。PyMCドキュメントのセクション5.8.1で説明されているように、配列変数のすべての要素は一緒に更新されます。あなたの場合、それはclustermean1ステップで配列全体を更新しようとすることを意味し、同様にclusterid。PyMCはギブスサンプリングを行いません。それは、提案がいくつかの簡単なヒューリスティックによって選択されるメトロポリスを実行します。これにより、アレイ全体に適切な値を提案することが難しくなります。


「アレイ全体を1つのステップで更新しようとする」と言ったらすぐに、メトロポリス(この場合)とギブスの欠点を理解しました。STANやJAGSについて、これをうまく行えるようにする特別なことはありますか?どちらの場合でも、PyMCでGibbsを実装するのに少し時間を費やします。ありがとうございました!(私はライトスピード以来あなたの仕事のファンでしたので、ありがとうございます!)
Ahmed Fasih

1
STANは離散変数を処理しませんが、JAGSは試してみる価値があります。
Tom Minka、2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.