我在这里开发的mle2
模型只是为了证明问题。我从两个单独的高斯分布x1
和x2
生成值,将它们组合在一起形成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
?
答案 0 :(得分:8)
混合模型存在许多技术挑战(在组件重新贴标签下的对称性等);除非您有非常具体的需求,否则最好使用为R编写的大量特殊用途混合建模包(仅library("sos"); findFn("{mixture model}")
或findFn("{mixture model} Gaussian")
)。
然而,在这种情况下,你有一个更具体的问题,即xsplit
参数的拟合优度/似然表面是“坏的”(即导数几乎无处不在)。特别是,如果您将数据集中的一对点x1
,x2
视为邻居,则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=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
结果看起来很合理。