如何在tidymodels中为交叉验证每折单独预处理以避免数据泄露?
使用tidymodels避免交叉验证中的数据泄露:正确的预处理流水线构建
你当前的问题在于提前对全局训练数据执行了prep()和bake(),导致交叉验证的所有折都使用了全局统计量(均值、标准差)进行预处理,这会引入数据泄露。正确的做法是让每个交叉验证折的训练子集单独生成预处理规则,再应用到对应折的测试子集。
tidymodels提供了原生的工作流(workflow)机制,可以自动完成这个过程,无需手动处理每折的拆分和预处理,代码更简洁且符合最佳实践:
修正后的代码示例
library(tidyverse) library(tidymodels) set.seed(1000) # 数据准备与初始分割 mtcars_sub <- mtcars %>% select(mpg, hp) init_split <- initial_split(mtcars_sub, prop = 0.9) train_data <- training(init_split) # 1. 定义**未提前预处理**的recipe(不要调用prep()) preprocessing_recipe <- recipe(mpg ~ hp, data = train_data) %>% step_normalize(all_predictors()) # 2. 定义模型(以线性回归为例,可替换为你需要的模型) lm_model <- linear_reg() %>% set_engine("lm") %>% set_mode("regression") # 3. 创建workflow:整合预处理流程与模型 lm_workflow <- workflow() %>% add_recipe(preprocessing_recipe) %>% add_model(lm_model) # 4. 对原始训练数据做交叉验证拆分(未经过预处理) cv_folds <- vfold_cv(train_data, v = 3) # 5. 运行交叉验证:自动为每折单独预处理 cv_results <- fit_resamples( object = lm_workflow, resamples = cv_folds, metrics = metric_set(rmse, rsq) # 可选:指定评估指标 )
关键说明
- 不要提前调用
prep():让fit_resamples()在每个交叉验证折内部自动对训练子集执行prep(),这样预处理的统计量完全基于该折的训练数据,避免泄露。 - 使用workflow整合流程:workflow会自动处理预处理与模型训练的衔接,确保每折的测试子集使用对应训练子集生成的预处理规则进行转换。
- 验证预处理效果:如果你想查看某折的预处理结果,可以用以下代码:
# 提取第一折的训练子集并应用该折的预处理规则 fold1_train <- analysis(cv_folds$splits[[1]]) fold1_recipe_prepped <- prep(preprocessing_recipe, data = fold1_train) fold1_train_processed <- bake(fold1_recipe_prepped, new_data = fold1_train) # 查看处理后hp的均值和标准差(应接近0和1) fold1_train_processed %>% summarise(mean_hp = mean(hp), sd_hp = sd(hp))
为什么你的原始代码会出错?
你先对全局训练数据执行了prep()和bake(),再基于预处理后的数据做交叉验证拆分,这意味着每折的训练/测试子集已经用全局统计量缩放完毕,完全失去了交叉验证中"模拟真实数据流程"的意义——真实场景中你不可能用测试数据的统计量来预处理训练数据。
内容的提问来源于stack exchange,提问作者Richi W
相关产品推荐
相关产品推荐

