使用mle2 / optim进行高斯混合建模

时间:2014-02-07 16:15:39

标签: r mle

我在这里开发的mle2模型只是为了证明问题。我从两个单独的高斯分布x1x2生成值,将它们组合在一起形成x=c(x1,x2),然后创建一个MLE,尝试将x值重新归类为归属通过x参数,在特定x值的左侧或特定xsplit值的右侧。

问题在于找到的参数并不理想。具体而言,始终返回xsplit,无论其起始值是什么。如果我改变其起始值(例如,4或9),则会产生对数可能性的巨大差异。

这是完全可重复的例子:

set.seed(1001)
library(bbmle)
x1 = rnorm(n=100,mean=4,sd=0.8)
x2 = rnorm(n=100,mean=12,sd=0.4)
x = c(x1,x2)
hist(x,breaks=20)
ff = function(m1,m2,sd1,sd2,xsplit) {
  outs = rep(NA,length(xvals))
  for(i in seq(1,length(xvals))) {
    if(xvals[i]<=xsplit) {
      outs[i] = dnorm(xvals[i],mean=m1,sd=sd1,log=T)
    }
    else {
      outs[i] = dnorm(xvals[i],mean=m2,sd=sd2,log=T)
    }
  }
  -sum(outs)
}

# change xsplit starting value here to 9 and 4
# and realize the difference in log likelihood
# Why isn't mle finding the right value for xsplit?
mo = mle2(ff,
          start=list(m1=1,m2=2,sd1=0.1,sd2=0.1,xsplit=9), 
          data=list(xvals=x))

#print mo to see log likelihood value
mo

#plot the result
c=coef(mo)
m1=as.numeric(c[1])
m2=as.numeric(c[2])
sd1=as.numeric(c[3])
sd2=as.numeric(c[4])
xsplit=as.numeric(c[5])
leftx = x[x<xsplit]
rightx = x[x>=xsplit]
y1=dnorm(leftx,mean=m1,sd=sd1)
y2=dnorm(rightx,mean=m2,sd=sd2)
points(leftx,y1*40,pch=20,cex=1.5,col="blue")
points(rightx,y2*90,pch=20,cex=1.5,col="red")

如何修改mle2以捕获正确的参数,特别是xsplit

1 个答案:

答案 0 :(得分:8)

混合模型存在许多技术挑战(在组件重新贴标签下的对称性等);除非您有非常具体的需求,否则最好使用为R编写的大量特殊用途混合建模包(仅library("sos"); findFn("{mixture model}")findFn("{mixture model} Gaussian"))。

然而,在这种情况下,你有一个更具体的问题,即xsplit参数的拟合优度/似然表面是“坏的”(即导数几乎无处不在)。特别是,如果您将数据集中的一对点x1x2视为邻居,则x1和{{1}之间的任何拆分参数的可能性完全相同(因为这些值中的任何一个将数据集拆分为相同的两个组件)。这意味着可能性表面是分段平坦的,这使得任何明智的优化器几乎都不可能 - 即使是那些没有明确依赖于衍生物的Nelder-Mead。您的选择是(1)使用某种强力随机优化器(例如optim()中的method =“SANN”); (2)从你的函数中取出x2并对其进行配置(即对于xsplit的每个可能选择,优化其他四个参数); (3)平滑你的分裂标准(即适合属于一个组件或另一个组件的逻辑概率); (4)使用专用混合模型拟合算法,如上所述。

xsplit

您的set.seed(1001) library(bbmle) x1 = rnorm(n=100,mean=4,sd=0.8) x2 = rnorm(n=100,mean=12,sd=0.4) x = c(x1,x2) 功能可以更紧凑地编写:

ff

我作弊了一点,只提取## ff can be written more compactly: ff2 <- function(m1,m2,sd1,sd2,xsplit) { p <- xvals<=xsplit -sum(dnorm(xvals,mean=ifelse(p,m1,m2), sd=ifelse(p,sd1,sd2),log=TRUE)) } ## ML estimation mo <- mle2(ff2, start=list(m1=1,m2=2,sd1=0.1,sd2=0.1,xsplit=9), data=list(xvals=x)) ## refit with a different starting value for xsplit mo2 <- update(mo,start=list(m1=1,m2=2,sd1=0.1,sd2=0.1,xsplit=4)) ## not used here, but maybe handy plotfun <- function(mo,xvals=x,sizes=c(40,90)) { c <- coef(mo) hist(xvals,col="gray") p <- xvals <= c["xsplit"] y <- with(as.list(coef(mo)), dnorm(xvals,mean=ifelse(p,m1,m2), sd=ifelse(p,sd1,sd2))*sizes[ifelse(p,1,2)]) points(xvals,y,pch=20,cex=1.5,col=c("blue","red")[ifelse(p,1,2)]) } plot(slice(mo),ylim=c(-0.5,10)) plot(slice(mo2),ylim=c(-0.5,10)) 参数:

xsplit附近的似然面:

xsplit=9

xsplit=9附近的似然面:

xsplit=4

另见p. 243 of Bolker 2008

更新:平滑

正如我上面提到的,一种解决方案是使两种混合物组分之间的边界平滑,或渐变,而不是尖锐。我使用了一个逻辑函数xsplit=4,其中点位于plogis(),一个刻度任意设置为2(你可以尝试使其更清晰;原则上你可以使它成为一个可调参数,但如果你这样做了你可能会再次遇到麻烦,因为优化器可能想让它变得无限......)换句话说,而是说xsplit的所有观察结果都是在组件1中的在{2}中x<xsplit的观察结果肯定是,我们说等于x>xsplit的观察值在任一组件中都有50/50的概率下降,并且确定性为在组件1中,xsplit增加到x以下。具有非常大的缩放参数的逻辑函数近似于先前尝试的锐分模型;通常,您希望使缩放参数“足够大”以获得合理的分割,并且小到足以避免遇到数字问题。 (如果你使比例太大,计算出的概率将下溢/溢出到0或1,你将回到你开始的地方......)

这是我的第二次或第三次尝试;我不得不做大量的摆弄(边界值远离0,或介于0和1之间,并在对数刻度上拟合标准偏差),但结果似乎合理。如果我没有在逻辑(xsplit)函数上使用clamp(),那么我得到0或1个概率;如果我不在正常概率上使用plogis(单侧),那么它们可以下溢到零 - 在任何一种情况下我都会获得无限或clamp()结果。在对数刻度上拟合标准偏差可以更好地工作,因为当优化程序为标准偏差尝试负值时,不会遇到问题......

NaN

结果看起来很合理。