在mlr3中用glmnet实现松弛LASSO报错:找不到对象'cv'
解决mlr3中classif.glmnet开启relax选项时"object 'cv' not found"错误
问题背景
将直接使用glmnet包的松弛LASSO回归代码迁移到mlr3框架时,使用classif.cv_glmnet学习器调优lambda可正常运行,但使用classif.glmnet学习器并开启relax选项训练时,出现错误提示object 'cv' not found,关闭relax选项则训练正常。
原代码与错误栈
复现代码
library(mlr3) library(mlr3learners) library(glmnet) # 定义任务 task.sonar <- mlr_tasks$get("sonar") # 交叉验证调优lambda learner.sonar.lasso.cv <- lrn("classif.cv_glmnet", alpha = 1, relax = TRUE, gamma = 0, nfolds = 15) set.seed(1112) learner.sonar.lasso.cv$train(task.sonar) # 拟合完整模型(报错代码) learner.sonar.lasso <- lrn("classif.glmnet", alpha = 1, lambda = learner.sonar.lasso.cv$model$lambda, relax = TRUE, gamma = 0) learner.sonar.lasso$train(task.sonar) # Error in eval(call, parent.frame()) : object 'cv' not found # 关闭relax选项可正常训练 learner.sonar.lasso.2 <- lrn("classif.glmnet", lambda = learner.sonar.lasso.cv$model$lambda, alpha = 1) learner.sonar.lasso.2$train(task.sonar)
完整错误栈
Fehler in eval(call, parent.frame()) : Objekt 'cv' nicht gefunden 23: eval(call, parent.frame()) 22: eval(call, parent.frame()) 21: update.default(fit, x = x, lambda = lam0, exclude = exclude, ..., relax = FALSE) 20: update(fit, x = x, lambda = lam0, exclude = exclude, ..., relax = FALSE) 19: update(fit, x = x, lambda = lam0, exclude = exclude, ..., relax = FALSE) 18: relax.glmnet(fit, x = x, y = y, weights = weights, offset = offset, lower.limits = lower.limits, upper.limits = upper.limits, penalty.factor = penalty.factor, check.args = FALSE, ...) 17: (if (cv) glmnet::cv.glmnet else glmnet::glmnet)(x = data, y = target, alpha = 1, lambda = c(0.215936661924212, 0.196753444025244, ...), relax = TRUE, family = "binomial") 16: eval(expr, p) 15: eval(expr, p) 14: eval.parent(expr, n = 1L) 13: invoke(if (cv) glmnet::cv.glmnet else glmnet::glmnet, x = data, y = target, .args = pv) 12: glmnet_invoke(data, target, pv) 11: .__LearnerClassifGlmnet__.train(self = self, private = private, super = super, task = task) 10: get_private(learner)$.train(task) 9: .f(learner = <environment>, task = <environment>) 8: eval(expr, p) 7: eval(expr, p) 6: eval.parent(expr, n = 1L) 5: invoke(.f, .args = .args, .opts = .opts, .seed = .seed, .timeout = .timeout) 4: encapsulate(learner$encapsulate["train"], .f = train_wrapper, .args = list(learner = learner, task = task), .pkgs = learner$packages, .seed = NA_integer_, .timeout = learner$timeout["train"]) 3: learner_train(learner, task, train_row_ids = train_row_ids, test_row_ids = test_row_ids, mode = mode) 2: .__Learner__train(self = self, private = private, super = super, task = task, row_ids = row_ids) 1: learner.sonar.lasso$train(task.sonar)
解决方案
方案1:直接复用classif.cv_glmnet的松弛模型
classif.cv_glmnet在开启relax=TRUE时,已经完成了松弛模型的训练,无需再用classif.glmnet重新拟合,可直接使用:
# 从cv训练结果中提取松弛后的完整模型 relaxed_full_model <- learner.sonar.lasso.cv$model$glmnet.fit # 直接用cv学习器进行预测(自动使用最优lambda对应的松弛模型) predictions <- learner.sonar.lasso.cv$predict(task.sonar)
方案2:手动调用relax.glmnet封装模型
若必须使用classif.glmnet学习器,可先训练基础模型,再手动调用relax.glmnet获取松弛模型并赋值:
# 先训练不带relax的基础glmnet模型 base_learner <- lrn("classif.glmnet", alpha = 1, lambda = learner.sonar.lasso.cv$model$lambda) base_learner$train(task.sonar) # 提取任务的特征和目标变量 x <- as.matrix(task.sonar$data(cols = task.sonar$feature_names)) y <- task.sonar$data(cols = task.sonar$target_names)[[1]] # 手动调用relax.glmnet生成松弛模型 relaxed_fit <- relax.glmnet(base_learner$model, x = x, y = y, gamma = 0) # 将松弛模型赋值给原学习器 base_learner$model <- relaxed_fit # 此时可正常使用该学习器进行预测 predictions_relaxed <- base_learner$predict(task.sonar)
错误原因
从错误栈可知,mlr3learners的classif.glmnet学习器内部调用glmnet_invoke函数时,使用了if (cv) glmnet::cv.glmnet else glmnet::glmnet的分支逻辑,但在调用relax.glmnet时,cv变量不在当前作用域内,导致eval执行时找不到该变量,从而抛出错误。
内容的提问来源于stack exchange,提问作者KxZzvv
相关产品推荐
相关产品推荐

