Предварительная обработка поиска по сетке с несколькими гиперпараметрами и несколькими оценками

#python #scikit-learn

#python #scikit-learn

Вопрос:

Долгосрочный пользователь R, изучающий Python. Я пытаюсь использовать GridSearch, чтобы попробовать разное количество компонентов для шага PCA в конвейере, а также для нескольких оценок. Я думал, что приведенный ниже код выполняет это (используя GridSearch документацию плюс другие источники), но результат best_params_ не имеет параметров предварительной обработки и параметров оценки; вместо этого он печатает {'prep2__pcadtm__n_components': 3} , что указывает мне, что его код не выполняет то, что я думал.

  • Является ли следующий код способом проверки предварительной обработки с несколькими гиперпараметрами и несколькими оценками (плюс гиперпараметры) в одном и том же 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 (наборы данных PCA для 2 и 3 компонентов) X 2 (оценки) X 2 (гиперпараметры для каждой оценки) = 8 тестируемых моделей]. Я думаю, что я не прав.

Наконец, для повышения эффективности было бы лучше, если prep2 бы шаг не вычислялся каждый раз при попытке создания новой оценки.

Ответ №1:

Действительно, когда param_grid есть список словарей, поиск происходит по объединению сеток, сгенерированных каждым словарем. Таким образом, ваш код фактически проверяет шесть комбинаций гиперпараметров:

  • PCA dim 2, глубина randomForest по умолчанию = Нет
  • PCA dim 3, глубина randomForest по умолчанию = Нет
  • Значения по умолчанию для PCA (dim2), глубина случайного леса 5
  • Значения по умолчанию для PCA (dim2), глубина случайного леса 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    
)
 

Это также приводит к меньшему количеству вычислений препроцессоров. Но смотрите Также memory параметр Pipeline .

Кроме того, обратите внимание, что этот подход довольно сильно изменяет cv-сгибы. Если вам нужен «плоский» поиск, возможно, напишите быстрый скрипт для создания более длинного списка в первом подходе.

Комментарии:

1. Отлично! Спасибо за правильный подход, альтернативу (2-й подход проще для меня) и особенно объяснение того, что на самом деле происходило. Очень полезный ответ для формирования моей ментальной концепции. Спасибо, что нашли время поделиться своими знаниями и опытом. Очень признателен!