Примечание
Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.
Настройка порога функции принятия решений после обучения
После обучения бинарного классификатора метод predict выдает предсказания меток классов, соответствующие пороговому значению выхода либо decision_function, либо predict_proba. Пороговое значение по умолчанию определяется как апостериорная оценка вероятности 0.5 или оценка решения 0.0. Однако такая стратегия по умолчанию может быть неоптимальной для конкретной задачи.
В этом примере показано, как использовать TunedThresholdClassifierCV для настройки порога принятия решений в зависимости от интересующей нас метрики.
# Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause
Набор данных diabetes
Чтобы проиллюстрировать настройку порога принятия решений, мы воспользуемся набором данных diabetes. Этот набор данных доступен на 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
Мы оцениваем нашу модель с помощью перекрёстной проверки. Для оценки качества модели мы используем точность и сбалансированную точность. Сбалансированная точность — это метрика, менее чувствительная к дисбалансу классов; она позволяет оценить показатель точности в контексте.
Перекрёстная проверка позволяет изучить дисперсию порога принятия решения на разных разбиениях данных. Однако набор данных довольно мал, и использование более чем 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
Наша прогностическая модель успешно выявляет взаимосвязь между данными и целевой переменной. Оценки на обучающей и тестовой выборках близки друг к другу, а значит, наша прогностическая модель не переобучается. Также можно заметить, что сбалансированная точность ниже обычной точности из-за упомянутого ранее дисбаланса классов.
Для этого классификатора мы оставили порог принятия решения, используемый для преобразования вероятности положительного класса в предсказание класса, равным значению по умолчанию: 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
По сравнению с обычной моделью мы видим, что показатель сбалансированной точности вырос. Разумеется, это происходит за счет снижения обычной точности. Это означает, что теперь наша модель более чувствительна к положительному классу, но чаще ошибается на отрицательном классе.
Однако важно отметить, что эта настроенная прогностическая модель внутри является той же моделью, что и обычная: у них одинаковые обученные коэффициенты.
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")

Во время кросс-валидации изменялся только порог принятия решения каждой модели.
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"
)

В среднем порог принятия решения около 0.32 максимизирует сбалансированную точность, что отличается от значения порога по умолчанию, равного 0.5. Поэтому настройка порога принятия решения особенно важна, когда выходные данные прогностической модели используются для принятия решений. Кроме того, метрику для настройки порога принятия решения следует выбирать внимательно. Здесь мы использовали сбалансированную точность, но она может быть не самой подходящей метрикой для рассматриваемой задачи. Выбор «правильной» метрики обычно зависит от задачи и может потребовать специальных знаний в предметной области. Подробнее см. пример Настройка порога принятия решения после обучения для обучения с учетом затрат.
Общее время выполнения скрипта: (0 минут 41.564 секунды)
Связанные примеры
© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/model_selection/plot_tuned_decision_threshold.html