Примечание
Перейти к концу для загрузки полного кода примера. Или запустите этот пример в вашем браузере через JupyterLite или Binder
Кривые калибровки вероятностей
При выполнении классификации часто требуется предсказывать не только метку класса, но и соответствующую вероятность. Эта вероятность даёт некую уверенность в прогнозе. В этом примере показано, как визуализировать, насколько хорошо калиброваны предсказанные вероятности с помощью кривых калибровки, также известных как диаграммы надёжности. Также будет продемонстрировано калибрование некалиброванного классификатора.
# Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause
Набор данных
Мы будем использовать синтетический набор данных для бинарной классификации с 100 000 примерами и 20 признаками. Из 20 признаков только 2 информативны, 10 избыточны (случайные комбинации информативных признаков), а оставшиеся 8 неинформативны (случайные числа). Из 100 000 примеров 1000 будет использовано для подбора модели, а остальные — для тестирования.
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с исотонической и сигмоидной калибровкой (см. Руководство пользователя)
Ниже приведены кривые калибровки для всех четырёх условий, где по оси абсцисс указана средняя предсказанная вероятность для каждого интервала, а по оси ординат — доля положительных классов в каждом интервале.
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()

Некалиброванный GaussianNB плохо калибруется из-за избыточных признаков, которые нарушают предположение о независимости признаков и приводят к излишне уверенному классификатору, что видно по типичной кривой, трансформирующей сигмоиду. Калибровка вероятностей GaussianNB с помощью Исотонической регрессии может исправить эту проблему, как видно из почти диагональной кривой калибровки. Сигмоидная регрессия также немного улучшает калибровку, хотя и не так сильно, как непараметрическая исотоническая регрессия. Это можно объяснить тем, что у нас достаточно данных для калибровки, так что можно использовать большую гибкость непараметрической модели.
Ниже мы проведем количественный анализ, учитывая несколько метрик классификации: Потеря Бриера, Логарифмическая потеря, Точность, полнота, F1-мера и AUC ROC.
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
Обратите внимание, что хотя калибровка улучшает потерю Бриера (метрика, состоящая из калибровочной части и части уточнения) и логарифмическую потерю, она не существенно изменяет метрики точности прогноза (точность, полнота и F1-мера). Это связано с тем, что калибровка не должна значительно изменять вероятности предсказания в месте порогового значения (в x = 0,5 на графике). Однако калибровка должна сделать предсказанные вероятности более точными и, следовательно, более полезными для принятия решений о распределении при неопределённости. Кроме того, AUC ROC не должна меняться вообще, поскольку калибровка — это монотонное преобразование. Действительно, метрики ранжирования не изменяются при калибровке.
Линейный классификатор опорных векторов
Далее мы сравним:
-
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()

LinearSVC показывает противоположное поведение по сравнению с GaussianNB; кривая калибровки имеет сигмоидную форму, что характерно для классификатора с недостаточной уверенностью. В случае с LinearSVC это вызвано свойством маржи функции потерь хинджа, которая фокусируется на примерах, близких к границе принятия решения (опорные векторы). Примеры, находящиеся далеко от границы принятия решения, не влияют на функцию потерь хинджа. Поэтому логично, что LinearSVC не пытается разделить примеры в областях высокой уверенности. Это приводит к более плоским кривым калибровки около 0 и 1, и это эмпирически показано с использованием различных наборов данных в работе Niculescu-Mizil & Caruana [1].
Оба вида калибровки (сигмоидная и исотоническая) могут исправить эту проблему и привести к схожим результатам.
Как и прежде, мы покажем потерю Бриера, логарифмическую потерю, точность, полноту, F1-меру и AUC ROC.
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
Как и в случае с GaussianNB выше, калибровка улучшает как потерю Бриера, так и логарифмическую потерю, но не сильно изменяет метрики точности прогноза (точность, полнота и F1-мера).
Краткое описание
Параметрическая сигмоидная калибровка может справляться с ситуациями, когда кривая калибровки базового классификатора является сигмоидальной (например, для LinearSVC) но не там, где она является перевёрнуто-сигмоидальной (например, GaussianNB). Непараметрическая изотоническая калибровка может справляться с обеими ситуациями, но может потребовать больше данных для получения хороших результатов.
Ссылки
Общее время выполнения скрипта: (0 минут 2.267 секунды)
Связанные примеры
© 2007–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/auto_examples/calibration/plot_calibration_curve.html