Используйте sklearn GridSearchCV с конвейером, предварительно обработав один раз

Я использую scickit-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 параметра. Но каждый раз, когда StandardScaler выполняется для другого значения параметра C, он возвращает тот же результат, поэтому было бы гораздо более эффективно вычислять его один раз, а затем просто запускать часть оценки конвейера.

Я могу вручную разделить конвейер между предварительной обработкой (не настроенными гипер-параметрами) и оценкой. Но чтобы применить предварительную обработку данных, я должен предоставить только набор тренировок. Таким образом, я должен был бы реализовать разрывы вручную, и вообще не использовать GridSearchCV.

Есть ли простой/стандартный способ избежать повторения предварительной обработки при использовании GridSearchCV?

Ответ 1

По существу, GridSearchCV также является оценкой, реализующей методы fit() и pred(), используемые конвейером.

Итак, вместо:

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() вместо нескольких вызовов, как вы описали.

Edit:

Изменено обновление до True, когда GridSearchCV используется внутри конвейера. Как упомянутый в документации:

refit: boolean, default = True     Установите лучшую оценку всего набора данных. Если "False", невозможно сделать прогнозы с использованием этого экземпляра GridSearchCV после установки.

Если refit = False, clf.fit() не будет иметь эффекта, потому что объект GridSearchCV внутри конвейера будет повторно инициализирован после fit(). Когда refit=True, GridSearchCV будет переоборудован с лучшей комбинацией параметров оценки для всех данных, которые передаются в fit().

Итак, если вы хотите сделать конвейер, просто чтобы увидеть результаты поиска сетки, только тогда подходит refit=False. Если вы хотите вызвать метод clf.predict(), необходимо использовать refit=True, иначе будет выведена ошибка Not Fitted.

Ответ 3

Для тех, кто наткнулся на немного другую проблему, которая у меня тоже была.

Предположим, у вас есть этот конвейер:

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