Построение ROC-AUC кривой в Sklearn: руководство для аналитиков
Пройдите тест, узнайте какой профессии подходите
Для кого эта статья:
- аналитики данных и специалисты по машинному обучению
- студенты и начинающие профессионалы в области Data Science
- эксперты, заинтересованные в углубленном понимании метрик оценки моделей классификации
Оценка качества моделей классификации — ключевой элемент успешного проекта машинного обучения. Среди множества метрик, ROC-AUC выделяется своей устойчивостью к несбалансированным данным и способностью комплексно оценить качество модели. Каждый практикующий аналитик данных сталкивается с необходимостью объективно оценить эффективность модели, особенно когда на кону стоят серьёзные бизнес-решения. Sklearn предоставляет мощные инструменты для построения и анализа ROC-AUC кривых, но многие специалисты до сих пор используют этот функционал поверхностно. 📊
Если вы регулярно работаете с моделями классификации и хотите освоить продвинутые методы их оценки, включая ROC-AUC анализ, рекомендую обратить внимание на Курс «Аналитик данных» с нуля от Skypro. Программа курса включает как теоретическую базу, так и практические задания по построению и интерпретации ROC-кривых в реальных проектах. Выпускники курса умеют обосновывать выбор моделей на основе метрик, что существенно повышает их ценность на рынке труда.
Концепция ROC-АУC кривой в оценке моделей классификации
ROC-кривая (Receiver Operating Characteristic) — графический метод оценки качества бинарных классификаторов, который отображает соотношение между чувствительностью модели (True Positive Rate, TPR) и вероятностью ложного срабатывания (False Positive Rate, FPR) при различных порогах отсечения. Площадь под ROC-кривой (AUC, Area Under Curve) служит агрегированной характеристикой качества классификации.
Использование ROC-AUC в анализе моделей классификации предоставляет ряд преимуществ:
- Инвариантность к дисбалансу классов — в отличие от точности (accuracy), ROC-AUC остаётся информативной метрикой даже при значительном смещении распределения целевого признака
- Полнота оценки — учитываются все возможные пороги классификации одновременно
- Возможность сравнения моделей независимо от выбранного порога отсечения
- Наглядность представления компромисса между ложноположительными и ложноотрицательными результатами
При выборе методов оценки важно понимать контекст задачи. Для этого полезно сравнить распространённые метрики:
Метрика | Преимущества | Недостатки | Применимость |
---|---|---|---|
ROC-AUC | Устойчивость к дисбалансу классов, комплексная оценка | Не всегда интуитивно понятна, сложна в интерпретации для неспециалистов | Задачи с варьирующим порогом отсечения, сравнение моделей |
Accuracy | Проста для понимания, интуитивная интерпретация | Чувствительна к несбалансированным данным | Сбалансированные наборы данных, равнозначность ошибок |
F1-score | Баланс между точностью и полнотой | Не учитывает истинно-отрицательные случаи | Несбалансированные данные, когда важны и точность, и полнота |
Precision-Recall AUC | Фокус на положительном классе | Игнорирует true negatives | Задачи с высоким приоритетом положительного класса |
Алексей Северин, Lead Data Scientist
Однажды мы разрабатывали модель прогнозирования оттока клиентов для крупного телеком-оператора. Первоначальная модель показала accuracy около 85%, что выглядело впечатляющим. Однако при детальном анализе выяснилось, что модель просто предсказывает "клиент останется" для всех случаев, и такая высокая точность объяснялась тем, что уходило лишь 15% клиентов.
Построив ROC-кривую, мы обнаружили, что AUC составляет всего 0.62, что немного лучше случайного угадывания. Это заставило нас пересмотреть подход, улучшить признаки и применить техники работы с несбалансированными данными. После доработки, несмотря на то, что accuracy выросла лишь до 87%, AUC достиг 0.91, что свидетельствовало о существенном улучшении качества модели и её способности различать уходящих клиентов.
Теоретическая интерпретация значений AUC:
- AUC = 0.5: модель не имеет дискриминационной способности (эквивалентна случайному угадыванию)
- 0.5 < AUC < 0.7: слабая дискриминационная способность
- 0.7 ≤ AUC < 0.8: приемлемая дискриминационная способность
- 0.8 ≤ AUC < 0.9: хорошая дискриминационная способность
- AUC ≥ 0.9: отличная дискриминационная способность
Распространённое заблуждение — восприятие AUC как вероятности того, что классификатор ранжирует случайно выбранный положительный пример выше, чем случайно выбранный отрицательный пример. Хотя статистически это верно, эта интерпретация часто упускает практическую значимость метрики в контексте конкретной задачи. 🧠

Математический фундамент построения ROC-кривой
Построение ROC-кривой основано на анализе соотношения между чувствительностью (TPR) и специфичностью модели, или, точнее, на соотношении TPR и FPR при различных пороговых значениях. Для их вычисления необходимо понимать базовую матрицу ошибок (confusion matrix) классификатора:
- True Positive Rate (TPR) = TP / (TP + FN) — доля правильно классифицированных положительных примеров
- False Positive Rate (FPR) = FP / (FP + TN) — доля отрицательных примеров, ошибочно классифицированных как положительные
Алгоритм построения ROC-кривой включает следующие шаги:
- Получение вероятностных предсказаний модели для тестового набора данных
- Сортировка предсказаний по убыванию вероятностей
- Последовательный перебор порогов классификации от максимального значения вероятности до минимального
- Расчет TPR и FPR для каждого порога
- Построение графика, где ось X представляет FPR, а ось Y — TPR
Площадь под ROC-кривой (AUC) вычисляется как определенный интеграл:
AUC = ∫ TPR(FPR) dFPR
На практике, поскольку мы имеем дискретный набор точек, AUC рассчитывается с использованием метода трапеций:
AUC = Σ (FPR[i+1] – FPR[i]) × (TPR[i] + TPR[i+1]) / 2
Для понимания математического смысла, полезно рассмотреть вероятностную интерпретацию AUC. Пусть у нас есть два примера из разных классов (положительный и отрицательный). AUC эквивалентна вероятности того, что модель присвоит положительному примеру более высокий ранг (вероятность принадлежности к положительному классу), чем отрицательному примеру.
AUC также можно выразить через статистику Уилкоксона-Манна-Уитни, которая определяет, насколько хорошо модель различает распределения вероятностей для положительного и отрицательного классов:
AUC = P(score(x₊) > score(x₋)), где x₊ и x₋ — случайные экземпляры положительного и отрицательного классов
Мария Терехина, Senior Data Analyst
В проекте по предсказанию кредитных дефолтов я столкнулась с интересной ситуацией. Две модели — градиентный бустинг и логистическая регрессия — показывали похожие значения AUC около 0.82. Казалось, что модели равноценны.
Однако детальный анализ формы ROC-кривых выявил существенные различия. Кривая бустинга была более выпуклой в левом верхнем углу, что указывало на лучшую идентификацию наиболее вероятных дефолтов. Логистическая регрессия, напротив, имела более равномерное распределение качества предсказаний.
Учитывая, что для бизнеса было критично выявлять самые рискованные заявки (даже ценой пропуска некоторых "средних" случаев), мы выбрали градиентный бустинг, хотя интегральные метрики моделей были идентичны. Этот выбор увеличил эффективность скоринговой системы на 23% при том же уровне отказов.
При расчете AUC могут возникать патологические случаи, например, когда модель всегда предсказывает один класс. В таких ситуациях AUC может не отражать реальную производительность модели. Математически это выражается в том, что ROC-кривая вырождается в прямую линию, соединяющую точки (0,0) и (1,1), давая AUC = 0.5.
Характеристики ROC-кривой | Математическая интерпретация | Практическое значение |
---|---|---|
Выпуклость | Степень отклонения от диагональной линии | Указывает на различительную способность модели |
Наклон в различных участках | Производная TPR по FPR | Эффективность классификации при определенных уровнях порога |
Точка (0,1) | Идеальный классификатор | Теоретический максимум производительности |
Точка на кривой с максимальным расстоянием от диагонали | max(TPR – FPR) | Оптимальный порог классификации по метрике Юдена |
Частичная AUC | Интеграл на ограниченном диапазоне FPR | Оценка модели на релевантном диапазоне работы |
Для непрерывных классификаторов можно доказать, что AUC инвариантна относительно монотонных преобразований выходов модели, что делает её особенно ценной для сравнения различных алгоритмов без необходимости тщательной калибровки их выходных вероятностей. 📏
Практическое построение ROC-кривой с помощью Sklearn
Sklearn предоставляет удобные инструменты для построения и анализа ROC-кривых. Рассмотрим пошаговый процесс с использованием библиотеки scikit-learn для практического построения ROC-кривой.
Начнем с импорта необходимых библиотек и подготовки данных:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, auc, roc_auc_score
from sklearn.ensemble import RandomForestClassifier
# Генерируем синтетические данные для бинарной классификации
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
# Разделяем на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
# Обучаем модель
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Получаем вероятностные предсказания для тестовой выборки
y_proba = model.predict_proba(X_test)[:, 1]
Теперь построим ROC-кривую с помощью функций из модуля metrics:
# Рассчитываем точки ROC-кривой
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
# Вычисляем площадь под кривой
roc_auc = auc(fpr, tpr)
# Визуализируем результат
plt.figure(figsize=(10, 8))
plt.plot(fpr, tpr, color='darkorange', lw=2,
label=f'ROC curve (AUC = {roc_auc:.3f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0\.0, 1.0])
plt.ylim([0\.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()
# Можно также напрямую вычислить AUC
print(f"ROC AUC score: {roc_auc_score(y_test, y_proba):.3f}")
При построении ROC-кривой для реальных проектов полезно также отображать пороги классификации на кривой, чтобы лучше понимать операционные характеристики модели при различных условиях:
def plot_roc_with_thresholds(y_true, y_proba, n_thresholds=10):
"""
Строит ROC-кривую с отмеченными порогами классификации
Parameters:
-----------
y_true : array-like
Истинные метки классов
y_proba : array-like
Вероятностные предсказания модели
n_thresholds : int
Количество порогов для отображения
"""
fpr, tpr, thresholds = roc_curve(y_true, y_proba)
roc_auc = auc(fpr, tpr)
# Выбираем равномерно распределенные индексы порогов
indices = np.linspace(0, len(thresholds) – 1, n_thresholds).astype(int)
plt.figure(figsize=(12, 10))
# Основная ROC-кривая
plt.plot(fpr, tpr, color='darkorange', lw=2,
label=f'ROC curve (AUC = {roc_auc:.3f})')
# Отмечаем выбранные пороги
for i in indices:
plt.annotate(f'{thresholds[i]:.2f}',
(fpr[i], tpr[i]),
textcoords="offset points",
xytext=(0,10),
ha='center')
plt.plot(fpr[i], tpr[i], 'ro', markersize=5)
# Диагональная линия (случайная модель)
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0\.0, 1.0])
plt.ylim([0\.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve with Classification Thresholds')
plt.legend(loc="lower right")
plt.grid(True)
plt.show()
# Пример использования
plot_roc_with_thresholds(y_test, y_proba, n_thresholds=8)
Для сравнения нескольких моделей на одном графике можно использовать следующий подход:
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from xgboost import XGBClassifier
# Создаем и обучаем несколько моделей
models = {
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'Logistic Regression': LogisticRegression(random_state=42),
'SVM': SVC(probability=True, random_state=42),
'XGBoost': XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42)
}
plt.figure(figsize=(12, 10))
# Цикл по моделям
for name, model in models.items():
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]
# Рассчитываем ROC-кривую
fpr, tpr, _ = roc_curve(y_test, y_proba)
roc_auc = auc(fpr, tpr)
# Отображаем на графике
plt.plot(fpr, tpr, lw=2,
label=f'{name} (AUC = {roc_auc:.3f})')
# Добавляем диагональную линию и оформление
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0\.0, 1.0])
plt.ylim([0\.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curves for Different Models')
plt.legend(loc="lower right")
plt.grid(True)
plt.show()
Для вычисления доверительных интервалов AUC можно использовать метод бутстрапа:
from sklearn.utils import resample
def bootstrap_auc(y_true, y_pred, n_bootstraps=1000, alpha=0.05):
"""
Вычисляет AUC с доверительными интервалами с помощью бутстрапа
Parameters:
-----------
y_true : array-like
Истинные метки классов
y_pred : array-like
Вероятностные предсказания модели
n_bootstraps : int
Количество итераций бутстрапа
alpha : float
Уровень значимости для доверительного интервала
Returns:
--------
auc : float
Среднее значение AUC
ci_lower : float
Нижняя граница доверительного интервала
ci_upper : float
Верхняя граница доверительного интервала
"""
auc_scores = []
for i in range(n_bootstraps):
# Создаем бутстрап-выборку
indices = resample(range(len(y_true)), replace=True)
y_true_boot = np.array(y_true)[indices]
y_pred_boot = np.array(y_pred)[indices]
# Вычисляем AUC для бутстрап-выборки
if len(np.unique(y_true_boot)) < 2:
# Пропускаем итерации, где все метки одинаковые
continue
auc_score = roc_auc_score(y_true_boot, y_pred_boot)
auc_scores.append(auc_score)
# Вычисляем средний AUC и доверительные интервалы
auc_score = np.mean(auc_scores)
ci_lower = np.percentile(auc_scores, alpha/2 * 100)
ci_upper = np.percentile(auc_scores, (1 – alpha/2) * 100)
return auc_score, ci_lower, ci_upper
# Пример использования
auc_mean, auc_ci_lower, auc_ci_upper = bootstrap_auc(y_test, y_proba)
print(f"AUC: {auc_mean:.3f} (95% CI: {auc_ci_lower:.3f}-{auc_ci_upper:.3f})")
Использование кросс-валидации для более надежной оценки ROC AUC:
from sklearn.model_selection import cross_val_predict, StratifiedKFold
# Настраиваем кросс-валидацию
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# Получаем предсказания через кросс-валидацию
y_proba_cv = cross_val_predict(
model,
X,
y,
cv=cv,
method='predict_proba'
)[:, 1]
# Строим ROC-кривую на основе кросс-валидации
fpr_cv, tpr_cv, _ = roc_curve(y, y_proba_cv)
roc_auc_cv = auc(fpr_cv, tpr_cv)
print(f"Cross-validated ROC AUC: {roc_auc_cv:.3f}")
Sklearn также позволяет сохранять и визуализировать информацию о порогах, что помогает выбрать оптимальное значение в зависимости от требований задачи. 🧮
Интерпретация метрики AUC и оптимизация порога модели
Интерпретация значений AUC требует не только понимания математического смысла этой метрики, но и учета контекста решаемой задачи. В условиях реальной разработки необходимо связать числовое значение AUC с бизнес-показателями и принять решение о пороге отсечения, который оптимизирует целевую функцию.
Рассмотрим распространённые методы выбора оптимального порога классификации:
- Индекс Юдена (J) – максимизация разности между TPR и FPR: (J = \max(TPR – FPR))
- Минимизация расстояния до идеальной классификации: (\min(\sqrt{(1-TPR)^2 + FPR^2}))
- Максимизация F1-score: оптимальный баланс между precision и recall
- Максимизация экономического эффекта: учитываются издержки от ошибок I и II рода
- Фиксированный FPR или TPR: обеспечение заданного уровня безопасности или полноты
Реализация поиска оптимального порога в Sklearn:
from sklearn.metrics import precision_recall_curve, f1_score
def find_optimal_threshold(y_true, y_proba, method='youden'):
"""
Находит оптимальный порог в зависимости от выбранного метода
Parameters:
-----------
y_true : array-like
Истинные метки классов
y_proba : array-like
Вероятностные предсказания модели
method : str
Метод выбора порога ('youden', 'distance', 'f1')
Returns:
--------
threshold : float
Оптимальный порог
"""
fpr, tpr, thresholds_roc = roc_curve(y_true, y_proba)
if method == 'youden':
# Индекс Юдена: максимизация (TPR – FPR)
optimal_idx = np.argmax(tpr – fpr)
return thresholds_roc[optimal_idx]
elif method == 'distance':
# Минимизация расстояния до идеальной точки (0,1)
distance = np.sqrt((1 – tpr) ** 2 + fpr ** 2)
optimal_idx = np.argmin(distance)
return thresholds_roc[optimal_idx]
elif method == 'f1':
# Максимизация F1-score
precision, recall, thresholds_pr = precision_recall_curve(y_true, y_proba)
# Добавляем крайний случай
if len(precision) > len(thresholds_pr):
thresholds_pr = np.append(thresholds_pr, 1.0)
f1_scores = 2 * (precision * recall) / (precision + recall + 1e-10)
optimal_idx = np.argmax(f1_scores)
return thresholds_pr[optimal_idx]
else:
raise ValueError(f"Unknown method: {method}")
Для экономического подхода к выбору порога можно внедрить анализ стоимости ошибок:
def find_cost_optimal_threshold(y_true, y_proba, cost_fp=1, cost_fn=5):
"""
Находит порог, минимизирующий общую стоимость ошибок
Parameters:
-----------
y_true : array-like
Истинные метки классов
y_proba : array-like
Вероятностные предсказания модели
cost_fp : float
Стоимость ложноположительной ошибки
cost_fn : float
Стоимость ложноотрицательной ошибки
Returns:
--------
threshold : float
Оптимальный порог
"""
thresholds = np.linspace(0.01, 0.99, 99)
costs = []
for threshold in thresholds:
y_pred = (y_proba >= threshold).astype(int)
# Количество ложноположительных и ложноотрицательных ошибок
fp = np.sum((y_pred == 1) & (y_true == 0))
fn = np.sum((y_pred == 0) & (y_true == 1))
# Общая стоимость ошибок
total_cost = fp * cost_fp + fn * cost_fn
costs.append(total_cost)
# Находим порог с минимальной стоимостью
optimal_idx = np.argmin(costs)
return thresholds[optimal_idx]
Важно учитывать, что простые метрики, такие как accuracy, могут быть неинформативны в случаях с несбалансированными данными. Рассмотрим сравнение эффективности различных порогов:
Метрика | Базовый порог (0.5) | Порог по Юдену | Порог по F1 | Экономический порог |
---|---|---|---|---|
Accuracy | 0.82 | 0.79 | 0.76 | 0.73 |
Precision | 0.78 | 0.65 | 0.60 | 0.53 |
Recall | 0.67 | 0.85 | 0.92 | 0.96 |
F1-score | 0.72 | 0.74 | 0.73 | 0.68 |
Бизнес-ценность | $5,200 | $8,300 | $9,100 | $10,500 |
При интерпретации ROC AUC следует учитывать несколько нюансов:
- Высокое значение AUC не всегда означает высокую практическую ценность модели. Важно соотносить это с контекстом задачи
- AUC показывает способность модели ранжировать экземпляры, но не оптимальность конкретного порога классификации
- В условиях сильного дисбаланса классов высокий AUC может сосуществовать с низкими показателями precision или recall при стандартном пороге 0.5
- При сравнении моделей с близкими значениями AUC, полезно рассмотреть форму ROC-кривой в областях, критичных для конкретной задачи
- Даже модель с невысоким AUC может быть полезна, если правильно выбран порог, оптимизирующий нужные бизнес-показатели
Калибровка вероятностей может улучшить интерпретируемость порогов без изменения AUC:
from sklearn.calibration import CalibratedClassifierCV
# Калибровка вероятностей модели
calibrated_model = CalibratedClassifierCV(model, cv=5, method='isotonic')
calibrated_model.fit(X_train, y_train)
# Получение калиброванных вероятностей
calibrated_proba = calibrated_model.predict_proba(X_test)[:, 1]
# AUC остается неизменным, но интерпретация порогов улучшается
print(f"Original AUC: {roc_auc_score(y_test, y_proba):.3f}")
print(f"Calibrated AUC: {roc_auc_score(y_test, calibrated_proba):.3f}")
Для систематического выбора порога полезно использовать визуализацию зависимости различных метрик от значения порога:
def plot_threshold_metrics(y_true, y_proba):
"""
Отображает зависимость различных метрик от порога классификации
"""
thresholds = np.linspace(0, 1, 100)
metrics = {
'Accuracy': [],
'Precision': [],
'Recall': [],
'F1 Score': [],
'Specificity': []
}
for threshold in thresholds:
y_pred = (y_proba >= threshold).astype(int)
# Вычисляем метрики для текущего порога
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
metrics['Accuracy'].append((tp + tn) / (tp + tn + fp + fn))
metrics['Precision'].append(tp / (tp + fp) if tp + fp > 0 else 0)
metrics['Recall'].append(tp / (tp + fn) if tp + fn > 0 else 0)
metrics['Specificity'].append(tn / (tn + fp) if tn + fp > 0 else 0)
# F1 нужно вычислить после precision и recall
precision = metrics['Precision'][-1]
recall = metrics['Recall'][-1]
f1 = 2 * (precision * recall) / (precision + recall) if precision + recall > 0 else 0
metrics['F1 Score'].append(f1)
# Визуализация
plt.figure(figsize=(12, 8))
for metric_name, metric_values in metrics.items():
plt.plot(thresholds, metric_values, label=metric_name)
plt.axvline(x=0.5, color='grey', linestyle='--', label='Standard threshold (0.5)')
plt.xlabel('Threshold')
plt.ylabel('Metric Value')
plt.title('Performance Metrics vs Classification Threshold')
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.grid(True)
plt.tight_layout()
plt.show()
Понимание взаимосвязи между ROC-кривой и Precision-Recall кривой также помогает в принятии более обоснованных решений о модели и пороге классификации, особенно в условиях несбалансированных данных. 📉
Многоклассовая классификация и ROC-AUC в Sklearn
При работе с задачами многоклассовой классификации применение ROC-AUC становится более сложным, поскольку классическая ROC-кривая определена для бинарной классификации. Sklearn предлагает несколько подходов к расширению ROC-AUC для многоклассовых задач.
Основные стратегии вычисления ROC-AUC для многоклассовой классификации:
- One-vs-Rest (OvR): для каждого класса строится отдельная ROC-кривая, рассматривая его как положительный класс, а все остальные — как отрицательный
- One-vs-One (OvO): ROC-кривая строится для каждой пары классов
- Микро-усреднение (micro-averaging): объединяются все пары (класс, не-класс) в один набор данных, и затем вычисляется общая ROC-кривая
- Макро-усреднение (macro-averaging): вычисляется средний AUC по всем классам
Реализация расчета ROC-AUC для многоклассовой задачи в Sklearn:
from sklearn.datasets import load_iris
from sklearn.multiclass import OneVsRestClassifier
from sklearn.preprocessing import label_binarize
import matplotlib.pyplot as plt
from itertools import cycle
# Загружаем данные Iris для демонстрации многоклассовой классификации
iris = load_iris()
X = iris.data
y = iris.target
# Бинаризуем выходные данные
y_bin = label_binarize(y, classes=[0, 1, 2])
n_classes = y_bin.shape[1]
# Разделяем на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y_bin, test_size=0.3,
random_state=42)
# Создаем и обучаем классификатор
clf = OneVsRestClassifier(RandomForestClassifier(n_estimators=100, random_state=42))
clf.fit(X_train, y_train)
# Получаем вероятности для каждого класса
y_score = clf.predict_proba(X_test)
# Вычисляем ROC-кривую и ROC-AUC для каждого класса
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_score[:, i])
roc_auc[i] = auc(fpr[i], tpr[i])
# Микро-усреднение
fpr["micro"], tpr["micro"], _ = roc_curve(y_test.ravel(), y_score.ravel())
roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
# Вычисляем макро-среднее
all_fpr = np.unique(np.concatenate([fpr[i] for i in range(n_classes)]))
mean_tpr = np.zeros_like(all_fpr)
for i in range(n_classes):
mean_tpr += np.interp(all_fpr, fpr[i], tpr[i])
mean_tpr /= n_classes
fpr["macro"] = all_fpr
tpr["macro"] = mean_tpr
roc_auc["macro"] = auc(fpr["macro"], tpr["macro"])
# Визуализация ROC-кривых
plt.figure(figsize=(12, 10))
# Отображаем ROC-кривую для каждого класса
colors = cycle(['blue', 'red', 'green'])
classes = iris.target_names
for i, color, cls in zip(range(n_classes), colors, classes):
plt.plot(fpr[i], tpr[i], color=color, lw=2,
label=f'ROC curve of class {cls} (AUC = {roc_auc[i]:.2f})')
# Отображаем микро- и макро-усреднённые кривые
plt.plot(fpr["micro"], tpr["micro"],
label=f'micro-average (AUC = {roc_auc["micro"]:.2f})',
color='deeppink', linestyle=':', linewidth=4)
plt.plot(fpr["macro"], tpr["macro"],
label=f'macro-average (AUC = {roc_auc["macro"]:.2f})',
color='black', linestyle=':', linewidth=4)
plt.plot([0, 1], [0, 1], 'k--', lw=2)
plt.xlim([0\.0, 1.0])
plt.ylim([0\.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Multi-class ROC Curves')
plt.legend(loc="lower right")
plt.grid(True)
plt.show()
При выборе метода усреднения важно учитывать особенности задачи:
- Микро-усреднение дает больший вес классам с большим количеством экземпляров
- Макро-усреднение придает равный вес всем классам, независимо от их размера
- Взвешенное усреднение позволяет задать свои веса для разных классов
Sklearn предоставляет удобную функцию для прямого вычисления микро- и макро-усредненных AUC:
# Простой способ вычисления усредненных AUC
macro_roc_auc = roc_auc_score(y_test, y_score, multi_class='ovr', average='macro')
micro_roc_auc = roc_auc_score(y_test, y_score, multi_class='ovr', average='micro')
weighted_roc_auc = roc_auc_score(y_test, y_score, multi_class='ovr', average='weighted')
print(f"Macro-averaged ROC AUC: {macro_roc_auc:.3f}")
print(f"Micro-averaged ROC AUC: {micro_roc_auc:.3f}")
print(f"Weighted ROC AUC: {weighted_roc_auc:.3f}")
Для многоклассовой классификации доступны разные стратегии вычисления ROC-AUC:
# Используем различные стратегии многоклассового AUC
ovr_auc = roc_auc_score(y_test, y_score, multi_class='ovr', average='macro')
ovo_auc = roc_auc_score(y_test, y_score, multi_class='ovo', average='macro')
print(f"OVR ROC AUC: {ovr_auc:.3f}")
print(f"OVO ROC AUC: {ovo_auc:.3f}")
При работе с несбалансированными многоклассовыми данными особенно важно правильно выбрать стратегию усреднения:
# Создаем несбалансированный набор данных
from sklearn.datasets import make_classification
# Генерируем несбалансированные данные
X_imbal, y_imbal = make_classification(
n_samples=1000, n_classes=3, n_informative=5,
weights=[0\.1, 0.3, 0.6], random_state=42
)
# Проверяем распределение классов
class_counts = np.bincount(y_imbal)
print("Class distribution:", class_counts)
# Бинаризуем метки для многоклассовой ROC
y_bin_imbal = label_binarize(y_imbal, classes=[0, 1, 2])
# Разделяем данные
X_train_imbal, X_test_imbal, y_train_imbal, y_test_imbal = train_test_split(
X_imbal, y_bin_imbal, test_size=0.3, random_state=42, stratify=y_bin_imbal
)
# Обучаем модель и получаем предсказания
clf_imbal = OneVsRestClassifier(RandomForestClassifier(random_state=42))
clf_imbal.fit(X_train_imbal, y_train_imbal)
y_score_imbal = clf_imbal.predict_proba(X_test_imbal)
# Сравниваем различные усреднения
macro_auc_imbal = roc_auc_score(y_test_imbal, y_score_imbal, average='macro')
micro_auc_imbal = roc_auc_score(y_test_imbal, y_score_imbal, average='micro')
weighted_auc_imbal = roc_auc_score(y_test_imbal, y_score_imbal, average='weighted')
print(f"Macro-averaged AUC (imbalanced): {macro_auc_imbal:.3f}")
print(f"Micro-averaged AUC (imbalanced): {micro_auc_imbal:.3f}")
print(f"Weighted AUC (imbalanced): {weighted_auc_imbal:.3f}")
При оценке многоклассовых моделей полезно сравнивать AUC с другими метриками, такими как F1-score и accuracy, особенно при наличии несбалансированных данных:
from sklearn.metrics import accuracy_score, f1_score, classification_report
# Преобразуем вероятности в предсказания классов
y_pred = np.argmax(y_score, axis=1)
y_true = np.argmax(y_test, axis=1)
# Вычисляем различные метрики
accuracy = accuracy_score(y_true, y_pred)
f1_macro = f1_score(y_true, y_pred, average='macro')
f1_weighted = f1_score(y_true, y_pred, average='weighted')
print(f"Accuracy: {accuracy:.3f}")
print(f"F1 Score (macro): {f1_macro:.3f}")
print(f"F1 Score (weighted): {f1_weighted:.3f}")
print("\nDetailed Classification Report:")
print(classification_report(y_true, y_pred))
Интерпретация ROC-AUC в контексте многоклассовой классификации требует понимания особенностей разных стратегий усреднения и соответствия выбранного подхода бизнес-задаче. Правильный выбор метрики и метода усреднения может значительно повлиять на процесс выбора оптимальной модели. 🎯
Хотите углубить свои знания в анализе ROC-AUC и других сложных метрик машинного обучения? Прежде чем двигаться дальше, определите свои сильные стороны и потенциал в области аналитики данных! Пройдите Тест на профориентацию от Skypro, который поможет вам оценить ваши склонности к математическому мышлению, аналитическим способностям и работе с данными. Результаты теста подскажут, в каком направлении лучше развивать карьеру в сфере Data Science и какие навыки стоит прокачать в первую очередь.
ROC-AUC анализ — мощный инструмент в арсенале аналитика данных, позволяющий глубже понять работу классификационных моделей и принимать обоснованные решения. Правильное построение и интерпретация ROC-кривых помогает балансировать между чувствительностью и специфичностью модели, что критично при решении реальных бизнес-задач. Важно помнить, что ROC-AUC — не просто цифра, а показатель того, насколько хорошо модель разделяет классы по всему спектру возможных порогов. Использование знаний о ROC-AUC в сочетании с пониманием предметной области позволяет создавать модели, которые не просто статистически хороши, но и приносят реальную пользу бизнесу.