第34课:交叉验证 - K-Fold与分层采样
学习目标
完成本课学习后,你将能够:
- 理解为什么需要交叉验证以及它如何提供更可靠的模型评估。
- 解释K-Fold交叉验证的标准流程及其在回归问题中的应用。
- 掌握使用Scikit-learn进行标准K-Fold与分层K-Fold交叉验证的Python实现。
- 分析交叉验证的结果(如平均分与标准差),并对比其与单次划分的优劣。
- 认识到在回归任务中使用分层采样的特殊考量(分位数分层)。
核心概念
为什么需要交叉验证?
在上一课中,我们学习了如何使用MSE、RMSE等指标来评估回归模型。通常的做法是将数据一次性划分为训练集和测试集(例如70/30或80/20)。但这种方法存在一个主要风险:评估结果依赖于这次随机划分。
想象一下,如果碰巧测试集中包含了几个非常极端的“难预测”样本,那么你的模型在测试集上的得分(例如R²)可能会很差,但这并不能完全代表模型的真实能力。反之亦然。这就像只通过一次考试就判断一个学生的学习水平一样,不够全面。
交叉验证(Cross-Validation) 正是为了解决这个问题而诞生的。它的核心思想是多次重复划分训练集和测试集,每次都训练一个新模型并在不同的“测试子集”上评估,最后取所有评估结果的平均值。这样得到的评估结果更稳定、更可靠。
K-Fold交叉验证
这是最常用的交叉验证方法。其步骤如下(假设K=5):
- 分割:将整个数据集随机分成5个大小相等(或近似相等)的子集,称为“折”(Fold)。
- 迭代:进行5轮实验。在每一轮中:
- 取其中1折作为验证集(Validation Set)。
- 剩余的4折合并作为训练集(Training Set)。
- 在训练集上训练模型,在验证集上评估并记录性能指标。
- 聚合:计算这5轮评估指标(如R²)的平均值和标准差。平均值作为模型性能的最终估计,标准差可以反映评估的稳定性。
最终,每个数据点都有且仅有一次被用作验证数据。
回归中的分层采样(Stratified Sampling)
在分类任务中,我们常使用分层K-Fold,确保每一折中各类别的比例与整个数据集一致。那么在回归任务中,我们的目标是预测一个连续值,如何进行“分层”呢?
一个常用的方法是:将连续的目标变量(y)离散化为几个分位数区间(如:前20%,20%-40%...),然后将这些区间视为“类别”,再执行标准的分层采样。
这样做的好处是,确保每一折中目标变量的分布(特别是高低值的比例)与完整数据集相似,避免某一折中全是容易预测的中等值,而另一折全是难预测的极端值,从而使评估更公平。
代码示例
下面的代码将演示如何使用Scikit-learn进行标准K-Fold和针对回归问题的分层K-Fold。
# 导入必要的库
import numpy as np
from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold, cross_val_score, StratifiedKFold
from sklearn.preprocessing import KBinsDiscretizer
# 1. 创建一个模拟的回归数据集
# - n_samples: 200个样本
# - n_features: 5个特征
# - noise: 添加一些噪声使问题更真实
# - random_state: 确保每次运行结果一致
X, y = make_regression(n_samples=200, n_features=5, noise=20, random_state=42)
print(f"数据集形状: X={X.shape}, y={y.shape}")
# 2. 初始化模型
model = LinearRegression()
# 3. 标准 K-Fold 交叉验证 (K=5)
print("\n=== 标准 K-Fold 交叉验证 (K=5) ===")
kf = KFold(n_splits=5, shuffle=True, random_state=42) # shuffle=True 非常重要!
# 使用cross_val_score函数简化流程,它内部自动完成训练和评估
# scoring='r2' 表示我们使用R²分数作为评估指标
scores_kf = cross_val_score(model, X, y, cv=kf, scoring='r2')
print(f"每一折的R²分数: {scores_kf}")
print(f"平均R²分数: {np.mean(scores_kf):.4f}")
print(f"分数标准差: {np.std(scores_kf):.4f}")
# 4. 针对回归的“分层”K-Fold交叉验证
print("\n=== 针对回归的分层 K-Fold 交叉验证 (K=5) ===")
# 关键步骤:将连续的y离散化。这里我们将其分成5个等频区间(分位数)
kbins = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='quantile')
# 注意:需要将y转换为二维数组
y_binned = kbins.fit_transform(y.reshape(-1, 1)).ravel()
print(f"离散化后的y(前10个样本): {y_binned[:10]}")
# 初始化分层K折交叉验证器
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 注意:cross_val_score可以直接传入分层后的y_binned作为cv的标识
scores_skf = cross_val_score(model, X, y, cv=skf.split(X, y_binned), scoring='r2')
print(f"每一折的R²分数: {scores_skf}")
print(f"平均R²分数: {np.mean(scores_skf):.4f}")
print(f"分数标准差: {np.std(scores_skf):.4f}")
# 5. 对比与单次划分的结果
print("\n=== 对比:单次划分(70/30) ===")
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model.fit(X_train, y_train)
score_single = model.score(X_test, y_test)
print(f"单次划分测试集R²分数: {score_single:.4f}")
实践练习
练习1(基础):调整K值 在上面的代码基础上,将K值分别设为3、10,并进行标准K-Fold交叉验证。观察平均R²分数和标准差的变化。你认为K值越大,评估就越可靠吗?为什么?
练习2(应用):完整工作流
加载Scikit-learn内置的diabetes数据集(一个关于糖尿病进展的回归数据集)。执行以下操作:
- 使用
StratifiedKFold(对目标值进行5分位数分层)和LinearRegression模型进行5折交叉验证。 - 打印每一折的R²分数以及最终的平均分和标准差。
- (可选)尝试另一种回归模型,如
Ridge,并比较其与LinearRegression的平均R²分数。
练习3(分析):解释标准差 假设你使用两种模型对同一数据集进行交叉验证,得到如下结果:
- 模型A:平均R² = 0.85,标准差 = 0.02
- 模型B:平均R² = 0.87,标准差 = 0.05
请简要分析这两个结果。在性能相近的情况下,你更倾向于选择哪个模型?为什么?
常见错误
- 忘记打乱数据:在创建
KFold或StratifiedKFold对象时,如果数据本身是有序的(例如按某个特征排序),而没有设置shuffle=True,会导致某一折全是“前”部分数据,另一折全是“后”部分数据,评估将不具代表性。 - 过度依赖单一指标:交叉验证返回的平均分很重要,但标准差同样关键。一个平均分稍低但标准差很小的模型,可能比一个平均分更高但波动剧烈的模型更稳定、更值得信赖。
- 错误的分层方法:对于回归问题,直接使用
StratifiedKFold会报错,因为它默认处理分类标签。必须先用KBinsDiscretizer等工具将连续值离散化,才能用于分层。 - 数据泄露:在交叉验证的循环中,任何预处理步骤(如标准化、缺失值填充)都必须仅从当前折的训练数据中学习,然后应用于当前折的训练集和验证集。如果在交叉验证之前就对全部数据进行了预处理,会导致信息从验证集泄露到训练过程,使评估结果过于乐观。应使用
Pipeline来封装预处理和模型。
小结
- 交叉验证通过多次评估取平均,提供了比单次划分更稳健、更可靠的模型性能估计。
- K-Fold是标准方法,将数据等分为K份,轮流作为验证集。关键是
shuffle=True。 - 在回归任务中,可以使用分层K-Fold(先对目标值分位数离散化),以确保各折数据分布一致,评估更公平。
- 分析交叉验证结果时,要同时关注平均分(性能水平)和标准差(性能稳定性)。
- 务必注意防止数据泄露,所有预处理步骤应在交叉验证的循环内部进行。
掌握交叉验证,你就拥有了科学评估模型、为后续调参和模型选择提供可靠依据的核心技能。下一课,我们将利用它来深入理解偏差-方差权衡。