Algoritma EM diimplementasikan secara manual

20

Saya ingin menerapkan algoritma EM manual dan kemudian membandingkannya dengan hasil normalmixEMdari mixtoolspaket. Tentu saja, saya akan senang jika keduanya menghasilkan hasil yang sama. Referensi utama adalah Geoffrey McLachlan (2000), Finite Mixture Models .

Saya memiliki kerapatan campuran dari dua Gaussians, dalam bentuk umum, log-likelihood diberikan oleh (McLachlan halaman 48):

logLc(Ψ)=i=1gj=1nzij{logπi+logfi(yi;θi)}.
z i j 1 i The adalah , jika pengamatan itu dari th kepadatan komponen, jika tidak . The adalah densitas distribusi normal. The adalah proporsi campuran, sehingga adalah probabilitas, bahwa observasi adalah dari distribusi Gaussian pertama dan adalah probabilitas, bahwa observasi adalah dari distribusi Gaussian kedua.zij1i0fiππ1π2

Langkah E sekarang, perhitungan ekspektasi bersyarat:

Q(Ψ;Ψ(0))=EΨ(0){logLc(|Ψ)|y}.
yang mengarah, setelah beberapa derivasi ke hasil (halaman 49):

τsaya(yj;Ψ(k))=πsaya(k)fsaya(yj;θsaya(k)f(yj;Ψ(k)=πsaya(k)fsaya(yj;θsaya(k)h=1gπh(k)fh(yj;θh(k))
dalam kasus dua Gaussians (halaman 82):

τsaya(yj;Ψ)=πsayaϕ(yj;μsaya,Σsaya)h=1gπhϕ(yj;μh,Σh)
Langkah M sekarang adalah maksimalisasi Q (halaman 49):

Q(Ψ;Ψ(k))=saya=1gj=1nτsaya(yj;Ψ(k)){logπsaya+logfsaya(yj;θsaya)}.
Ini mengarah ke (dalam kasus dua orang Gauss) (halaman 82):

μsaya(k+1)=j=1nτsayaj(k)yjj=1nτsayaj(k)Σsaya(k+1)=j=1nτsayaj(k)(yj-μsaya(k+1))(yj-μsaya(k+1))Tj=1nτsayaj(k)
dan kami tahu itu (hlm. 50)

πsaya(k+1)=j=1nτsaya(yj;Ψ(k))n(saya=1,...,g).
Kami mengulangi langkah-langkah E, M hingga kecil. L.(Ψ(k+1))-L.(Ψ(k))

Saya mencoba menulis kode R (data dapat ditemukan di sini ).

# 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 tidak berfungsi, karena beberapa pengamatan memiliki kemungkinan nol dan log ini -Inf. Di mana kesalahan saya?

Stat Tistician
sumber
Masalahnya bukan masalah statistik, melainkan masalah numerik. Anda harus menambahkan kemungkinan untuk kemungkinan lebih kecil dari presisi mesin dalam kode Anda.
JohnRos
mengapa Anda tidak mencoba mencari-cari fungsi mixtools dengan contoh yang sangat sederhana yang dapat diverifikasi dengan tangan, katakan saja lima atau sepuluh nilai dan dua kali pengaturan, pertama. kemudian, jika Anda berhasil di sana, generalisasi kode Anda dan verifikasi di setiap langkah.

Jawaban:

17

Anda memiliki beberapa masalah dalam kode sumber:

  1. Seperti yang ditunjukkan oleh @Pat, Anda seharusnya tidak menggunakan log (dnorm ()) karena nilai ini dapat dengan mudah menuju infinity. Anda harus menggunakan logmvdnorm

  2. Saat Anda menggunakan jumlah , berhati-hatilah untuk menghapus nilai tak terbatas atau hilang

  3. Anda mengulang variabel k salah, Anda harus memperbarui loglik [k + 1] tetapi Anda memperbarui loglik [k]

  4. Nilai awal untuk metode dan mixtools Anda berbeda. Anda menggunakan dalam metode Anda, tetapi menggunakan untuk mixtools (yaitu standar deviasi, dari manual mixtools).σΣσ

  5. Data Anda tidak terlihat seperti campuran normal (lihat histogram yang saya rencanakan di bagian akhir). Dan satu komponen campuran memiliki sd yang sangat kecil, jadi saya sewenang-wenang menambahkan baris untuk mengatur dan agar sama untuk beberapa sampel ekstrim. Saya menambahkannya hanya untuk memastikan kode dapat bekerja.τ 2τ1τ2

Saya juga menyarankan Anda memasukkan kode lengkap (misalnya bagaimana Anda menginisialisasi loglik []) dalam kode sumber Anda dan membuat indentasi kode untuk membuatnya mudah dibaca.

Setelah semua, terima kasih telah memperkenalkan paket mixtools , dan saya berencana untuk menggunakannya dalam penelitian masa depan saya.

Saya juga menaruh kode kerja saya untuk referensi Anda:

# 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

zhanxw
sumber
@zahnxw terima kasih atas jawaban Anda, jadi apakah itu berarti, bahwa kode saya salah? Jadi ide basi tidak berfungsi?
Stat Tistician
"Saya juga menyarankan Anda memasukkan kode lengkap (misalnya bagaimana Anda menginisialisasi loglik []) dalam kode sumber Anda dan inden kode untuk membuatnya mudah dibaca." Nah ini kodenya? loglik [] didefinisikan seperti yang saya nyatakan dalam kode yang saya posting?
Stat Tistician
1
@StatTistician idenya benar, tetapi implementasinya memang memiliki kekurangan. Misalnya, Anda tidak mempertimbangkan aliran bawah. Selain itu, Anda mengulang variabel k membingungkan, Anda pertama-tama mengatur loglik [1] dan loglik [2], setelah memasukkan loop sementara, Anda mengatur loglik [1] lagi. Ini bukan cara alami untuk melakukannya. Saran saya tentang menginisialisasi loglik [] berarti kode:, loklik <- rep(NA, 100)yang akan mengalokasikan loglik [1], loglik [2] ... loglik [100]. Saya mengajukan pertanyaan itu karena dalam kode asli Anda, saya tidak menemukan penghapusan loglik, mungkin kode tersebut terpotong saat menempel?
zhanxw
Seperti yang saya posting di bawah ini: Terima kasih atas bantuan Anda, tetapi saya menjatuhkan topik ini, karena terlalu maju untuk saya.
Stat Tistician
Apakah sekarang ada cara untuk menentukan bagian mana dari data yang termasuk campuran mana?
Kardinal
2

Saya terus mendapatkan kesalahan ketika mencoba membuka file .rar Anda, tapi itu mungkin saya melakukan sesuatu yang konyol.

Saya tidak dapat melihat kesalahan yang jelas dalam kode Anda. Kemungkinan alasan Anda mendapatkan nol adalah karena presisi titik apung. Ingat, ketika Anda menghitung , Anda mengevaluasi . Tidak perlu perbedaan yang sangat besar antara dan untuk ini dibulatkan menjadi 0 ketika Anda melakukannya di komputer. Ini terlihat dua kali lipat dalam model campuran, karena beberapa data Anda tidak akan "ditugaskan" untuk masing-masing komponen campuran sehingga dapat berakhir sangat jauh darinya. Secara teori, poin-poin ini juga harus berakhir dengan nilaiexp ( - 0,5 ( y - μ ) 2 / σ 2 ) μ y τf(y;θ)exp(-0,5(y-μ)2/σ2)μyτ ketika Anda mengevaluasi kemungkinan log, menangkal masalah - tetapi berkat kesalahan floating point, kuantitasnya telah dievaluasi sebagai -Jika pada tahap ini, jadi semuanya rusak :).

Jika itu masalahnya, ada beberapa solusi yang mungkin:

Pertama adalah memindahkan ke dalam logaritma. Jadi alih-alih mengevaluasiτ

τlog(f(y|θ))

evaluasi

log(f(y|θ)τ) .

Secara matematis sama, tetapi pikirkan tentang apa yang terjadi ketika dan adalah . Saat ini Anda mendapatkan:f(y|θ)0τ0

  • 0log(0)=0(-sayanf)=NSebuahN

tetapi dengan tau pindah Anda dapatkan

  • log(00)=log(1)=0

dengan asumsi R mengevaluasi (Saya tidak tahu apakah itu benar atau tidak karena saya cenderung menggunakan matlab)00=1

Solusi lain adalah untuk memperluas hal-hal di dalam logaritma. Dengan asumsi Anda menggunakan logaritma natural:

τlog(f(y|θ))

=τlog(exp(-0,5(y-μ)2/σ2)/2πσ2)

=-0,5τlog(2πσ2)-0,5τ(y-μ)2σ2 .

Secara matematis sama, tetapi harus lebih tahan terhadap kesalahan floating point karena Anda telah menghindari menghitung kekuatan negatif yang besar. Ini berarti Anda tidak dapat lagi menggunakan fungsi evaluasi norma bawaan, tetapi jika itu bukan masalah, ini mungkin jawaban yang lebih baik. Misalnya, katakanlah kita memiliki situasi di mana

-0,5(y-μ)2σ2=-0,5402=-800 .

Evaluasi itu seperti yang saya sarankan, dan Anda mendapatkan -800. Namun, dalam matlab jika kita mengekspansi pengambilan log, kita mendapatkan .log(exp(-800))=log(0)=-sayanf

Menepuk
sumber
mh, jujur ​​saja: saya tidak cukup baik untuk membuat hal ini bekerja. Yang saya tertarik adalah: Dapatkah saya mendapatkan hasil yang sama dengan algoritma saya sebagai versi yang diimplementasikan dari paket mixtools. Tapi dari sudut pandang saya, ini sepertinya meminta bulan. Tapi saya pikir Anda berusaha keras untuk jawaban Anda, jadi saya akan menerimanya! Terima kasih!
Stat Tistician