glmnet中的自动插入符号参数调整失败

wor*_*nit 8 r glmnet r-caret

上下文和错误消息

我尝试在插入符号中使用glmnet来拟合两类预测模型.使用插入符默认调整网格时出现错误.我不认为这是由于格式错误的数据,因为,当指定我自己的调整网格时,没有问题.错误消息是:

Error in loop$lambda[loop$alpha == alph[i]] <- np[which.max(np)] : 
replacement has length zero
Run Code Online (Sandbox Code Playgroud)

当检查发生错误的行时,可以看到R试图在NA which.na()的向量np上找到最大值(由caret/glmnet选择的lambda值?).我没有正确调试这个,因为我无法找到一种方法来调用后逐步执行每行代码train().我希望有经验的人可以帮助我.

最小的工作示例

我创建了一个最小的工作示例,使我的数据集尽可能小(它以约200行和~40列开始),同时保留错误.请注意,manualModelFit工作正常,但modelFit无法计算:

library(caret)
library(glmnet)
# create data frame of features
var1 <- c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1)
var2 <- c(1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1)
trainData <- data.frame(v1 = var1, v2 = var2)
# create fature vector of outcomes
trainClass <- as.factor(c('event','event','event','event','event','event','event','event','event','event','nonEvent','event','event','event','event','event','nonEvent'))
# set k for k-fold CV
kInner = 5
# set randomization seed
mySeed = 1622017
# set options for caret in fitControl
fitControl <- trainControl( method = 'cv', number = kInner, classProbs = TRUE, allowParallel = FALSE, summaryFunction = twoClassSummary, verboseIter = FALSE)
# run parameter tuning with a user-specified tuning grid
set.seed(mySeed)
myTuneGrid <- expand.grid(alpha = c(0,0.5,1), lambda = c(0,0.5,1))
manualModelFit <- train(x = trainData, y = trainClass, method = 'glmnet' , trControl = fitControl, metric = 'ROC', tuneGrid = myTuneGrid)
# run default parameter tuning
set.seed(mySeed)
modelFit <- train(x = trainData, y = trainClass, method = 'glmnet' , trControl = fitControl, metric = 'ROC')
Run Code Online (Sandbox Code Playgroud)

问题

是什么导致失败?这是Caret/glmnet中的错误还是由于我忽略了数据集的属性?我分析的多个数据集中会出现此错误.

Van*_*man 6

确实,问题出在tuneGrid。在第225行train.default有代码

tuneGrid <- models$grid(x = x, y = y, len = tuneLength, 
            search = trControl$search)
Run Code Online (Sandbox Code Playgroud)

你的例子给了我

  alpha lambda
1  0.10     NA
2  0.55     NA
3  1.00     NA
Warning messages:
1: In lognet(x, is.sparse, ix, jx, y, weights, offset, alpha, nobs,  :
  one multinomial or binomial class has fewer than 8  observations; dangerous ground
2: from glmnet Fortran code (error code -2); Convergence for 2th lambda value not reached after maxit=100000 iterations; solutions for larger lambdas returned 
Run Code Online (Sandbox Code Playgroud)

显然,NAfor for lambda会在稍后导致循环。models$grid是以下功能:

findGrid <- function (x, y, len = NULL, search = "grid") {
    if (search == "grid") {
        numLev <- if (is.character(y) | is.factor(y)) 
            length(levels(y))
        else NA
        if (!is.na(numLev)) {
            fam <- ifelse(numLev > 2, "multinomial", "binomial")
        }
        else fam <- "gaussian"
        init <- glmnet(as.matrix(x), y, family = fam, nlambda = len + 
                        2, alpha = 0.5)
        lambda <- unique(init$lambda)
        lambda <- lambda[-c(1, length(lambda))]
        lambda <- lambda[1:min(length(lambda), len)]
        out <- expand.grid(alpha = seq(0.1, 1, length = len), 
                           lambda = lambda)
    }
    else {
        out <- data.frame(alpha = runif(len, min = 0, 1), lambda = 2^runif(len, 
                                                                           min = -10, 3))
    }
    out
}
Run Code Online (Sandbox Code Playgroud)

我改名为findGrid。如果与之一起运行,findGrid(trainData, trainClass, 3)则应得到相同的警告,并返回错误的网格。在这种二进制情况下,它所做的就是:

init <- glmnet(as.matrix(x), y, family = "binomial", nlambda = len + 2, alpha = 0.5)
lambda <- unique(init$lambda) # contains one value, 
lambda <- lambda[-c(1, length(lambda))]
lambda <- lambda[1:min(length(lambda), len)]
out <- expand.grid(alpha = seq(0.1, 1, length = len), 
                   lambda = lambda)
Run Code Online (Sandbox Code Playgroud)

现在,经过lambda <- unique(init$lambda)lambda只包含一个值,该值9.9e+35。因此,以后使用索引的所有内容将不再起作用,NA而是创建。增加迭代次数glmnet并不能避免该错误。因此,让我们跳过这些行并使用获得的网格,看看是否可以解决问题。

init <- glmnet(as.matrix(x), y, family = "binomial", nlambda = len + 2, alpha = 0.5)
lambda <- unique(init$lambda) # contains one value, 
out <- expand.grid(alpha = seq(0.1, 1, length = len), lambda = lambda)
modelFit <- train(x = trainData, y = trainClass, method = 'glmnet' , trControl = fitControl, metric = 'ROC', 
                  tuneGrid = out) # <-- use the tuneGrid we made
Run Code Online (Sandbox Code Playgroud)

可以运行,但还会给我17条警告,所有形式:

Warning messages:
1: In eval(expr, envir, enclos) :
  model fit failed for Fold1: alpha=0.10, lambda=9.9e+35 Error in lognet(x, is.sparse, ix, jx, y, weights, offset, alpha, nobs,  : 
  one multinomial or binomial class has 1 or 0 observations; not allowed
Run Code Online (Sandbox Code Playgroud)

因此,您将必须找到一种制作适当网格的方法。这可以通过某种方式修复glmnet或进行一些猜测/反复试验来完成。但是,我不愿在此答案中寻找调优网格的方法,因为它很可能是特定于数据的问题。起点是查看您的完整数据集在某些类别中是否也没有观察到的数据。

另外,要自己调试它,最简单的方法是调用View(caret:::train.default)该函数。:::从隐藏的命名空间导入它。接下来,您可以将所有代码复制到一个train2函数中,并使用浏览器语句逐行调试代码(至少,这就是我所做的)。R无法找到的任何其他函数也必须加上前缀caret:::