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

«В каких случаях метрика ROC AUC плохо оценивает качество бинарного классификатора?» — вопрос из категории Метрики и функции потерь, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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, что лучше отражает реальность