交叉验证为什么会泄漏?把缺失填补放进 Pipeline 后逐折评估

交叉验证中的泄漏常发生在划分之前:先用整张表计算缺失值的填补中位数,再把已经填好的数据送入交叉验证,验证折的信息已参与预处理。把 SimpleImputer 和分类器装进同一条 Pipeline,逐折拟合并核对中位数,才能检查这一步的边界。

交叉验证中的泄漏常发生在划分之前:先用整张表计算缺失值的填补中位数,再把已经填好的数据送入交叉验证,验证折的信息已参与预处理。把 SimpleImputer 和分类器装进同一条 Pipeline,逐折拟合并核对中位数,才能检查这一步的边界。

准备环境与教学数据

以下代码已在 Windows 的 Python 3.11 独立环境运行。文中数据是人为构造或库自带的教学样本,不代表任何业务系统的效果;例子不调用在线大模型。

交叉验证为什么会泄漏?把缺失填补放进 Pipeline 后逐折评估

在空目录安装依赖,把完整代码保存为 demo.py,运行 python demo.py。使用现有 Python 环境时,先核对依赖版本。

python -m pip install numpy==2.4.6 scikit-learn==1.9.1

先定义每折的训练边界

下面人为生成90条二分类样本,部分数值故意设为空。StratifiedKFold 分成3折;每轮只有该折训练行可用于学习中位数和分类器参数。示例只讨论缺失填补这一种预处理泄漏,测试集若另行留出也应完全隔离。

Pipeline 在交叉验证里会随每折模型一起重新拟合。示例请求 return_estimator,再取出每折 SimpleImputer 的 statistics_,与对应训练行的 np.nanmedian 比较。只看到评分并不足以证明没有泄漏,逐折统计量才直接核对了本题的关键假设。

按同一批折检查结果

代码打印三折验证准确率、各折训练中位数和整张表中位数。各折中位数允许相同或不同,真正要满足的是 fitted_median 等于该折训练中位数;整表中位数只用来说明“先全表填补”的计算范围,不能拿去训练。

把自己的数据换入时,先固定行编号和标签,再检查缺失列是否在某折全空。全空列需要明确处理策略,否则不同折可能出现特征数量变化;目标列和用户标识列也不应误进填补器。

完整可运行代码

import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_validate
from sklearn.pipeline import Pipeline

rng = np.random.default_rng(14)
X = rng.normal(size=(90, 2))
y = (X[:, 0] + 0.6 * X[:, 1] > 0).astype(int)
X[::7, 0] = np.nan
X[2::9, 1] = np.nan
cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=9)
model = Pipeline([
    ("impute", SimpleImputer(strategy="median")),
    ("classify", LogisticRegression(max_iter=1000)),
])
result = cross_validate(model, X, y, cv=cv, scoring="accuracy",
                        return_estimator=True)
print("fold_accuracy", np.round(result["test_score"], 3).tolist())
for n, ((train, valid), fitted) in enumerate(
        zip(cv.split(X, y), result["estimator"]), 1):
    learned = fitted.named_steps["impute"].statistics_
    expected = np.nanmedian(X[train], axis=0)
    assert np.allclose(learned, expected)
    assert set(train).isdisjoint(valid)
    print("fold", n, "train_rows", len(train),
          "valid_rows", len(valid),
          "fitted_median", np.round(learned, 3).tolist())
print("whole_table_median_for_comparison",
      np.round(np.nanmedian(X, axis=0), 3).tolist())

运行结果与核对

下列是本文代码在上述环境的实际输出。更换运行环境时,先核对输入、代码和断言,再比较输出数值。

fold_accuracy [0.867, 0.833, 0.867]
fold 1 train_rows 60 valid_rows 30 fitted_median [-0.104, -0.082]
fold 2 train_rows 60 valid_rows 30 fitted_median [-0.029, -0.215]
fold 3 train_rows 60 valid_rows 30 fitted_median [-0.096, -0.054]
whole_table_median_for_comparison [-0.069, -0.117]

确认三行 fitted_median 分别等于同折训练行计算的中位数,且每折训练与验证索引无交集。若代码里的统计量改为在全表上提前 fit 的填补器,逐折断言就失去边界保证;应恢复 Pipeline 后再评估。

适用边界

这是固定随机种子的90条合成样本。准确率只说明这段教学代码能运行,不能推断正式模型质量;Pipeline 也不能自动修复标签晚到、同用户跨折或先用全数据筛特征等其他泄漏。

常见问题

已经把数据先随机分好了,再做全表填补可以吗?

不可以在划分前对全部行 fit 填补器;先划分,或把填补器放进交叉验证内部。独立测试集也只能 transform,不能参与拟合中位数。

参考资料

以下接口行为依据官方文档核对于 2026 年 10 月 1 日;本文的运行结果仅覆盖上述教学输入。

Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/31745.html

赞 (0)
AI小管家的头像AI小管家
OCR 识别结果怎么还原阅读顺序?按文字框坐标排序并检查跨栏错序
上一篇 2小时前
同一用户多条记录怎么验证模型?用 GroupKFold 保持逐折用户隔离
下一篇 2小时前

相关推荐

联系我们

联系我们

1

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
关注微信
分享本页
返回顶部