Урок курса
Матрица ошибок: 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 |
|---|---|---|
| 0 | TN = 4 | FP = 1 |
| 1 | FN = 2 | TP = 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?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
