Модуль 7. Метрики классификации: от confusion matrix до ROC-AUC

Цель модуля: перестать измерять классификацию одной цифрой «точность» и научиться выбирать метрику под цену ошибок вашего продукта. После модуля вы читаете confusion matrix как таблицу бизнес-последствий, понимаете, что ROC оценивает качество ранжирования, а не порог, интерпретируете AUC как вероятность и выбираете порог отдельно от модели.

Перед началом: модули 1–3 (классификация, логистическая регрессия, порог вероятности) и модули 4–6 (модели выдают скоры). Инструменты: песочница сайта (чистый Python), сниппет ниже. Трудоёмкость: ~2 ч (чтение 50 мин · сниппет и практика 50 мин).

🔥 Точность 97% — и модель, которая не умеет ничего

Антифрод-сервис: 1000 транзакций в день, 30 из них мошеннические (3%). Модель относит к «честным» все транзакции без исключения — её accuracy 97%. Цифра в отчёте красивая, пользы ноль: ни одного мошенничества не поймано. Класс редкий, поэтому «ничего не делать» уже даёт 97% точности.

А теперь модель, которая ловит 24 мошенничества из 30, но попутно блокирует 50 честных транзакций: её accuracy 94.4% — формально хуже. И именно она приносит пользу. Мораль: точность молчит, когда классы неравны, а разница между моделями живёт в том, какие ошибки они делают. Чтобы это увидеть, нужна таблица из четырёх клеток, а не одна цифра.

📖 7.1. Confusion matrix: четыре исхода вместо одной цифры

На каждом примере классификатор даёт один из четырёх исходов:

  • TP (true positive) — предсказали «да», и на самом деле «да»: пойманный фрод, удержанный от оттока клиент.
  • FP (false positive) — ложная тревога: заблокировали честного клиента, пометили обычный тикет как срочный.
  • FN (false negative) — пропуск: мошенничество прошло, уходящий клиент ушёл без звонка.
  • TN (true negative) — верно оставили в покое.

Ценность таблицы в том, что каждая клетка — последствие, а не число. FP в антифроде — звонок в поддержку, ручная разблокировка, испорченное доверие; FN — прямые деньги. В прогнозе оттока FP означает скидку тому, кто и не собирался уходить, FN — потерянного клиента. В маршрутизации тикетов FP — несрочная заявка вытеснила срочную из очереди дежурного.

Правило перед любой работой с метриками: выпишите четыре клетки и рядом цену каждой ошибки в деньгах, времени или доверии. Это разговор с продуктом, а не с моделью, — и он определяет всё остальное в модуле.

📖 7.2. Precision, recall, F1 — кратко

Из четырёх клеток собираются две главные метрики:

  • Precision = TP / (TP + FP) — какая доля «пойманных» действительно положительная. Отвечает на вопрос «насколько можно доверять тревоге».
  • Recall = TP / (TP + FN) — какую долю всех позитивов мы поймали. Отвечает на вопрос «сколько мы упустили».

Это те самые precision/recall из пороговой логики модуля 3: понижаете порог — ловите больше (recall растёт), но среди пойманных больше мусора (precision падает). F1 — гармоническое среднее двух метрик, удобная одна цифра, когда обе важны одинаково. Полный разбор precision/recall/F1 с формулами и вариациями — в модуле 8 курса «Дообучение LLM»; здесь же важно запомнить: это метрики при выбранном пороге, а порог — решение бизнеса, а не свойство модели.

📖 7.3. ROC и AUC: качество ранжирования, а не порога

Модель выдаёт не «да/нет», а скор (вероятность из модуля 3). Порог можно двигать — и вместе с ним поплывут precision и recall. ROC отвечает на другой вопрос: насколько хорошо модель ранжирует, если отречься от порога. Для каждого порога считаем две величины: TPR = recall и FPR = FP / (FP + TN) — долю ложных тревог среди негативов.

Порог 0 — ловим всех, но и пугаем всех: точка (1, 1). Порог 1 — никого не трогаем: точка (0, 0). Промежуточные пороги рисуют кривую; чем ближе она к левому верхнему углу, тем лучше модель отделяет позитивы от негативов. Площадь под кривой — AUC, и она читается буквально: вероятность, что случайный позитив получит скор выше, чем случайный негатив. 0.5 — монетка (модель не различает классы), 1.0 — идеал.

Запустите сниппет roc-auc.py: 8 позитивов и 8 негативов со скорами, полный перебор порогов и AUC по ним. Ключевые строки вывода:

 порог    TPR    FPR  TPR-FPR
  0.72   0.62   0.12     0.50  ← лучший баланс

AUC = 0.828
Порог баланса (Youden): 0.72

AUC = 0.828 означает: в 82.8% пар «позитив против негатива» позитив получил скор выше. Модель рабочая, но не блестящая. Откуда потери? Пар всего 8 × 8 = 64; правильный порядок модель держит в 53 из них. И пять из одиннадцати испорченных пар объясняет один-единственный негатив со скором 0.82 — он стоит выше пяти позитивов. Каждая такая инверсия портит все пары негатива с позитивами, которые он обогнал, — поэтому один завышенный скор так дорого стоит в AUC.

Теперь про порог. Строка «Youden» — это максимум TPR − FPR, точка баланса: 0.72 даёт 62% пойманных позитивов при 12% ложных тревог. Но это статистический оптимум, а не бизнес-решение. Ночному дежурству нужен больший recall (больше тревог — не страшно), авто-блокировке — высокая precision. Модель при смене порога не меняется — меняется цена её ошибок.

📖 7.4. Дисбаланс, PR-кривая и выбор метрики под продукт

При сильном дисбалансе ROC умеет льстить: FPR делится на огромное число негативов, поэтому даже сотни ложных тревог дают малый рост FPR, и кривая выглядит приличной. Здесь честнее кривая precision-recall: precision не разбавляется негативами и сразу показывает, что будет с тревогами, когда мы гонимся за редким классом. Практическое правило: редкий класс (мошенничество, авария, отток 2%) и важна цена ложной тревоги — смотрите PR-AUC, precision при заданном recall или recall при допустимом FPR.

Как выбирать метрику под продукт:

  • Антифрод, аварийные алерты. Recall при фиксированном допустимом FPR — «ловим максимум угроз, не топя систему в тревогах».
  • Спам-фильтр, авто-модерация. Precision — отправить честное письмо в спам дороже, чем пропустить одно спам-письмо.
  • Приоритизация оттока. Ранжирование по скору: команда успевает обработать первые N клиентов, важна точность именно в топе.
  • Медицина, тяжёлые последствия пропуска. Recall, даже ценой множества ложных тревог.

И два организационных правила. Первое: метрику фиксируют до экспериментов — иначе её невольно подкручивают под результат. Второе: порог — это отдельное решение, которое принимают по продукту, а не значение 0.5 «по умолчанию» из библиотеки.

📖 7.5. Компромиссы

  • Accuracy vs дисбалансные метрики. Accuracy понятна и годится при равных классах; при дисбалансе она прячет бесполезность модели за красивым числом. Recall/precision/PR-AUC показывают правду.
  • Precision vs recall. Выбор не математический, а продуктовый: что дороже — пропустить позитив (FN) или зря потревожить негатив (FP). Порог двигает этот баланс, не меняя модель.
  • ROC-AUC vs PR-AUC. ROC-кривая стабильна и сравнивает модели при любом дисбалансе; PR-кривая чувствительнее к редкому классу и честнее отвечает «что будет с тревогами». При сильном дисбалансе — PR.
  • AUC vs калибровка. AUC говорит про порядок скоров, а не про их значения: модель с AUC 0.9 может в 0.8 выдавать скор 0.3. Для пороговых решений это неважно, для «умножаем деньги на вероятность» — нужна отдельная калибровка.
  • Порог по умолчанию vs подобранный. 0.5 — удобный ноль, но не решение. Подбор порога по Youden или по цене ошибок бесплатен и почти всегда меняет качество продукта сильнее, чем смена модели.

💻 Практика

  1. (15 мин) Сниппет с вариациями. Запустите roc-auc.py. Найдите в данных негатив со скором 0.82 и опустите его скор до 0.45 — как изменились AUC и порог Youden? Объясните через пары «позитив-негатив».
  2. (20 мин) Таблица последствий. Для своей рабочей задачи выпишите TP/FP/FN/TN и цену каждой ошибки в деньгах или времени. Это главный артефакт модуля, а не код.
  3. (15 мин) Метрика под продукт. Выберите одну главную метрику из 7.4 и запишите её вместе с порогом и обоснованием до экспериментов.
  4. (10 мин) Чтение ROC. По таблице порогов из вывода сниппета выберите порог для двух сценариев: ночное дежурство и авто-блокировка. Обоснуйте разницу одним предложением каждый.

🧩 Quiz "Проверь себя"

1. Модель относит все транзакции к «честным». Позитивов 3%. Что покажет accuracy?
2. Класс редкий и ложная тревога дорога. Что смотреть в первую очередь?
3. AUC = 0.5. Что это значит?
4. Что такое FN?
5. Зачем ROC перебирает все пороги?

✅ Итоги модуля

  • Заполнять confusion matrix своей задачи и называть цену каждой ошибки.
  • Объяснять, почему accuracy лжёт при дисбалансе.
  • Читать AUC как вероятность правильного порядка пары «позитив, негатив» и отличать 0.5 от 0.83.
  • Выбирать метрику под продукт: recall, precision или ранжирование.
  • Подбирать порог отдельно от модели и обосновывать его бизнесом.
  • Объяснять, почему один негатив с высоким скором портит AUC.

🔗 Мостик к Модулю 8

Заметьте, откуда взялся красивый AUC 0.828: из готовых скоров. А откуда они? Из фич. Пока признаки честные, метрики что-то значат. Но если в таблицу попал признак, который «знает» будущее, вы получите AUC 0.99 на валидации и провал в проде — и никакие метрики это не поймают, потому что на валидации они честно показывали отлично. Следующий модуль — о фичах, пропусках, кодировании категорий и про главную ловушку: target leakage, утечке таргета в признаки.