如何在MLR3中使用bag和KNN方法实现缺失值插补
MLR3中实现Bag/KNN缺失值插补的解决方案
插补模型与任务类型不一致的影响
插补操作的作用对象仅为特征列,与最终任务类型(分类/回归/生存)无关联,仅需要根据待插补的特征类型选择对应模型即可:
- 插补数值型特征:使用回归类学习器
- 插补分类型特征:使用分类类学习器
你之前出现的不兼容问题,基本都是因为未限制插补算子的作用范围,误修改了任务的目标列(如你生存任务里的time、status列)导致的。
可运行的代码实现
依赖包预安装
先确认安装所有需要的包:
install.packages(c("mlr3", "mlr3proba", "mlr3pipelines", "mlr3learners", "NADIA", "VIM", "missForest", "mice", "glmnet"))
代码核心修复说明
所有插补算子增加affect_columns = selector_non_target()参数,限定仅对特征列做插补,不触碰任务目标列,避免报错。
1. 任务构造
library(mlr3) library(mlr3proba) library(mlr3pipelines) library(mlr3learners) library(NADIA) set.seed(123) # 固定随机种子保证可复现 na <- sample(1:1151, 1151*0.1) data = tsk("actg")$data() data$age[na] <- NA data$tx[na] <- NA task = TaskSurv$new("actg_na", backend = data, time = "time", event = "status")
2. 管道算子定义
preproc = po("removeconstants", ratio = 0.05) # 移除近零方差特征 # 简单基准插补 sim_impute = po("imputemedian", affect_columns = selector_type("numeric")) %>>% po("imputemode", affect_columns = selector_type("factor")) # NADIA系列插补 bag_impute = NADIA::PipeOpmissForest$new(affect_columns = selector_non_target()) knn_impute = NADIA::PipeOpVIM_kNN$new(affect_columns = selector_non_target()) mic_impute = NADIA::PipeOpMice$new(affect_columns = selector_non_target(), m = 2, maxit = 2) # 调低迭代次数加快运行 miA_impute = NADIA::PipeOpMice_A$new(affect_columns = selector_non_target()) ran_impute = po("imputesample", affect_columns = selector_non_target()) # 原生mlr3pipelines实现KNN、Bag插补示例 # KNN插补:数值列用KNN回归,分类列用KNN分类 knn_impute_native = po("imputelearner", learner = lrn("regr.kknn"), affect_columns = selector_type("numeric")) %>>% po("imputelearner", learner = lrn("classif.kknn"), affect_columns = selector_type("factor")) # Bag(随机森林)插补:数值列用回归随机森林,分类列用分类随机森林 bag_impute_native = po("imputelearner", learner = lrn("regr.ranger"), affect_columns = selector_type("numeric")) %>>% po("imputelearner", learner = lrn("classif.ranger"), affect_columns = selector_type("factor"))
3. 学习器定义
# 原生KNN插补的学习器 knn_native_learner = as_learner(preproc %>>% knn_impute_native %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test")))) # NADIA missForest(Bag)插补的学习器 bag_nadia_learner = as_learner(preproc %>>% bag_impute %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test")))) # 其他学习器按相同逻辑修改 simlearner = as_learner(preproc %>>% sim_impute %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test")))) knnlearner = as_learner(preproc %>>% knn_impute %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test")))) miclearner = as_learner(preproc %>>% mic_impute %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test")))) miAlearner = as_learner(preproc %>>% miA_impute %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test")))) ranlearner = as_learner(preproc %>>% ran_impute %>>% po("encode") %>>% po("learner", lrn("surv.glmnet", predict_sets = c("train", "test"))))
4. 训练测试
# 所有学习器均可正常训练 simlearner$train(task) baglearner$train(task) knnlearner$train(task) miclearner$train(task) miAlearner$train(task) ranlearner$train(task) # 训练完成后可正常预测 pred = simlearner$predict(task) print(pred$score())
调优失败的原因说明
你之前调优失败是因为插补时修改了目标列,导致每次插补后的目标值都有变化,模型训练的标签不稳定,调优时的评估指标完全不可信,自然会失败。修复插补的作用范围后即可正常做超参数调优。
内容的提问来源于stack exchange,提问作者Ali Alhadab
相关产品推荐
相关产品推荐

