Eu quero implementar o algoritmo EM manualmente e depois compará-lo com os resultados normalmixEM
do mixtools
pacote. Claro, eu ficaria feliz se os dois tivessem os mesmos resultados. A referência principal é Geoffrey McLachlan (2000), Modelos de Mistura Finita .
Eu tenho uma densidade de mistura de dois gaussianos, de forma geral, a probabilidade de log é dada por (McLachlan página 48):
A etapa E é agora, cálculo da expectativa condicional:
I tried to write a R code (data can be found here).
# 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
The algorithm is not working, since some observations have the likelihood of zero and the log of this is -Inf
. Where is my mistake?
fonte
Respostas:
You have several problems in the source code:
As @Pat pointed out, you should not use log(dnorm()) as this value can easily go to infinity. You should use logmvdnorm
When you use sum, be aware to remove infinite or missing values
You looping variable k is wrong, you should update loglik[k+1] but you update loglik[k]
The initial values for your method and mixtools are different. You are usingΣ in your method, but using σ for mixtools(i.e. standard deviation, from mixtools manual).
Your data do not look like a mixture of normal (check histogram I plotted at the end). And one component of the mixture has very small s.d., so I arbitrarily added a line to setτ1 and τ2 to be equal for some extreme samples. I add them just to make sure the code can work.
Eu também sugiro que você coloque códigos completos (por exemplo, como você inicializa o loglik []) no seu código-fonte e indente o código para facilitar a leitura.
Afinal, obrigado por apresentar o pacote mixtools e pretendo usá-los em minhas pesquisas futuras.
Eu também coloquei meu código de trabalho para sua referência:
Historograma
fonte
loklik <- rep(NA, 100)
que pré-alocará o loglik [1], loglik [2] ... loglik [100]. Eu levanto essa pergunta porque, no seu código original, não encontrei o delcaration do loglik, talvez o código esteja truncado durante a colagem?Eu continuo recebendo um erro ao tentar abrir o arquivo .rar, mas isso pode ser apenas eu que estou fazendo algo bobo.
Não vejo erros óbvios no seu código. Um possível motivo para você obter zeros é devido à precisão do ponto flutuante. Lembre-se, quando você calculaf( y; θ ) , você está avaliando exp( - 0,5 ( y- μ )2/ σ2) . Não é preciso uma grande diferença entreμ e y para que isso seja arredondado para 0 quando você o faz em um computador. Isso é duplamente perceptível nos modelos de mistura, pois alguns de seus dados não serão "atribuídos" a cada componente da mistura e, portanto, podem ficar muito distantes dele. Em teoria, esses pontos também devem acabar com um baixo valor deτ quando você avalia a probabilidade do log, combatendo o problema - mas, graças ao erro de ponto flutuante, a quantidade já foi avaliada como -Inf nesse estágio, para que tudo quebre :).
Se esse for o problema, existem algumas soluções possíveis:
Um é mover o seuτ dentro do logaritmo. Então, em vez de avaliar
Avalie
Matematicamente o mesmo, mas pense no que acontece quandof( y| θ ) e τ são ≈ 0 . Atualmente você obtém:
mas com tau mudou você começa
assumindo que R avalia0 00 0= 1 (Não sei se funciona ou não, pois tenho tendência a usar o matlab)
Outra solução é expandir as coisas dentro do logaritmo. Supondo que você esteja usando logaritmos naturais:
Matematicamente o mesmo, mas deve ser mais resistente a erros de ponto flutuante, pois você evitou calcular uma grande potência negativa. Isso significa que você não pode mais usar a função de avaliação de norma incorporada, mas se isso não for um problema, essa provavelmente é a melhor resposta. Por exemplo, digamos que temos a situação em que
Avalie isso como sugeri, e você receberá -800. No entanto, no matlab, se expomos o take the log, obtemosregistro( exp( - 800 ) ) = log( 0 ) = - In f .
fonte