我正在使用scikit-learn来调整模型的超参数。我使用管道将预处理与估计器连接起来。我问题的简化版本如下所示:
import numpy as npfrom sklearn.model_selection import GridSearchCVfrom sklearn.pipeline import make_pipelinefrom sklearn.preprocessing import StandardScalerfrom sklearn.linear_model import LogisticRegressiongrid = 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,)))
在我的情况下,预处理(在示例中是StandardScaler())非常耗时,而且我没有调整它的任何参数。
因此,当我执行这个示例时,StandardScaler会被执行12次。2次fit/predict * 2次cv * 3个参数。但每次StandardScaler为参数C的不同值执行时,它返回的输出都是相同的,因此,如果能计算一次,然后只运行管道的估计器部分,将会更加高效。
我可以手动将管道拆分为预处理部分(不调整超参数)和估计器部分。但要对数据应用预处理,我应该只提供训练集。因此,我将不得不手动实现分割,并且完全不使用GridSearchCV。
在使用GridSearchCV时,有没有简单/标准的方法可以避免重复预处理?
回答:
更新:理想情况下,以下答案不应使用,因为如评论中讨论的,它会导致数据泄露。在这个答案中,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()
这样做会只调用一次StandardScaler(),在一次clf.fit()
调用中,而不是像你描述的多次调用。
编辑:
当GridSearchCV在管道内使用时,将refit改为True
。如文档中提到的:
refit : 布尔值,默认值=True 使用整个数据集重新拟合最佳估计器。如果为“False”,在拟合后无法使用此GridSearchCV实例进行预测。
如果refit=False,clf.fit()
将无效,因为管道内的GridSearchCV对象会在fit()
后被重新初始化。当refit=True
时,GridSearchCV将使用在fit()
中传入的整个数据重新拟合最佳得分的参数组合。
所以如果你只是想查看网格搜索的得分,那么refit=False
是合适的。如果你想调用clf.predict()
方法,必须使用refit=True
,否则会抛出未拟合错误。