Spec-Zone.ru › scikit-learn

Примечание

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

Кривые калибровки вероятностей

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

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

Набор данных

Мы будем использовать синтетический набор данных для бинарной классификации со 100 000 образцами и 20 признаками. Из 20 признаков только 2 являются информативными, 10 — избыточными (случайные комбинации информативных признаков), а остальные 8 — неинформативными (случайные числа). Из 100 000 образцов 1 000 будут использоваться для обучения модели, а остальные — для тестирования.

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

X, y = make_classification(
    n_samples=100_000, n_features=20, n_informative=2, n_redundant=10, random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.99, random_state=42
)

Кривые калибровки

Наивный байесовский классификатор с распределением Гаусса

Сначала мы сравним:

  • LogisticRegression (используется в качестве базового классификатора, поскольку правильно настроенная регуляризация логистической регрессии часто обеспечивает хорошую калибровку по умолчанию благодаря использованию логарифмической функции потерь)
  • Некалиброванный GaussianNB
  • GaussianNB с изотонической и сигмоидальной калибровкой (см. Руководство пользователя)

Ниже построены кривые калибровки для всех 4 вариантов: по оси x отложена средняя предсказанная вероятность для каждого интервала, а по оси y — доля положительных классов в каждом интервале.

import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

from sklearn.calibration import CalibratedClassifierCV, CalibrationDisplay
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB

lr = LogisticRegression(C=1.0)
gnb = GaussianNB()
gnb_isotonic = CalibratedClassifierCV(gnb, cv=2, method="isotonic")
gnb_sigmoid = CalibratedClassifierCV(gnb, cv=2, method="sigmoid")

clf_list = [
    (lr, "Logistic"),
    (gnb, "Naive Bayes"),
    (gnb_isotonic, "Naive Bayes + Isotonic"),
    (gnb_sigmoid, "Naive Bayes + Sigmoid"),
]
fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)
colors = plt.get_cmap("Dark2")

ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    display = CalibrationDisplay.from_estimator(
        clf,
        X_test,
        y_test,
        n_bins=10,
        name=name,
        ax=ax_calibration_curve,
        color=colors(i),
    )
    calibration_displays[name] = display

ax_calibration_curve.grid()
ax_calibration_curve.set_title("Calibration plots (Naive Bayes)")

# Add histogram
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
    row, col = grid_positions[i]
    ax = fig.add_subplot(gs[row, col])

    ax.hist(
        calibration_displays[name].y_prob,
        range=(0, 1),
        bins=10,
        label=name,
        color=colors(i),
    )
    ax.set(title=name, xlabel="Mean predicted probability", ylabel="Count")

plt.tight_layout()
plt.show()
Calibration plots (Naive Bayes), Logistic, Naive Bayes, Naive Bayes + Isotonic, Naive Bayes + Sigmoid

Некалиброванный GaussianNB плохо откалиброван из-за избыточных признаков, нарушающих предположение о независимости признаков и приводящих к чрезмерно уверенным предсказаниям классификатора. На это указывает типичная кривая в форме перевернутой сигмоиды. Калибровка вероятностей GaussianNB с помощью изотонической регрессии позволяет устранить эту проблему, что видно по почти диагональной кривой калибровки. Сигмоидальная регрессия также немного улучшает калибровку, хотя и не так значительно, как непараметрическая изотоническая регрессия. Это можно объяснить тем, что у нас достаточно данных для калибровки, поэтому можно воспользоваться большей гибкостью непараметрической модели.

Ниже мы проведем количественный анализ с использованием нескольких метрик классификации: потери по оценке Брайера, логарифмические потери, точность, полнота и оценка F1 и ROC AUC.

from collections import defaultdict

import pandas as pd

from sklearn.metrics import (
    brier_score_loss,
    f1_score,
    log_loss,
    precision_score,
    recall_score,
    roc_auc_score,
)

scores = defaultdict(list)
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    y_prob = clf.predict_proba(X_test)
    y_pred = clf.predict(X_test)
    scores["Classifier"].append(name)

    for metric in [brier_score_loss, log_loss, roc_auc_score]:
        score_name = metric.__name__.replace("_", " ").replace("score", "").capitalize()
        scores[score_name].append(metric(y_test, y_prob[:, 1]))

    for metric in [precision_score, recall_score, f1_score]:
        score_name = metric.__name__.replace("_", " ").replace("score", "").capitalize()
        scores[score_name].append(metric(y_test, y_pred))

    score_df = pd.DataFrame(scores).set_index("Classifier")
    score_df.round(decimals=3)

score_df
Потери по оценке Брайера Логарифмические потери ROC AUC Точность Полнота F1
Классификатор
Логистическая регрессия 0.098932 0.323200 0.937443 0.871965 0.851348 0.861533
Наивный Байес 0.117608 0.782755 0.940373 0.857400 0.875941 0.866571
Наивный Байес + изотоническая регрессия 0.098332 0.370738 0.938613 0.883065 0.836224 0.859007
Наивный Байес + сигмоидальная регрессия 0.108880 0.368896 0.940201 0.861106 0.871277 0.866161


Обратите внимание: хотя калибровка улучшает потери по оценке Брайера (метрику, состоящую из компонента калибровки и компонента уточнения) и логарифмические потери, она существенно не изменяет показатели точности предсказаний (точность, полноту и оценку F1). Это объясняется тем, что калибровка не должна заметно менять вероятности предсказаний в точке порога принятия решения (x = 0.5 на графике). Однако калибровка должна повышать точность предсказанных вероятностей и тем самым делать их более полезными при принятии решений о распределении ресурсов в условиях неопределенности. Кроме того, ROC AUC не должна изменяться, поскольку калибровка является монотонным преобразованием. Действительно, калибровка не влияет ни на одну метрику ранжирования.

Линейный классификатор на опорных векторах

Далее мы сравним:

  • LogisticRegression (базовый классификатор)
  • Некалиброванный LinearSVC. Поскольку SVC по умолчанию не выдает вероятности, мы наивно масштабируем выход функции decision_function в диапазон [0, 1] с помощью min-max-нормализации.
  • LinearSVC с изотонической и сигмоидальной калибровкой (см. Руководство пользователя)
import numpy as np

from sklearn.svm import LinearSVC


class NaivelyCalibratedLinearSVC(LinearSVC):
    """LinearSVC with `predict_proba` method that naively scales
    `decision_function` output for binary classification."""

    def fit(self, X, y):
        super().fit(X, y)
        df = self.decision_function(X)
        self.df_min_ = df.min()
        self.df_max_ = df.max()

    def predict_proba(self, X):
        """Min-max scale output of `decision_function` to [0, 1]."""
        df = self.decision_function(X)
        calibrated_df = (df - self.df_min_) / (self.df_max_ - self.df_min_)
        proba_pos_class = np.clip(calibrated_df, 0, 1)
        proba_neg_class = 1 - proba_pos_class
        proba = np.c_[proba_neg_class, proba_pos_class]
        return proba
lr = LogisticRegression(C=1.0)
svc = NaivelyCalibratedLinearSVC(max_iter=10_000)
svc_isotonic = CalibratedClassifierCV(svc, cv=2, method="isotonic")
svc_sigmoid = CalibratedClassifierCV(svc, cv=2, method="sigmoid")

clf_list = [
    (lr, "Logistic"),
    (svc, "SVC"),
    (svc_isotonic, "SVC + Isotonic"),
    (svc_sigmoid, "SVC + Sigmoid"),
]
fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)

ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    display = CalibrationDisplay.from_estimator(
        clf,
        X_test,
        y_test,
        n_bins=10,
        name=name,
        ax=ax_calibration_curve,
        color=colors(i),
    )
    calibration_displays[name] = display

ax_calibration_curve.grid()
ax_calibration_curve.set_title("Calibration plots (SVC)")

# Add histogram
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
    row, col = grid_positions[i]
    ax = fig.add_subplot(gs[row, col])

    ax.hist(
        calibration_displays[name].y_prob,
        range=(0, 1),
        bins=10,
        label=name,
        color=colors(i),
    )
    ax.set(title=name, xlabel="Mean predicted probability", ylabel="Count")

plt.tight_layout()
plt.show()
Calibration plots (SVC), Logistic, SVC, SVC + Isotonic, SVC + Sigmoid

LinearSVC демонстрирует поведение, противоположное GaussianNB: кривая калибровки имеет форму сигмоиды, что характерно для недостаточно уверенного классификатора. В случае LinearSVC это вызвано свойством отступа функции потерь hinge, которая учитывает образцы, находящиеся близко к границе принятия решения (опорные векторы). Образцы, расположенные далеко от границы принятия решения, не влияют на функцию потерь hinge. Поэтому логично, что LinearSVC не пытается разделять образцы в областях высокой уверенности. В результате кривые калибровки становятся более пологими вблизи 0 и 1; это эмпирически показано на различных наборах данных в работе Niculescu-Mizil & Caruana [1].

Оба типа калибровки (сигмоидальная и изотоническая) могут устранить эту проблему и дают схожие результаты.

Как и прежде, мы приводим значения потерь по оценке Брайера, логарифмических потерь, точности, полноты и оценки F1 и ROC AUC.

scores = defaultdict(list)
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    y_prob = clf.predict_proba(X_test)
    y_pred = clf.predict(X_test)
    scores["Classifier"].append(name)

    for metric in [brier_score_loss, log_loss, roc_auc_score]:
        score_name = metric.__name__.replace("_", " ").replace("score", "").capitalize()
        scores[score_name].append(metric(y_test, y_prob[:, 1]))

    for metric in [precision_score, recall_score, f1_score]:
        score_name = metric.__name__.replace("_", " ").replace("score", "").capitalize()
        scores[score_name].append(metric(y_test, y_pred))

    score_df = pd.DataFrame(scores).set_index("Classifier")
    score_df.round(decimals=3)

score_df
Потери по оценке Брайера Логарифмические потери ROC AUC Точность Полнота F1
Классификатор
Логистическая регрессия 0.098932 0.323200 0.937443 0.871965 0.851348 0.861533
SVC 0.144943 0.465660 0.937597 0.872186 0.851792 0.861868
SVC + изотоническая регрессия 0.099820 0.376999 0.936480 0.853174 0.877981 0.865400
SVC + сигмоидальная регрессия 0.098758 0.321301 0.937532 0.873724 0.848743 0.861053


Как и в случае с рассмотренным выше GaussianNB, калибровка улучшает показатели и потерь по оценке Брайера, и логарифмических потерь, но мало влияет на показатели точности предсказаний (точность, полноту и оценку F1).

Итоги

Параметрическая сигмоидальная калибровка позволяет справиться с ситуациями, когда кривая калибровки базового классификатора имеет форму сигмоиды (например, для LinearSVC), но не с теми, где она имеет форму перевернутой сигмоиды (например, для GaussianNB). Непараметрическая изотоническая калибровка подходит для обеих ситуаций, но для получения хороших результатов ей может потребоваться больше данных.

Список литературы

[1]

Предсказание хороших вероятностей с помощью обучения с учителем, A. Niculescu-Mizil & R. Caruana, ICML 2005

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

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_calibration_curve.ipynb

Download Python source code: plot_calibration_curve.py

Download zipped: plot_calibration_curve.zip

Похожие примеры

Калибровка вероятностей классификаторов

Сравнение калибровки классификаторов

Калибровка вероятностей для классификации на 3 класса

Анализ сходимости моделей логистической регрессии с регуляризацией

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/calibration/plot_calibration_curve.html

Spec-Zone.ru

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