网格搜索预处理多个超参数和多个估计器

Posted

技术标签:

【中文标题】网格搜索预处理多个超参数和多个估计器【英文标题】:Grid search preprocess multiple hyperparameters and multiple estimators 【发布时间】:2021-03-19 05:49:04 【问题描述】:

长期 R 用户,学习 Python。我正在尝试使用 GridSearch 为管道中的 PCA 步骤以及多个估计器尝试不同数量的组件。我认为下面的代码正在执行这些操作(使用GridSearch 文档和其他来源),但best_params_ 的结果没有预处理参数和估计参数;而是打印'prep2__pcadtm__n_components': 3,这向我表明他的代码没有按照我的想法进行。

下面的代码是用多个超参数和多个估计器(加上超参数)测试预处理的方法吗 相同的 GridSearch? 如果不是... 如何包含预处理 多个超参数和多个估计器(加上超参数) 在同一个 GridSearch 中?

MWE

## Dependencies
import seaborn as sns
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.compose import ColumnTransformer
from sklearn.neighbors import KNeighborsClassifier
from imblearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
    
## load data set
df = sns.load_dataset('mpg').drop(["name"], axis = 1).dropna()

## Factoize the outcome
factor = pd.factorize(df['origin'])
df.origin = factor[0]
definitions = factor[1]
outcome_order = definitions.tolist()

X = df.loc[:, ~df.columns.isin(['origin'])]
y = df.iloc[:,7].values

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20, random_state = 21) 


scaler = StandardScaler()
pca = PCA(n_components = 2)
dtm_i = list(range(2, len(X_train.columns)))
dtm_i2 = list(range(0, len(X_train.columns)-2))

preprocess = ColumnTransformer(transformers=[('scaler', scaler, dtm_i)], remainder='passthrough')
preprocess2 = ColumnTransformer(transformers=[('pcadtm', pca, dtm_i2)], remainder='passthrough')


pipeline = Pipeline([
    ('prep', preprocess),
    ('prep2', preprocess2),
    ('clf', RandomForestClassifier())
])


search_space = [
    
       'prep2__pcadtm__n_components': [2, 3]
    ,
    
       'clf': [RandomForestClassifier()],
       'clf__max_depth': [5, 15]
    ,
    
       'clf': [KNeighborsClassifier()],
       'clf__n_neighbors' : [2, 3]
    
]


# Create grid search
grid_imba = GridSearchCV(
    estimator = pipeline,
    param_grid = search_space,
    scoring = 'accuracy',
    cv = 3,
    return_train_score = True
)

## Fit the model using all the different estimators
grid_imba.fit(X_train, y_train);

## Extract best
best_params = grid_imba.best_params_
print(best_params)
##'prep2__pcadtm__n_components': 3

我认为GridSearch 会生成 2 个和 3 个 PCA 组件数据集,然后使用估算器将其传递到下一步。反过来,两个 PCA 输出都将使用随机森林进行测试,然后是 KNN [每个都有 2 个超参数。含义 2(2 和 3 个组件的 PCA 数据集)X 2(估计器)X 2(每个估计器的超参数)= 8 个正在测试的模型]。我认为我不正确。

最后,为了提高效率,最好不要在每次尝试新的估算器时都计算 prep2 步骤。

【问题讨论】:

【参考方案1】:

确实,当param_grid 是字典列表时,搜索发生在每个字典生成的网格的联合 上。所以你的代码实际上检查了六个超参数组合:

PCA 暗淡 2,RandomForest 默认深度 = 无 PCA 暗淡 3,RandomForest 默认深度 = 无 PCA 默认值 (dim2),随机森林深度 5 PCA 默认值 (dim2),RandomForest 深度 15 PCA 默认值 (dim2),KNN k=2 PCA 默认值 (dim2),KNN k=3

你需要类似的东西

search_space = 
    'prep2__pcadtm__n_components': [2, 3],
    'clf': [RandomForestClassifier(max_depth=5),
            RandomForestClassifier(max_depth=15),
            KNeighborsClassifier(n_neighbors=2),
            KNeighborsClassifier(n_neighbors=3)],

根据您的实际需求,列出每个不同模型所需的所有超参数组合当然可能会变得笨拙。在这种情况下,嵌套搜索可能是最简单的:

rf_gs = GridSearchCV(
    estimator=RandomForestClassifier(),
    param_grid='max_depth': [5, 15],
)
kn_gs = GridSearchCV(
    estimator=KNeighborsClassifier(),
    param_grid='n_neighbors': [2, 3],
)

pipeline = Pipeline([
    ('prep', preprocess),
    ('prep2', preprocess2),
    ('clf', RandomForestClassifier())
])


search = GridSearchCV(
    estimator=pipeline,
    param_grid=
        'prep2__pcadtm__n_components': [2, 3],
        'clf': [rf_gs, kn_gs],
    ,
    scoring = 'accuracy',
    cv = 3,
    return_train_score = True    
)

这还具有减少预处理器计算次数的效果。但另请参阅Pipelinememory 参数。

另外,请注意,这种方法相当显着地改变了 cv 折叠。如果您想要“平面”搜索,可以编写一个快速脚本以在第一种方法中生成更长的列表。

【讨论】:

完美!感谢您提供正确的方法,一种替代方法(第二种方法对我来说在精神上更容易),特别是解释实际发生的事情。对塑造我的心理概念非常有帮助的答案。感谢您花时间传递您的知识和经验。非常感谢!

以上是关于网格搜索预处理多个超参数和多个估计器的主要内容,如果未能解决你的问题,请参考以下文章

你能从 sklearn 网格搜索 (GridSearchCV) 中获得所有估计器吗?

Scikit 网格搜索参数(不是超参数)

在 Python Bagging Classifier 中将最佳网格搜索超参数分配给最终模型

Python:没有机器学习的网格搜索?

在多个分类器上进行网格搜索

搜索估计器参数返回的结果不在网格中?