手动执行EM算法


20

我想手动实现EM算法,然后比较它的结果normalmixEMmixtools包。当然,如果它们都能带来相同的结果,我将很高兴。主要参考文献是Geoffrey McLachlan(2000),有限混合模型

我有两个高斯混合密度,一般形式下,对数似然由(McLachlan第48页)给出:

logLc(Ψ)=i=1gj=1nzij{logπi+logfi(yi;θ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)
,我们知道(p。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函数进行处理,该示例可以手动进行验证,比如说五个或十个值和两个时间序列,首先。然后,如果您发现它可以在其中工作,请对代码进行概括并在每个步骤中进行验证。

Answers:


17

您在源代码中有几个问题:

  1. 正如@Pat指出的那样,您不应该使用log(dnorm()),因为此值很容易变为无穷大。您应该使用logmvdnorm

  2. 使用sum时,请注意删除无穷或缺失的值

  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]。我提出这个问题是因为在您的原始代码中,我没有找到loglik的替代物,也许代码在粘贴过程中被截断了?
zhanxw

正如我在下面发布的:感谢您的帮助,但是由于这个主题对我来说太高级了,因此我将其删除。
Stat Tistician

现在是否可以确定数据的哪一部分属于哪种混合?
红衣主教

2

尝试打开您的.rar文件时,我始终收到错误消息,但这可能只是我在做一些愚蠢的事情。

我发现您的代码中没有明显的错误。您得到零的可能原因是由于浮点精度。请记住,当您计算,您正在评估。和之间的差别并不大,在计算机上将其舍入为0时。这在混合模型中会倍加引起注意,因为您的某些数据不会被“分配”给每个混合组件,因此最终可能会远离它。从理论上讲,这些点还应该以的低值结束f(y;θ)exp(0.5(yμ)2/σ2)μyτ 当您评估对数似然性时,可以解决问题-但由于浮点错误,此阶段该数量已经被评估为-Inf,所以一切都破了:)。

如果这是问题所在,则有一些可能的解决方案:

一种是将移到对数内。因此,与其评估τ

τlog(f(y|θ))

评估

log(f(y|θ)τ)

在数学上是相同的,但请考虑当和为时会发生什么。目前您得到:f(y|θ)τ0

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

但是随着tau的移动,你得到

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

假设R的值为(我不知道它是否符合条件,因为我倾向于使用matlab)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

按照我的建议进行评估,您得到-800。但是,在matlab中,如果我们对记录进行得到。log(exp(800))=log(0)=Inf


嗯,说实话:我还不足以使这件事起作用。我感兴趣的是:我的算法能否获得与mixtools包的已实现版本相同的结果。但是从我的角度来看,这似乎是在向月亮求助。但是我想您会尽力而为,所以我会接受的!谢谢!
Stat Tistician
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.