Метрики качества: 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 пропуск.