如果我们在管道中包含转换器,则scikit-learn的cross_val_score和GridsearchCV的k折交叉验证分数是否有偏差?

Kai*_*hao 5 pipeline machine-learning scikit-learn cross-validation grid-search

应该使用诸如StandardScaler之类的数据预处理器来fit_transform训练集,而仅转换(不适合)测试集。我希望相同的拟合/变换过程适用于交叉验证以调整模型。然而,我发现cross_val_scoreGridSearchCVfit_transform整个列车组与预处理器(而不是fit_transform的inner_train集,改造inner_validation集)。我相信这可以人为地从inner_validation集中删除方差,从而使cv得分(用于通过GridSearch选择最佳模型的度量)出现偏差。这是一个问题还是我实际上错过了任何事情?

为了演示上述问题,我使用Kaggle的乳腺癌威斯康星州(诊断)数据集尝试了以下三个简单的测试案例。

  1. 我故意将整个X调整为 StandardScaler()
X_sc = StandardScaler().fit_transform(X)
lr = LogisticRegression(penalty='l2', random_state=42)
cross_val_score(lr, X_sc, y, cv=5)
Run Code Online (Sandbox Code Playgroud)
  1. 我在中包含SC和LR Pipeline并运行cross_val_score
pipe = Pipeline([
    ('sc', StandardScaler()),
    ('lr', LogisticRegression(penalty='l2', random_state=42))
])
cross_val_score(pipe, X, y, cv=5)
Run Code Online (Sandbox Code Playgroud)
  1. 与2相同,但具有 GridSearchCV
pipe = Pipeline([
    ('sc', StandardScaler()),
    ('lr', LogisticRegression(random_state=42))
])
params = {
    'lr__penalty': ['l2']
}
gs=GridSearchCV(pipe,
param_grid=params, cv=5).fit(X, y)
gs.cv_results_
Run Code Online (Sandbox Code Playgroud)

它们都产生相同的验证分数。[0.9826087、0.97391304、0.97345133、0.97345133、0.99115044]

Shi*_*han 7

不,与整个数据集sklearn无关fit_transform

为了检查这一点,我将子类化为StandardScaler打印发送给它的数据集的大小。

class StScaler(StandardScaler):
    def fit_transform(self,X,y=None):
        print(len(X))
        return super().fit_transform(X,y)
Run Code Online (Sandbox Code Playgroud)

如果您现在StandardScaler在代码中进行替换,您会看到第一种情况下传递的数据集大小实际上更大。

但是为什么准确度保持完全相同呢?我认为这是因为LogisticRegression对特征尺度不是很敏感。如果我们改为使用对比例非常敏感的分类器,KNeighborsClassifier例如,您会发现两种情况之间的准确度开始变化。

X,y = load_breast_cancer(return_X_y=True)
X_sc = StScaler().fit_transform(X)
lr = KNeighborsClassifier(n_neighbors=1)
cross_val_score(lr, X_sc,y, cv=5)
Run Code Online (Sandbox Code Playgroud)

输出:

569
[0.94782609 0.96521739 0.97345133 0.92920354 0.9380531 ]
Run Code Online (Sandbox Code Playgroud)

而第二种情况,

pipe = Pipeline([
    ('sc', StScaler()),
    ('lr', KNeighborsClassifier(n_neighbors=1))
])
print(cross_val_score(pipe, X, y, cv=5))
Run Code Online (Sandbox Code Playgroud)

输出:

454
454
456
456
456
[0.95652174 0.97391304 0.97345133 0.92920354 0.9380531 ]
Run Code Online (Sandbox Code Playgroud)

在准确性方面没有大的变化,但仍然发生了变化。

  • 希哈卜,再次感谢。令我惊讶的是,许多像我这样的 Bootcamp 学生在使用 GridSearchCV 时实际上错误地应用了预处理器(在使用 GridSearch 调整模型之前预处理训练数据)。我将就这个主题写一篇博客文章。如果您不介意,我想引用本次讨论中的一些文本和代码,并感谢您提供的解决方案。让我知道这是否可以。谢谢! (2认同)
  • Shihab,这里是博客文章“使用管道预处理数据以防止交叉验证期间数据泄漏”的链接。如果您有任何意见,请告诉我。https://towardsdatascience.com/pre-process-data-with-pipeline-to-prevent-data-leakage-during-cross-validation-e3442cca7fdc (2认同)