mlr3基准测试:如何比较含不同分组预测变量的XGBoost模型?
用mlr3基准测试比较不同变量组模型的问题解决
问题描述
我的数据预测变量分两组(问卷得分、生物变量),想通过mlr3基准测试对比不同组训练的XGBoost模型,但运行代码后Friedman事后检验报错:
警告:仅可用两个学习器,返回全局检验。
错误:不是无重复的完全区组设计
原代码如下:
reg.tasks=list() reg.tasks$everything = as_task_classif(df,target='Reiteration',id='everything') reg.tasks$biological = as_task_classif(df %>% dplyr::select(any_of(biological.vars)),target='Reiteration',id='everything') future::plan(list('multisession')) at=auto_tuner(method=tnr('random_search'), learner=lts(lrn('classif.xgboost')), resampling=rsmp('cv',folds=4), measures=msr('classif.bacc'), term_time=1*60))) # just to test outer_resampling=rsmp('cv',folds=3) bm_design=data.table::data.table( task=c(reg.tasks$everything,reg.tasks$everything,reg.tasks$biological), learner=list(at,lrn('classif.featureless'),at), resampling=list(outer_reampling,outer_resampling,outer_resampling) bmr=bechnmark(bm_design,store_models=TRUE) bma=as.BenchmarkAggr(brr,measures=msr('classif.bacc')) bma$friedman_posthoc()
问题出在哪
- 模型区分逻辑错误:你想对比「全变量XGBoost、生物变量XGBoost、无特征模型」,但现在把变量组做成不同task,同时重复使用同一个
auto_tuner学习器,导致mlr3只识别出2种独立学习器(调优XGBoost、无特征),触发“仅两个学习器”的警告。 - 区组设计不完整:Friedman检验要求每个外层CV折(区组)里,所有待对比的模型都要有结果。现在
everything任务配了2个学习器,biological任务只配了1个,导致部分折缺少模型结果,不符合无重复完全区组设计要求,直接报错。 - 代码笔误:
outer_reampling(应为outer_resampling)、bechnmark(应为benchmark)、brr(应为bmr)这些拼写错误会直接导致运行失败。
怎么改
方案一:保留多task,构建完整基准设计
如果坚持用不同task区分变量组,要给每个task都搭配所有待比较的学习器,确保每个区组的结果完整:
library(mlr3) library(mlr3tuning) library(data.table) library(dplyr) # 构建不同变量组的任务,注意每个task的id要唯一 reg.tasks = list( everything = as_task_classif(df, target = 'Reiteration', id = 'all_vars'), biological = as_task_classif(df %>% select(any_of(biological.vars), Reiteration), target = 'Reiteration', id = 'bio_vars') ) # 定义基础学习器和调优器 xgboost_lrn = lrn('classif.xgboost') at = auto_tuner( method = tnr('random_search'), learner = xgboost_lrn, resampling = rsmp('cv', folds = 4), measures = msr('classif.bacc'), term_time = 1*60 ) featureless_lrn = lrn('classif.featureless') # 构建完整设计:每个任务都配所有学习器 bm_design = data.table::data.table( task = c(reg.tasks$everything, reg.tasks$everything, reg.tasks$biological, reg.tasks$biological), learner = list(at, featureless_lrn, at, featureless_lrn), resampling = list(rsmp('cv', folds=3), rsmp('cv', folds=3), rsmp('cv', folds=3), rsmp('cv', folds=3)) ) # 运行基准测试 future::plan('multisession') bmr = benchmark(bm_design, store_models = TRUE) # 聚合结果并检验 bma = as.BenchmarkAggr(bmr, measures = msr('classif.bacc')) bma$friedman() # 先跑全局检验 # 若要事后检验,建议再加一个问卷变量组的task,凑够3种以上模型
方案二:用预处理包装器统一处理变量选择(更推荐)
把变量分组转化为学习器的预处理步骤,所有模型在同一个task上运行,更符合mlr3的设计逻辑:
library(mlr3) library(mlr3tuning) library(mlr3pipelines) library(data.table) library(dplyr) # 定义变量筛选的预处理组件 filter_all = po('select', selector = selector_all()) filter_bio = po('select', selector = selector_name(biological.vars)) filter_quest = po('select', selector = selector_name(questionnaire.vars)) # 假设你有问卷变量列表 # 构建带变量筛选的XGBoost学习器,给每个学习器唯一id xgboost_all = filter_all %>>% lrn('classif.xgboost') xgboost_all$id = 'xgboost_all_vars' xgboost_bio = filter_bio %>>% lrn('classif.xgboost') xgboost_bio$id = 'xgboost_bio_vars' xgboost_quest = filter_quest %>>% lrn('classif.xgboost') xgboost_quest$id = 'xgboost_quest_vars' # 给每个带筛选的学习器加调优 at_all = auto_tuner( method = tnr('random_search'), learner = xgboost_all, resampling = rsmp('cv', folds = 4), measures = msr('classif.bacc'), term_time = 1*60 ) at_bio = auto_tuner( method = tnr('random_search'), learner = xgboost_bio, resampling = rsmp('cv', folds = 4), measures = msr('classif.bacc'), term_time = 1*60 ) at_quest = auto_tuner( method = tnr('random_search'), learner = xgboost_quest, resampling = rsmp('cv', folds = 4), measures = msr('classif.bacc'), term_time = 1*60 ) # 构建基准设计:同一个task,多个不同的学习器 task = as_task_classif(df, target = 'Reiteration', id = 'full_data') featureless_lrn = lrn('classif.featureless') bm_design = data.table::data.table( task = list(task, task, task, task), learner = list(at_all, at_bio, at_quest, featureless_lrn), resampling = list(rsmp('cv', folds=3), rsmp('cv', folds=3), rsmp('cv', folds=3), rsmp('cv', folds=3)) ) # 运行基准测试 future::plan('multisession') bmr = benchmark(bm_design, store_models = TRUE) # 聚合结果并做检验 bma = as.BenchmarkAggr(bmr, measures = msr('classif.bacc')) bma$friedman() # 全局检验 bma$friedman_posthoc() # 现在有4个模型,满足事后检验要求
关键提醒
- Friedman事后检验需要至少3个待对比的模型,所以设计里要保证有3种及以上独立的模型/学习器组合。
- 基准设计必须是完全区组设计:每个外层CV折里,所有待对比的模型都要运行一次,不能缺结果。
- 每个学习器的
id必须唯一,否则聚合结果时会被合并,导致无法区分不同模型。
内容的提问来源于stack exchange,提问作者JacquieS
相关产品推荐
相关产品推荐

