Пользовательская стратегия повторного обучения для поиска по сетке с кросс-валидацией#

Этот пример показывает, как классификатор оптимизируется с помощью перекрестной проверки, что выполняется с использованием GridSearchCV объект на тестовом наборе, который включает только половину доступных размеченных данных.

Производительность выбранных гиперпараметров и обученной модели затем измеряется на выделенном оценочном наборе, который не использовался на этапе выбора модели.

Более подробная информация об инструментах, доступных для выбора модели, находится в разделах Кросс-валидация: оценка производительности оценщика и Настройка гиперпараметров оценщика.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Набор данных#

Мы будем работать с digits наборе данных. Цель — классифицировать изображения рукописных цифр. Мы преобразуем задачу в бинарную классификацию для более лёгкого понимания: цель — определить, является ли цифра 8 или нет.

from sklearn import datasets

digits = datasets.load_digits()

Чтобы обучить классификатор на изображениях, нам нужно преобразовать их в векторы. Каждое изображение размером 8 на 8 пикселей нужно преобразовать в вектор из 64 пикселей. Таким образом, мы получим итоговый массив данных формы (n_images, n_pixels).

n_samples = len(digits.images)
X = digits.images.reshape((n_samples, -1))
y = digits.target == 8
print(
    f"The number of images is {X.shape[0]} and each image contains {X.shape[1]} pixels"
)
The number of images is 1797 and each image contains 64 pixels

Как представлено во введении, данные будут разделены на обучающий и тестовый наборы равного размера.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

Определяем нашу стратегию поиска по сетке#

Мы выберем классификатор, выполнив поиск лучших гиперпараметров на сгибах обучающего набора. Для этого нам нужно определить оценки для выбора лучшего кандидата.

scores = ["precision", "recall"]

Мы также можем определить функцию для передачи в refit параметр GridSearchCV экземпляр. Он будет реализовывать пользовательскую стратегию для выбора лучшего кандидата из cv_results_ атрибут объекта GridSearchCV. После выбора кандидата он автоматически переобучается с помощью GridSearchCV экземпляр.

Здесь стратегия заключается в составлении короткого списка моделей, которые являются лучшими с точки зрения точности и полноты. Из выбранных моделей мы окончательно выбираем самую быструю модель для предсказания. Обратите внимание, что эти пользовательские выборы полностью произвольны.

import pandas as pd


def print_dataframe(filtered_cv_results):
    """Pretty print for filtered dataframe"""
    for mean_precision, std_precision, mean_recall, std_recall, params in zip(
        filtered_cv_results["mean_test_precision"],
        filtered_cv_results["std_test_precision"],
        filtered_cv_results["mean_test_recall"],
        filtered_cv_results["std_test_recall"],
        filtered_cv_results["params"],
    ):
        print(
            f"precision: {mean_precision:0.3f}{std_precision:0.03f}),"
            f" recall: {mean_recall:0.3f}{std_recall:0.03f}),"
            f" for {params}"
        )
    print()


def refit_strategy(cv_results):
    """Define the strategy to select the best estimator.

    The strategy defined here is to filter-out all results below a precision threshold
    of 0.98, rank the remaining by recall and keep all models with one standard
    deviation of the best by recall. Once these models are selected, we can select the
    fastest model to predict.

    Parameters
    ----------
    cv_results : dict of numpy (masked) ndarrays
        CV results as returned by the `GridSearchCV`.

    Returns
    -------
    best_index : int
        The index of the best estimator as it appears in `cv_results`.
    """
    # print the info about the grid-search for the different scores
    precision_threshold = 0.98

    cv_results_ = pd.DataFrame(cv_results)
    print("All grid-search results:")
    print_dataframe(cv_results_)

    # Filter-out all results below the threshold
    high_precision_cv_results = cv_results_[
        cv_results_["mean_test_precision"] > precision_threshold
    ]

    print(f"Models with a precision higher than {precision_threshold}:")
    print_dataframe(high_precision_cv_results)

    high_precision_cv_results = high_precision_cv_results[
        [
            "mean_score_time",
            "mean_test_recall",
            "std_test_recall",
            "mean_test_precision",
            "std_test_precision",
            "rank_test_recall",
            "rank_test_precision",
            "params",
        ]
    ]

    # Select the most performant models in terms of recall
    # (within 1 sigma from the best)
    best_recall_std = high_precision_cv_results["mean_test_recall"].std()
    best_recall = high_precision_cv_results["mean_test_recall"].max()
    best_recall_threshold = best_recall - best_recall_std

    high_recall_cv_results = high_precision_cv_results[
        high_precision_cv_results["mean_test_recall"] > best_recall_threshold
    ]
    print(
        "Out of the previously selected high precision models, we keep all the\n"
        "the models within one standard deviation of the highest recall model:"
    )
    print_dataframe(high_recall_cv_results)

    # From the best candidates, select the fastest model to predict
    fastest_top_recall_high_precision_index = high_recall_cv_results[
        "mean_score_time"
    ].idxmin()

    print(
        "\nThe selected final model is the fastest to predict out of the previously\n"
        "selected subset of best models based on precision and recall.\n"
        "Its scoring time is:\n\n"
        f"{high_recall_cv_results.loc[fastest_top_recall_high_precision_index]}"
    )

    return fastest_top_recall_high_precision_index

Настройка гиперпараметров#

После того как мы определили нашу стратегию выбора лучшей модели, мы определяем значения гиперпараметров и создаём экземпляр поиска по сетке:

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

tuned_parameters = [
    {"kernel": ["rbf"], "gamma": [1e-3, 1e-4], "C": [1, 10, 100, 1000]},
    {"kernel": ["linear"], "C": [1, 10, 100, 1000]},
]

grid_search = GridSearchCV(
    SVC(), tuned_parameters, scoring=scores, refit=refit_strategy
)
grid_search.fit(X_train, y_train)
All grid-search results:
precision: 1.000 (±0.000), recall: 0.854 (±0.063), for {'C': 1, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.257 (±0.061), for {'C': 1, 'gamma': 0.0001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 10, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 0.968 (±0.039), recall: 0.780 (±0.083), for {'C': 10, 'gamma': 0.0001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 100, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 0.905 (±0.058), recall: 0.889 (±0.074), for {'C': 100, 'gamma': 0.0001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 1000, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 0.904 (±0.058), recall: 0.890 (±0.073), for {'C': 1000, 'gamma': 0.0001, 'kernel': 'rbf'}
precision: 0.695 (±0.073), recall: 0.743 (±0.065), for {'C': 1, 'kernel': 'linear'}
precision: 0.643 (±0.066), recall: 0.757 (±0.066), for {'C': 10, 'kernel': 'linear'}
precision: 0.611 (±0.028), recall: 0.744 (±0.044), for {'C': 100, 'kernel': 'linear'}
precision: 0.618 (±0.039), recall: 0.744 (±0.044), for {'C': 1000, 'kernel': 'linear'}

Models with a precision higher than 0.98:
precision: 1.000 (±0.000), recall: 0.854 (±0.063), for {'C': 1, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.257 (±0.061), for {'C': 1, 'gamma': 0.0001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 10, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 100, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 1000, 'gamma': 0.001, 'kernel': 'rbf'}

Out of the previously selected high precision models, we keep all the
the models within one standard deviation of the highest recall model:
precision: 1.000 (±0.000), recall: 0.854 (±0.063), for {'C': 1, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 10, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 100, 'gamma': 0.001, 'kernel': 'rbf'}
precision: 1.000 (±0.000), recall: 0.877 (±0.069), for {'C': 1000, 'gamma': 0.001, 'kernel': 'rbf'}


The selected final model is the fastest to predict out of the previously
selected subset of best models based on precision and recall.
Its scoring time is:

mean_score_time                                         0.005289
mean_test_recall                                        0.853676
std_test_recall                                         0.063184
mean_test_precision                                          1.0
std_test_precision                                           0.0
rank_test_recall                                               6
rank_test_precision                                            1
params                 {'C': 1, 'gamma': 0.001, 'kernel': 'rbf'}
Name: 0, dtype: object
GridSearchCV(estimator=SVC(),
             param_grid=[{'C': [1, 10, 100, 1000], 'gamma': [0.001, 0.0001],
                          'kernel': ['rbf']},
                         {'C': [1, 10, 100, 1000], 'kernel': ['linear']}],
             refit=,
             scoring=['precision', 'recall'])
В среде Jupyter, пожалуйста, перезапустите эту ячейку, чтобы показать HTML-представление, или доверьтесь блокноту.
На GitHub HTML-представление не может отображаться, попробуйте загрузить эту страницу с помощью nbviewer.org.


Параметры, выбранные поиском по сетке с нашей пользовательской стратегией:

grid_search.best_params_
{'C': 1, 'gamma': 0.001, 'kernel': 'rbf'}

Наконец, мы оцениваем тонко настроенную модель на оставленном набора для оценки: grid_search object был автоматически переобучен на полном обучающем наборе с параметрами, выбранными нашей пользовательской стратегией повторного обучения.

Мы можем использовать отчет о классификации для вычисления стандартных метрик классификации на отложенном наборе:

from sklearn.metrics import classification_report

y_pred = grid_search.predict(X_test)
print(classification_report(y_test, y_pred))
              precision    recall  f1-score   support

       False       0.98      1.00      0.99       807
        True       1.00      0.85      0.92        92

    accuracy                           0.98       899
   macro avg       0.99      0.92      0.95       899
weighted avg       0.98      0.98      0.98       899

Примечание

Задача слишком проста: плато гиперпараметров слишком плоское, и выходная модель одинакова для точности и полноты при равном качестве.

Общее время выполнения скрипта: (0 минут 10.207 секунд)

Связанные примеры

Precision-Recall

Precision-Recall

Баланс сложности модели и кросс-валидационной оценки

Баланс сложности модели и кросс-валидационной оценки

Сравнение рандомизированного поиска и поиска по сетке для оценки гиперпараметров

Сравнение рандомизированного поиска и поиска по сетке для оценки гиперпараметров

Рекурсивное исключение признаков с перекрестной проверкой

Рекурсивное исключение признаков с перекрестной проверкой

Галерея, созданная Sphinx-Gallery