问题 使用sklearn的GridSearchCV和管道,只需预处理一次


我正在使用scickit-learn来调整模型超参数。我正在使用管道将预处理链接到估算器。我的问题的简单版本看起来像这样:

import numpy as np
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression


grid = GridSearchCV(make_pipeline(StandardScaler(), LogisticRegression()),
                    param_grid={'logisticregression__C': [0.1, 10.]},
                    cv=2,
                    refit=False)

_ = grid.fit(X=np.random.rand(10, 3),
             y=np.random.randint(2, size=(10,)))

在我的情况下,预处理(玩具示例中的StandardScale())是耗时的,我没有调整它的任何参数。

因此,当我执行该示例时,StandardScaler执行12次。 2拟合/预测* 2 cv * 3参数。但是每次为参数C的不同值执行StandardScaler时,它返回相同的输出,因此计算它一次就更有效率,然后只运行管道的估计器部分。

我可以在预处理(没有调整超参数)和估算器之间手动拆分管道。但是要将预处理应用于数据,我应该只提供训练集。所以,我必须手动实现拆分,而根本不使用GridSearchCV。

是否有一种简单/标准的方法可以避免在使用GridSearchCV时重复预处理?


10820
2018-04-12 10:10


起源



答案:


从本质上讲,GridSearchCV也是一个估算器,实现了管道使用的fit()和predict()方法。

所以代替:

grid = GridSearchCV(make_pipeline(StandardScaler(), LogisticRegression()),
                    param_grid={'logisticregression__C': [0.1, 10.]},
                    cv=2,
                    refit=False)

做这个:

clf = make_pipeline(StandardScaler(), 
                    GridSearchCV(LogisticRegression(),
                                 param_grid={'logisticregression__C': [0.1, 10.]},
                                 cv=2,
                                 refit=True))

clf.fit()
clf.predict()

它将做的是,只调用一次StandardScalar(),一次调用 clf.fit() 而不是你描述的多个电话。

编辑:

改为改装 True,当在管道内使用GridSearchCV时。如 在文档中提到

refit:boolean,default = True       使用整个数据集重新设置最佳估算器。如果“False”,则无法使用此GridSearchCV实例进行预测   装修后。

如果refit = False, clf.fit() 将无效,因为管道内的GridSearchCV对象将在之后重新初始化 fit()。 什么时候 refit=True,GridSearchCV将在传入的整个数据上重新配置最佳得分参数组合 fit()

因此,如果你想制作管道,只是为了看到网格搜索的分数,只有那样 refit=False 是合适的。如果你想打电话给 clf.predict() 方法, refit=True 必须使用,否则将抛出错误。


12
2018-04-12 10:21



我没想到在管道中使用GridSearchCV,听起来很棒。非常感谢! - Marc Garcia
@MarcGarcia但确保转过来 refit=True,否则它会在调用时抛出错误 clf.predict() - Vivek Kumar
@MarcGarcia编辑答案反映同样的问题 - Vivek Kumar
这种技术不是使用StandardScalar()中的所有数据而不仅仅是训练集吗?我没有看到它如何允许避免手动分割。 - Victor Deplasse
@VivekKumar好的,我明白了。但是在fit()期间,GridSearchCV将通过StandardScaler()预处理的数据上的CV来调整超参数,因此StandardScalar()也将被安装在GridSearchCV的验证集上(而不是传递给predict()的测试集) ,这对我来说是不正确的,因为验证集不应该被预处理。 - Victor Deplasse


答案:


从本质上讲,GridSearchCV也是一个估算器,实现了管道使用的fit()和predict()方法。

所以代替:

grid = GridSearchCV(make_pipeline(StandardScaler(), LogisticRegression()),
                    param_grid={'logisticregression__C': [0.1, 10.]},
                    cv=2,
                    refit=False)

做这个:

clf = make_pipeline(StandardScaler(), 
                    GridSearchCV(LogisticRegression(),
                                 param_grid={'logisticregression__C': [0.1, 10.]},
                                 cv=2,
                                 refit=True))

clf.fit()
clf.predict()

它将做的是,只调用一次StandardScalar(),一次调用 clf.fit() 而不是你描述的多个电话。

编辑:

改为改装 True,当在管道内使用GridSearchCV时。如 在文档中提到

refit:boolean,default = True       使用整个数据集重新设置最佳估算器。如果“False”,则无法使用此GridSearchCV实例进行预测   装修后。

如果refit = False, clf.fit() 将无效,因为管道内的GridSearchCV对象将在之后重新初始化 fit()。 什么时候 refit=True,GridSearchCV将在传入的整个数据上重新配置最佳得分参数组合 fit()

因此,如果你想制作管道,只是为了看到网格搜索的分数,只有那样 refit=False 是合适的。如果你想打电话给 clf.predict() 方法, refit=True 必须使用,否则将抛出错误。


12
2018-04-12 10:21



我没想到在管道中使用GridSearchCV,听起来很棒。非常感谢! - Marc Garcia
@MarcGarcia但确保转过来 refit=True,否则它会在调用时抛出错误 clf.predict() - Vivek Kumar
@MarcGarcia编辑答案反映同样的问题 - Vivek Kumar
这种技术不是使用StandardScalar()中的所有数据而不仅仅是训练集吗?我没有看到它如何允许避免手动分割。 - Victor Deplasse
@VivekKumar好的,我明白了。但是在fit()期间,GridSearchCV将通过StandardScaler()预处理的数据上的CV来调整超参数,因此StandardScalar()也将被安装在GridSearchCV的验证集上(而不是传递给predict()的测试集) ,这对我来说是不正确的,因为验证集不应该被预处理。 - Victor Deplasse


在当前版本的scikit-learn(0.18.1)中无法执行此操作。已经在github项目上提出了一个修复:

https://github.com/scikit-learn/scikit-learn/issues/8830

https://github.com/scikit-learn/scikit-learn/pull/8322


2
2018-05-13 11:14