Scikit-Learn:在交叉验证期间避免数据泄漏

时间:2018-01-28 01:58:06

标签: scikit-learn pipeline cross-validation

我刚刚阅读了k-fold交叉验证,并意识到我无意中使用当前的预处理设置泄漏了数据。

通常,我有一个火车和测试数据集。我在整个火车数据集上做了一堆数据插补和一次热编码,然后运行k-fold交叉验证。

泄漏是因为,如果我进行5倍交叉验证,我会对80%的列车数据进行培训,并对其余20%的列车数据进行测试。

我真的应该根据80%的火车来估算20%(而我之前使用的是100%的数据)。

1)这是考虑交叉验证的正确方法吗?

2)我一直在查看Pipeline中的sklearn.pipeline类,这对于进行一系列转换然后最终将模型拟合到结果数据似乎很有用。但是,我正在做一些类似于"在float64列中使用平均值","使用模式"等来归咎所有其他数据的错误数据

对于这种插补,没有明显的变压器。如何将此步骤添加到Pipeline?我会创建自己的BaseEstimator的子类吗?

这里的任何指导都会很棒!

2 个答案:

答案 0 :(得分:2)

1)是的,你应该使用80%的训练数据来估算20%的测试数据。

2)我写了a blog post来回答你的第二个问题,但我会在这里包含核心部分。

使用sklearn.pipeline,您可以将不同的预处理规则应用于不同的要素类型(例如,数字,分类)。在下面的示例代码中,我在缩放它们之前估算了数字特征的中位数。分类和布尔特征用模式估算 - 分类特征是单热编码。

您可以在管道末尾添加估算器以进行回归,分类等。

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])

答案 1 :(得分:0)

我建议将5倍交叉验证视为简单地将数据分成5个部分(或折叠)。您可以支持一次测试,并将其他4个一起用于训练集。我们再重复这个过程4次,直到每次折叠都有机会进行测试。

为了使您的估算工作正确并且不受污染,您需要确定用于测试的4倍的平均值,并使用它来在训练集和测试集中估算该值。

我喜欢用StratifiedKFold实现CV拆分。这将确保您在折叠中的每个类具有相同数量的样本。

要回答关于使用Pipelines的问题,我想你应该用你的自定义Imputation变换器将BaseEstimator子类化。在CV-split的循环内部,您应该从训练集计算平均值,然后将此平均值设置为变换器中的参数。然后你可以调用fit或transform。