Scikit-Learn:在交叉验证期间避免数据泄漏
Posted
技术标签:
【中文标题】Scikit-Learn:在交叉验证期间避免数据泄漏【英文标题】:Scikit-Learn: Avoiding Data Leakage During Cross-Validation 【发布时间】:2018-07-07 00:54:12 【问题描述】:我刚刚阅读了 k 折交叉验证,并意识到我在使用当前的预处理设置时无意中泄露了数据。
通常,我有一个训练和测试数据集。我对整个火车数据集进行了一堆数据插补和 one-hot 编码,然后运行 k 折交叉验证。
出现泄漏是因为,如果我进行 5 折交叉验证,我将使用 80% 的训练数据进行训练,并在剩余的 20% 的训练数据上对其进行测试。
我真的应该根据 80% 的训练来估算 20%(而我之前使用的是 100% 的数据)。
1) 这是思考交叉验证的正确方法吗?
2) 我一直在查看sklearn.pipeline
中的Pipeline
类,它似乎对进行一堆转换然后最终将模型拟合到结果数据很有用。但是,我正在做很多事情,例如“用平均值估算 float64
列中的缺失数据”、“用模式估算所有其他数据”等。
这种插补没有明显的变换器。我将如何将此步骤添加到Pipeline
?我可以自己创建BaseEstimator
的子类吗?
这里的任何指导都会很棒!
【问题讨论】:
是的,您应该在新类中扩展 BaseEstimator 和 Transformermixin 类,并在其中使用Imputer
。
【参考方案1】:
1) 是的,您应该使用 80% 的训练数据来估算 20% 的测试数据。
2) 我写了a blog post 来回答你的第二个问题,但我会在这里包含核心部分。
使用sklearn.pipeline
,您可以将单独的预处理规则应用于不同的特征类型(例如,数字、分类)。在下面的示例代码中,我在缩放数字特征之前估算了它们的中值。分类特征和布尔特征是用模式估算的——分类特征是 one-hot 编码的。
您可以在管道末端包含一个估计器,用于回归、分类等。
import numpy as np
from sklearn.pipeline import make_pipeline, FeatureUnion
from sklearn.preprocessing import OneHotEncoder, Imputer, StandardScaler
preprocess_pipeline = make_pipeline(
FeatureUnion(transformer_list=[
("numeric_features", make_pipeline(
TypeSelector(np.number),
Imputer(strategy="median"),
StandardScaler()
)),
("categorical_features", make_pipeline(
TypeSelector("category"),
Imputer(strategy="most_frequent"),
OneHotEncoder()
)),
("boolean_features", make_pipeline(
TypeSelector("bool"),
Imputer(strategy="most_frequent")
))
])
)
管道的TypeSelector
部分假定对象X
是pandas DataFrame
。使用TypeSelector.transform
选择具有给定数据类型的列子集。
from sklearn.base import BaseEstimator, TransformerMixin
import pandas as pd
class TypeSelector(BaseEstimator, TransformerMixin):
def __init__(self, dtype):
self.dtype = dtype
def fit(self, X, y=None):
return self
def transform(self, X):
assert isinstance(X, pd.DataFrame)
return X.select_dtypes(include=[self.dtype])
【讨论】:
【参考方案2】:我建议将 5 折交叉验证视为简单地将数据分成 5 个部分(或折叠)。您将其中的一个折叠起来用于测试,并将另外 4 个折叠起来用于您的训练集。我们再重复这个过程 4 次,直到每个折叠都有机会被测试。
为了使您的插补正常工作并且不受污染,您需要确定用于测试的 4 折的平均值,并使用它来插补训练集和测试集中的值。
我喜欢用StratifiedKFold 实现CV 拆分。这将确保您在折叠中的每个类都有相同数量的样本。
要回答您有关使用管道的问题,我想说您可能应该使用自定义的 Imputation 转换器对 BaseEstimator 进行子类化。在 CV 拆分的循环内部,您应该从训练集中计算平均值,然后将此平均值设置为转换器中的参数。然后你可以调用 fit 或 transform。
【讨论】:
以上是关于Scikit-Learn:在交叉验证期间避免数据泄漏的主要内容,如果未能解决你的问题,请参考以下文章
在交叉验证后对所有训练数据进行 scikit-learn 训练