如何用tidymodels结合网格搜索与单验证折调优KNN模型?
大数据集下用tidymodels实现单验证折的KNN网格搜索
问题描述
我刚学习KNN算法与机器学习,正在使用R语言的tidymodels进行练习。已掌握通过10折交叉验证实现网格搜索的代码:
hist_data_split <- initial_split(hist_data, strata = fraud) hist_data_train <- training(hist_data_split) hist_data_test <- testing(hist_data_split) folds <- vfold_cv(hist_data_train, strata = fraud) nearest_neighbor_grid <- grid_regular(neighbors(range = c(1, 500)), levels = 25) knn_rec_1 <- recipe(fraud ~ ., data = hist_data_train) knn_spec_1 <- nearest_neighbor(mode = "classification", engine = "kknn", neighbors = tune(), weight_func = "rectangular") knn_wf_1 <- workflow(preprocessor = knn_rec_1, spec = knn_spec_1) knn_fit_1 <- tune_grid(knn_wf_1, resamples = folds, metrics = metric_set(accuracy, sens, spec, roc_auc), control = control_resamples(save_pred = T), grid = nearest_neighbor_grid)
但hist_data数据量达169173,最优K值约为411,10折交叉验证调参耗时过长,提示改用单验证折替代交叉验证。尝试在vfold_cv中设置v=1时,R报错“At least one row should be selected for the analysis set”,想请教如何修改代码实现单验证折的网格搜索,是否可将tune_grid的resamples参数设为1?
解决方案
1. 报错原因
vfold_cv(v=1)会将训练集全部分配给验证集,导致训练子集为空,不符合模型训练的基本要求,因此触发报错。要实现单验证折,需要将训练集拆分为训练子集和独立验证子集,并使用tidymodels支持的验证集对象传入tune_grid。
2. 代码修改方案
以下两种方法都可以实现单验证折的网格搜索:
方法一:使用initial_validation_split快速创建验证集
# 保留原始数据集拆分逻辑 hist_data_split <- initial_split(hist_data, strata = fraud) hist_data_train <- training(hist_data_split) hist_data_test <- testing(hist_data_split) # 将训练集进一步拆分为训练子集和验证子集(示例用8:2比例) val_split <- initial_validation_split(hist_data_train, strata = fraud, prop = 0.8) val_train <- training(val_split) val_test <- testing(val_split) # 创建符合tidymodels要求的验证集对象 val_set <- validation_set(val_split) # 网格搜索部分仅需替换resamples参数为val_set nearest_neighbor_grid <- grid_regular(neighbors(range = c(1, 500)), levels = 25) knn_rec_1 <- recipe(fraud ~ ., data = val_train) knn_spec_1 <- nearest_neighbor(mode = "classification", engine = "kknn", neighbors = tune(), weight_func = "rectangular") knn_wf_1 <- workflow(preprocessor = knn_rec_1, spec = knn_spec_1) knn_fit_1 <- tune_grid(knn_wf_1, resamples = val_set, metrics = metric_set(accuracy, sens, spec, roc_auc), control = control_resamples(save_pred = TRUE), grid = nearest_neighbor_grid)
方法二:手动构建验证集对象
如果需要自定义拆分逻辑,可以手动构建验证集对象:
# 原始数据集拆分 hist_data_split <- initial_split(hist_data, strata = fraud) hist_data_train <- training(hist_data_split) hist_data_test <- testing(hist_data_split) # 手动拆分训练集为训练子集和验证子集 val_split <- initial_split(hist_data_train, strata = fraud, prop = 0.8) val_train <- training(val_split) val_test <- testing(val_split) # 构建验证集对象 val_set <- tibble( splits = list(split(val_train, val_test)), id = "validation" ) # 后续网格搜索代码与方法一完全一致
3. 关键注意事项
- 不能直接将
tune_grid的resamples参数设为1,该参数需要接受交叉验证对象或验证集对象,而非数值。 - 拆分时始终保留
strata = fraud,确保训练子集和验证子集的类别分布与原始数据集一致,避免数据偏差。 - 单验证折的调参结果稳定性弱于交叉验证,确定最优K值后,建议使用整个训练集重新拟合模型,再在测试集上做最终评估。
内容的提问来源于stack exchange,提问作者Ethan Mark
相关产品推荐
相关产品推荐

