簡體   English   中英

glmnet中的自動插入符號參數調整失敗

[英]Automatic caret parameter tuning fails in 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 

顯然,對於lambda的NA ,后來會導致循環。 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 ,而是會創建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(...)步驟中嘗試的lambdas的數量,你將得到至少一些不會失敗的。 我剛剛選擇了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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM