Метрики качества: precision, recall и F1

Точность «в лоб» обманчива на несбалансированных данных; для текстовых классификаторов нужны precision, recall и F1.

Precision — доля верных среди предсказанных «положительными». Recall — доля найденных среди всех настоящих «положительных». F1 — их гармоническое среднее.

Почему accuracy обманывает

Пусть из 1000 писем только 10 — спам. Классификатор, который всегда говорит «не спам», будет прав в 990 случаях — accuracy 99%! Но он не поймал ни одного спама, то есть бесполезен. На несбалансированных данных (а в NLP они почти всегда такие) доля правильных ответов вводит в заблуждение. Нужны метрики, которые смотрят отдельно на нужный класс.

Матрица ошибок

Всё строится на четырёх числах. Пусть «положительный» класс — спам.

Предсказано: спамПредсказано: не спам
На деле спамTP (верно поймали)FN (пропустили)
На деле не спамFP (ложная тревога)TN (верно пропустили)

Формулы

Precision = TP / (TP + FP)   — из помеченных спамом сколько правда спам
Recall    = TP / (TP + FN)   — из всего спама сколько мы поймали
F1        = 2 * P * R / (P + R)   — баланс между ними

Precision дорог, когда дорога ложная тревога: пометить важное письмо спамом — плохо. Recall дорог, когда дорог пропуск: в медицине пропустить болезнь хуже ложной тревоги. F1 нужен, когда важны оба.

Считаем метрики руками

# y_true — истина, y_pred — предсказания (1 = спам)
y_true = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
y_pred = [1, 1, 0, 1, 0, 1, 0, 0, 0, 0]

TP = sum(1 for t, p in zip(y_true, y_pred) if t == 1 and p == 1)
FP = sum(1 for t, p in zip(y_true, y_pred) if t == 0 and p == 1)
FN = sum(1 for t, p in zip(y_true, y_pred) if t == 1 and p == 0)
TN = sum(1 for t, p in zip(y_true, y_pred) if t == 0 and p == 0)

precision = TP / (TP + FP)
recall = TP / (TP + FN)
f1 = 2 * precision * recall / (precision + recall)
accuracy = (TP + TN) / len(y_true)

print("TP=%d FP=%d FN=%d TN=%d" % (TP, FP, FN, TN))
print("Accuracy :", round(accuracy, 3))
print("Precision:", round(precision, 3))
print("Recall   :", round(recall, 3))
print("F1       :", round(f1, 3))

Вывод:

TP=3 FP=1 FN=1 TN=5
Accuracy : 0.8
Precision: 0.75
Recall   : 0.75
F1       : 0.75

Из четырёх писем, помеченных спамом, три оказались верными (precision 0.75). Из четырёх настоящих спамов поймали три (recall 0.75). Один спам пропустили (FN), одно нормальное письмо ошибочно пометили (FP). F1 свёл всё в одно число — 0.75.

Компромисс precision и recall

Эти метрики тянут в разные стороны. Сделаем фильтр строже (пометим спамом только то, в чём уверены) — вырастет precision, но упадёт recall (пропустим больше). Сделаем агрессивнее — наоборот. Часто настраивают порог уверенности модели, балансируя precision и recall под задачу. F1 удобен как единая цель, когда явного приоритета нет.

Macro и micro усреднение

Для нескольких классов (например, тональность: позитив/нейтрал/негатив) метрики считают по каждому классу, а потом усредняют. Macro — простое среднее по классам (каждый класс равноправен). Micro — общий пул TP/FP/FN (доминируют крупные классы). Выбор зависит от того, важны ли редкие классы.

Итог

  • Accuracy обманчива на несбалансированных данных — частый случай в NLP.
  • Precision — точность срабатываний; recall — полнота охвата.
  • F1 — гармоническое среднее precision и recall, баланс между ними.
  • Приоритет метрики зависит от цены ошибки: ложная тревога vs пропуск.
Проверьте себя
1. Почему accuracy плохо подходит для оценки спам-фильтра, где спама всего 1%?
AAccuracy невозможно посчитать на текстах
BМодель «всё не спам» даёт 99% accuracy, не поймав ни одного спама
CAccuracy требует слишком много памяти
DAccuracy работает только для двух классов
2. Что измеряет recall?
AИз помеченных положительными — сколько действительно положительные
BИз всех настоящих положительных — сколько модель поймала
CОбщую долю правильных ответов
DСкорость работы модели
3. В какой ситуации recall важнее precision?
AКогда ложная тревога недопустима
BКогда пропустить положительный пример опаснее ложной тревоги (например, диагностика болезни)
CКогда классы идеально сбалансированы
DКогда нужно ускорить модель
Поддержать проект