交叉验证与模型选择
交叉验证(Cross-Validation, CV)是机器学习里”如何诚实评估模型”的核心方法,也是超参调优、模型选择的标准工具。本页系统讲解 K 折、分层 K 折、留一法、时序 CV、分组 CV 等变体,嵌套 CV 的数学原理,以及网格/随机/贝叶斯搜索的正确用法与对比。前置阅读:模型评估与指标、监督学习。
把交叉验证想象成多次模拟考试取平均分:只用一次期末考试(单一训练/测试划分)来评判学生水平,可能碰巧考了他复习过的题型。多次考试、每次换题、最后取平均——这样估出来的水平才可靠。
- 训练/验证/测试三件套:训练集 = 平时作业(学习用);验证集 = 模拟考试(调超参、选模型用);测试集 = 期末考试(只在最后用一次,评估泛化)。
- K 折 CV = 把模拟考卷切成 K 份轮流当考题:数据分 K 份,每次拿 1 份当验证、其余 K-1 份训练,循环 K 次取平均——每条样本都被验证过一次,评估最稳。
- 分层 K 折 = 每份都保持类别比例:不平衡数据用普通 K 折可能某份里一个正样本都没有,分层保证每份比例一致。
- 时序 CV = 永远用过去预测未来:时间序列不能随机打乱,必须严格用前面的数据训练、后面的数据验证,否则就是”用未来信息预测过去”(数据泄漏)。
为什么单一划分不可靠
Section titled “为什么单一划分不可靠”把数据随机切成一份训练集、一份测试集,评估结果取决于这次随机切分——数据少时方差很大。一个极端例子:只有 20 条样本,换一种切法,准确率可能从 70% 跳到 90%。交叉验证的本质是降低评估方差:让每条数据都参与验证,取多次结果的平均。
从概率角度理解:设模型在数据集 D 上的真实泛化误差为 ,单次 holdout 得到的估计为 。由于 holdout 只用了一个子集做测试, 是一个随机变量——其方差取决于测试集大小 :
测试集越小,方差越大。K 折 CV 通过重复 K 次评估取平均,等效于把”有效测试集大小”从 扩大到 (每条样本恰好被测试一次),从而显著降低估计方差。
K 折交叉验证
Section titled “K 折交叉验证”设数据集 含 个样本。K 折步骤:
- 把 尽量均等地划分成 个互不相交的子集 ,每个子集约 个样本。
- 对 从 1 到 :用 作为验证集,其余 份合并 作为训练集,训练模型 并在 上评估,得到分数 。
- 最终分数为 K 个分数的平均,并附标准差衡量稳定性:
典型取 或 。 越大训练成本越高(每次训练用更多数据),但评估方差越小; 时即”留一法”(Leave-One-Out)。
K 的选择:偏差-方差权衡
Section titled “K 的选择:偏差-方差权衡”K 折 CV 的估计误差也受偏差-方差权衡影响:
- 训练集大小效应(影响偏差): 越大,每折训练集 越接近全集 ,训练出的模型越接近最终模型,偏差越小。 时训练集只有一半数据,偏差最大。
- 折间相关性效应(影响方差): 越大,各折训练集重叠越多( 份数据是共享的),模型 之间高度相关,取平均降低方差的效果递减。
Kohavi (1996) 的经典实验表明: 在多数场景下偏差和方差的平衡最好; 是精度与效率的实用折中。
留一法的数学特殊性
Section titled “留一法的数学特殊性”当 (留一法),每折训练集大小为 ,偏差几乎为零。但 LOOCV 有一个微妙问题:各折训练集之间有 个样本重叠,模型之间高度相关。对于稳定模型(如线性回归),LOOCV 的方差甚至比 更大。
对于线性回归有一个优雅的封闭解(“留一残差”公式),无需真正训练 次:
其中 是帽子矩阵(hat matrix) 的第 个对角元素,也叫杠杆值(leverage)。杠杆值高的样本对模型影响大,留一误差也大——这与直觉一致。
- 分层 K 折(Stratified K-Fold):每个折里类别比例与全集保持一致。例如全集有 10% 正样本,则每折也保持约 10% 正样本。分类任务(尤其类别不平衡)必用,否则某折可能完全没有正样本。
- 留一法(LOOCV): 的特例,每次只留 1 条验证。评估方差最小但计算量最大,且每次训练集几乎相同导致方差估计有偏,一般不推荐。
- 重复 K 折(RepeatedKFold):把 K 折 CV 重复 次(每次重新随机分折),共 次评估。在不增加单次训练成本的前提下进一步降低方差——对于小数据集特别有效。
- 分组 K 折(GroupKFold):同一”组”(如同一患者、同一会话)必须同时在训练集或验证集,防止信息泄漏,医疗/用户数据必备。
- 时序 CV(TimeSeriesSplit):第 1 折用前 份训练、第 份验证;第 2 折用前 份训练、第 份验证……严格递增,绝不回头,适用于一切有时间顺序的数据。
- 留 P 组(LeavePGroupsOut):留出若干整组做验证,组内相关性强时使用。
用交叉验证做超参搜索
Section titled “用交叉验证做超参搜索”选定评估指标后,遍历超参组合,每组用交叉验证取平均分,选最优组合。三种搜索策略对比:
网格搜索(GridSearchCV)
Section titled “网格搜索(GridSearchCV)”穷举所有超参组合。例如 3 个超参各 5 个值就是 组,乘以 5 折 CV 即 625 次训练。网格搜索的缺点是组合爆炸,且浪费资源在不重要的超参维度上——如果只有 1 个关键超参,网格搜索会搜索 4 个无关维度的所有组合。
随机搜索(RandomizedSearchCV)
Section titled “随机搜索(RandomizedSearchCV)”从参数空间随机采 组。Bergstra & Bengio (2012) 的经典论文证明:在相同试验次数下,随机搜索往往不逊于甚至优于网格搜索。关键洞察是**“有效维度”通常远低于参数空间的维度**——只有少数超参真正重要,随机采样更容易在这些关键轴上探索不同值。
设超参空间维度为 ,网格搜索在每维取 个值,每个维度只采样 个不同值(因为预算被分摊到所有维度组合)。随机搜索每个维度都能取到 个不同值( 是试验次数),关键维度的覆盖率高得多。
贝叶斯优化(Optuna / Hyperopt)
Section titled “贝叶斯优化(Optuna / Hyperopt)”贝叶斯优化的核心思想:用上一个试验的结果指导下一个采样,越搜越聪明。它维护一个代理模型(surrogate model)来近似超参到性能的映射,然后用采集函数(acquisition function)决定下一个采样点。
贝叶斯优化的数学框架:
- 代理模型:用高斯过程(GP)或树结构 Parzen 估计器(TPE)来建模 ,其中 是超参向量。
- 采集函数:平衡”探索”(exploration,去不确定区域采样)与”利用”(exploitation,在已知好区域细化)。常用的 Expected Improvement:
其中 是当前最优值。EI 高的点就是”既可能好、又不确定”的点。
- 迭代:每次选 EI 最大的点试验 → 更新代理模型 → 重新计算 EI → 选下一个点……
Optuna 使用的是 TPE(Tree-structured Parzen Estimator),它不直接建模 ,而是用贝叶斯定理建模 :
TPE 把 拆成两部分:(“好”的参数分布,)和 (“差”的参数分布,)。最大化 的比值就是下一个采样点——即”在好区域密度高、在差区域密度低”的地方。
三种搜索策略对比
Section titled “三种搜索策略对比”| 特性 | 网格搜索 | 随机搜索 | 贝叶斯优化 |
|---|---|---|---|
| 试验效率 | 低(穷举所有组合) | 中(随机覆盖关键维度) | 高(历史指导采样) |
| 并行性 | 极好(所有组合独立) | 极好(所有采样独立) | 差(需要历史结果指导下一步,串行性强) |
| 连续/条件参数 | 不支持(必须离散化) | 支持 | 原生支持 |
| 适用场景 | 参数少、维度低 | 参数中等、需要快速并行 | 参数多、训练昂贵、预算紧张 |
| 过拟合风险 | 中(依赖网格设计) | 中 | 高(试验次数多时可能过拟合 CV) |
嵌套交叉验证(Nested CV)
Section titled “嵌套交叉验证(Nested CV)”为什么需要嵌套 CV
Section titled “为什么需要嵌套 CV”当超参搜索本身也用 CV 时,用同一份数据既选超参又评估会”偷看”验证集,导致选择偏差(selection bias)——评估偏乐观。原因:超参搜索本质上是在验证集上”优化”了,验证分数自然偏高。
数学上,设超参搜索在 CV 上的最优分数为 ,真实泛化性能为 。由于 是多个候选中的最大值,它是一个向上偏的估计:
候选超参组合越多(网格越密、搜索次数越多),乐观偏差越大——这叫多重比较问题(multiple comparisons problem)。
嵌套 CV 的结构
Section titled “嵌套 CV 的结构”嵌套 CV 用两层循环解决:
- 外层(outer loop): 折,用于泛化评估。每折的验证集是”最终未见数据”的模拟。
- 内层(inner loop):在每折的训练集上,再用 折做超参搜索,选出最优超参。
- 外层评估:用内层选出的超参在训练集上训练,在外层验证集上评估。
总训练次数 = 。例如 次训练——计算量是单层 CV 的约 倍。
嵌套 CV 给出的是什么
Section titled “嵌套 CV 给出的是什么”嵌套 CV 评估的是**“包含超参搜索的整个流程”的泛化性能**——而非某个固定超参的模型性能。这是一个重要区别:
- 如果你只想知道”在给定超参下这个模型有多好”→ 用单层 CV。
- 如果你想知道”这个模型 + 自动调参流程,在新数据上有多好”→ 用嵌套 CV。
实践中,嵌套 CV 用于研究报告和模型选型对比(诚实评估),单层 CV 用于最终模型的超参确定(选出超参后在全集重训)。
CV 估计的置信区间
Section titled “CV 估计的置信区间”K 折 CV 的 个分数 本身可以用来构造置信区间。但由于各折训练集高度重叠, 之间不独立,直接用 -分布构造 CI 会偏窄(低估不确定性)。
改进方法——重复 K 折(Repeated K-Fold):将 K 折 CV 重复 次,得到 个分数,它们的分布更接近真实分布,CI 更可靠。Nadeau & Bengio (2003) 提出了考虑折间相关性的修正方差估计:
其中 是折分数的样本方差, 修正项反映了折间重叠导致的额外相关性。
数据泄漏的几个陷阱
Section titled “数据泄漏的几个陷阱”- 先归一化/填充再划分:预处理(标准化、缺失值填充、特征选择)用到了全数据的统计量,验证集信息泄漏到训练。正确做法是把预处理放进 Pipeline,由 CV 在每折内部 fit_transform。
- 时序数据随机划分:用未来的数据训练去预测过去,是时间序列里最常见的错误。
- 同组样本跨划分:同一患者的多次记录分到训练和验证两边,模型”记住”了这个人,评估虚高。
- 重复样本跨划分:同一图像做不同增强后的副本分散到训练和验证集,等效于”看过答案”。
5 折交叉验证
Section titled “5 折交叉验证”时序 CV vs 普通 K 折
Section titled “时序 CV vs 普通 K 折”单层 CV vs 嵌套 CV 对比
Section titled “单层 CV vs 嵌套 CV 对比”分层 K 折 + Pipeline 防泄漏
Section titled “分层 K 折 + Pipeline 防泄漏”from sklearn.datasets import make_classificationfrom sklearn.model_selection import cross_val_score, StratifiedKFoldfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.preprocessing import StandardScalerfrom sklearn.pipeline import make_pipeline
X, y = make_classification(n_samples=1000, weights=[0.1], random_state=42)
# 关键:把标准化放进 Pipeline,CV 每折内部各自 fit,杜绝泄漏pipe = make_pipeline(StandardScaler(), RandomForestClassifier(random_state=42))cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)scores = cross_val_score(pipe, X, y, cv=cv, scoring="f1")print(f"5 折 F1: {scores.mean():.3f} ± {scores.std():.3f}")Optuna 贝叶斯优化超参
Section titled “Optuna 贝叶斯优化超参”import optunafrom sklearn.model_selection import cross_val_scorefrom sklearn.ensemble import GradientBoostingClassifier
def objective(trial): params = dict( n_estimators=trial.suggest_int("n_estimators", 50, 300), max_depth=trial.suggest_int("max_depth", 2, 8), learning_rate=trial.suggest_float("learning_rate", 0.01, 0.3, log=True), ) clf = GradientBoostingClassifier(**params, random_state=42) return cross_val_score(clf, X, y, cv=5, scoring="f1").mean()
study = optuna.create_study(direction="maximize")study.optimize(objective, n_trials=30)print(f"最优 F1: {study.best_value:.3f}, 超参: {study.best_params}")嵌套交叉验证完整示例
Section titled “嵌套交叉验证完整示例”from sklearn.datasets import make_classificationfrom sklearn.model_selection import GridSearchCV, cross_val_score, StratifiedKFoldfrom sklearn.ensemble import RandomForestClassifierimport numpy as np
X, y = make_classification(n_samples=500, n_features=20, random_state=42)
# 内层:超参搜索(3 折网格搜索)inner_cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)param_grid = { "max_depth": [3, 5, 7, None], "min_samples_leaf": [1, 5, 10],}clf = RandomForestClassifier(n_estimators=50, random_state=42)grid = GridSearchCV(clf, param_grid, cv=inner_cv, scoring="f1", n_jobs=-1)
# 外层:泛化评估(5 折)outer_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)nested_scores = cross_val_score(grid, X, y, cv=outer_cv, scoring="f1")
print(f"嵌套 CV F1: {nested_scores.mean():.3f} ± {nested_scores.std():.3f}")# 对比:非嵌套(同一份数据做搜索和评估,分数会偏乐观)grid.fit(X, y)print(f"非嵌套(乐观偏差)F1: {grid.best_score_:.3f}")运行后你会看到嵌套 CV 分数低于非嵌套分数——这个差值就是选择偏差的大小,超参组合越多差值越大。
重复 K 折构造更可靠的置信区间
Section titled “重复 K 折构造更可靠的置信区间”from sklearn.model_selection import RepeatedStratifiedKFoldfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.pipeline import make_pipelinefrom sklearn.preprocessing import StandardScalerimport numpy as np
X, y = make_classification(n_samples=300, n_features=15, random_state=42)
# 5 折重复 10 次 = 50 次评估,CI 更可靠rskf = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=42)pipe = make_pipeline(StandardScaler(), RandomForestClassifier(random_state=42))scores = cross_val_score(pipe, X, y, cv=rskf, scoring="f1")
# 95% 置信区间(近似正态)mean, std = scores.mean(), scores.std()ci_low, ci_high = mean - 1.96 * std, mean + 1.96 * stdprint(f"F1 = {mean:.3f}, 95% CI = [{ci_low:.3f}, {ci_high:.3f}]")HalvingGridSearchCV:资源递增搜索
Section titled “HalvingGridSearchCV:资源递增搜索”scikit-learn 1.0+ 提供了逐步淘汰(successive halving)策略:先用少量资源评估所有候选,淘汰一半差的,再用更多资源评估剩下的,以此类推。总训练次数远少于完整 GridSearchCV。
from sklearn.experimental import enable_halving_search_cv # noqafrom sklearn.model_selection import HalvingGridSearchCVfrom sklearn.ensemble import RandomForestClassifier
param_grid = {"max_depth": [3, 5, 7, 10, None], "min_samples_leaf": [1, 5, 10, 20]}# 资源从少到多,逐步淘汰search = HalvingGridSearchCV( RandomForestClassifier(random_state=42), param_grid, factor=3, cv=5, scoring="f1", random_state=42, n_jobs=-1)search.fit(X, y)print(f"最优参数: {search.best_params_}, F1: {search.best_score_:.3f}")- 分类默认用 StratifiedKFold,回归用 KFold;不平衡数据绝不裸用 KFold。
- 时序数据一律用 TimeSeriesSplit,绝不能随机打乱,否则评估完全失真。
- 有组结构的用 GroupKFold:同一患者/同一用户/同一会话必须整体留在同一侧。
- 预处理永远放进 Pipeline,由 CV 在每折内 fit,避免数据泄漏。
- K 取 5 或 10 最常用。数据少时可加大 K 或用 RepeatedKFold,但 LOOCV 一般不划算;评估要报均值加标准差。
- 模型最终选定的超参,要在全集上重训一次,再用独立测试集评估——验证集只是用来选超参,测试集只碰一次。
- 嵌套 CV 用于”诚实地评估模型本身”,单层 CV 用于最终选参——别混用。
- 超参搜索预算有限时,贝叶斯优化(Optuna)优于随机搜索优于网格搜索,但搜索空间要先调研好范围。
- 贝叶斯优化试验次数过多时注意过拟合 CV:搜索 1000+ 组超参后,CV 分数本身就开始过拟合——可设置较少试验次数或用嵌套 CV 评估。
- 报告模型性能时附上置信区间:仅报平均分不够,用 RepeatedKFold 构造 CI 才能判断差异是否显著。
- Kaggle 竞赛标准流程:所有竞赛选手都用 5 折/10 折 OOF(out-of-fold)预测做模型融合与评估,单一 holdout 划分在竞赛中被认为不可靠。
- 超参自动调优平台:Optuna、Ray Tune、Weights & Biases Sweeps 内部都基于交叉验证 + 贝叶斯优化,是 AutoML 的核心组件。
- 医疗模型稳健性评估:临床试验中用 GroupKFold 按患者分组,确保”模型对患者泛化”而非”对单次检查记忆”,是医疗 AI 通过审查的前提。
- 金融时序模型回测:用 TimeSeriesSplit(滚动窗口或扩展窗口)做回测,避免未来函数,是量化策略评估的硬性规范。
- AutoML 产品:Google AutoML、H2O DriverlessAI、Azure AutoML 内部大量使用嵌套 CV 做模型选择,详见 自动化机器学习与可解释性。
- LLM 超参搜索:大语言模型微调的超参搜索(learning rate、batch size、LoRA rank 等)同样适用 CV 思路,但因单次训练成本极高,通常用 Optuna + 早停(median pruner)来节约算力。
2025-2026 年最新进展
Section titled “2025-2026 年最新进展”- Optuna v4.x(2024-2025):引入了多目标优化(multi-objective optimization)支持——可同时优化准确率和推理延迟,用 Pareto 前沿替代单一最优解;改进的
QMOMF(Quasi-Monte Carlo 采样)在冷启动阶段比纯随机采样更快找到好区域。 - scikit-learn 1.5-1.7(2024-2025):
HalvingRandomSearchCV稳定化;新增FixedThresholdClassifier/TunedThresholdClassifier可与 CV 结合做阈值调优——解决了分类阈值(默认 0.5)不一定是 F1/Recall 最优的问题。 - ENABLE 超参搜索框架:Meta(Facebook)开源的分布式超参搜索框架,内置 ASHA(Asynchronous Successive Halving)算法,专为大规模分布式训练设计,比 Optuna 更适合 GPU 集群上的深度学习调参。
- 微分贝叶斯优化(Differentiable Bayesian Optimization, DBO):2024-2025 年的研究热点——利用自动微分让贝叶斯优化的采集函数可微,在深度学习的超参(如网络结构参数)搜索中效率显著提升。
- CV 与 conformal prediction 的结合:2025 年越来越多的工作将交叉验证与保形预测(conformal prediction)结合,为 CV 评估提供有限样本保证的预测集(prediction set),而非仅一个点估计——在医疗和安全关键领域尤为重要。
- 时序 CV 的 PurgedKFold:金融机器学习(Marcos López de Prado 体系)推广了”清洗 + embargo”的时序 CV 方法,在训练/验证折之间留出隔离带,彻底消除标签重叠带来的泄漏——已成为量化金融的标准实践。
典型类库与工具
Section titled “典型类库与工具”| 类库 | 语言 | 说明 |
|---|---|---|
| scikit-learn | Python | KFold、StratifiedKFold、GroupKFold、TimeSeriesSplit、RepeatedKFold、cross_val_score、GridSearchCV、RandomizedSearchCV、HalvingGridSearchCV |
| Optuna | Python | 高效的贝叶斯超参优化框架(TPE + 多目标),支持剪枝、可视化、分布式搜索 |
| Hyperopt | Python | 基于 TPE 的贝叶斯优化库,早期主流方案 |
| Ray Tune | Python | 分布式超参搜索,支持 ASHA、Population-Based Training 等高级调度策略 |
| Weights & Biases Sweeps | Python | 团队协作的实验跟踪 + 超参搜索一体化平台 |
| ENABLE / Ax | Python | Meta 开源的贝叶斯优化平台,支持高维参数空间与多目标优化 |
| 术语 | 英文 | 解释 |
|---|---|---|
| 交叉验证 | Cross-Validation | 将数据分成多份轮流做训练/验证,取多次评估平均以降低方差的方法 |
| K 折交叉验证 | K-Fold CV | 将数据等分 K 份,每次用 1 份验证其余训练,循环 K 次 |
| 分层 K 折 | Stratified K-Fold | 每个折保持与全集一致的类别比例,分类任务标配 |
| 留一法 | Leave-One-Out (LOOCV) | K 等于样本数的特例,每次只留 1 条做验证 |
| 时序交叉验证 | TimeSeriesSplit | 严格按时间顺序,用过去训练验证未来,用于时序数据 |
| 分组交叉验证 | GroupKFold | 保证同一组样本不跨划分,防止组内信息泄漏 |
| 嵌套交叉验证 | Nested CV | 外层评估泛化、内层选超参的两层 CV,无偏评估的黄金标准 |
| 选择偏差 | Selection Bias | 在验证集上选超参导致评估偏乐观的现象 |
| 数据泄漏 | Data Leakage | 验证/测试集信息间接进入训练,导致评估过于乐观 |
| 网格搜索 | Grid Search | 穷举所有超参组合,全面但组合爆炸 |
| 贝叶斯优化 | Bayesian Optimization | 用历史试验结果指导下一组采样,越搜越聪明的超参优化方法 |
| 代理模型 | Surrogate Model | 贝叶斯优化中用于近似超参到性能映射的轻量模型(GP 或 TPE) |
| 采集函数 | Acquisition Function | 贝叶斯优化中决定下一个采样点的函数(如 Expected Improvement) |
| 逐步淘汰 | Successive Halving | 先用少资源评估全部候选、逐步淘汰差的,用多资源精评少数候选的搜索策略 |
| 保形预测 | Conformal Prediction | 提供有限样本保证的预测区间方法,可与 CV 结合 |
- Kohavi, “A Study of Cross-Validation and Bootstrap for Accuracy Estimation and Model Selection” (IJCAI, 1996):经典实验研究,建议 10 折 CV 优于留一法,是 K=5/10 经验值的来源。
- Bergstra & Bengio, “Random Search for Hyper-Parameter Optimization” (JMLR, 2012):证明随机搜索在相同预算下常优于网格搜索,且更易并行,超参搜索的范式转变。
- Cawley & Talbot, “On Over-fitting in Model Selection and Subsequent Selection Bias in Performance Evaluation” (JMLR, 2010):论证嵌套 CV 才是无偏评估,揭示单层 CV 的乐观偏差。
- Akiba et al., “Optuna: A Next-generation Hyperparameter Optimization Framework” (KDD, 2019):Optuna 官方论文,提出 TPE 与剪枝的高效实现,工业界超参优化首选。
- Snoek et al., “Practical Bayesian Optimization of Machine Learning Hyperparameters” (NeurIPS, 2012):贝叶斯优化用于深度学习超参的经典工作,是 Spearmint/Skopt 的基础。
- Nadeau & Bengio, “Inference for the Generalization Error” (Machine Learning, 2003):提出考虑折间相关性的 CV 置信区间修正方法,是 CV 统计推断的理论基础。
- López de Prado, “Advances in Financial Machine Learning” (2018):第 7 章系统讲解 PurgedKFold 与 embargo 技巧,金融时序 CV 的权威参考。
- Jamieson & Talwalkar, “Non-stochastic Best Arm Identification and Hyperparameter Optimization” (AISTATS, 2016): successive halving 的理论基础,是 HalvingGridSearchCV 的算法来源。