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

Mar*_*cia 21 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,)))
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Viv*_*mar 27

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

所以代替:

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

做这个:

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

clf.fit()
clf.predict()
Run Code Online (Sandbox Code Playgroud)

它的作用是,只调用一次StandardScalar(),对一次调用clf.fit()而不是如你所描述的多次调用.

编辑:

True在管道内使用GridSearchCV时更改为refit .如文档中所述:

refit:boolean,default = True用整个数据集重新定义最佳估计量.如果"False",则在拟合后无法使用此GridSearchCV实例进行预测.

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

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

  • 这种技术不是使用StandardScalar()中的所有数据而不仅仅是训练集吗?我没有看到它如何允许避免手动分割. (6认同)
  • @VivekKumar好的我明白了.但是在fit()期间,GridSearchCV将通过StandardScaler()预处理的数据上的CV调整超参数,因此StandardScalar()也将被安装在GridSearchCV的验证集上(而不是传递给predict()的测试集) ,这对我来说不正确,因为验证集不应该被预处理. (6认同)
  • 我没想到要在管道本身中使用GridSearchCV,这听起来是个绝妙的主意。非常感谢! (2认同)

小智 8

对于那些偶然发现一些不同问题的人,我也遇到过。

假设你有这个管道:

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

然后,在指定参数时,您需要包含用于估算器的“ clf_ ”名称。所以参数网格将是:

params={'clf__max_features':[0.3, 0.5, 0.7],
        'clf__min_samples_leaf':[1, 2, 3],
        'clf__max_depth':[None]
        }
Run Code Online (Sandbox Code Playgroud)


Vic*_*sse 5

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

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

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