AUC - ROC Curve
Узнайте, как вычислить и построить AUC-ROC кривую в Python с помощью sklearn. Разберитесь с TPR, FPR, порогами и когда использовать AUC вместо accuracy.
AUC-ROC кривая (Area Under the Receiver Operating Characteristic Curve — площадь под характеристической кривой приёмника) — один из важнейших показателей оценки моделей бинарной классификации. Она отражает способность модели разделять положительные и отрицательные примеры при каждом возможном пороге принятия решения, давая значительно более полную картину, чем единственное значение accuracy.
В этой главе рассматривается:
- Что такое ROC-кривая и как она строится
- Как интерпретировать TPR, FPR и лежащие в их основе термины матрицы ошибок
- Как вычислить AUC-ROC с помощью Python-библиотеки
scikit-learn - Как построить ROC-кривую с помощью
matplotlib - Когда AUC-ROC является правильной метрикой, а когда — нет
Ключевые термины: TP, FP, TN, FN
Прежде чем перейти к самой кривой, важно разобраться в четырёх исходах, которые может давать бинарный классификатор. Для каждого предсказания истинная метка либо положительная (P), либо отрицательная (N), а предсказанная метка также либо положительная, либо отрицательная:
| Предсказано: положительное | Предсказано: отрицательное | |
|---|---|---|
| Фактически положительное | True Positive (TP) | False Negative (FN) |
| Фактически отрицательное | False Positive (FP) | True Negative (TN) |
Эти четыре ячейки лежат в основе каждой метрики оценки, рассматриваемой в данной главе. Подробное введение см. в главе Матрица ошибок.
Что такое ROC-кривая?
Классификатор, как правило, выводит оценку вероятности (число от 0 до 1), а не жёсткую метку. Вы выбираете порог — например, 0.5 — и всё, что выше порога, классифицируется как положительное.
Изменение порога меняет баланс между захватом истинно положительных примеров и случайной пометкой истинно отрицательных:
- Очень низкий порог захватывает больше положительных (высокая полнота), но также даёт больше ложных срабатываний.
- Очень высокий порог избирателен (высокая точность), но пропускает реальные положительные примеры.
ROC-кривая отображает этот компромисс для каждого возможного порога одновременно:
- Ось X — False Positive Rate (FPR): какая доля фактически отрицательных примеров ошибочно помечена как положительные.
- Ось Y — True Positive Rate (TPR), также называемая Recall или Sensitivity: какая доля фактически положительных примеров правильно идентифицирована.
TPR = TP / (TP + FN) и FPR = FP / (FP + TN)
Каждая точка на ROC-кривой соответствует одному значению порога. Вместе они прочерчивают путь от (0, 0) — наиболее строгий порог, при котором ничего не предсказывается положительным — до (1, 1) — наименее строгий порог, при котором всё предсказывается положительным.
Что такое AUC?
AUC (Area Under the Curve — площадь под кривой) сворачивает всю ROC-кривую в единственное число от 0 до 1:
| AUC | Значение |
|---|---|
| 1.0 | Идеальный классификатор — разделяет каждый положительный пример от каждого отрицательного |
| 0.5 | Без навыка — эквивалентно случайному угадыванию |
| < 0.5 | Хуже случайного (предсказанные вероятности инвертированы) |
| 0.7 – 0.8 | Приемлемо для многих практических задач |
| 0.8 – 0.9 | Хорошо |
| > 0.9 | Отлично |
Интуитивно AUC — это вероятность того, что модель поставит случайно выбранный положительный пример выше случайно выбранного отрицательного.
Вычисление AUC-ROC с помощью scikit-learn
Модуль sklearn.metrics предоставляет roc_curve() для получения значений TPR/FPR при каждом пороге и roc_auc_score() для получения единственного числа AUC.
Минимальный рабочий пример
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
# 1. Create a toy binary-classification dataset
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
# 2. Split into train / test sets
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# 3. Train a logistic regression model
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
# 4. Get probability scores for the positive class (column 1)
y_proba = model.predict_proba(X_test)[:, 1]
# 5. Compute AUC
auc = roc_auc_score(y_test, y_proba)
print(f"AUC-ROC: {auc:.3f}")
# 6. Get the (fpr, tpr, thresholds) arrays for plotting
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
print(f"Number of threshold points: {len(thresholds)}")Вывод будет близким к:
AUC-ROC: 0.944
Number of threshold points: 30Точное значение немного варьируется в зависимости от версии scikit-learn, но для данного датасета оно должно находиться в диапазоне 0.90–0.97.
Построение ROC-кривой
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_proba)
fpr, tpr, _ = roc_curve(y_test, y_proba)
plt.figure(figsize=(7, 5))
plt.plot(fpr, tpr, color="steelblue", lw=2, label=f"ROC curve (AUC = {auc:.2f})")
plt.plot([0, 1], [0, 1], color="gray", linestyle="--", label="Random baseline (AUC = 0.50)")
plt.xlabel("False Positive Rate (FPR)")
plt.ylabel("True Positive Rate (TPR)")
plt.title("ROC Curve")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("roc_curve.png", dpi=120)
plt.show()Диагональная пунктирная линия представляет случайный классификатор. Чем дальше ROC-кривая выгибается в сторону верхнего левого угла, тем лучше модель.
Сравнение нескольких моделей
Распространённый рабочий процесс — обучить несколько моделей и сравнить их ROC-кривые на одном графике:
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
models = {
"Logistic Regression": LogisticRegression(random_state=42),
"Decision Tree": DecisionTreeClassifier(max_depth=3, random_state=42),
}
plt.figure(figsize=(7, 5))
for name, clf in models.items():
clf.fit(X_train, y_train)
y_proba = clf.predict_proba(X_test)[:, 1]
fpr, tpr, _ = roc_curve(y_test, y_proba)
auc = roc_auc_score(y_test, y_proba)
plt.plot(fpr, tpr, lw=2, label=f"{name} (AUC = {auc:.2f})")
plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.50)")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve Comparison")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("roc_comparison.png", dpi=120)
plt.show()Модель с большей площадью под кривой, как правило, является лучшим выбором для данного датасета. Для строгого рабочего процесса выбора модели сочетайте это с кросс-валидацией.
Выбор рабочей точки (порога)
AUC суммирует все пороги, но в итоге вам нужно выбрать один порог для использования в продакшене. Две распространённые стратегии:
1. Статистика J Юдена
J Юдена максимизирует TPR − FPR, находя единственную точку на ROC-кривой, наиболее удалённую от диагонали:
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve
import numpy as np
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
j_scores = tpr - fpr
best_idx = int(np.argmax(j_scores))
best_threshold = thresholds[best_idx]
print(f"Best threshold (Youden's J): {best_threshold:.3f}")
print(f"TPR at best threshold: {tpr[best_idx]:.3f}")
print(f"FPR at best threshold: {fpr[best_idx]:.3f}")2. Порог, определяемый бизнес-требованиями
В задаче обнаружения мошенничества вы можете допускать больше ложноположительных срабатываний, чтобы поймать больше мошеннических операций (более низкий порог). В медицинской диагностике вы можете стремиться к минимуму ложноотрицательных результатов (тоже более низкий порог). В конечном счёте выбор порога — это бизнес-решение, определяемое стоимостью каждого типа ошибки.
AUC-ROC и другие метрики
| Метрика | Лучше всего подходит, когда... | Следует остерегаться... |
|---|---|---|
| Accuracy | Классы сбалансированы | Вводит в заблуждение на несбалансированных датасетах |
| Precision / Recall | Важен главным образом один класс | Требует задания порога |
| F1-Score | Нужен гармонический баланс precision и recall | По-прежнему зависит от порога |
| AUC-ROC | Сравнение моделей или подбор порогов | Не имеет смысла для многоклассовых задач без расширения |
| AUC-PR (Precision-Recall AUC) | Сильный дисбаланс классов (редкие положительные) | Менее интуитивен, чем ROC |
При сильно несбалансированных данных — например, 1% положительных примеров — кривая AUC-PR (precision vs. recall) зачастую более информативна, чем AUC-ROC, поскольку FPR может выглядеть малым даже тогда, когда многие отрицательные примеры классифицируются неверно.
Распространённые ошибки
Передача жёстких меток вместо вероятностей. roc_auc_score требует оценок вероятностей, а не меток 0/1. Используйте model.predict_proba(X_test)[:, 1], а не model.predict(X_test).
Забывают указать положительный класс. По умолчанию roc_curve и roc_auc_score считают метку с большим целочисленным значением положительной. Явно указывайте pos_label=1, если ваши метки не являются целыми числами 0/1.
Переобучение на AUC обучающего набора. Всегда оценивайте на отложенном тестовом наборе или используйте кросс-валидацию для получения надёжной оценки AUC. В главе Grid Search показано, как передавать scoring='roc_auc' непосредственно в GridSearchCV.
AUC ≈ 0.5 не всегда означает, что модель плохая. Это может означать, что положительные и отрицательные примеры действительно перекрываются в пространстве признаков, или что ваши признаки не несут информации для данной задачи.
Практический пример: предсказание заболевания
Следующий пример от начала до конца использует датасет рака молочной железы, входящий в состав scikit-learn:
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt
# Load dataset (569 samples, 30 features, binary labels: malignant=0 / benign=1)
data = load_breast_cancer()
X, y = data.data, data.target
# Scale features — important for logistic regression
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42, stratify=y
)
classifiers = {
"Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
"Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
}
plt.figure(figsize=(7, 5))
for name, clf in classifiers.items():
clf.fit(X_train, y_train)
y_proba = clf.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_proba)
fpr, tpr, _ = roc_curve(y_test, y_proba)
plt.plot(fpr, tpr, lw=2, label=f"{name} (AUC = {auc:.3f})")
plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.50)")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve — Breast Cancer Dataset")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("breast_cancer_roc.png", dpi=120)
plt.show()Оба классификатора должны давать значения AUC выше 0.98 на данном датасете, подтверждая, что признаки рака молочной железы обладают высокой предсказательной силой.
Связанные темы
- Матрица ошибок — строительные блоки TP/FP/TN/FN, используемые на протяжении всей этой главы
- Логистическая регрессия — наиболее распространённая модель, используемая вместе с оценкой AUC-ROC
- Кросс-валидация — как получать надёжные оценки AUC, обобщаемые за пределы одного разбиения на обучающий и тестовый наборы
- Grid Search — как подбирать гиперпараметры с
scoring='roc_auc' - Дерево решений — ещё один бинарный классификатор, вероятностные выходы которого можно оценивать с помощью AUC-ROC
Резюме
| Концепция | Ключевой момент |
|---|---|
| ROC-кривая | Строит TPR против FPR при каждом пороге принятия решения |
| AUC | Площадь под ROC-кривой; 1.0 = идеально, 0.5 = случайно |
roc_auc_score | Передавайте оценки вероятностей, а не жёсткие метки |
roc_curve | Возвращает массивы (fpr, tpr, thresholds) для построения графика |
| Выбор порога | Используйте J Юдена или знания предметной области для выбора рабочего порога |
| Когда предпочтительнее AUC-PR | Сильно несбалансированные датасеты с редкими положительными примерами |
AUC-ROC даёт единственное, не зависящее от порога число, обобщающее дискриминативную способность вашей модели по всем рабочим точкам. Используйте его для сравнения моделей, подбора гиперпараметров и передачи информации о качестве классификатора — а затем выбирайте конкретный порог, соответствующий допустимому в вашем приложении соотношению ложноположительных и ложноотрицательных результатов.