Метрики классификации: accuracy, precision, recall, F1

Почему «точность» (accuracy) обманчива и какие метрики показывают правду.

Метрика — число, которым измеряют качество модели на тестовых данных.

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

Для бинарной классификации все предсказания делятся на четыре типа. Пусть «1» — это положительный класс (например, «болен»):

  • TP (true positive) — верно предсказали «1» (болен и определили как больного).
  • FP (false positive) — ложная тревога: предсказали «1», а на деле «0» (здорового назвали больным).
  • TN (true negative) — верно предсказали «0».
  • FN (false negative) — пропуск: предсказали «0», а на деле «1» (больного назвали здоровым).

Эти четыре числа и есть матрица ошибок. Из них считают все метрики.

Четыре метрики и их смысл

  • Accuracy (точность общая) = доля верных ответов = (TP+TN) / всего. Проста, но обманчива при перекосе классов.
  • Precision (точность положительных) = TP / (TP+FP). Из тех, кого назвали «1», сколько действительно «1». Высокая precision — мало ложных тревог.
  • Recall (полнота) = TP / (TP+FN). Из всех реальных «1» сколько поймали. Высокий recall — мало пропусков.
  • F1 — баланс precision и recall (их гармоническое среднее). Хорош, когда важны обе.

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

Пусть болен 1 человек из 100. Модель, которая всем говорит «здоров», даёт accuracy 99% — и при этом бесполезна: ни одного больного не нашла. Recall у неё 0. Вот почему при редком классе смотрят на precision и recall, а не на одну accuracy.

Precision vs recall: компромисс

Что дороже ошибитьсяВажнее метрикаПример
Пропустить положительныйRecallДиагностика болезни
Поднять ложную тревогуPrecisionБлокировка письма как спама

Поднимая порог классификатора, обычно растёт precision, но падает recall, и наоборот. Идеала «и то, и другое по максимуму» не бывает — выбирают под задачу.

Считаем все метрики на Python

Без библиотек, на двух списках. Код запускается:

# 1 = положительный класс
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 1, 0]

tp = fp = tn = fn = 0
for t, p in zip(y_true, y_pred):
    if t == 1 and p == 1: tp += 1
    elif t == 0 and p == 1: fp += 1
    elif t == 0 and p == 0: tn += 1
    elif t == 1 and p == 0: fn += 1

print("TP =", tp, "FP =", fp, "TN =", tn, "FN =", fn)

accuracy = (tp + tn) / len(y_true)
precision = tp / (tp + fp)
recall = tp / (tp + fn)
f1 = 2 * precision * recall / (precision + recall)

print("Accuracy  =", round(accuracy, 2))
print("Precision =", round(precision, 2))
print("Recall    =", round(recall, 2))
print("F1        =", round(f1, 2))

Вывод:

TP = 4 FP = 1 TN = 4 FN = 1
Accuracy  = 0.8
Precision = 0.8
Recall    = 0.8
F1        = 0.8

Поменяйте предсказания в y_pred и проследите, как смещаются precision и recall, — это лучший способ прочувствовать метрики.

Итог

  • Матрица ошибок (TP, FP, TN, FN) — основа всех метрик классификации.
  • Accuracy обманчива при редком классе; смотрите precision и recall.
  • Precision — мало ложных тревог; recall — мало пропусков; F1 — их баланс.
  • Выбор метрики зависит от того, какая ошибка дороже в вашей задаче.
Проверьте себя
1. Почему accuracy может вводить в заблуждение?
AОна всегда занижает качество
BПри сильном перекосе классов модель, отвечающая всё время одним классом, даёт высокую accuracy, оставаясь бесполезной
CЕё невозможно посчитать
DОна работает только для регрессии
2. Что измеряет recall (полнота)?
AДолю верных среди всех предсказанных положительных
BДолю пойманных среди всех реальных положительных: TP / (TP + FN)
CОбщую долю верных ответов
DСкорость работы модели
3. Для диагностики опасной болезни какую метрику обычно важнее максимизировать?
APrecision, чтобы не было ложных тревог
BRecall, чтобы не пропустить больного
CТолько accuracy
DСкорость инференса
Поддержать проект