Пост-фактумная настройка точки отсечения функции принятия решений#

После обучения бинарного классификатора predict метод выводит предсказания меток классов, соответствующие пороговой обработке либо decision_function или predict_proba вывод. Порог по умолчанию определяется как оценка апостериорной вероятности 0.5 или оценка решения 0.0. Однако эта стратегия по умолчанию может быть не оптимальной для конкретной задачи.

Этот пример показывает, как использовать TunedThresholdClassifierCV для настройки порога принятия решений в зависимости от интересующей метрики.

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

Набор данных по диабету#

Чтобы проиллюстрировать настройку порога принятия решений, мы будем использовать набор данных по диабету. Этот набор данных доступен на OpenML: https://www.openml.org/d/37. Мы используем fetch_openml функция для получения этого набора данных.

from sklearn.datasets import fetch_openml

diabetes = fetch_openml(data_id=37, as_frame=True, parser="pandas")
data, target = diabetes.data, diabetes.target

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

target.value_counts()
class
tested_negative    500
tested_positive    268
Name: count, dtype: int64

Мы видим, что имеем дело с задачей бинарной классификации. Поскольку метки не закодированы как 0 и 1, мы явно указываем, что рассматриваем класс с меткой “tested_negative” как отрицательный класс (который также является наиболее частым), а класс с меткой “tested_positive” как положительный класс:

neg_label, pos_label = target.value_counts().index

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

Наш базовый классификатор#

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

from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

model = make_pipeline(StandardScaler(), LogisticRegression())
model
Pipeline(steps=[('standardscaler', StandardScaler()),
                ('logisticregression', LogisticRegression())])
В среде Jupyter, пожалуйста, перезапустите эту ячейку, чтобы показать HTML-представление, или доверьтесь блокноту.
На GitHub HTML-представление не может отображаться, попробуйте загрузить эту страницу с помощью nbviewer.org.


Мы оцениваем нашу модель с помощью перекрестной проверки. Мы используем точность и сбалансированную точность для отчета о производительности нашей модели. Сбалансированная точность — это метрика, менее чувствительная к дисбалансу классов, что позволит нам оценить показатель точности в перспективе.

Кросс-валидация позволяет изучить дисперсию порога решения по разным разбиениям данных. Однако набор данных довольно мал, и использование более 5 фолдов для оценки дисперсии было бы вредным. Поэтому мы используем RepeatedStratifiedKFold где мы применяем несколько повторений 5-кратной перекрестной проверки.

import pandas as pd

from sklearn.model_selection import RepeatedStratifiedKFold, cross_validate

scoring = ["accuracy", "balanced_accuracy"]
cv_scores = [
    "train_accuracy",
    "test_accuracy",
    "train_balanced_accuracy",
    "test_balanced_accuracy",
]
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=42)
cv_results_vanilla_model = pd.DataFrame(
    cross_validate(
        model,
        data,
        target,
        scoring=scoring,
        cv=cv,
        return_train_score=True,
        return_estimator=True,
    )
)
cv_results_vanilla_model[cv_scores].aggregate(["mean", "std"]).T
mean std
train_accuracy 0.779751 0.007822
точность_теста 0.770926 0.030585
train_balanced_accuracy 0.732913 0.009788
test_balanced_accuracy 0.723665 0.035914


Наша прогнозная модель успешно улавливает взаимосвязь между данными и целевой переменной. Оценки на обучении и тестировании близки друг к другу, что означает, что наша прогнозная модель не переобучается. Мы также можем наблюдать, что сбалансированная точность ниже обычной точности из-за ранее упомянутого дисбаланса классов.

Для этого классификатора мы оставляем порог принятия решения, используемый для преобразования вероятности положительного класса в предсказание класса, равным значению по умолчанию: 0.5. Однако этот порог может быть не оптимальным. Если наша цель — максимизировать сбалансированную точность, мы должны выбрать другой порог, который максимизирует эту метрику.

The TunedThresholdClassifierCV мета-оценщик позволяет настроить порог принятия решений классификатора для заданной метрики интереса.





Настройка порога принятия решения#

Мы создаем TunedThresholdClassifierCV и настроить его для максимизации сбалансированной точности. Мы оцениваем модель, используя ту же стратегию перекрестной проверки, что и ранее.

from sklearn.model_selection import TunedThresholdClassifierCV

tuned_model = TunedThresholdClassifierCV(estimator=model, scoring="balanced_accuracy")
cv_results_tuned_model = pd.DataFrame(
    cross_validate(
        tuned_model,
        data,
        target,
        scoring=scoring,
        cv=cv,
        return_train_score=True,
        return_estimator=True,
    )
)
cv_results_tuned_model[cv_scores].aggregate(["mean", "std"]).T
mean std
train_accuracy 0.752470 0.015579
точность_теста 0.739950 0.036592
train_balanced_accuracy 0.757915 0.009747
test_balanced_accuracy 0.744029 0.035445


По сравнению с базовой моделью мы наблюдаем увеличение сбалансированной точности. Конечно, это происходит за счет более низкой общей точности. Это означает, что наша модель теперь более чувствительна к положительному классу, но делает больше ошибок на отрицательном классе.

Однако важно отметить, что эта настроенная прогностическая модель внутренне является той же моделью, что и базовая модель: они имеют одинаковые подобранные коэффициенты.

import matplotlib.pyplot as plt

vanilla_model_coef = pd.DataFrame(
    [est[-1].coef_.ravel() for est in cv_results_vanilla_model["estimator"]],
    columns=diabetes.feature_names,
)
tuned_model_coef = pd.DataFrame(
    [est.estimator_[-1].coef_.ravel() for est in cv_results_tuned_model["estimator"]],
    columns=diabetes.feature_names,
)

fig, ax = plt.subplots(ncols=2, figsize=(12, 4), sharex=True, sharey=True)
vanilla_model_coef.boxplot(ax=ax[0])
ax[0].set_ylabel("Coefficient value")
ax[0].set_title("Vanilla model")
tuned_model_coef.boxplot(ax=ax[1])
ax[1].set_title("Tuned model")
_ = fig.suptitle("Coefficients of the predictive models")
Coefficients of the predictive models, Vanilla model, Tuned model

Только порог принятия решения каждой модели изменялся во время перекрёстной проверки.

decision_threshold = pd.Series(
    [est.best_threshold_ for est in cv_results_tuned_model["estimator"]],
)
ax = decision_threshold.plot.kde()
ax.axvline(
    decision_threshold.mean(),
    color="k",
    linestyle="--",
    label=f"Mean decision threshold: {decision_threshold.mean():.2f}",
)
ax.set_xlabel("Decision threshold")
ax.legend(loc="upper right")
_ = ax.set_title(
    "Distribution of the decision threshold \nacross different cross-validation folds"
)
Distribution of the decision threshold  across different cross-validation folds

В среднем, порог принятия решения около 0.32 максимизирует сбалансированную точность, что отличается от стандартного порога принятия решения 0.5. Таким образом, настройка порога принятия решения особенно важна, когда выход прогнозной модели используется для принятия решений. Кроме того, метрика, используемая для настройки порога принятия решения, должна быть выбрана тщательно. Здесь мы использовали сбалансированную точность, но она может быть не самой подходящей метрикой для данной задачи. Выбор «правильной» метрики обычно зависит от проблемы и может требовать некоторых знаний предметной области. См. пример под названием, Последующая настройка порога принятия решений для обучения с учетом стоимости, для получения дополнительной информации.

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

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

Последующая настройка порога принятия решений для обучения с учетом стоимости

Последующая настройка порога принятия решений для обучения с учетом стоимости

Основные новости выпуска scikit-learn 1.5

Основные новости выпуска scikit-learn 1.5

Влияние изменения порога для самообучения

Влияние изменения порога для самообучения

Примеры использования FrozenEstimator

Примеры использования FrozenEstimator

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