В каких случаях метрика ROC AUC плохо оценивает качество бинарного классификатора?

Ответ

ROC AUC — мощная метрика, но в моей практике есть несколько сценариев, когда она может вводить в заблуждение:

  1. Сильный дисбаланс классов. Это самый частый случай. ROC AUC показывает способность модели разделять классы, но если негативных примеров (class 0) в 1000 раз больше, чем позитивных (class 1), высокий AUC может маскировать плохое качество предсказания именно миноритарного (важного) класса. Модель, которая всегда предсказывает 0, все равно получит приличный AUC.

    • Решение: Использовать Precision-Recall AUC (PR AUC), которая фокусируется на успешности предсказания позитивного класса и гораздо чувствительнее к дисбалансу.
  2. Разная стоимость ошибок. ROC AUC не учитывает, что в реальной задаче ложноположительный (FP) и ложноотрицательный (FN) результат могут иметь разную цену. Например, в медицине пропустить болезнь (FN) часто гораздо хуже, чем ложная тревога (FP).

    • Решение: Анализировать матрицу ошибок (confusion matrix) для конкретного бизнес-порога и оптимизировать под метрики, учитывающие стоимость (например, F-бета мера, где бета > 1 делает упор на recall).
  3. Когда важен конкретный порог классификации. ROC AUC усредняет качество по всем возможным порогам. На практике мы выбираем один рабочий порог (например, 0.5). Модель может иметь отличный AUC, но в районе нужного нам порога её качество (precision, recall) будет низким.

    • Решение: Строить Precision-Recall кривую для выбранного порога или калибровать модель.

Пример на Python, иллюстрирующий проблему с дисбалансом:

import numpy as np
from sklearn.metrics import roc_auc_score, average_precision_score

# Имитация сильного дисбаланса: 990 негативных, 10 позитивных примеров
y_true = np.array([0]*990 + [1]*10)
# Модель предсказывает слабые вероятности для позитивного класса
y_pred_proba = np.array([0.05]*990 + [0.6]*10)

roc_auc = roc_auc_score(y_true, y_pred_proba)
pr_auc = average_precision_score(y_true, y_pred_proba)

print(f"ROC AUC: {roc_auc:.3f}")  # Может быть высоким, например 0.92
print(f"PR AUC:  {pr_auc:.3f}")   # Будет значительно ниже, например 0.45, что лучше отражает реальность

Ответ 18+ 🔞

А, ну вот, опять про ROC AUC! Чувак, эта метрика — как дорогой спорткар, который выглядит охуенно, но в пробке жрёт бензина дохуя и никуда не едет. Вроде круто, а на деле — манда с ушами. Давай разберём, где она тебя может наебать.

Первое и самое жирное — сильный дисбаланс классов. Представь: у тебя 1000 здоровых и 1 больной. Твоя модель, будучи хитрой жопой, просто всех помечает как здоровых. И что ты получаешь? ROC AUC — почти единица, красота! А по факту ты нихуя не нашёл того единственного больного, ради которого всё и затевалось. Это пиздец как обманчиво.
Что делать? Забей на ROC и смотри на Precision-Recall AUC (PR AUC). Она не размазывает всё по стенке, а тыкает тебя носом в то, как хорошо ты ловишь именно тот редкий, но важный класс. Разница — как между «вроде неплохо» и «да ты нихуя не сделал».

Второй момент — разная цена ошибки. ROC AUC считает, что ложная тревога и пропуск цели — это однохуйственно. А в жизни? Скажем, в спам-фильтре: ну отправил ты левое письмо в спам (ложноположительное) — да похуй. А вот если важное письмо от начальства улетело в спам (ложноотрицательное) — это уже волнение ебать, тебе пизда. ROC про это забьёт болт.
Что делать? Лезь в матрицу ошибок и смотри, что происходит на твоём рабочем пороге. Может, тебе нужна не максимальная точность, а чтобы recall был под 90%, даже если precision просядет. Используй F-бетту, настраивай порог вручную — не доверяй слепо этой усреднённой картинке.

Ну и третье — ROC AUC живёт в идеальном мире всех порогов, а ты-то работаешь с одним, конкретным. Может быть так: кривая ROC — красавица, AUC — 0.95, но ровно в том месте, где ты выставил порог 0.5, precision и recall — просто пизда рулю. Модель как бы умеет разделять классы, но не там, где тебе нужно.
Что делать? Строй Precision-Recall кривую и смотри, что происходит вокруг твоего порога. Или калибруй вероятности — чтобы 0.5 действительно означало неуверенность, а не хуй с горы.

Вот, смотри код, тут всё наглядно. Особенно этот дисбаланс — просто ёперный театр.

import numpy as np
from sklearn.metrics import roc_auc_score, average_precision_score

# Симулируем жесть: 990 здоровых, 10 больных
y_true = np.array([0]*990 + [1]*10)
# Модель немного подумала и выдала такие вероятности
y_pred_proba = np.array([0.05]*990 + [0.6]*10)

roc_auc = roc_auc_score(y_true, y_pred_proba)
pr_auc = average_precision_score(y_true, y_pred_proba)

print(f"ROC AUC: {roc_auc:.3f}")  # Высокий, типа 0.92. Ура, мы гении!
print(f"PR AUC:  {pr_auc:.3f}")   # А тут 0.45. Ой, бля. А вот это уже правда.

Видишь? ROC AUC — 0.92, можно рапортовать начальству. А PR AUC — 0.45, что означает, что модель находит позитивный класс хуже, чем обезьяна с дартсом. Доверия к ROC после такого — ебать ноль.

Так что, чувак, не гонись за одной цифрой. Смотри на задачу, думай, что важнее, и выбирай метрики с умом. А то получится как в той поговорке: «Смотрю в ROC, вижу хуй».