glmnet中的自动脱字符参数调整失败
上下文和错误消息
我尝试使用glmnet在插入符号中拟合一个两级预测模型。 使用插入符号默认调节网格时会发生错误。 我不认为这是由于格式错误的数据造成的,因为在指定我自己的调谐网格时,没有问题。 错误消息是:
Error in loop$lambda[loop$alpha == alph[i]] <- np[which.max(np)] :
replacement has length zero
在检查发生错误的行时,可以看到R尝试在NA的向量np
(由caret / glmnet选择的lambda值?)上找到最大值which.na()
)。 我没有正确调试这个,因为我找不到在调用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')
问题
什么导致失败? 这是否是caret / glmnet中的错误,还是由于我忽略的数据集的属性? 此错误发生在我分析的多个数据集中。
确实,问题出在tuneGrid
。 在train.default
225 train.default
有代码
tuneGrid <- models$grid(x = x, y = y, len = tuneLength,
search = trControl$search)
这是你的例子给我的
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
显然NA
的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
}
我将其重命名为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)
现在,在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
哪个运行,但也给我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
所以你将不得不找到一个方法来创建一个合适的网格。 这可以通过固定glmnet
或通过一些猜测/试验和错误来完成。 然而,我在这个答案中寻找调谐网格的方法犹豫不决,因为它可能是一个特定于数据的问题。 一个起点是查看您的完整数据集在某些类别中是否也有少量观察值。
此外,为了自己调试,最简单的方式是调用View(caret:::train.default)
来查看函数。 :::
从隐藏的命名空间导入它。 接下来,您可以将所有代码复制到train2
函数中,并使用浏览器语句逐行调试代码(至少,这就是我所做的)。 R找不到的其他函数也必须以caret:::
为前缀。
我遇到了同样的问题,我想我会分享我的解决方案。 正如@Vandenman所说,你需要一种制作适当网格的方式。 这对我有效。 基本上,如果你在init <- glmnet(...)
步骤中尝试增加lambda表达式的数量,至少会得到一些不失败的结果。 我刚刚选择了52(我敢打赌这个号码对你有用,但你可以随时改变它,计算时间对我来说可以忽略不计)。 然后你选择len
其中超过那些没有失败间隔均匀。
my_glmnet <- getModelInfo("glmnet") %>% magrittr::extract2("glmnet")
my_glmnet$grid <- 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 = 52, alpha = 0.5)
lambda <- unique(init$lambda)
lambda <- lambda[-c(1, length(lambda))]
l_seq <- seq(1, length(lambda), length = len) %>% round %>% unique
lambda <- lambda[l_seq]
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
}
然后你可以用method = my_glmnet
运行train
。
上一篇: Automatic caret parameter tuning fails in glmnet
下一篇: level Unicode GUI Native apps in C++ for Windows/Linux/Mac