如何使用 GridSearchCV 在嵌套管道中测试预处理组合?

Posted

技术标签:

【中文标题】如何使用 GridSearchCV 在嵌套管道中测试预处理组合?【英文标题】:How to test preprocessing combinations in nested pipeline using GridSearchCV? 【发布时间】:2020-12-21 04:57:56 【问题描述】:

我一直在研究这个分类问题,使用 sklearn 的 Pipeline 将预处理步骤(缩放)和使用 Logistic 回归的交叉验证步骤 (GridSearchCV) 结合起来。

这是简化的代码:

# import dependencies
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler, MinMaxScaler, RobustScaler   

# scaler and encoder options
scaler = StandardScaler()   # there are 3 options that I want to try
encoder = OneHotEncoder()   # only one option, no need to GridSearch it

# use ColumnTransformer to apply different preprocesses to numerical and categorical columns
preprocessor = ColumnTransformer(transformers = [('categorical', encoder, cat_columns),
                                                 ('numerical', scaler, num_columns),
                                                ])

# combine the preprocessor with LogisticRegression() using Pipeline 
full_pipeline = Pipeline(steps = [('preprocessor', preprocessor),
                                  ('log_reg', LogisticRegression())])

我正在尝试做的是尝试不同的缩放方法(例如标准缩放、稳健缩放等),然后在尝试所有这些之后,选择产生最佳度量(即准确性)的缩放方法。但是,我不知道如何使用 GridSearchCV:

from sklearn.model_selection import GridSearchCV

# set params combination I want to try
scaler_options = 'numerical':[StandardScaler(), RobustScaler(), MinMaxScaler()]

# initialize GridSearchCV using full_pipeline as final estimator
grid_cv = GridSearchCV(full_pipeline, param_grid = scaler_options, cv = 5)

# fit the data 
grid_cv.fit(X_train, y_train)

我知道上面的代码不起作用,特别是因为我设置为 param_grid 的 scaler_options。我意识到我设置的 scaler_options 不能被 GridSearchCV 处理。为什么?因为它不是管道的超参数(与 'log_reg__C' 不同,来自 LogisticRegression() 的超参数可以由 GridSearchCV 访问)。但它是我嵌套在 full_pipeline 中的 ColumnTransformer 的一个组件。

所以主要问题是,如何自动化 GridSearchCV 来测试我的所有缩放选项?由于缩放器是子管道的一个组件(即之前的 ColumnTransformer)。

【问题讨论】:

更新:我想我已经找到了解决方案,即创建一个自定义转换器,其中包含一个具有“scaling_options”作为其初始化参数的类,以选择我想要应用的缩放方法。这样我可以插入以下字典 preprocessor__customtransformer__scaling_options: [list of options] 作为 param_grid。如果我错了,请纠正我。 请edit你的问题添加一些解释或代码,而不是像你一样使用cmets。 【参考方案1】:

按照您的建议,您可以创建一个 class,它接受您要使用的 缩放器 __init()__ 参数。 然后你可以在你的网格搜索参数中指定你的类应该用来初始化类的 Scaler。

我写道,希望对您有所帮助:

class ScalerSelector(BaseEstimator, TransformerMixin):
    
    def __init__(self, scaler=StandardScaler()):
        super().__init__()
        self.scaler = scaler

    def fit(self, X, y=None):
        return self.scaler.fit(X)

    def transform(self, X, y=None):
        return self.scaler.transform(X)

在这里你可以找到一个完整的例子,你可以运行它来测试:

# import dependencies
from sklearn.tree import DecisionTreeClassifier

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import GridSearchCV

from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler   
from sklearn.datasets import load_breast_cancer

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler   

import pandas as pd

class ScalerSelector(BaseEstimator, TransformerMixin):
    
    def __init__(self, scaler=StandardScaler()):
        super().__init__()
        self.scaler = scaler

    def fit(self, X, y=None):
        return self.scaler.fit(X)

    def transform(self, X, y=None):
        return self.scaler.transform(X)


data = load_breast_cancer()
features = data["data"]
target = data["target"]
data = pd.DataFrame(data['data'], columns=data['feature_names'])
col_names = data.columns.tolist()

# scaler and encoder options
my_scaler = ScalerSelector()

preprocessor = ColumnTransformer(transformers = [('numerical', my_scaler, col_names)
                                                ])

# combine the preprocessor with LogisticRegression() using Pipeline 
full_pipeline = Pipeline(steps = [('preprocessor', preprocessor),
                                  ('log_reg', LogisticRegression())
                                  ])

# set params combination I want to try
scaler_options = 'preprocessor__numerical__scaler':[StandardScaler(), RobustScaler(), MinMaxScaler()]

# initialize GridSearchCV using full_pipeline as final estimator
grid_cv = GridSearchCV(full_pipeline, param_grid = scaler_options)

# fit the data 
grid_cv.fit(data, target)

# best params :
grid_cv.best_params_

【讨论】:

【参考方案2】:

无需创建自定义转换器即可实现您的目标。您甚至可以将 'passthrough' 参数传递给 param_grid 以试验您根本不想在该步骤中进行任何缩放的场景。

在本例中,假设我们想调查模型是否更好地对数值特征施加 Scaler 变换器,num_features

cat_features = selector(dtype_exclude='number')(train.drop('target', axis=1))
num_features = selector(dtype_include='number')(train.drop('target', axis=1))

cat_preprocessor = Pipeline(steps=[
    ('oh', OneHotEncoder(handle_unknown='ignore')),
    ('ss', StandardScaler()) 
])
num_preprocessor = Pipeline(steps=[ 
    ('pt', PowerTransformer(method='yeo-johnson')),
    ('ss', StandardScaler()) # Create a place holder for your test here !!!                                   
]) 
preprocessor = ColumnTransformer(transformers=[ 
    ('cat', cat_preprocessor, cat_features),
    ('num', num_preprocessor, num_features)                                                       
])
model = Pipeline(steps=[
    ('prep', preprocessor),
    ('clf', RidgeClassifier())
])
X = train.drop('target', axis=1)
y = train['target']
param_grid = 
    'prep__cat__ss': ['passthrough', StandardScaler(with_mean=False)] # 'passthrough', 

gs = GridSearchCV(
    estimator=model,
    param_grid=param_grid,
    scoring='roc_auc',
    n_jobs=-1,
    cv=2
)
gs.fit(X, y)

【讨论】:

以上是关于如何使用 GridSearchCV 在嵌套管道中测试预处理组合?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现 sklearn 的 Estimator 接口以在 GridSearchCV 管道中使用?

使用嵌套在 GridSearchCV 中的 RFECV 时,如何避免使用 estimator_params?

sklearn - 如何从传递给 GridSearchCV 的管道中检索 PCA 组件和解释方差

如何使用 GridSearchCV 比较多个模型以及 python 中的管道和超参数调整

使用带有管道和 GridSearch 的 cross_val_score 进行嵌套交叉验证

使用 `GridSearchCV` 测试完全从管道中删除步骤的效果? [复制]