Редкий положительный класс: почему ROC AUC не заменяет precision–recall кривую

Служба поддержки ищет заявки, которые с высокой вероятностью станут жалобами. Жалоб мало, а оператор может вручную просмотреть только первые десятки записей. Модель получила приличный ROC AUC, но в начале отсортированного списка слишком много обычных заявок. Для этой команды важен не только общий порядок всех пар объектов, а доля настоящих жалоб среди выбранных для просмотра.

Две кривые смотрят на разные отношения

ROC AUC измеряет, как часто случайная жалоба получает score выше случайной обычной заявки; равный score даёт половину успеха. Он полезен для оценки ранжирования, но не показывает напрямую, сколько ложных тревог окажется в верхней части очереди. Precision — доля жалоб среди выбранных моделью; recall — доля всех жалоб, которые туда попали. При редком событии большое число отрицательных объектов делает цену даже небольшой доли ложных срабатываний заметной для операторов.

Average precision (AP) суммирует precision вдоль PR-кривой, взвешивая улучшение recall. Не путайте его автоматически с трапециевидной площадью под нарисованной кривой: в scikit-learn это отдельный расчёт. Для постоянного score базовая AP равна доле положительного класса. Так результат 0,3 можно оценить по-разному, если событий 1% или 25%.

В игрушечной очереди ниже две жалобы среди двадцати заявок. Один положительный объект модель поставила первым, второй — ниже трёх ложных тревог. Код считает обе метрики и постоянный прогноз как точку сравнения.

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

y = np.array([1, 1] + [0] * 18)
scores = np.array([0.95, 0.55, 0.8, 0.7, 0.6] + [0.1] * 15)
baseline = np.full(len(y), y.mean())
print("доля события:", round(y.mean(), 3))
print("ROC AUC:", round(roc_auc_score(y, scores), 3))
print("AP:", round(average_precision_score(y, scores), 3))
print("AP постоянного прогноза:", round(average_precision_score(y, baseline), 3))
assert average_precision_score(y, baseline) == y.mean()

На этих двадцати заявках ROC AUC равен 0,917, AP — 0,7, базовая AP — 0,1. Один положительный объект находится первым, другой — ниже трёх отрицательных. Если оператор успеет посмотреть лишь первые три заявки, он найдёт одну жалобу, хотя агрегированная AP выглядит высокой. Поэтому рядом с метрикой ранжирования считайте precision@k и число найденных жалоб на доступном объёме очереди. AP агрегирует пороги, а рабочее решение живёт на конкретной нагрузке.

Как сделать сравнение честным

Проверьте долю положительного класса именно в том наборе, где считаете метрики. Если тест искусственно сбалансирован, а в работе жалоб 1%, полученный precision не будет соответствовать нагрузке операторов. Разделите данные так, чтобы будущая очередь была похожа на проверочную по времени, каналам и составу заявок. Для редкого класса укажите вместе с AP число положительных случаев: один дополнительный случай может заметно изменить результат маленького теста.

Оцените и верх списка. Выведите для нескольких доступных нагрузок, например 50 и 100 проверок в день, сколько жалоб находилось бы в тестовом периоде. Если ставка на пропуск жалобы высока, команда может выбрать больший охват ценой лишней работы. Экономику конкретного порога и соотношение precision/recall мы разбирали в статье о цене ошибок классификации. Здесь задача раньше: понять, насколько модель вообще умеет поднимать редкие события наверх.

Калибровка вероятностей — другой вопрос. Даже модель с высоким AP может присваивать неверные абсолютные вероятности; для планирования ожидаемых потерь проверьте их отдельно. Разбор Brier score и калибровки показывает, как одинаковый порядок сочетается с разной честностью чисел.

Смотрите и на неоднородность очереди. Жалобы из разных каналов могут иметь разные признаки, а общая AP улучшаться за счёт большого канала, пока меньший теряет качество. Выделите важные для поддержки сегменты и посчитайте в каждом число заявок, число жалоб и найденные случаи среди первых доступных для проверки. В маленьком сегменте балл нестабилен, поэтому не делайте вывод по одному месяцу. Сохраните примеры пропущенных жалоб: их анализ подскажет, нужны ли новые признаки или изменились правила оформления заявок.

В курсе «Data Scientist: от данных до деплоя моделей» вы формулируете метрику из задачи, строите корректную валидацию, сравниваете модели и разбираете ошибки по классам до выпуска сервиса. На редком событии это даёт отчёт, в котором есть и базовая доля, и результат в рабочей очереди. Откройте программу курса.

Продолжить на практике

  • Data Scientist: от данных до деплоя моделей ↗

    Практический курс по Data Science: от постановки ML-задачи и работы с данными до inference API, CI/CD и мониторинга моделей в продакшне. Вы пройдёте EDA, feature engineering, обучение и оценку моделей, experiment tracking, error analysis и deployment — чтобы собирать полноценный рабочий контур вокруг ML-решения.