W3docs

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 даёт единственное, не зависящее от порога число, обобщающее дискриминативную способность вашей модели по всем рабочим точкам. Используйте его для сравнения моделей, подбора гиперпараметров и передачи информации о качестве классификатора — а затем выбирайте конкретный порог, соответствующий допустимому в вашем приложении соотношению ложноположительных и ложноотрицательных результатов.

Was this page helpful?