使用mlr3构建MaxEnt模型学习器的错误排查与调参疑问
问题解答:mlr3构建maxnet学习器的错误与调参问题
1) 解决"only defined on a data frame with all numeric-alike variables"错误
这个错误的核心原因是maxnet要求输入的特征数据和响应变量必须是数值类型,你的代码里存在两处不符合要求的问题:
响应变量类型错误
maxnet的p参数需要接收0/1的数值向量,但你将presence转成了因子,task$data(cols = task$target_names)返回的是因子列,不是数值格式。
特征数据包含非数值类型
你的学习器声明支持factor和ordered类型特征,但maxnet无法直接处理这类非数值数据,必须转换为数值格式。
修复方案:
修改LearnerClassifMaxnet的.train和.predict函数,添加类型转换逻辑:
- 在
.train中,将响应变量转成0/1数值,将特征数据中的因子/有序因子转成数值(或哑编码,示例采用简单数值转换,可根据需求调整) - 在
.predict中,对预测数据做同样的类型转换
2) 调参代码能否正确更新?
你初始化时设置的param_set$values = list(...)是默认参数值,在调参过程中可以正确更新。
只要通过以下方式修改参数,.train和.predict函数中通过self$param_set$get_values(tags = "train")/tags = "predict"就能获取到最新的参数值:
- 手动修改:
learner_test$param_set$set_values(regmult = 2, response_type = "logistic") - 用mlr3tuning自动调参:tuner会自动更新param_set的values
注意点:
参数的tags设置正确(train标签的参数在训练阶段生效,predict标签的在预测阶段生效),你的代码中已经正确设置了tags,所以调参时参数会被正确传递到对应的阶段。
修改后的完整Learner代码
LearnerClassifMaxnet = R6Class("LearnerClassifMaxnet", inherit = LearnerClassif, public = list( initialize = function() { ## 定义学习器的参数集 param_set = ps( regmult = p_dbl(default = 1, tags = "train"), addsamplestobackground = p_lgl(default = TRUE, tags = "train"), clamp = p_lgl(default = TRUE, tags = "predict"), classes = p_fct(default = "default", levels = c("default", "l", "lq", "h", "lqh", "lqhp", "lqhpt"), tags = "train"), response_type = p_fct(default = "cloglog", levels = c("link","exponential","cloglog","logistic"), tags = "predict") ) param_set$values = list(regmult = 1, addsamplestobackground = TRUE, clamp = TRUE, classes = "default", response_type = "cloglog") ## 初始化学习器 super$initialize( id = "classif.maxnet", feature_types = c("logical", "integer", "numeric", "factor", "ordered"), predict_types = "prob", packages = c("maxnet", "dplyr"), # 添加dplyr用于类型转换 param_set = param_set, properties = c("weights", "twoclass"), label = "Maximum Entropy", man = "mlr3learners::mlr_learners_classif.maxnet" ) } ), ## 定义训练函数 private = list( .train = function(task) { pv = self$param_set$get_values(tags = "train") if ("weights" %in% task$properties) { pv$weights = task$weights$weight } # 转换响应变量为0/1数值 p = as.integer(task$data(cols = task$target_names)[[1]]) - 1 # 转换特征数据:因子/有序因子转数值,转成矩阵 data = task$data(cols = task$feature_names) %>% mutate(across(c(factor, ordered), as.numeric)) %>% as.matrix() ## 构建maxnet模型 invoke( maxnet::maxnet, p = p, data = data, f = maxnet.formula(p = p, data = data, classes = pv$classes), regmult = pv$regmult, regfun = maxnet.default.regularization, addsamplestobackground = pv$addsamplestobackground ) }, ## 定义预测函数 .predict = function(task){ pv = self$param_set$get_values(tags = "predict") ## 确保训练和预测数据列顺序一致 newdata = mlr3extralearners:::ordered_features(task, self) # 转换预测数据类型,和训练数据保持一致 newdata = newdata %>% mutate(across(c(factor, ordered), as.numeric)) %>% as.matrix() ## 计算预测值 prob = invoke(predict, object = self$model, newdata = newdata, clamp = pv$clamp, type = pv$response_type) # 整理成mlr3要求的概率格式(两列:0和1的概率) prob_matrix = cbind(1 - prob, prob) colnames(prob_matrix) = task$class_names list(prob = prob_matrix) } ) )
修改后的错误复现代码
library(mlr3) library(mlr3learners) library(paradox) library(R6) library(mlr3misc) library(maxnet) library(dplyr) # 加载dplyr用于类型转换 source("C:/R_functions/LearnerClassifMaxnet.R") data("bradypus") bradypus_data <- bradypus[, !(colnames(bradypus) %in% c("ecoreg"))] bradypus_data$presence <- as.factor(bradypus_data$presence) bradypus_task <- mlr3::as_task_classif(x = bradypus_data, target = "presence", positive = "1") learner_test = LearnerClassifMaxnet$new() learner_test$train(bradypus_task) p = learner_test$predict(bradypus_task) # 查看预测结果 print(p$prob)
内容的提问来源于stack exchange,提问作者Pierre Levoisin
相关产品推荐
相关产品推荐

