library(tidymodels)
tidymodels_prefer()
theme_set(theme_bw())35 重采样
35.1 重采样的流程
{#sec-resampling-process-and-terminology}
图 35.1 展示了一套引入重采样操作的标准数据分配方案。完成初始数据划分后,重采样可生成多组不同的训练子集:
- 分析集(analysis set)/训练集:用于估算、预处理、模型训练及后处理的子集,通常占原始数据集的 60%~80%。
- 评估集(assessment set)/验证集:仅用于模型预测、性能评估及模型选择的子集,通常占原始数据集的 20%~40%。在模型开发过程中,评估集不参与模型训练。
35.2 重采样的目标
重采样的目标是通过多次重复的训练/评估过程,旨在有限数据集上更稳健地评估模型性能。在模型开发过程中,重采样可用于:
- 模型性能的稳健评估
- 模型参数的调优
- 模型选择
小节 27.1 中,我们介绍了偏差与方差平衡的概念。重采样可用于评估模型的偏差和方差,帮助我们选择合适的模型。可以简单的理解为:
- 偏差:衡量模型的准确性,模型预测值与真实值之间的误差,偏差越大,预测越不准确。
- 方差:衡量模型的精度,模型预测值之间的离散程度,方差越大,数据的分布越分散。
各类重采样方法均基于原始训练集生成重抽样样本。下面以基础自助重采样为例,介绍核心工具与函数,再对其余重采样方法展开论述。
# load and spend data
set.seed(82)
concrete_split <- initial_split(concrete)
concrete_tr <- training(concrete_split)
concrete_te <- testing(concrete_split)
# define resampling method
set.seed(380)
concrete_rs <- rsample::bootstraps(concrete, times = 5) # 创建5个自助法重采样样本
concrete_rs# Bootstrap sampling
# A tibble: 5 × 2
splits id
<list> <chr>
1 <split [1030/355]> Bootstrap1
2 <split [1030/408]> Bootstrap2
3 <split [1030/369]> Bootstrap3
4 <split [1030/375]> Bootstrap4
5 <split [1030/378]> Bootstrap5
concete_rs 是一个 bootstraps 对象,包含了 5 个自助法重采样样本。
- 该重抽样对象除
tibble外,还包括bootstraps和rset两个类,将其与标准的tibble对象区分开来。bootstraps类表示该对象是一个自助法重采样对象,rset类表示该对象是一个重采样对象。 -
id列:每个重采样样本的唯一标识符。 -
splits列:每个重采样样本的训练集和评估集的划分信息。
35.3 验证集
- 验证集是用于模型评估和选择的独立数据集。在模型开发过程中,验证集不参与模型训练,但用于评估模型的性能,并帮助我们选择最佳的模型。
- 使用验证集的一个要点是,在我们对模型流程做出最终判定后该如何处理它。通常情况下,完整训练集会被用于最终模型拟合。
- 当训练数据集较小时,验证集的使用可能会导致训练数据不足,从而影响模型的性能。在这种情况下,我们可以使用交叉验证(小节 35.4,小节 35.5) 等方法来充分利用数据。
- 可以把验证集简单的理解为1折交叉验证。在数据量非常大的情况下,使用验证集就可以了;在数据量较小的情况下,使用交叉验证可以更好地利用数据。
# 创建验证集
concrete_split <- initial_validation_split(concrete, prop = c(0.8, 0.1)) # 80%训练集,10%验证集,10%测试集
concrete_tr <- training(concrete_split)
concrete_vl <- validation(concrete_split)
concrete_ts <- testing(concrete_split)
# 定义验证集rset
concrete_rs_val <- rsample::validation_set(concrete_split)
concrete_rs_val# A tibble: 1 × 2
splits id
<list> <chr>
1 <split [824/103]> validation
35.4 蒙特卡洛 交叉验证
图 35.2 展示了基于 30 个初始数据点生成的三个蒙特卡洛交叉验证重抽样示意图。每个重采样样本都包含了不同的训练集和评估集划分,且每个样本的训练集和评估集都是独立的。
- 在精确性层面(方差),将超过 70% 的训练集划入评估集几乎不会带来增益。
- 同时,尽管增加重采样次数总能提升效果,但当重采样次数超过 50 或 60 次时,增益幅度会逐步递减。
- 针对偏差指标,当留存训练集比例接近 80% 时,偏差的下降速度会有所放缓。因此:
- 75% 至 80% 左右的留出比例或许可作为合理经验取值。
- 50 次左右的重采样次数或许可作为合理经验取值。
- 在实际应用中,建议可根据数据集的大小和模型的复杂度进行调整。
# Monte Carlo cross-validation (0.9/0.1) with 3 resamples
# A tibble: 3 × 2
splits id
<list> <chr>
1 <split [741/83]> Resample1
2 <split [741/83]> Resample2
3 <split [741/83]> Resample3
35.5 V折交叉验证
图 35.3 展示了三次 3 折交叉验证的迭代流程。每一轮迭代选取不同子集作为评估集,剩余两部分子集合并构成训练集,所有样本仅参与一次模型评估。
研究推荐优先选取折数 \(V=10\)。10 折交叉验证在偏差、方差优化层面均具备优良表现。5 折交叉验证对偏差的改善效果有限,而增加重复次数能够进一步提升 10 折交叉验证的估计精度。
# 10-fold cross-validation repeated 2 times
# A tibble: 20 × 3
splits id id2
<list> <chr> <chr>
1 <split [741/83]> Repeat1 Fold01
2 <split [741/83]> Repeat1 Fold02
3 <split [741/83]> Repeat1 Fold03
4 <split [741/83]> Repeat1 Fold04
5 <split [742/82]> Repeat1 Fold05
6 <split [742/82]> Repeat1 Fold06
7 <split [742/82]> Repeat1 Fold07
8 <split [742/82]> Repeat1 Fold08
9 <split [742/82]> Repeat1 Fold09
10 <split [742/82]> Repeat1 Fold10
11 <split [741/83]> Repeat2 Fold01
12 <split [741/83]> Repeat2 Fold02
13 <split [741/83]> Repeat2 Fold03
14 <split [741/83]> Repeat2 Fold04
15 <split [742/82]> Repeat2 Fold05
16 <split [742/82]> Repeat2 Fold06
17 <split [742/82]> Repeat2 Fold07
18 <split [742/82]> Repeat2 Fold08
19 <split [742/82]> Repeat2 Fold09
20 <split [742/82]> Repeat2 Fold10
35.6 自助法
自助法(Bootstrap)是一种重抽样统计推断技术:从原始样本中有放回地抽取与原始样本大小相同的样本(自助样本),重复多次(如 2000 次),通过计算每个自助样本上的统计量来估计该统计量的抽样分布,从而获得标准误、置信区间、偏差等,而无需假设总体分布。图 35.4 展示了从初始 30 个数据点中生成的三个自助法重采样。
# 创建自助法重采样对象
set.seed(380)
rsample::bootstraps(concrete, times = 5, apparent = TRUE) # 创建 5 个自助法重采样样本# Bootstrap sampling with apparent sample
# A tibble: 6 × 2
splits id
<list> <chr>
1 <split [1030/355]> Bootstrap1
2 <split [1030/408]> Bootstrap2
3 <split [1030/369]> Bootstrap3
4 <split [1030/375]> Bootstrap4
5 <split [1030/378]> Bootstrap5
6 <split [1030/1030]> Apparent
-
apparent = TRUE:除自助样本外,额外附加原始全量数据作为第一个“重抽样样本”(称为 Apparent 样本),方便与真实训练集对比。
| 方法 | 原理 | 数据利用率 | 偏差 | 方差 | 计算成本 | 典型用途 |
|---|---|---|---|---|---|---|
| 简单拆分(Hold‑out) | 随机划分训练/测试集(如 80%/20%) | 低(部分数据仅用于评估) | 较高(受单次拆分影响) | 高(随机拆分波动大) | 极低 | 快速基线评估、大数据 |
| k 折交叉验证(k‑fold CV) | 将数据分为 k 组,轮流以 k‑1 组训练,1 组测试,取平均 | 中等(所有数据均被用于训练和测试,但每次模型训练时仍只使用部分) | 较低(k 较大时更接近全数据) | 中等 | 中等(k 次训练) | 模型选择、超参数调优 |
| 留一交叉验证(LOOCV) | k= n 的特殊 k 折交叉验证 | 高(每次训练用 n‑1 个样本) | 极低 | 极高(训练集高度重复,导致预测值强相关) | 极高(n 次训练,小样本时可用) | 小样本、线性模型 |
| 重复 k 折交叉验证 | 多次重复 k 折(每次随机重新划分) | 较高 | 低 | 低(平均后方差显著降低) | 较高(k×重复次数) | 追求稳定评估 |
| 自助法(Bootstrap) | 有放回抽取 n 个样本,多次(B 次) | 中等(每个自助样本约含 63.2% 独特观测) | 略乐观(训练集与原始集重叠) | 较低 | 高(B 次训练) | 模型性能分布、置信区间、小样本统计推断 |
35.7 时间序列数据
35.8 空间数据
小节 28.6 中,我们介绍了空间数据的核心特征:空间自相关性。空间数据中的观测值通常受地理位置影响,导致相邻位置的数据点更相似(如气温、污染水平等),而远距离的数据点差异较大。这种空间相关性违反了统计独立性的假设,需在数据划分时加以考虑。
与时序数据类似,空间数据类型所采用的重采样方案模拟了初始数据的划分流程。我们可以采用以下方法为训练集构建分组:
- 聚类方法:这类方法对数据进行划分,使得拥有空间坐标的数据点在同一聚类内彼此间距离,小于其与其他聚类中点的距离。
- 基于网格的方法:构建由大小均等区块组成的网格(矩形或六边形),使其覆盖所有训练集样本点。
在分组的基础上,就可以使用与之前介绍的重采样方法相同的方式进行训练集的划分。
- 留一组外重采样:该方法生成与分组数量相同的重采样样本。在每一次重采样中,某一组的数据点被用作评估集,其余分组用于训练模型。该流程针对每一组重复执行。
- V 折交叉验证:该方法将每个分组分配至 V 个元分组(即折)中的某一个。在 V 轮迭代的每一轮中,留出其中一折作为评估集,剩余折用于训练模型。
- 重复 V 折交叉验证:该方法更换随机数种子重新执行分组流程,从而实现多轮 V 折交叉验证。
library(sf) # 用于处理空间数据
library(spatialsample) # 用于空间数据的重采样
library(tidysdm) # 用于空间数据的初始划分和分组
ames_sf <- ames |>
st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326) # 将数据框转换为空间数据框
# 空间数据的初始划分
set.seed(318)
ames_block_buff_split <- tidysdm::spatial_initial_split(
ames_sf,
prop = 0.2, # 20%数据用于测试,80%数据用于训练
strategy = spatial_block_cv, # 设定策略
method = "continuous", # 连续型变量
n = 25, # 划分为25个空间块
square = FALSE, # 使用非方形块-可以让块的形状更贴合真实的地理分布
buffer = 250 # 设置训练集和测试集之间的缓冲区为250米,可以进一步降低相邻位置样本相互泄漏信息的风险
)
ames_tr <- training(ames_block_buff_split)
ames_te <- testing(ames_block_buff_split)
# 空间数据重采样与数据分割的参数基本相同
set.seed(652)
ames_rs <- spatialsample::spatial_block_cv(
ames_tr,
v = 10, # V折交叉验证
method = "continuous", # 连续型变量
n = 25, # 划分为25个空间块
square = FALSE, # 使用非方形块-可以让块的形状更贴合真实的地理分布
buffer = 250 # 设置训练集和测试集之间的缓冲区为250米,可以进一步降低相邻位置样本相互泄漏信息的风险
)
# 展示空间数据重采样对象的图形网格
autoplot(ames_rs, cex = 1, show_grid = T)
# 绘制单次划分结果,从而查看分析集与评估集的空间分布情况
autoplot(ames_rs$splits[[1]], cex = 1)
35.9 分组或多层数据
小节 28.5 中,我们介绍了分组或多层数据的核心特征:组内相关性。分组或多层数据中的观测值通常受组别影响,导致同一组别的数据点更相似,而不同组别的数据点差异较大。这种组内相关性违反了统计独立性的假设,需在数据划分时加以考虑。
假设训练集包含10000条样本,对应100名顾客,我们可以采用 V 折交叉验证确定哪些顾客划入分析集、哪些划入评估集。
和初始划分规则保持一致,属于特定顾客的所有样本行都会被划分至同一个分区(即分析集或者评估集)。
# load and spend data
data(Orthodont, package = "nlme")
set.seed(93)
orth_split <- group_initial_split(Orthodont, group = Subject, prop = 2 / 3)
orth_tr <- training(orth_split)
orth_te <- testing(orth_split)
# define resampling method
dplyr::n_distinct(orth_tr$Subject) # 计算训练集中的唯一组数[1] 18
set.seed(714)
# 核心逻辑:生成分组交叉验证计划后,检查每个折的评估集中包含多少个不同的 Subject,用于验证分组的正确性或了解数据分布。
orth_rs <-
group_vfold_cv(orth_tr, group = Subject, v = 10) |> # 创建10折交叉验证重采样样本,确保同一Subject的所有数据点都在同一数据集中
mutate(
# 对 splits 中的每一个重抽样划分对象 x,执行匿名函数 \(x) ...
num_subjects = map_int(
splits,
# assessment(x) 提取评估集(测试集合)。
\(x) dplyr::n_distinct(assessment(x)$Subject)
)
)
orth_rs# Group 10-fold cross-validation
# A tibble: 10 × 3
splits id num_subjects
<list> <chr> <int>
1 <split [64/8]> Resample01 2
2 <split [64/8]> Resample02 2
3 <split [64/8]> Resample03 2
4 <split [64/8]> Resample04 2
5 <split [64/8]> Resample05 2
6 <split [64/8]> Resample06 2
7 <split [64/8]> Resample07 2
8 <split [64/8]> Resample08 2
9 <split [68/4]> Resample09 1
10 <split [68/4]> Resample10 1
35.10 性能评估
在进行重采样时,我们通常会生成多个训练/测试分割,然后在每个分割上训练模型并评估其性能。这种做法有助于我们更全面地了解模型在不同数据子集上的表现。
tidymodels提供了一系列函数来简化性能评估的过程。我们可以使用fit_resamples()函数在每个重采样分割上训练模型,并使用collect_metrics()函数收集性能指标。
fit_resamples()函数:
- 前两个参数分别是模型对象(
model specification)和预处理器(preprocessor)。 - 其中,第一个参数可以是一个
workflow对象,包含了模型和预处理步骤。
我们使用混凝土数据,创建10折交叉验证重采样对象,并在每个分割上训练线性回归模型,最后收集性能指标。
# 创建10折交叉验证重采样对象
set.seed(426)
concrete_split <- initial_split(concrete, prop = 3 / 4)
concrete_tr <- training(concrete_split)
concrete_te <- testing(concrete_split)
concrete_rs <- vfold_cv(concrete_tr, v = 10)
# 使用简单线性回归模型进行性能评估
lm_spec <- linear_reg() |> set_engine("lm") |> set_mode("regression") # 定义线性回归模型规范
# 使用`fit_resamples()`函数在每个重采样分割上训练模型
concrete_res <- fit_resamples(
lm_spec,
compressive_strength ~ ., # 目标变量为Sale_Price,使用所有其他变量作为预测变量
resamples = concrete_rs, # 使用10折交叉验证重采样对象
# control = control_resamples(save_pred = TRUE) # 保存每个分割的预测结果
)
concrete_res# Resampling results
# 10-fold cross-validation
# A tibble: 10 × 4
splits id .metrics .notes
<list> <chr> <list> <list>
1 <split [694/78]> Fold01 <tibble [2 × 4]> <tibble [0 × 4]>
2 <split [694/78]> Fold02 <tibble [2 × 4]> <tibble [0 × 4]>
3 <split [695/77]> Fold03 <tibble [2 × 4]> <tibble [0 × 4]>
4 <split [695/77]> Fold04 <tibble [2 × 4]> <tibble [0 × 4]>
5 <split [695/77]> Fold05 <tibble [2 × 4]> <tibble [0 × 4]>
6 <split [695/77]> Fold06 <tibble [2 × 4]> <tibble [0 × 4]>
7 <split [695/77]> Fold07 <tibble [2 × 4]> <tibble [0 × 4]>
8 <split [695/77]> Fold08 <tibble [2 × 4]> <tibble [0 × 4]>
9 <split [695/77]> Fold09 <tibble [2 × 4]> <tibble [0 × 4]>
10 <split [695/77]> Fold10 <tibble [2 × 4]> <tibble [0 × 4]>
以上输出看起来很像我们的重采样对象。其中新增了一些列:
-
.metrics包含数据框,其中存储了特定重采样样本的性能统计信息。 -
.notes列则包含模型产生的任何警告或错误信息,这 10 个模型拟合均未产生此类信息。 - 值得注意的是,如果出现错误,
fit_resamples()不会停止计算。
在得到性能评估结果后,我们可以使用collect_**()系列函数收集所有重采样分割的性能指标。
-
collect_metrics():收集所有重采样分割的性能指标,并返回一个数据框,其中包含每个指标的平均值和标准误。 -
collect_note():收集所有重采样分割的警告或错误信息,并返回一个数据框,其中包含每个分割的警告或错误信息。
# 收集性能指标
concrete_metrics <- collect_metrics(concrete_res) # 总体评估结果
concrete_metrics# A tibble: 2 × 6
.metric .estimator mean n std_err .config
<chr> <chr> <dbl> <int> <dbl> <chr>
1 rmse standard 6.50 10 0.239 pre0_mod0_post0
2 rsq standard 0.852 10 0.0117 pre0_mod0_post0
concrete_metrics_by_resample <- collect_metrics(concrete_res, summarize = FALSE) # 按重采样分割收集性能指标
concrete_metrics_by_resample# A tibble: 20 × 5
id .metric .estimator .estimate .config
<chr> <chr> <chr> <dbl> <chr>
1 Fold01 rmse standard 6.76 pre0_mod0_post0
2 Fold01 rsq standard 0.846 pre0_mod0_post0
3 Fold02 rmse standard 6.93 pre0_mod0_post0
4 Fold02 rsq standard 0.804 pre0_mod0_post0
5 Fold03 rmse standard 6.65 pre0_mod0_post0
6 Fold03 rsq standard 0.830 pre0_mod0_post0
7 Fold04 rmse standard 7.17 pre0_mod0_post0
8 Fold04 rsq standard 0.863 pre0_mod0_post0
9 Fold05 rmse standard 5.48 pre0_mod0_post0
10 Fold05 rsq standard 0.916 pre0_mod0_post0
11 Fold06 rmse standard 7.14 pre0_mod0_post0
12 Fold06 rsq standard 0.857 pre0_mod0_post0
13 Fold07 rmse standard 6.45 pre0_mod0_post0
14 Fold07 rsq standard 0.867 pre0_mod0_post0
15 Fold08 rmse standard 6.38 pre0_mod0_post0
16 Fold08 rsq standard 0.819 pre0_mod0_post0
17 Fold09 rmse standard 7.17 pre0_mod0_post0
18 Fold09 rsq standard 0.814 pre0_mod0_post0
19 Fold10 rmse standard 4.92 pre0_mod0_post0
20 Fold10 rsq standard 0.901 pre0_mod0_post0
35.10.1 fit_resample()中的控制参数
35.10.1.1 并行处理
-
future:
tidymodels使用future包进行并行计算。通过设置不同的计划(plan()),可以在本地或远程计算资源上运行模型训练和评估。 - 在
windows系统中,默认的并行处理方式是multisession,也只能使用这种方法。 - 一旦我们确定了并行处理的计划,就可以在运行可并行执行的操作之前,执行启动并行计算的代码。
# future
library(future)
parallelly::availableCores() # 查看可用的CPU核心数system
12
plan(multisession, workers = 4) # 设置并行处理计划,使用4个工作进程35.10.1.2 其他选项
-
control_resamples():用于控制fit_resamples()的行为。常用参数save_pred表示是否保存每个重采样分割的预测结果,默认为FALSE。 -
fit_resamples()函数还可以接受其他参数,如metrics用于指定性能指标,可以是单一指标,也可以是多个指标集。
ctrl <- control_resamples(save_pred = TRUE) # 保存每个分割的预测结果
reg_metrics <- metric_set(rmse, rsq, mae) # 定义性能指标集,包括均方根误差(RMSE)、决定系数(R²)、Lin's Concordance Correlation Coefficient (CCC)和平均绝对误差(MAE)
concrete_opts_res <- fit_resamples(
lm_spec,
compressive_strength ~ .,
resamples = concrete_rs,
metrics = reg_metrics, # 使用自定义的性能指标集
control = ctrl # 使用自定义的控制参数
)
concrete_opts_res# Resampling results
# 10-fold cross-validation
# A tibble: 10 × 5
splits id .metrics .notes .predictions
<list> <chr> <list> <list> <list>
1 <split [694/78]> Fold01 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [78 × 4]>
2 <split [694/78]> Fold02 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [78 × 4]>
3 <split [695/77]> Fold03 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
4 <split [695/77]> Fold04 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
5 <split [695/77]> Fold05 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
6 <split [695/77]> Fold06 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
7 <split [695/77]> Fold07 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
8 <split [695/77]> Fold08 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
9 <split [695/77]> Fold09 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
10 <split [695/77]> Fold10 <tibble [3 × 4]> <tibble [0 × 4]> <tibble [77 × 4]>
# 收集性能指标
concrete_opts_metrics <- collect_metrics(concrete_opts_res)
concrete_opts_metrics# A tibble: 3 × 6
.metric .estimator mean n std_err .config
<chr> <chr> <dbl> <int> <dbl> <chr>
1 mae standard 8.22 10 0.267 pre0_mod0_post0
2 rmse standard 10.5 10 0.307 pre0_mod0_post0
3 rsq standard 0.623 10 0.0257 pre0_mod0_post0
-
concrete_opts_metrics的输出中,.metric列表示性能指标,.predictions列表示每个重采样分割的预测结果,要提取每个分割的预测结果,可以使用collect_predictions()函数或augment()函数。 - 提取预测结果后,我们就可以进行进一步分析,以了解模型还可以在哪些方面进行改进。
- probably包提供了一个简单的接口,用于获取观测值与预测值的图表(即回归“校准”图):
# collect_predictions()函数用于收集每个重采样分割的预测结果,并返回一个数据框,其中包含每个分割的预测结果和实际值。
heldout_predictions <- collect_predictions(concrete_opts_res) # 收集每个重采样分割的预测结果
heldout_predictions# A tibble: 772 × 5
.pred id compressive_strength .row .config
<dbl> <chr> <dbl> <int> <chr>
1 40.6 Fold01 40.8 8 pre0_mod0_post0
2 29.3 Fold01 29.2 14 pre0_mod0_post0
3 32.7 Fold01 37.4 27 pre0_mod0_post0
4 24.1 Fold01 20.7 38 pre0_mod0_post0
5 61.6 Fold01 55.2 53 pre0_mod0_post0
6 12.3 Fold01 9.74 71 pre0_mod0_post0
7 38.2 Fold01 59.1 83 pre0_mod0_post0
8 12.5 Fold01 15.0 119 pre0_mod0_post0
9 13.9 Fold01 14.2 123 pre0_mod0_post0
10 37.8 Fold01 29.6 125 pre0_mod0_post0
# ℹ 762 more rows
# augment()函数用于将每个重采样分割的预测结果与原始数据框进行合并,并返回一个数据框,其中包含每个分割的预测结果、实际值和其他相关信息。
augment(concrete_opts_res)# A tibble: 772 × 11
compressive_strength .pred .resid cement blast_furnace_slag fly_ash water
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 32.8 45.5 -12.7 168 42.1 164. 122.
2 53.7 54.8 -1.13 190 190 0 228
3 32.8 32.8 0.0740 160 188 146 203
4 12.6 9.69 2.86 190. 0 125. 167.
5 37.3 47.4 -10.1 219. 0 124. 158.
6 7.4 16.9 -9.53 169. 42.2 124. 158.
7 10.4 8.13 2.22 123. 184. 0 204.
8 40.8 40.6 0.205 380 95 0 228
9 45.9 36.3 9.60 214. 0 174. 155.
10 29.4 28.8 0.580 156 178 187 221
# ℹ 762 more rows
# ℹ 4 more variables: superplasticizer <dbl>, coarse_aggregate <dbl>,
# fine_aggregate <dbl>, age <int>
# probably包提供了一个简单的接口,用于获取观测值与预测值的图表(即回归“校准”图)
library(probably)
cal_plot_regression(concrete_opts_res)
上图显示,模型的结果不算太差,但存在相当大的异常值,且模型在预测较大值时表现不佳。我们可以进一步分析这些异常值,找出模型的不足之处,并尝试改进模型。
35.11 常见问题及解答
- 每次重采样得到的结果不同是否不好?
重采样的目的是观察当数据发生变化时模型流程会产生何种变动。很有可能部分关键估计值在不同重采样样本之间存在差异。例如,当采用算法筛选预测变量时,你可能会得到 B 组不同的预测变量清单,这属于正常现象。这能够让你感知到模型流程中该环节存在多大的噪声。
- 训练出的 b 个模型会怎样?应该保留哪一个?
重采样过程中构建的 B 组模型仅用于评估模型的数据拟合性能,完成评估后无需继续留存;但可保留该批模型用于模型诊断。依托多组重复模型分析模型各模块的波动特征具备较高参考价值。
- 什么是嵌套重抽样?
这是一种额外增加一重重采样的方案(小节 36.4 中有详细说明)。举例来说,假设你正在使用十折交叉验证。在十次迭代的每一轮内部,你可能额外执行二十次自助抽样迭代(该操作会对分析集重采样)。该过程最终会训练出两百个互不相同的模型。
嵌套重抽样通过两层独立的重抽样分离调参与评估:
- 外层循环(Outer loop):只用于最终评估模型性能(产生一个无偏的泛化误差估计)。
- 内层循环(Inner loop):在外层训练集内部再进行一个独立的重抽样,用于选择最优超参数(或特征、模型等),并将这个“最优配置”在外层测试集上测试。+
嵌套重抽样常用于:
- 比较多种模型类型(如随机森林 vs. 提升树 vs. 线性模型),再报告最终获胜模型的实际泛化能力。
- 特征工程包含自动化选择(如 step_* 调优),需要一次完整的“端到端”评估。
- 论文或学术环境下要求无偏估计,不满足于单一交叉验证。
对于日常的工程型调参且数据充足,使用单一交叉验证 + 最终留出测试集通常足够;嵌套重抽样的主要价值在于提供严格的无偏泛化误差估计,防止模式选择过拟合。