2014-02-07 2 views
5

У меня есть модель mle2, которую я здесь разработал, чтобы продемонстрировать проблему. Я генерирую значения из двух отдельных гауссовских распределений x1 и x2, объединяя их вместе, чтобы сформировать x=c(x1,x2), а затем создайте MLE, который пытается повторно классифицировать значения x как принадлежащие левому полю конкретного значения x или права конкретного значения x через пареметр xsplit.Моделирование гауссовой смеси с mle2/optim

Проблема в том, что найденные параметры не идеальны. В частности, 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

для чего это стоит, это проблема оптимизации, а не проблема 'mle2'; 'mle2' просто обертывает функцию' optim'. Модели смешивания **, как известно, трудно подстраиваются - для них разработано множество алгоритмов оптимизации специального назначения. –

+0

, если mle2 обертывает функцию оптимизации, тогда я не понимаю, почему она объясняет, как это происходит, потому что под капотом это делает оптимизм. – CodeGuy

+0

Было бы проще начать с использования 'nls' для соответствия функции сортировки' a1 * exp (-x^2/b1) + a2 * exp (-x^2/b2) ', а затем классифицировать данные по относительные амплитуды этих двух гауссианов? (Конечно, это не сработает, когда критерий Рэлея не очень хорошо встречен) –

ответ

8

Модели смеси содержат множество технических проблем (симметрия при повторной маркировке компонентов и т. Д.); если у вас нет особых потребностей, вам может быть лучше использовать одно из большого количества пакетов моделирования специального назначения, которые были написаны для R (только library("sos"); findFn("{mixture model}") или findFn("{mixture model} Gaussian")).

Однако в этом случае у вас есть более конкретная проблема, заключающаяся в том, что поверхность хорошего качества/правдоподобия параметра xsplit является «плохим» (т. Е. Производная равна нулю почти всюду). В частности, если вы считаете пару точек x1, x2 в вашем наборе данных, которые являются соседями, вероятность одинакова для любого параметра разделения между x1 и x2 (поскольку любое из этих значений разбивает набор данных на те же самые два компонента). Это означает, что поверхность правдоподобия кусочно плоская, что делает практически невозможным любой разумный оптимизатор - даже такие, как Nelder-Mead, которые явно не зависят от производных. Ваш выбор: (1) использовать какой-то стохастический оптимизатор грубой силы (например, метод = «SANN» в optim()); (2) возьмите xsplit из вашей функции и профиля над ней (т. Е. Для каждого возможного выбора xsplit, оптимизируйте остальные четыре параметра); (3) сгладить ваш критерий расщепления (т. Е. Соответствовать логистической вероятности принадлежности к одному компоненту или другому); (4) использовать алгоритм подгонки модели специального назначения, как было рекомендовано выше.

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:

xsplit=4

Также см p. 243 of Bolker 2008.

Обновление: сглаживание

Как уже говорилось выше, одно решение, чтобы сделать границу между двумя компонентами смеси гладкими или постепенным, а не резким. Я использовал логистическую функцию plogis() с серединой в xsplit и шкалу, произвольно установленную на 2 (вы могли бы попытаться сделать ее более резкой, в принципе вы могли бы сделать ее регулируемым параметром, но если вы это сделаете, вы, вероятно, столкнетесь с проблемой снова, потому что оптимизатор может захотеть сделать его бесконечным ...) Иными словами, скорее, говоря, что все наблюдения с x<xsplit являются определенно в компоненте 1, и все наблюдения с x>xsplit являются определенно в компоненте 2, мы говорим, что наблюдения, которые являются равные xsplit, имеют 50/50 вероятность падения в любом компоненте с уверенностью в том, что в компоненте 1 увеличивается, так как x уменьшается ниже xsplit. Логистическая функция с очень большим параметром масштабирования аппроксимирует ранее опробованную модель резкого раскола; обычно вы хотите сделать параметр масштабирования «достаточно большим», чтобы получить разумный раскол и достаточно маленький, чтобы не сталкиваться с числовыми проблемами. (Если вы сделаете масштаб слишком большим, расчетные вероятности будут переполнены/переполнены до 0 или 1, и вы вернетесь туда, где вы начали ...)

Это моя вторая или третья попытка; Мне приходилось делать значительную попытку (ограничивающие значения от 0 или от 0 до 1 и установку стандартных отклонений в масштабе шкалы), но результаты кажутся разумными. Если я не использую clamp() в функции логистики (plogis), тогда я получаю 0 или 1 вероятности; если я не использую clamp() (односторонний) по нормальным вероятностям, то они могут быть переполнены до нуля - в любом случае я получаю бесконечный результат или NaN результатов. Установка стандартных отклонений по логарифмической шкале работает лучше, потому что один не столкнуться с проблемами, когда оптимизатор пытается отрицательные значения для стандартного отклонения ...

## bound x values between lwr and upr 
clamp <- function(x,lwr=0.001,upr=0.999) { 
    pmin(upr,pmax(lwr,x)) 
} 

ff3 <- function(m1,m2,logsd1,logsd2,xsplit) { 
    p <- clamp(plogis(2*(xvals-xsplit))) 
    -sum(log((1-p)*clamp(dnorm(xvals,m1,exp(logsd1)),upr=Inf)+ 
        p*clamp(dnorm(xvals,m2,exp(logsd2)),upr=Inf))) 
} 
xvals <- x 
ff3(1,2,0.1,0.1,4)         
mo3 <- mle2(ff3, 
      start=list(m1=1,m2=2,logsd1=-1,logsd2=-1,xsplit=4), 
      data=list(xvals=x)) 
## Coefficients: 
##   m1   m2  logsd1  logsd2  xsplit 
## 3.99915532 12.00242510 -0.09344953 -1.13971551 8.43767997 

Результаты выглядят разумными.

+0

Спасибо за этот ответ. Кажется, я начинаю понимать. Вы упомянули один из вариантов (3), чтобы сгладить критерий фитинга. Я не знаю, как это сделать, и не понимаю, что вы имеете в виду. Не могли бы вы реализовать это в этом примере? – CodeGuy

+0

Не могли бы вы немного прокомментировать этот код? Например, я никогда не слышал о функции pmax или pmin и просто пытался понять, что делает ваша функция «зажима»? Какова была идея логистической функции? – CodeGuy

+0

Кроме того, зачем использовать logSD вместо SD? – CodeGuy

Смежные вопросы