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

时间:2017-04-12 10:10:47

标签: python numpy machine-learning scikit-learn grid-search

我使用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时重复预处理?

3 个答案:

答案 0 :(得分:20)

基本上,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()而不是你所描述的多次调用。

修改

在管道内使用GridSearchCV时,将refit更改为True。正如mentioned in documentation

  

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

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

因此,如果您想制作管道,只是为了查看网格搜索的分数,只有refit=False才合适。如果您要调用clf.predict()方法,则必须使用refit=True,否则将抛出Not Fitted错误。

答案 1 :(得分:2)

在当前版本的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 :(得分:0)

对于那些偶然发现了一些其他问题的人,我也是。

假设您具有以下管道:

classifier = Pipeline([
    ('vectorizer', CountVectorizer(max_features=100000, ngram_range=(1, 3))),
    ('clf', RandomForestClassifier(n_estimators=10, random_state=SEED, n_jobs=-1))])

然后,在指定参数时,您需要包括此用于估算器的“ clf _ ”名称。因此参数网格将为:

params={'clf__max_features':[0.3, 0.5, 0.7],
        'clf__min_samples_leaf':[1, 2, 3],
        'clf__max_depth':[None]
        }