Алгоритм ЕМ реалізований вручну


20

Я хочу реалізувати алгоритм EM вручну , а потім порівняти його з результатами normalmixEMз mixtoolsпакета. Звичайно, я був би радий, якщо вони обоє призведуть до однакових результатів. Основна довідка - Джеффрі Маклахлан (2000), Моделі кінцевих сумішей .

У мене щільність суміші двох гауссів, загалом вигляд, імовірність журналу визначається (Маклаклан, сторінка 48):

logLc(Ψ)=i=1gj=1nzij{logπi+logfi(yi;θi)}.
z i j 1 i є , якщо спостереження було від - го компонента щільності, в іншому випадку . щільність нормального розподілу. являє собою суміш пропорції, так є ймовірність того , що спостереження з першим гауссова розподілу і є ймовірність того , що спостереження від другого розподілу Гаусса.zij1i0fiππ1π2

Е крок тепер, обчислення умовного очікування:

Q(Ψ;Ψ(0))=EΨ(0){logLc(|Ψ)|y}.
що призводить після декількох результатів до результату (стор. 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))
у випадку двох гауссів (стор. 82):

τi(yj;Ψ)=πiϕ(yj;μi,Σi)h=1gπhϕ(yj;μh,Σh)
Теперкрок M - це максимізація Q (стор. 49):

Q(Ψ;Ψ(k))=i=1gj=1nτi(yj;Ψ(k)){logπi+logfi(yj;θi)}.
Це призводить до (у випадку двох гауссів) (стор. 82):

μi(k+1)=j=1nτij(k)yjj=1nτij(k)Σi(k+1)=j=1nτij(k)(yjμi(k+1))(yjμi(k+1))Tj=1nτij(k)
і ми знаємо це (стор. 50)

πi(k+1)=j=1nτi(yj;Ψ(k))n(i=1,,g).
Повторюємо кроки E, M, покиL(Ψ(k+1))L(Ψ(k)) невеликий.

Я спробував написати R-код (дані можна знайти тут ).

# 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

Алгоритм не працює, оскільки деякі спостереження мають ймовірність нуля і журнал цього є -Inf. Де моя помилка?


Проблема не є статистичною, а скоріше числовою. Ви повинні додати в своєму коді випадкові випадки, менші за точність машини.
JohnRos

Чому ви не намагаєтесь дуже точно розпізнати функцію mixtools дуже простим прикладом, який можна перевірити вручну, скажімо спочатку лише п'ять-десять значень і два часові серії. тоді, якщо ви виявите, що він працює там, узагальнюйте свій код і підтверджуйте на кожному кроці.

Відповіді:


17

У вихідного коду є кілька проблем:

  1. Як зазначав @Pat, ви не повинні використовувати log (dnorm ()), оскільки це значення може легко перейти до нескінченності. Вам слід використовувати logmvdnorm

  2. Коли ви використовуєте суму , пам’ятайте про видалення нескінченних чи відсутніх значень

  3. Якщо ви цитуєте змінну k неправильно, вам слід оновити loglik [k + 1], але ви оновите loglik [k]

  4. Початкові значення для вашого методу та mixtools різні. Ви використовуєте у своєму методі, але використовуєте для mixtools (тобто стандартне відхилення від посібника з mixtools).Σσ

  5. Ваші дані не схожі на нормальну суміш (перевірте гістограму, яку я накреслив наприкінці). І один компонент суміші має дуже малий sd, тому я довільно додав рядок, щоб встановити та рівними для деяких крайніх зразків. Я додаю їх просто для того, щоб переконатися, що код може працювати.τ1τ2

Я також пропоную вам покласти цілі коди (наприклад, як ви ініціалізуєте loglik []) у свій вихідний код та відступити його, щоб полегшити його читання.

Зрештою, дякую за представлення пакету mixtools , і я планую використовувати їх у своїх майбутніх дослідженнях.

Я також ставлю свій робочий код для вашої довідки:

# 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

Історіграма Гістограма


@zahnxw дякую за вашу відповідь, чи це означає, що мій код неправильний? Так ідея basi не працює?
Stat Tistician

"Я також пропоную ввести вихідні коди (наприклад, як ініціалізувати loglik []) у вихідний код і відступити його, щоб полегшити його читання." Ну це мій код? loglik [] визначається так, як я заявив про це у коді, який я розмістив?
Stat Tistician

1
@StatTistician ідея правильна, але в реалізації є вади. Наприклад, ви не врахували недостатність. Крім того, ви циклічна змінна k плутає, ви спочатку встановлюєте loglik [1] і loglik [2], після введення циклу while ви знову встановлюєте loglik [1]. Це не природний спосіб зробити. Моя пропозиція щодо ініціалізації loglik [] означає код:, loklik <- rep(NA, 100)який попередньо виділить loglik [1], loglik [2] ... loglik [100]. Я піднімаю це питання, тому що у вашому оригінальному коді я не знайшов відміни логліка, можливо, код усікається під час вставки?
zhanxw

Як я опублікував нижче: Дякую за вашу допомогу, але я припиняю цю тему, оскільки вона для мене занадто розвинена.
Stat Tistician

Чи існує тепер спосіб визначити, яка частина даних належить до якої суміші?
Кардинал

2

Я постійно отримую помилку при спробі відкрити ваш .rar файл, але це може бути просто я роблю щось нерозумно.

f(у;θ)досвід(-0,5(у-мк)2/σ2)мкуτ

Якщо це проблема, є кілька можливих рішень:

τ

τжурнал(f(у|θ))

оцінити

журнал(f(у|θ)τ)

f(у|θ)τ0

  • 0журнал(0)=0(-Янf)=NаN

але з тау переїхав ти отримуєш

  • журнал(00)=журнал(1)=0

00=1

Ще одне рішення - розширити вміст всередині логарифму. Припустимо, що ви використовуєте природні логарифми:

τжурнал(f(у|θ))

=τжурнал(досвід(-0,5(у-мк)2/σ2)/2πσ2)

=-0,5τжурнал(2πσ2)-0,5τ(у-мк)2σ2

Математично те саме, але має бути більш стійким до помилок з плаваючою комою, оскільки ви уникали обчислення великої негативної сили. Це означає, що ви більше не можете використовувати вбудовану функцію оцінки норми, але якщо це не проблема, це, мабуть, краща відповідь. Наприклад, скажімо, у нас ситуація, де

0.5(yμ)2σ2=0.5402=800

log(exp(800))=log(0)=Inf


mh, чесно кажучи: я недостатньо хороший, щоб цю справу опрацювати. Що мене зацікавило: чи можу я отримати такий же результат за допомогою свого алгоритму, що і реалізована версія пакету mixtools. Але, з моєї точки зору, це, здається, просить місяця. Але я думаю, ви доклали зусиль у своїй відповіді, тому я прийму це! Спасибі!
Stat Tistician
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.