Метрики классификации
В прошлой теме качество классификатора измерялось одним числом — долей верных ответов. Этого часто мало. Представьте: из 1000 пациентов болен один, а модель всем говорит «здоров». Accuracy — 99,9 %, а пользы ноль: единственного больного она пропустила.
Чтобы видеть, какие ошибки делает модель, используют матрицу ошибок и метрики, построенные на ней.
Матрица ошибок
Пусть класс 1 — «положительный» (болен, спам, мошенник), класс 0 — «отрицательный». Каждый ответ модели попадает в одну из четырёх клеток:
| предсказано 0 | предсказано 1 | |
|---|---|---|
| на самом деле 0 | TN — верно отвергнут | FP — ложная тревога |
| на самом деле 1 | FN — пропуск | TP — верно найден |
from sklearn.metrics import confusion_matrix
y_true = [0, 0, 0, 1, 1, 1, 1]
y_pred = [0, 1, 0, 1, 1, 0, 1]
print(confusion_matrix(y_true, y_pred))
# [[2 1]
# [1 3]]
Строки — настоящие классы, столбцы — предсказанные, оба по возрастанию. Если в данных встретился только один класс, матрица будет 1 × 1. Чтобы размер был всегда одинаковым, перечислите классы явно: confusion_matrix(y_true, y_pred, labels=[0, 1]).
Числа из матрицы удобно достать так: tn, fp, fn, tp = confusion_matrix(...).ravel().
Точность и полнота
- Precision (точность) — какая доля объектов, которые модель назвала положительными, действительно положительна:
TP / (TP + FP). Низкая точность — много ложных тревог. - Recall (полнота) — какую долю настоящих положительных модель нашла:
TP / (TP + FN). Низкая полнота — много пропусков.
from sklearn.metrics import precision_score, recall_score
print(precision_score(y_true, y_pred)) # 3 / 4 = 0.75
print(recall_score(y_true, y_pred)) # 3 / 4 = 0.75
Что важнее, зависит от задачи: при поиске болезни страшнее пропуск (нужна полнота), в спам-фильтре страшнее отправить важное письмо в спам (нужна точность).
F1-мера
Одно число, которое учитывает и точность, и полноту, — их среднее гармоническое:
F1 = 2 · P · R / (P + R) = 2 · TP / (2 · TP + FP + FN)
Оно маленькое, если плоха хотя бы одна из двух метрик.
from sklearn.metrics import f1_score
print(f1_score(y_true, y_pred))
Классы-строки и нулевой знаменатель
По умолчанию положительным считается класс 1. Если классы — строки, укажите нужный параметром pos_label:
y_true = ["кот", "пёс", "кот", "кот"]
y_pred = ["кот", "кот", "пёс", "кот"]
print(recall_score(y_true, y_pred, pos_label="кот")) # 2 / 3
Если модель ни разу не предсказала положительный класс, в формуле точности получается деление на ноль. scikit-learn тогда вернёт 0 и напечатает предупреждение; параметр zero_division=0 делает то же самое без предупреждения.
Полный отчёт по всем классам сразу печатает classification_report(y_true, y_pred).
Порог вероятности
predict у большинства классификаторов относит объект к классу 1, если его вероятность не меньше 0.5. Но порог можно выбрать самому — по вероятностям из predict_proba:
proba = model.predict_proba(X_test)[:, 1]
pred = [1 if p >= 0.3 else 0 for p in proba]
Понижаем порог — модель чаще говорит «1»: полнота растёт, точность падает. Повышаем — наоборот. Подбирают порог под задачу, например перебирая несколько значений и сравнивая F1.
Итог
confusion_matrix(y_true, y_pred, labels=[0, 1])— TN, FP / FN, TP;precision_score— доля верных среди «найденных»,recall_score— доля найденных среди настоящих;f1_score— баланс точности и полноты;pos_label— какой класс положительный,zero_division— что вернуть при делении на ноль;- порог по
predict_probaпозволяет менять баланс точности и полноты.