标准化为什么会造成数据泄漏?用 scikit-learn Pipeline 只拟合训练集

用 StandardScaler 与 Pipeline 演示训练均值核对,避免测试数据参与预处理拟合,并说明交叉验证和预测列顺序的边界。

标准化本身很常见,问题在于用哪些数据计算均值与方差。如果先对全表 fit_transform,再切训练集和测试集,测试集的信息已经参与了训练流程。这里用 scikit-learn Pipeline 把 StandardScaler 与分类器绑定起来,并手工核对预处理只从训练样本学习。

先切分,再拟合整条流程

fit 是学习参数;transform 是用已学到的参数转换数据。训练部分使用 fit,测试部分只使用 transform。Pipeline.fit 会依次拟合前面的变换器,再训练最后的分类器;Pipeline.predict 会使用已拟合的变换器处理新样本,避免预测时忘记标准化。

标准化为什么会造成数据泄漏?用 scikit-learn Pipeline 只拟合训练集

以下训练值为 1、2、10、12,测试值为 100、120,都是人为构造,用明显分布差异展示拟合范围。没有测试真值,不能据此评价分类好坏。

运行代码并核对均值

以下示例在 Python 3.11、scikit-learn 1.7.2 的独立环境运行通过。先在练习目录创建虚拟环境并安装对应版本:

python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\python.exe -m pip install "scikit-learn==1.7.2"
# 以下脚本统一用该环境的 Python 运行,例如:
.\.venv\Scripts\python.exe demo.py
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

# 人为构造有明显分布差异的数值,只演示预处理的拟合范围。
X_train = np.array([[1.0], [2.0], [10.0], [12.0]])
y_train = np.array([0, 0, 1, 1])
X_test = np.array([[100.0], [120.0]])
model = Pipeline([
    ("scale", StandardScaler()),
    ("clf", LogisticRegression(max_iter=1000, random_state=42)),
])
model.fit(X_train, y_train)
scale = model.named_steps["scale"]
assert np.allclose(scale.mean_, X_train.mean(axis=0))
assert not np.allclose(scale.mean_, np.vstack([X_train, X_test]).mean(axis=0))
assert np.allclose(scale.transform(X_train).mean(axis=0), 0)
before = scale.mean_.copy()
prediction = model.predict(X_test)
assert np.allclose(before, scale.mean_)
print("训练均值", scale.mean_.tolist())
print("混合全量均值", np.vstack([X_train, X_test]).mean(axis=0).tolist())
print("测试变换后", scale.transform(X_test).round(3).ravel().tolist())
print("测试预测", prediction.tolist())

当前环境输出:

训练均值 [6.25]
混合全量均值 [40.833333333333336]
测试变换后 [19.469, 23.622]
测试预测 [1, 1]

训练均值应为 6.25,而混入测试行后的均值约为 40.833。断言要求模型中的 scale.mean_ 等于训练均值,训练变换后的均值接近零,predict 后训练均值不变。这比只看“代码成功运行”更能检查拟合范围。

在交叉验证中也要保留 Pipeline

当需要 K 折验证时,把整条 Pipeline 交给 cross_validate 或参数搜索器。每折会在该折训练部分拟合标准化器,再处理该折验证部分。若提前对完整训练池 fit_transform,再把结果交给交叉验证,折内验证样本仍参与了预处理。

例如数值表 X 和标签 y 已对齐,且样本独立同分布时,可使用以下结构:

from sklearn.model_selection import StratifiedKFold, cross_validate

cv = StratifiedKFold(n_splits=4, shuffle=True, random_state=42)
# X_pool、y_pool 是你保留独立测试集后的训练池,需先准备好。
result = cross_validate(model, X_pool, y_pool, cv=cv,
                        scoring="f1_macro", error_score="raise")
print(result["test_score"])

这段展示接入结构,X_pool、y_pool 需要替换为真实训练池;未在上面的四行玩具数据执行四折验证。最小类别样本数必须支持所选折数;有关联用户时改用组划分,时间数据用时间划分。

Pipeline 能解决哪些问题

它能把预处理和估计器的调用顺序绑定,减少在测试数据上误拟合的机会。它不会发现“申请通过后才知道的字段”是否泄露答案,也不会自动去除重复样本、隔离同用户或修复错误标签。特征是否在预测时真实可得,需要检查采集流程。

若输入列数不一致,检查训练与预测的列名、顺序和单位;如果数值列出现字符串,先在训练数据的处理规则中明确解析与缺失处理。不要为了修复报错在预测数据上重新 fit。稀疏矩阵默认居中可能报错,应研究 StandardScaler(with_mean=False),本例使用的是密集数值数组。

相关问答与依据

只用训练集拟合,测试均值不为零正常吗?正常。测试集用训练均值和标准差转换,不要求自身均值为零;过大的偏移反而提示分布需要复查。

保存模型时只保存分类器可以吗?如果预测需要标准化,就应保存包含预处理的流程及输入列约定,否则新样本会按错误尺度进入模型。

依据:常见陷阱官方指南说明只从训练数据学习预处理;Pipeline 文档说明 fit 与 predict 的串联;StandardScaler 文档说明 mean_ 和稀疏矩阵限制。本例只验证预处理范围,没有真实业务泛化测试。

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

赞 (0)
AI小管家的头像AI小管家
同一用户的数据怎么划分训练集?用 GroupShuffleSplit 阻止跨集合泄漏
上一篇 11小时前
分类模型错在哪?用混淆矩阵导出误分类样本并回查原始编号
下一篇 11小时前

相关推荐

联系我们

联系我们

1

在线咨询: QQ交谈

邮件:admin@example.com

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

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