EM алгоритм реализован вручную


20

Я хочу реализовать алгоритм EM вручную , а затем сравнить его с результатами normalmixEMиз mixtoolsпакета. Конечно, я был бы счастлив, если бы они оба привели к одинаковым результатам. Основное упоминание - Джеффри МакЛахлан (2000), Модели конечных смесей .

У меня плотность смеси двух гауссианов, в общем виде, логарифмическая вероятность определяется (McLachlan стр. 48):

logLc(Ψ)=i=1gj=1nzij{logπi+logfi(yi;θi)}.
zij являются1 , если наблюдение было отi - гокомпонента плотности,противном случае0 . fi плотность нормального распределения. π - пропорция смеси, поэтомуπ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)
Мшаг теперь максимизация 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. Σσ

  5. τ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

Historgram Гистограмма


@zahnxw спасибо за ваш ответ, значит ли это, что мой код неверен? Так что основная идея не работает?
Стат Тистициан

«Я также предлагаю вам вставить полные коды (например, как вы инициализируете loglik []) в свой исходный код и сделать отступ для кода, чтобы его было легко читать». Ну это мой код? loglik [] определяется так, как я объявил в коде, который я разместил?
Стат Тистициан

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

Как я писал ниже: Спасибо за вашу помощь, но я опускаю эту тему, так как она слишком сложна для меня.
Стат Тистициан

Есть ли теперь способ определить, какая часть данных принадлежит какой смеси?
кардинал

2

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

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

Если это проблема, есть несколько возможных решений:

τ

τlog(f(y|θ))

оценивать

log(f(y|θ)τ)

f(y|θ)τ0

  • 0log(0)=0(Inf)=NaN

но с тау переехал ты получаешь

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

00=1

Другое решение состоит в том, чтобы расширить содержимое внутри логарифма. Предполагая, что вы используете натуральные логарифмы:

τlog(f(y|θ))

=τlog(exp(0.5(yμ)2/σ2)/2πσ2)

=0.5τlog(2πσ2)0.5τ(yμ)2σ2

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

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

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


хм, если честно: я недостаточно хорош, чтобы заставить эту вещь работать. Меня заинтересовало следующее: могу ли я получить тот же результат с моим алгоритмом, что и реализованная версия пакета mixtools. Но с моей точки зрения это похоже на луну. Но я думаю, что вы приложили усилия к своему ответу, поэтому я приму его! Благодарность!
Стат Тициан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.