Урок курса

Матрица ошибок: TP, FP, TN, FN

Машинное обучение с нуля на Python: первая модель

Accuracy, которую мы считали в предыдущих уроках, — это одно число: доля верных ответов среди всех. Она не различает, какие именно ошибки делает модель. Матрица ошибок вскрывает эту структуру: вместо одной цифры — четыре счётчика, каждый со своим смыслом.

Структура матрицы ошибок и четыре вида исходов (TP, FP, TN, FN)

Матрица ошибок — таблица 2×2. Строки в ней — истинные классы объекта, столбцы — то, что предсказала модель. Каждая ячейка просто считает, сколько объектов попало в конкретную комбинацию (истинный класс, предсказанный класс).

Чтобы читать таблицу без путаницы, зафиксируем соглашение: 0 — отрицательный класс, 1 — положительный. Тогда четыре ячейки расшифровываются так:

                Предсказан 0    Предсказан 1
Истинный 0         TN               FP
Истинный 1         FN               TP

TP (True Positive) — модель сказала «1», и это правда. Объект действительно положительный.

TN (True Negative) — модель сказала «0», и это правда. Объект действительно отрицательный.

FP (False Positive) — модель сказала «1», но объект на самом деле 0. Это «ложная тревога»: сработал сигнал там, где ничего нет. Классический пример — спам-фильтр, который отправил важное письмо в спам.

FN (False Negative) — модель сказала «0», но объект на самом деле 1. Это «пропуск»: угроза прошла незамеченной. В медицинском контексте — больной, которого модель признала здоровым.

FP и FN — разные по природе ошибки с разными последствиями. Это именно то, что accuracy скрывает: она штрафует FP и FN одинаково, хотя в большинстве реальных задач цена пропуска и цена ложной тревоги принципиально разная.

Важно: сумма всех четырёх счётчиков равна числу объектов в выборке — TP + FP + TN + FN = N. Ни один объект не теряется и не считается дважды.

Вызов confusion_matrix с явным labels и соответствие индексов TP, FP, TN, FN

В sklearn матрица ошибок строится одной строкой:

from sklearn.metrics import confusion_matrix

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

cm = confusion_matrix(y_true, y_pred, labels=[0, 1])
print(cm)

Результат:

[[4 1]
 [2 3]]

Функция возвращает NumPy-массив формы (2, 2). Параметр labels=[0, 1] здесь принципиален: он явно фиксирует, какой класс занимает нулевую строку и нулевой столбец, а какой — первую. Без него sklearn определяет порядок по объединению уникальных меток из y_true и y_pred. Обычно это работает предсказуемо, но если в обоих массивах оказался только один класс — например, y_true и y_pred содержат исключительно 0 — матрица схлопнется до 1×1, и чтение по индексам сломается. Явный labels=[0, 1] гарантирует форму 2×2 независимо от того, какие метки реально встретились в данных.

Соответствие индексов фиксировано при labels=[0, 1]:

tn = cm[0, 0]  # истинный 0, предсказан 0
fp = cm[0, 1]  # истинный 0, предсказан 1
fn = cm[1, 0]  # истинный 1, предсказан 0
tp = cm[1, 1]  # истинный 1, предсказан 1

print(tn, fp, fn, tp)     # 4 1 2 3
print(tn + fp + fn + tp)  # 10

Проверка суммы — не формальность. Если число не совпало с len(y_true), значит, где-то ошибка в индексировании или данных.

На этих данных: 5 объектов с истинным классом 0 (строка 0: TN=4, FP=1) и 5 объектов с истинным классом 1 (строка 1: FN=2, TP=3). Итого 4+1+2+3 = 10.

Истинный классПредсказан 0Предсказан 1
0TN = 4FP = 1
1FN = 2TP = 3
Строки — истинные классы, столбцы — предсказанные. Данные совпадают с примером урока.

Ошибка перепутанных строк и столбцов: как различить FP и FN в массиве

Самая частая ошибка при чтении матрицы — перепутать cm[0,1] и cm[1,0]. Логика кажется симметричной: оба индекса «не на главной диагонали», оба — ошибки. Но смысл у них разный.

Разберём по определению индекса: первый индекс — строка — это истинный класс, второй индекс — столбец — это предсказанный класс.

  • cm[0, 1]: истинный класс 0, предсказан класс 1 → FP (ложная тревога).
  • cm[1, 0]: истинный класс 1, предсказан класс 0 → FN (пропуск).

Если перепутать и написать fn = cm[0, 1], счётчик пропусков окажется неверным — и все метрики, которые строятся на FN, дадут неправильный результат.

Как проверить присвоение. Прежде всего убедитесь, что каждая переменная ссылается на правильную пару (истинный класс, предсказанный класс). Для наших данных:

# На этих данных: y_true=[0,0,1,1,0,1,0,1,1,0], y_pred=[0,1,1,0,0,1,0,1,0,0]
# cm = [[4 1]
#        [2 3]]

tn = cm[0, 0]  # истинный 0, предсказан 0  → TN = 4
fp = cm[0, 1]  # истинный 0, предсказан 1  → FP = 1
fn = cm[1, 0]  # истинный 1, предсказан 0  → FN = 2
tp = cm[1, 1]  # истинный 1, предсказан 1  → TP = 3

Комментарий к каждой строке — это и есть первичная проверка: он буквально читается как «строка соответствует истинному классу X, столбец — предсказанному классу Y». Если комментарий совпадает с определением FP или FN — присвоение верно.

Проверка суммы строки как дополнительный контроль. После того как переменные назначены, полезно убедиться, что строка 1 суммируется в число реальных положительных объектов:

# строка 1 содержит все объекты с истинным классом 1
assert fn + tp == sum(1 for y in y_true if y == 1)  # 2 + 3 == 5

Эта проверка подтверждает, что матрица построена корректно и строки не перепутаны местами. Она не гарантирует, что именно fn получила ячейку cm[1,0], а не cm[1,1]: если переставить имена fn и tp между собой, сумма не изменится. Поэтому правило индекса («строка = истинный класс, столбец = предсказанный класс») остаётся основным — проверка суммы лишь подкрепляет его.

Аналогично строка 0 должна суммироваться в число реальных отрицательных объектов: tn + fp == 5.

Попробуйте решить

Матрица ошибок построена с labels=[0, 1]. Результат:
cm = [[12, 3], [2, 8]]
Чему равны FP и FN?

Продолжить с проверкой и прогрессом

Откройте интерактивный раннер с заданиями урока.

Перейти к интерактивному уроку