代码之家  ›  专栏  ›  技术社区  ›  user3245256

r: 对于相同的λ,glmnet和caret的系数不同

  •  0
  • user3245256  · 技术社区  · 8 年前

    我读了一些问答;关于这一点,但我仍然不太明白,为什么基于相同样本和相同超参数的glmnet和caret模型的系数略有不同。非常感谢您的解释!

    我正在使用插入符号训练脊线回归:

    library(ISLR)
    Hitters = na.omit(Hitters)
    x = model.matrix(Salary ~ ., Hitters)[, -1] #Dropping the intercept column.
    y = Hitters$Salary
    
    set.seed(0)
    train = sample(1:nrow(x), 7*nrow(x)/10)
    
    library(caret)
    set.seed(0)
    train_control = trainControl(method = 'cv', number = 10)
    grid = 10 ^ seq(5, -2, length = 100)
    tune.grid = expand.grid(lambda = grid, alpha = 0)
    ridge.caret = train(x[train, ], y[train],
                        method = 'glmnet',
                        trControl = train_control,
                        tuneGrid = tune.grid)
    ridge.caret$bestTune
    # alpha is 0 and best lambda is 242.0128
    

    现在,我使用上面找到的lambda(和alpha)来训练整个数据集的岭回归。最后,我提取系数:

    ridge_full <- train(x, y,
                        method = 'glmnet',
                        trControl = trainControl(method = 'none'), 
                        tuneGrid = expand.grid(
                          lambda = ridge.caret$bestTune$lambda, alpha = 0)
                        )
    coef(ridge_full$finalModel, s = ridge.caret$bestTune$lambda)
    

    最后,使用完全相同的alpha和lambda,我尝试使用glmnet包拟合相同的岭回归,并提取系数:

    library(glmnet)
    ridge_full2 = glmnet(x, y, alpha = 0, lambda = ridge.caret$bestTune$lambda)
    coef(ridge_full2)
    
    1 回复  |  直到 8 年前
        1
  •  2
  •   missuse    8 年前

    原因是插入符号未使用您指定的确切lambda。您可以通过以下方式进行检查:

    ridge_full$finalModel$lambda
    

    最接近的值为261.28915和238.07694。

    当你这样做的时候

    coef(ridge_full$finalModel, s = ridge.caret$bestTune$lambda)
    

    其中s为242.0128,根据实际计算的系数对系数进行插值。

    其中,当您向glmnet调用提供lambda时,模型将返回该lambda的精确系数,该系数与插入符号返回的系数略有不同。

    发生这种情况的原因:

    在所有数据上指定一个alpha和一个lambda进行拟合时,插入符号将实际拟合:

       fit = function(x, y, wts, param, lev, last, classProbs, ...) {
                        numLev <- if(is.character(y) | is.factor(y)) length(levels(y)) else NA
    
                        theDots <- list(...)
    
                        if(all(names(theDots) != "family")) {
                          if(!is.na(numLev)) {
                            fam <- ifelse(numLev > 2, "multinomial", "binomial")
                          } else fam <- "gaussian"
                          theDots$family <- fam
                        }
    
                        ## pass in any model weights
                        if(!is.null(wts)) theDots$weights <- wts
    
                        if(!(class(x)[1] %in% c("matrix", "sparseMatrix")))
                          x <- Matrix::as.matrix(x)
    
                        modelArgs <- c(list(x = x,
                                            y = y,
                                            alpha = param$alpha),
                                       theDots)
    
                        out <- do.call(glmnet::glmnet, modelArgs)
                        if(!is.na(param$lambda[1])) out$lambdaOpt <- param$lambda[1]
                        out
                      }
    

    这是从 here .

    在您的示例中,这转化为

    fit <- glmnet::glmnet(x, y,
                           alpha = 0)
    
    lambda <- unique(fit$lambda)
    

    这些λ值对应于 ridge_full$finalModel$lambda :

    all.equal(lambda, ridge_full$finalModel$lambda)
    #output
    TRUE