Machine Learning Academy · Урок

Метрики классификации: точность, precision, recall и F1

Вы вычислите все четыре метрики для одних и тех же прогнозов и поймёте, чему отдавать приоритет в зависимости от цены ложноположительных и ложноотрицательных результатов

Урок 1 из 413 шагов

«Метрики классификации: точность, precision, recall и F1» — бесплатный урок Machine Learning Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Machine Learning Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Machine Learning Academy содержит 4 уроков всего.

Почему одной точности часто недостаточно

Точность — доля правильных предсказаний — самая интуитивно понятная метрика, но при несбалансированных классах она вводит в заблуждение. Рассмотрим медицинский тест на заболевание, которым страдает 1% пациентов. Модель, всегда предсказывающая «здоров», достигает точности 99%, оставаясь совершенно бесполезной: она пропускает каждый реальный случай заболевания. При обнаружении мошенничества, скрининге на рак или прогнозировании редких событий точность скрывает неспособность модели обнаруживать редкий, но критически важный положительный класс. Поэтому нам нужны метрики, которые отдельно измеряют качество работы с каждым классом: прецизионность, полноту и F1-меру.

import numpy as np

# 1000 patients: 990 healthy, 10 sick
y_true = [0]*990 + [1]*10
y_pred_dummy = [0]*1000  # Always predict healthy

correct = sum(p == t for p, t in zip(y_pred_dummy, y_true))
accuracy = correct / len(y_true)
print(f'Dummy accuracy: {accuracy:.1%}')  # 99.0% -- misleadingly high!
print('But 0 sick patients correctly identified!')
print('This is why we need precision and recall.')

Матрица ошибок: истинные значения

Матрица ошибок — основа всех метрик классификации. При бинарной классификации это таблица 2x2: истинно положительные (TP) — правильно предсказанные положительные случаи; истинно отрицательные (TN) — правильно предсказанные отрицательные случаи; ложноположительные (FP) — предсказаны как положительные, но фактически являются отрицательными (ошибка I рода); ложноотрицательные (FN) — предсказаны как отрицательные, но фактически являются положительными (ошибка II рода). Каждая метрика классификации выводится из некоторого сочетания этих четырёх чисел. Анализ исходной матрицы ошибок показывает, какой тип ошибок модель совершает чаще всего.

from sklearn.metrics import confusion_matrix
import numpy as np

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]

cm = confusion_matrix(y_true, y_pred)
print('Confusion Matrix:')
print(cm)
# [[TN, FP],
#  [FN, TP]]
TN, FP, FN, TP = cm.ravel()
print(f'TP={TP}, TN={TN}, FP={FP}, FN={FN}')

Точность: просто, но ограниченно

Точность = (TP + TN) / (TP + TN + FP + FN). Она отвечает на вопрос: какая доля всех предсказаний была правильной? Точность является объективной метрикой только тогда, когда классы примерно сбалансированы, а стоимость ложно положительных и ложно отрицательных ошибок примерно одинакова. Например, распознавание рукописных цифр, где на каждый класс цифр приходится примерно 10% примеров, сбалансировано, поэтому точность является разумной мерой. Используйте точность, когда: классы сбалансированы и все ошибки имеют одинаковую стоимость. Избегайте её, когда: классы распределены неравномерно (один класс преобладает) или ложноотрицательные ошибки дороже ложноположительных (или наоборот).

from sklearn.metrics import accuracy_score, confusion_matrix
import numpy as np

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]

acc = accuracy_score(y_true, y_pred)
print(f'Accuracy = {acc:.2f}')  # (TP+TN) / total

# Manual calculation
TN, FP, FN, TP = confusion_matrix(y_true, y_pred).ravel()
acc_manual = (TP + TN) / (TP + TN + FP + FN)
print(f'Manual: ({TP}+{TN}) / ({TP}+{TN}+{FP}+{FN}) = {acc_manual:.2f}')

Прецизионность: качество положительных предсказаний

Прецизионность = TP / (TP + FP). Она отвечает на вопрос: какая доля всех образцов, предсказанных как положительные, действительно является положительной? Прецизионность — это метрика, которую следует оптимизировать, когда ложноположительные ошибки имеют высокую стоимость. При обнаружении спама фильтр с высокой прецизионностью редко помечает обычные письма как спам. При тестировании лекарств высокая прецизионность означает, что мы редко одобряем неработающий препарат. Низкая прецизионность означает, что многие положительные предсказания ошибочны и пользователи получают слишком много ложных предупреждений. Прецизионность ничего не говорит о ложноотрицательных ошибках: модель, которая предсказывает положительный результат только для одного очевидного случая, имеет прецизионность 1.0, но пропускает всё остальное.

from sklearn.metrics import precision_score, confusion_matrix
import numpy as np

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]

prec = precision_score(y_true, y_pred)
print(f'Precision = {prec:.2f}')

# Manual calculation
TN, FP, FN, TP = confusion_matrix(y_true, y_pred).ravel()
print(f'Manual: TP/(TP+FP) = {TP}/({TP}+{FP}) = {TP/(TP+FP):.2f}')
print('Of all PREDICTED positives, this fraction are truly positive.')

Полнота: охват истинно положительных случаев

Полнота = TP / (TP + FN) (также называется чувствительностью или долей истинно положительных случаев). Она отвечает на вопрос: какую долю всех действительно положительных образцов мы обнаружили? Полноту следует оптимизировать, когда ложноотрицательные ошибки имеют высокую стоимость. При скрининге на рак высокая полнота означает, что мы выявляем большинство пациентов, действительно больных раком. При обнаружении мошенничества высокая полнота означает, что мы обнаруживаем большинство мошеннических операций. Низкая полнота означает, что мы пропускаем слишком много действительно положительных случаев, что опасно в медицинских системах и системах безопасности. Полнота и прецизионность противоречат друг другу: увеличение одной обычно уменьшает другую.

from sklearn.metrics import recall_score, confusion_matrix
import numpy as np

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]

rec = recall_score(y_true, y_pred)
print(f'Recall = {rec:.2f}')

# Manual calculation
TN, FP, FN, TP = confusion_matrix(y_true, y_pred).ravel()
print(f'Manual: TP/(TP+FN) = {TP}/({TP}+{FN}) = {TP/(TP+FN):.2f}')
print('Of all ACTUAL positives, this fraction were correctly identified.')

Компромисс между прецизионностью и полнотой

Прецизионность и полнота связаны обратно пропорционально, если изменять порог классификации. При снижении порога (если положительный результат предсказывается более часто) полнота увеличивается (обнаруживается больше действительно положительных случаев), но прецизионность снижается (проходит больше ложноположительных случаев). Повышение порога даёт противоположный эффект. Не существует единственного порога, который одновременно максимизирует обе метрики. Выбор приоритетной метрики зависит от бизнес-задачи: при фильтрации электронной почты следует отдавать приоритет прецизионности (не раздражать пользователей ложными уведомлениями о спаме), а при скрининге на заболевания — полноте (не пропускать больных пациентов).

from sklearn.metrics import precision_score, recall_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import numpy as np

X, y = load_breast_cancer(return_X_y=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, random_state=42)

clf = LogisticRegression(max_iter=10000)
clf.fit(X_tr, y_tr)
proba = clf.predict_proba(X_te)[:, 1]

for thresh in [0.3, 0.5, 0.7, 0.9]:
    y_pred = (proba >= thresh).astype(int)
    prec = precision_score(y_te, y_pred, zero_division=0)
    rec  = recall_score(y_te, y_pred)
    print(f'threshold={thresh}: precision={prec:.3f}, recall={rec:.3f}')

F1-мера: гармоническое среднее прецизионности и полноты

F1-мера = 2 * (прецизионность * полнота) / (прецизионность + полнота). F1-мера — это гармоническое среднее прецизионности и полноты. В отличие от арифметического среднего, гармоническое среднее штрафует за крайние значения: модель с прецизионностью=1.0 и полнотой=0.0 получает F1=0, а не 0.5. Поэтому F1 является справедливой сводной метрикой, для которой и прецизионность, и полнота должны быть достаточно высокими. F1 — это стандартная метрика для несбалансированной классификации текстов (в тестах NLP её всегда приводят), обнаружения мошенничества и любых задач, где дисбаланс классов делает точность вводящей в заблуждение.

from sklearn.metrics import f1_score, precision_score, recall_score
import numpy as np

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]

prec = precision_score(y_true, y_pred)
rec  = recall_score(y_true, y_pred)
f1   = f1_score(y_true, y_pred)

print(f'Precision = {prec:.3f}')
print(f'Recall    = {rec:.3f}')
print(f'F1-Score  = {f1:.3f}')
print(f'Manual F1 = 2 * ({prec:.3f} * {rec:.3f}) / ({prec:.3f} + {rec:.3f}) = {2*prec*rec/(prec+rec):.3f}')

Fbeta-мера: взвешенный баланс прецизионности и полноты

Fbeta-мера обобщает F1, вводя параметр beta, который определяет вес полноты относительно прецизионности: F_beta = (1 + beta^2) * Precision * Recall / (beta^2 * Precision + Recall). При beta=1 это стандартная F1-мера. При beta=2 (F2) полнота получает вдвое больший вес, чем прецизионность, что полезно, когда пропуск положительного случая вдвое дороже ложной тревоги. При beta=0.5 (F0.5) больший вес получает прецизионность, что полезно в рекомендательных системах, где точность рекомендаций важнее охвата.

from sklearn.metrics import fbeta_score
import numpy as np

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]

for beta in [0.5, 1.0, 2.0]:
    score = fbeta_score(y_true, y_pred, beta=beta)
    print(f'F{beta:.1f} = {score:.3f}')

print()
print('beta=0.5: precision weighted 2x -> use when false alarms costly')
print('beta=1.0: balanced precision/recall -> standard F1')
print('beta=2.0: recall weighted 2x -> use when missing positives costly')

Усреднение для нескольких классов: макро-, микро- и взвешенное

При многоклассовой классификации необходимо объединить значения прецизионности, полноты и F1 для отдельных классов в одно число. Макроусреднение: вычислить метрику для каждого класса независимо, а затем усреднить — все классы считаются равными независимо от их размера. Взвешенное усреднение: то же самое, но с весами, пропорциональными представленности класса, поэтому более крупные классы оказывают большее влияние. Микроусреднение: сначала объединить TP/FP/FN для всех классов, а затем вычислить метрику — при отсутствии образцов с несколькими метками оно эквивалентно точности для прецизионности, полноты и F1. Используйте макроусреднение для несбалансированных наборов данных, в которых важны небольшие миноритарные классы.

from sklearn.metrics import f1_score
import numpy as np

# Multi-class predictions
y_true = [0, 0, 1, 1, 2, 2, 2]
y_pred = [0, 1, 1, 1, 2, 0, 2]

print('Macro F1:    ', f1_score(y_true, y_pred, average='macro').round(3))
print('Micro F1:    ', f1_score(y_true, y_pred, average='micro').round(3))
print('Weighted F1: ', f1_score(y_true, y_pred, average='weighted').round(3))
print('Per-class F1:', f1_score(y_true, y_pred, average=None).round(3))
print()
print('Macro treats all classes equally (even rare ones)')
print('Weighted rewards correct prediction of common classes')

classification_report: все метрики в одном месте

classification_report() создаёт отформатированную таблицу с прецизионностью, полнотой, F1-мерой и представленностью (числом истинных экземпляров) для каждого класса, а также с макро- и взвешенными средними. Это стандартный результат, который следует включать в любой отчёт об оценке ML-модели. Столбец «представленность» особенно важен: для класса с представленностью=5 оценки прецизионности и полноты будут нестабильными, поэтому их следует интерпретировать осторожно. Всегда проверяйте значения представленности вместе со значениями F1, чтобы оценить надёжность метрик.

from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42)

clf = RandomForestClassifier(random_state=42)
clf.fit(X_tr, y_tr)
y_pred = clf.predict(X_te)

print(classification_report(
    y_te, y_pred,
    target_names=load_iris().target_names
))

Сбалансированная точность для несбалансированных классов

Сбалансированная точность — это арифметическое среднее чувствительности (полноты для каждого класса). Для бинарной классификации: balanced_accuracy = (TPR + TNR) / 2, где TNR (доля истинно отрицательных случаев) = TN / (TN+FP). В отличие от обычной точности, сбалансированная точность придаёт каждому классу одинаковый вес независимо от количества его образцов. Модель, всегда предсказывающая мажоритарный класс, получает сбалансированную точность 0.5, что верно показывает: она не лучше случайного угадывания. Используйте сбалансированную точность, когда классы несбалансированы и Вам нужна одна метрика, одинаково отражающая качество работы со всеми классами без необходимости вычислять метрики для каждого класса отдельно.

from sklearn.metrics import balanced_accuracy_score, accuracy_score
import numpy as np

# Imbalanced: 990 class 0, 10 class 1
y_true = np.array([0]*990 + [1]*10)
y_dummy = np.zeros(1000, dtype=int)  # Always predict class 0

acc_standard = accuracy_score(y_true, y_dummy)
acc_balanced = balanced_accuracy_score(y_true, y_dummy)

print(f'Standard accuracy: {acc_standard:.3f}')  # 99% -- misleading
print(f'Balanced accuracy: {acc_balanced:.3f}')  # 0.5 -- correctly shows no skill
print()
print('Balanced accuracy correctly penalises ignoring the minority class')

Быстрая проверка

Проверьте своё понимание концепций машинного обучения с помощью Python из этого урока.

Итоги урока

В этом уроке Вы узнали: точность вводит в заблуждение при несбалансированных классах — используйте матрицу ошибок как источник истинных значений; прецизионность измеряет качество положительных предсказаний (минимизирует ложноположительные ошибки), а полнота измеряет охват действительно положительных случаев (минимизирует ложноотрицательные ошибки); F1-мера гармонически объединяет обе характеристики в сбалансированную метрику. Далее мы рассмотрим ROC-кривые и AUC-ROC для оценки моделей, не зависящей от порога.

Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
30
Уроки
120

Часто задаваемые вопросы

Урок «Метрики классификации: точность, precision, recall и F1» бесплатный?

Да — полный текст урока «Метрики классификации: точность, precision, recall и F1» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Machine Learning Academy, подпишись на CoddyKit PRO. Курс Machine Learning Academy содержит 4 уроков всего.

Чему я научусь в уроке «Метрики классификации: точность, precision, recall и F1»?

Вы вычислите все четыре метрики для одних и тех же прогнозов и поймёте, чему отдавать приоритет в зависимости от цены ложноположительных и ложноотрицательных результатов Ты практикуешь Machine Learning Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Machine Learning Academy?

Предыдущий опыт не требуется. Machine Learning Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Метрики классификации: точность, precision, recall и F1»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Machine Learning Academy?

Да. Каждый урок Machine Learning Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Метрики классификации: точность, precision, recall и F1
  2. ROC-кривые и AUC-ROC
  3. Метрики регрессии: MAE, MSE, RMSE и R-квадрат
  4. Выбор подходящей метрики для бизнес-задачи
← Назад к Machine Learning Academy