默认的lme4优化器需要对高维数据进行大量迭代


12

TL; DR:lme4优化似乎是线性在默认情况下,模型参数数量,并且是这样慢比等效的glm与组虚拟变量模型。我有什么可以加快速度的吗?


我正在尝试适应一个相当大的分层logit模型(约5万行,100列,50组)。将正常的logit模型拟合到数据(带有用于组的虚拟变量)可以很好地工作,但是层次模型似乎被卡住了:第一个优化阶段可以很好地完成,但是第二个阶段需要进行很多迭代,而无需进行任何更改并且不停止。

编辑:我怀疑问题主要是我有这么多的参数,因为当我尝试将其设置maxfn为较低的值时会给出警告:

Warning message:
In commonArgs(par, fn, control, environment()) :
  maxfun < 10 * length(par)^2 is not recommended.

但是,参数估计在优化过程中完全没有改变,因此我仍然对执行该操作感到困惑。当我尝试设置maxfn优化器控件(尽管有警告)时,它似乎在完成优化后挂起。

这是一些重现随机数据问题的代码:

library(lme4)

set.seed(1)

SIZE <- 50000
NGRP <- 50
NCOL <- 100

test.case <- data.frame(i=1:SIZE)
test.case[["grouping"]] <- sample(NGRP, size=SIZE, replace=TRUE, prob=1/(1:NGRP))
test.case[["y"]] <- sample(c(0, 1), size=SIZE, replace=TRUE, prob=c(0.05, 0.95))

test.formula = y ~ (1 | grouping)

for (i in 1:NCOL) {
    colname <- paste("col", i, sep="")
    test.case[[colname]] <- runif(SIZE)
    test.formula <- update.formula(test.formula, as.formula(paste(". ~ . +", colname)))
}

print(test.formula)

test.model <- glmer(test.formula, data=test.case, family='binomial', verbose=TRUE)

输出:

start par. =  1 fn =  19900.78 
At return
eval:  15 fn:      19769.402 par:  0.00000
(NM) 20: f = 19769.4 at           0     <other numbers>
(NM) 40: f = 19769.4 at           0     <other numbers>

我尝试将其设置ncol为其他值,但似乎完成的迭代次数是每列40次(大约)。显然,随着我添加更多列,这变得非常痛苦。我是否可以对优化算法进行调整,以减少对列数的依赖?


1
了解您要拟合的特定模型(特别是随机效应结构)会很有帮助。
帕特里克·福舍尔

不幸的是,精确模型是专有的。有一个级别的随机效应,组的大小在〜100到5000之间。请告知我是否可以提供有关模型的任何其他相关信息。
本·库恩

好的,我添加了一些重现此问题的代码。
本·库恩

1
我没有完整的答案,所以我将其留为评论。以我的经验,glmer速度相当慢,尤其是对于具有复杂随机效应结构(例如,许多随机斜率,交叉随机效应等)的模型而言。我的第一个建议是尝试使用简化的随机效果结构。但是,如果仅使用随机截距模型遇到此问题,则问题可能仅是案例数,在这种情况下,您需要尝试一些专门用于大数据的工具。
Patrick S. Forscher

它有2个组而不是50个组的相同问题。另外,使用较少的列数进行测试,似乎迭代次数在列数中大致是线性的……是否有优化方法在这里会更好?
本·库恩

Answers:


12

您可以尝试的一件事是更改优化器。请参阅此github问题上的 Ben Bolker的评论。Bobyqa的nlopt实现通常比默认实现快得多(至少在我尝试的时候)。

library(nloptr)
defaultControl <- list(algorithm="NLOPT_LN_BOBYQA",xtol_rel=1e-6,maxeval=1e5)
nloptwrap2 <- function(fn,par,lower,upper,control=list(),...) {
    for (n in names(defaultControl)) 
      if (is.null(control[[n]])) control[[n]] <- defaultControl[[n]]
    res <- nloptr(x0=par,eval_f=fn,lb=lower,ub=upper,opts=control,...)
    with(res,list(par=solution,
                  fval=objective,
                  feval=iterations,
                  conv=if (status>0) 0 else status,
                  message=message))
}

system.time(test.model <- glmer(test.formula, data=test.case, 
family='binomial', verbose=TRUE))

system.time(test.model2 <- update(test.model,
control=glmerControl(optimizer="nloptwrap2"))

另外,请参阅此答案以获取更多选项以及R-sig-mixed-models中的该线程(看起来与您的问题更相关)。

编辑: 我给了您一些有关的过时信息nloptr。在lme4 1.1-7和时,nloptr会自动导入(见?nloptwrap)。您要做的就是添加

control = [g]lmerControl(optimizer = "nloptwrap") # +g if fitting with glmer

给你打电话。


谢谢!我正在尝试nlopt代码。我确实想知道是否存在除优化器实现错误之外的其他事情,因为拟合几乎等效的虚拟化glm的速度如此之快,但我会看到...
Ben Kuhn

好吧,它肯定更快,但是由于出现错误而停止了PIRLS step-halvings failed to reduce deviance in pwrssUpdate。您知道这里可能会发生什么吗?错误消息并不完全透明...
Ben Kuhn

对于踢球,您可以尝试将nAGQ设置为0(有关更多信息,请参阅我链接的线程)。我不记得是什么原因导致了PIRLS错误,但我将环顾四周。
alexforrence

非常感谢!您能否指出我一个资源,在这里我可以详细了解这些方法的详细信息,以便将来自己解决此类问题?目前,优化感觉对我来说就像是黑魔法。
Ben Kuhn

2
在默认的bobyqa(运行时间约15秒)中,nAGQ = 0对我的测试示例有效,而在bobyqa的情况下,nAGQ = 0则对我nloptr有用。这是 John C. Nash(optimand optimx软件包的共同作者)的采访,他对优化进行了高级解释。如果您查找optimxnloptr使用CRAN,它们各自的参考手册将为您提供有关语法的更多信息。nloptr还提供了一个小插图,进一步介绍了细节。
alexforrence
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.