【问题标题】:Use sklearn's GridSearchCV with a pipeline, preprocessing just once将 sklearn 的 GridSearchCV 与管道一起使用,只需预处理一次
【发布时间】:2017-09-08 01:24:29
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    更新: 理想情况下,不应使用以下答案,因为它会导致数据泄漏,如 cmets 中所述。在这个答案中,GridSearchCV 将调整已经由StandardScaler 预处理的数据的超参数,这是不正确的。在大多数情况下,这应该无关紧要,但对缩放过于敏感的算法会给出错误的结果。


    本质上,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 时,将改装更改为 True。作为mentioned in documentation

    refit : 布尔值,默认 = True 用整个数据集重新拟合最佳估计器。如果为“False”,则无法使用此 GridSearchCV 实例进行预测 装好后。

    如果 refit=False,clf.fit() 将不起作用,因为管道内的 GridSearchCV 对象将在fit() 之后重新初始化。 当refit=True 时,GridSearchCV 将在fit() 中传递的整个数据上使用最佳评分参数组合进行重新拟合。

    所以如果你想制作管道,只看网格搜索的分数,只有refit=False是合适的。如果要调用clf.predict()方法,必须使用refit=True,否则会抛出Not Fitted错误。

    【讨论】:

    • 我没有考虑在管道本身中使用 GridSearchCV,听起来是个绝妙的主意。非常感谢!
    • @MarcGarcia 但是一定要打开refit=True,否则调用clf.predict()时会报错
    • 这种技术不是使用 StandardScalar() 中的所有数据而不仅仅是训练集吗?我看不出它如何避免手动进行拆分。
    • @VivekKumar 好的,我明白了。但是在 fit() 期间,GridSearchCV 将通过对 StandardScaler() 预处理的数据的 CV 来调整超参数,因此 StandardScalar() 也将适合 GridSearchCV 的验证集(而不是传递给 predict() 的测试集) ,这对我来说是不正确的,因为不应该对验证集进行预处理。
    • @ShashwatSiddhant param_grid 在你的情况下进入GridSearchCV。这里与make_pipeline 无关。所以在你的情况下,param_grid 应该只包含'C''gamma'
    【解决方案2】:

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

    假设你有这个管道:

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

    【讨论】:

      【解决方案3】:

      在当前版本的 scikit-learn (0.18.1) 中无法做到这一点。已经在 github 项目上提出了一个修复:

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

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

      【讨论】:

        猜你喜欢
        • 2020-10-21
        • 2014-01-29
        • 2020-05-11
        • 2019-09-22
        • 2020-06-01
        • 2020-04-18
        • 1970-01-01
        相关资源
        最近更新 更多