EM algoritması manuel olarak uygulandı


20

Ben elle EM algoritmayı uygulamak ve ardından sonuçlarına karşılaştırmak istediğiniz normalmixEMbir mixtoolspakette. Tabii ki, her ikisi de aynı sonuçlara yol açarsa mutlu olurum. Ana referans, Sonlu Karışım Modelleri olan Geoffrey McLachlan (2000) .

Genelde iki Gausslu karışım yoğunluğum var, log olabilirliği (McLachlan sayfa 48):

günlükLc(Ψ)=Σben=1gΣj=1nzbenj{günlükπben+günlükfben(yben;θben)}.
zbenj olangözlem ile ilgili ise,inciaksi halde, bileşen yoğunluğu. normal dağılım yoğunluğudur. π1ben0fbenπkarışım oranıdır, bu nedenle π1 , bir gözlemin ilk Gauss dağılımından ve π2 , bir gözlemin ikinci Gauss dağılımından olma olasılığıdır.

D aşaması, hemen koşullu beklenti hesaplama:

S(Ψ;Ψ(0))=EΨ(0){günlükLc(|Ψ)|y}.
sonuçta birkaç derivasyondan sonra yol açar (sayfa 49):

τi(yj;Ψ(k))=πi(k)fi(yj;θi(k)f(yj;Ψ(k)=πi(k)fi(yj;θi(k)h=1gπh(k)fh(yj;θh(k))
iki Gauss hastası durumunda (sayfa 82):

τben(yj;Ψ)=πbenφ(yj;μben,Σben)Σh=1gπhφ(yj;μh,Σh)
Kaşaması artık Q (sayfa 49) maksimize etmektir:

S(Ψ;Ψ(k))=Σben=1gΣj=1nτben(yj;Ψ(k)){günlükπben+günlükfben(yj;θben)}.
Bu, (iki Gauss durumunda) (sayfa 82):

μben(k+1)=Σj=1nτbenj(k)yjΣj=1nτbenj(k)Σben(k+1)=Σj=1nτbenj(k)(yj-μben(k+1))(yj-μben(k+1))TΣj=1nτbenj(k)
ve bu (. 50 p) bilmek

πben(k+1)=Σj=1nτben(yj;Ψ(k))n(ben=1,...,g).
Bu E tekrar, E aşamaları kadarL(Ψ(k+1))-L(Ψ(k)) küçüktür.

Bir R kodu yazmaya çalıştım (veriler burada bulunabilir ).

# EM algorithm manually
# dat is the data

# initial values
pi1       <-  0.5
pi2       <-  0.5
mu1       <- -0.01
mu2       <-  0.01
sigma1    <-  0.01
sigma2    <-  0.02
loglik[1] <-  0
loglik[2] <- sum(pi1*(log(pi1) + log(dnorm(dat,mu1,sigma1)))) + 
             sum(pi2*(log(pi2) + log(dnorm(dat,mu2,sigma2))))

tau1 <- 0
tau2 <- 0
k    <- 1

# loop
while(abs(loglik[k+1]-loglik[k]) >= 0.00001) {

  # E step
  tau1 <- pi1*dnorm(dat,mean=mu1,sd=sigma1)/(pi1*dnorm(x,mean=mu1,sd=sigma1) + 
          pi2*dnorm(dat,mean=mu2,sd=sigma2))
  tau2 <- pi2*dnorm(dat,mean=mu2,sd=sigma2)/(pi1*dnorm(x,mean=mu1,sd=sigma1) + 
          pi2*dnorm(dat,mean=mu2,sd=sigma2))

  # M step
  pi1 <- sum(tau1)/length(dat)
  pi2 <- sum(tau2)/length(dat)

  mu1 <- sum(tau1*x)/sum(tau1)
  mu2 <- sum(tau2*x)/sum(tau2)

  sigma1 <- sum(tau1*(x-mu1)^2)/sum(tau1)
  sigma2 <- sum(tau2*(x-mu2)^2)/sum(tau2)

  loglik[k] <- sum(tau1*(log(pi1) + log(dnorm(x,mu1,sigma1)))) + 
               sum(tau2*(log(pi2) + log(dnorm(x,mu2,sigma2))))
  k         <- k+1
}


# compare
library(mixtools)
gm <- normalmixEM(x, k=2, lambda=c(0.5,0.5), mu=c(-0.01,0.01), sigma=c(0.01,0.02))
gm$lambda
gm$mu
gm$sigma

gm$loglik

Algoritma çalışmıyor, çünkü bazı gözlemler sıfır olma olasılığına sahip ve bunun günlüğü -Inf. Benim hatam nerede?


Sorun istatistiksel bir sorun değil, sayısal bir sorun. Kodunuzdaki makine hassasiyetinden daha küçük olasılıklar için olasılıklar eklemelisiniz.
JohnRos

neden mixtools işlevini elle doğrulanabilen çok basit bir örnekle çok fazla denemeye çalışmıyorsunuz? sonra, orada çalıştığını görürseniz, kodunuzu genelleştirin ve her adımda doğrulayın.

Yanıtlar:


17

Kaynak kodunda birkaç sorununuz var:

  1. @Pat'ın işaret ettiği gibi, bu değer kolayca sonsuza gidebileceğinden log (dnorm ()) kullanmamalısınız. Logmvdnorm kullanmalısınız

  2. Toplam kullandığınızda , sonsuz veya eksik değerleri kaldırmayı unutmayın

  3. Döngü k değişkeniniz yanlış, loglik [k + 1] 'i güncellemeniz gerekir ancak loglik [k]' yı güncellemeniz gerekir

  4. Σσ

  5. τ1τ2

Ayrıca, kaynak kodunuzda tam kodlar (örneğin loglik [] 'i nasıl başlattığınızı) koymanızı ve okumayı kolaylaştırmak için kodu girintili hale getirmenizi öneririm.

Sonuçta, mixtools paketini tanıttığınız için teşekkürler ve bunları gelecekteki araştırmamda kullanmayı planlıyorum.

Ayrıca referans için çalışma kodumu koydum:

# EM algorithm manually
# dat is the data
setwd("~/Downloads/")
load("datem.Rdata")
x <- dat

# initial values
pi1<-0.5
pi2<-0.5
mu1<--0.01
mu2<-0.01
sigma1<-sqrt(0.01)
sigma2<-sqrt(0.02)
loglik<- rep(NA, 1000)
loglik[1]<-0
loglik[2]<-mysum(pi1*(log(pi1)+log(dnorm(dat,mu1,sigma1))))+mysum(pi2*(log(pi2)+log(dnorm(dat,mu2,sigma2))))

mysum <- function(x) {
  sum(x[is.finite(x)])
}
logdnorm <- function(x, mu, sigma) {
  mysum(sapply(x, function(x) {logdmvnorm(x, mu, sigma)}))  
}
tau1<-0
tau2<-0
#k<-1
k<-2

# loop
while(abs(loglik[k]-loglik[k-1]) >= 0.00001) {
  # E step
  tau1<-pi1*dnorm(dat,mean=mu1,sd=sigma1)/(pi1*dnorm(x,mean=mu1,sd=sigma1)+pi2*dnorm(dat,mean=mu2,sd=sigma2))
  tau2<-pi2*dnorm(dat,mean=mu2,sd=sigma2)/(pi1*dnorm(x,mean=mu1,sd=sigma1)+pi2*dnorm(dat,mean=mu2,sd=sigma2))
  tau1[is.na(tau1)] <- 0.5
  tau2[is.na(tau2)] <- 0.5

  # M step
  pi1<-mysum(tau1)/length(dat)
  pi2<-mysum(tau2)/length(dat)

  mu1<-mysum(tau1*x)/mysum(tau1)
  mu2<-mysum(tau2*x)/mysum(tau2)

  sigma1<-mysum(tau1*(x-mu1)^2)/mysum(tau1)
  sigma2<-mysum(tau2*(x-mu2)^2)/mysum(tau2)

  #  loglik[k]<-sum(tau1*(log(pi1)+log(dnorm(x,mu1,sigma1))))+sum(tau2*(log(pi2)+log(dnorm(x,mu2,sigma2))))
  loglik[k+1]<-mysum(tau1*(log(pi1)+logdnorm(x,mu1,sigma1)))+mysum(tau2*(log(pi2)+logdnorm(x,mu2,sigma2)))
  k<-k+1
}

# compare
library(mixtools)
gm<-normalmixEM(x,k=2,lambda=c(0.5,0.5),mu=c(-0.01,0.01),sigma=c(0.01,0.02))
gm$lambda
	gm$mu
gm$sigma

gm$loglik

Historgram Histogram


@zahnxw Cevabınız için teşekkürler, bu benim kodumun yanlış olduğu anlamına mı geliyor? Yani basi fikri çalışmıyor?
Stat Tistician

"Ayrıca, kaynak kodunuza tam kodlar (örneğin loglik [] nasıl başlatıldığını) koymanızı ve okumayı kolaylaştırmak için kodu girintili hale getirmenizi öneririm." Peki bu benim kodum mu? loglik [], gönderdiğim kodda beyan ettiğim gibi tanımlanıyor mu?
Stat Tistician

1
@StatTistician fikri doğrudur, ancak uygulamanın kusurları vardır. Örneğin, yetersiz akışı düşünmediniz. Ayrıca, k değişkenini karıştırmak kafa karıştırıcıdır, önce loglik [1] ve loglik [2] 'yi ayarlarken while döngüsüne girdikten sonra loglik [1]' i tekrar ayarlarsınız. Bu doğal bir yol değil. Loglik [] 'nin başlatılmasıyla ilgili önerim loklik <- rep(NA, 100), loglik [1], loglik [2] ... loglik [100]' ü önceden ayıracak kod: anlamına gelir . Bu soruyu gündeme getiriyorum çünkü orijinal kodunuzda, loglik ayrışmasını bulamadım, belki kod yapıştırma sırasında kesildi?
zhanxw

Aşağıda yayınladığım gibi: Yardımınız için teşekkürler, ancak bu konuyu bırakıyorum, çünkü bu benim için çok gelişmiş.
Stat Tistician

Verilerin hangi kısmının hangi karışıma ait olduğunu belirlemenin bir yolu var mı?
Kardinal

2

.Rar dosyanızı açmaya çalışırken bir hata almaya devam ediyorum, ancak bu sadece aptalca bir şey yapıyor olabilir.

f(y;θ)exp(-0.5(y-μ)2/σ2)μyτ

Sorun buysa, bazı olası çözümler vardır:

τ

τgünlük(f(y|θ))

değerlendirmek

günlük(f(y|θ)τ)

f(y|θ)τ0

  • 0günlük(0)=0(-bennf)=N-birN-

ama tau taşındıkça

  • günlük(00)=günlük(1)=0

R'nin değerlendirdiğini varsayarak 00=1 (Matlab kullanmaya meyilli olduğumdan emin değilim, bilmiyorum)

Başka bir çözüm, logaritma içindeki şeyleri genişletmektir. Doğal logaritma kullandığınızı varsayarsak:

τgünlük(f(y|θ))

=τgünlük(exp(-0.5(y-μ)2/σ2)/2πσ2)

=-0.5τgünlük(2πσ2)-0.5τ(y-μ)2σ2.

Matematiksel olarak aynı, ancak büyük bir negatif güç hesaplamaktan kaçındığınız için kayan nokta hatalarına daha dayanıklı olmalıdır. Bu, yerleşik norm değerlendirme işlevini artık kullanamayacağınız anlamına gelir, ancak bu bir sorun değilse, muhtemelen daha iyi cevaptır. Örneğin, diyelim ki

-0.5(y-μ)2σ2=-0.5*402=-800.

Bunu jsut'un önerdiği gibi değerlendir ve -800 elde et. Ancak, matlab'da eğer günlüğü alırsak,günlük(exp(-800))=günlük(0)=-bennf.


mh, dürüst olmak gerekirse: Bu şeyi çalıştıracak kadar iyi değilim. İlgilendiğim şey: Algoritmamla mixtools paketinin uygulanan sürümü ile aynı sonucu alabilir miyim. Ama benim açımdan, bu, ay istiyor gibi görünüyor. Ama bence cevabına çaba gösterersin, bu yüzden kabul edeceğim! Teşekkürler!
Stat Tistician
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.