Spec-Zone.ru › scikit-learn

Примечание

Перейти к концу для скачивания полного примера кода. Или запустите этот пример в вашем браузере через JupyterLite или Binder

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

После обучения бинарного классификатора метод 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.
Pipeline(steps=[('standardscaler', StandardScaler()),
                ('logisticregression', LogisticRegression())])
...


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

Перекрестная проверка позволяет изучить дисперсию порога принятия решений при разных разбиениях данных. Однако набор данных довольно мал, и использование более 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
test_accuracy 0.770926 0.030585
train_balanced_accuracy 0.732913 0.009788
test_balanced_accuracy 0.723665 0.035914


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

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

Метаэстиматор 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
meanstd
train_accuracy0.7524700.015579
test_accuracy0.7399500.036592
train_balanced_accuracy0.7579150.009747
test_balanced_accuracy0.7440290.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.994 секунды)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_tuned_decision_threshold.ipynb

Download Python source code: plot_tuned_decision_threshold.py

Download zipped: plot_tuned_decision_threshold.zip

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

Настройка порога принятия решений для задач с учётом затрат

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

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

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

© 2007–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/auto_examples/model_selection/plot_tuned_decision_threshold.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API