Одним из самых наиболее развёрнутых способов оценки качества классификации является применение матрицы ошибок. Матрица ошибок представляет собой квадратную таблицу, в которой отображается количество предсказанных и фактических классов для классификационной модели. В этой матрице строки представляют истинные классы (реальные метки), а столбцы представляют предсказанные классы (метки, которые предсказала модель). Размер матрицы соответствует количеству классов.
Обычно для бинарной классификации она выглядит так:
где:
TP (True Positive) – истинно положительные примеры;
TN (True Negative) – истинно отрицательные примеры;
FP (False Positive) – ложноположительные примеры;
FN (False Negative) – ложноотрицательные примеры.
Допустим у нас имеется список с истинными метками классов y_true
и список с предсказанными моделью метками классов y_pred
.
y_true = [0, 1, 0, 0, 1, 0, 1, 1, 0]
y_pred = [0, 0, 0, 0, 1, 0, 1, 1, 0]
Объявив 1
положительным классом, а 0
– отрицательным классом, мы можем рассмотреть элементы матрицы ошибок в терминах истинно положительных (True Positive), истинно отрицательных (True Negative), ложноположительных (False Positive) и ложноотрицательных (False Negative) примеров.
В этом случае, матрица ошибок будет иметь следующий вид:
Давайте определимся, какие числа в данной матрице являются истинно/ложноположительными и истинно/ложноотрицательными. В этом вопросе отталкиваться нужно от спрогнозированного класса. Например, если спрогнозировали 0, то значит мы предсказали отрицательный класс (отрицательный – потому что изначально класс 0, мы объявили отрицательным классом (Negative), а класс 1 – положительным (Positive)).
Модель правильно (True) предсказала
0
(Negative) в 5 случаях – значитTN=5
.Модель неправильно (False) предсказала
0
(Negative) в 1 случае – значитFN=1
.Модель правильно (True) предсказала
1
(Positive) в 3 случаях – значитTP=3
.Модель неправильно (False) предсказала
1
(Positive) в 0 случаев – значитFP=0
.
Элементы главной диагонали (от верхнего левого до нижнего правого угла) представляют правильно классифицированные образцы. Чем больше значения на диагонали, тем лучше модель справляется с классификацией этих классов.
В библиотеке scikit-learn за создание матрицы ошибок отвечает функция confusion_matrix()
, которая имеет следующий синтаксис:
confusion_matrix(y_true, y_pred, *, labels=None, sample_weight=None, normalize=None)
Параметры функции confusion_matrix()
:
y_true
: Истинные метки классов.y_pred
: Предсказанные моделью метки классов.labels
: Указывает список уникальных классов. Если не указано, классы будут извлечены изy_true
иy_pred
. Порядок классов в матрице соответствует порядку этих меток.sample_weight
: Массив весов для каждой выборки. Если не указан, все выборки считаются равновесными.-
normalize
: Опция для нормализации матрицы ошибок:'true'
: нормализация по истинным меткам (каждая строка будет делиться на сумму строки).'pred'
: нормализация по предсказанным меткам (каждый столбец будет делиться на сумму столбца).'all'
: нормализация по всем элементам матрицы (каждый элемент будет делиться на общую сумму).Если указано
None
, матрица останется ненормализованной.
Функция confusion_matrix()
возвращает двумерный массив, в котором строки соответствуют истинным классам, а столбцы – предсказанным классам.
Применим функцию confusion_matrix()
к данным из предыдущего примера:
from sklearn.metrics import confusion_matrix
y_true = [0, 1, 0, 0, 1, 0, 1, 1, 0] # Истинные метки классов
y_pred = [0, 0, 0, 0, 1, 0, 1, 1, 0] # Предсказанные метки классов
cm = confusion_matrix(y_true, y_pred)
print(cm)
Результат:
[[5 0]
[1 3]]
На выходе получаем матрицу ошибок на основе истинных меток классов (y_true
) и предсказанных меток (y_pred
).
Функцию confusion_matrix()
можно использовать не только для бинарной, но и для задач многоклассовой классификации. Приведём пример с тремя классами (0
, 1
и 2
):
from sklearn.metrics import confusion_matrix
y_true = [0, 1, 2, 2, 1, 0, 1, 1, 0, 2, 1, 1, 0, 2, 0] # Истинные метки классов
y_pred = [0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1] # Предсказанные метки классов
cm = confusion_matrix(y_true, y_pred)
print(cm)
Результат:
[[4 1 0]
[1 5 0]
[2 1 1]]
В этом случае строки и столбцы соответствуют каждому классу. Например, первая строка показывает, как были классифицированы объекты, истинно принадлежащие классу 0
. Такие матрицы легко интерпретировать для анализа ошибок между классами.
Для более наглядного отображения матрица ошибок, её можно визуализировать с помощью тепловой карты (heatmap). Например, используя функцию heatmap()
библиотеки seaborn и функции настройки графика из библиотеки matplotlib, построим диаграмму матрицы ошибок на данных из предыдущего примера:
import seaborn as sns
import matplotlib.pyplot as plt
# Построение тепловой карты
sns.heatmap(cm, annot=True, fmt='d', cmap='Greens', annot_kws={'fontsize': 14})
plt.ylabel('Истинные значения')
plt.xlabel('Предсказанные значения')
plt.title('Матрица ошибок', pad=15)
plt.show()
Тепловая карта позволяет более наглядно оценить эффективность модели, особенно когда набор данных содержит большое количество классов.
Параметр labels функции confusion_matrix()
Параметр labels
функции confusion_matrix()
позволяет задать порядок классов, для которых будет построена матрица ошибок, а также указать, какие именно классы включить в матрицу.
Допустим, у нас имеются списки с фактическими и предсказанными изображениями животных:
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
Сначала отобразим матрицу ошибок с параметрами по умолчанию функции confusion_matrix()
:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred)
print(cm)
Результат:
[[1 0 2]
[0 2 0]
[1 0 3]]
В данном случае, метки по вертикали и по горизонтали расположены в алфавитном порядке: 'корова'
, 'кошка'
, 'лошадь'
.
А теперь, используя параметр labels
, изменим порядок отображения меток в матрице ошибок, например, на следующий: 'лошадь'
, 'корова'
, 'кошка'
.
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, labels=['лошадь', 'корова', 'кошка'])
print(cm)
Результат:
[[3 1 0]
[2 1 0]
[0 0 2]]
Как видим, матрица приняла иной вид.
Параметр labels
также позволяет строить матрицу ошибок не для всех классов, содержащихся в наборе данных, что может быть востребовано, если вам нужно построить матрицу только для подмножества классов.
Например, отобразим матрицу ошибок только для классов 'корова'
и 'лошадь'
:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, labels=['корова', 'лошадь'])
print(cm)
Результат:
[[1 2]
[1 3]]
Если в y_true
или y_pred
отсутствует класс, который вы указали в labels
, строка и столбец для этого класса будут содержать только нули. Например:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, labels=['корова', 'лошадь', 'собака'])
print(cm)
Результат:
[[1 2 0]
[1 3 0]
[0 0 0]]
параметр normalize функции confusion_matrix()
Параметр normalize
функции confusion_matrix()
управляет нормализацией матрицы ошибок. Нормализация позволяет представить значения в относительных долях или процентах, а не в абсолютных значениях.
По умолчанию параметр normalize
принимает аргумент None
, то есть функция confusion_matrix()
возвращает матрицу с абсолютными значениями промахов и попаданий:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred)
print(cm)
Результат:
[[1 0 2]
[0 2 0]
[1 0 3]]
Когда параметр normalize
принимает значение 'true'
, то происходит нормализация по истинным меткам, то есть по строкам. Каждый элемент матрицы делится на сумму элементов своей строки, что превращает матрицу в представление ошибок модели в долях от общего числа элементов в строке по каждому классу. Это может быть полезным, когда вы хотите понять, насколько хорошо модель классифицирует объекты каждого конкретного класса.
Например:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, normalize='true')
print(cm)
Результат:
[[0.33333333 0. 0.66666667]
[0. 1. 0. ]
[0.25 0. 0.75 ]]
Вариант normalize='pred'
нормализует по предсказанным меткам, то есть по столбцам. Каждый элемент матрицы делится на сумму элементов своей колонки. Это позволяет видеть, насколько уверенно модель классифицирует примеры в каждый класс. Например, если модель часто ошибочно предсказывает какой-то класс, это будет отражено высокой нормализованной долей в этой колонке.
Пример использования normalize='pred'
:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, normalize='pred')
print(cm)
Результат:
[[0.5 0. 0.4]
[0. 1. 0. ]
[0.5 0. 0.6]]
Если normalize='all'
, то происходит нормализация по всей матрице, то есть каждый элемент делится на общее количество предсказаний. Таким образом, каждый элемент матрицы отражает долю всех предсказаний, которые попали в конкретную ячейку. Это даёт общее представление о том, насколько часто модель делает правильные предсказания или ошибается.
Например:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, normalize='all')
print(cm)
Результат:
[[0.11111111 0. 0.22222222]
[0. 0.22222222 0. ]
[0.11111111 0. 0.33333333]]
параметр sample_weight функции confusion_matrix()
Параметр sample_weight
функции confusion_matrix()
позволяет сделать матрицу ошибок более гибкой и чувствительной к реальным условиям. В основе его применения лежит простая, но фундаментальная идея: не все образцы одинаково важны, и иногда некоторым из них нужно придать больший вес. По умолчанию, при вычислении матрицы ошибок, каждое наблюдение (предсказание) учитывается с одинаковым весом.
Параметр sample_weight
принимает список или массив, где каждый элемент соответствует весу конкретного образца в выборке. Чем больше вес, тем больше это наблюдение влияет на итоговые значения в матрице ошибок.
Допустим, в примере с животными, изображение лошади, находящееся на пятой по счёту позиции, является сложно идентифицируемым, и решено присвоить ему более высокий вес. В этом случае результат работы функции confusion_matrix()
будет следующим:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, sample_weight=[1, 1, 1, 1, 2, 1, 1, 1, 1])
print(cm)
Результат:
[[1 0 2]
[0 2 0]
[2 0 3]]
Для сравнения матрица ошибок при одинаковом весе всех меток имеет вид:
[[1 0 2]
[0 2 0]
[1 0 3]]
Этот же пример с нормализацией normalize='all'
:
from sklearn.metrics import confusion_matrix
y_true = ['корова', 'лошадь', 'кошка', 'кошка', 'лошадь', 'корова', 'корова', 'лошадь', 'лошадь']
y_pred = ['лошадь', 'лошадь', 'кошка', 'кошка', 'корова', 'лошадь', 'корова', 'лошадь', 'лошадь']
cm = confusion_matrix(y_true, y_pred, normalize='all',
sample_weight=[1, 1, 1, 1, 2, 1, 1, 1, 1])
print(cm)
Результат:
[[0.1 0. 0.2]
[0. 0.2 0. ]
[0.2 0. 0.3]]
Для сравнения матрица ошибок при одинаковом весе всех меток:
[[0.11111111 0. 0.22222222]
[0. 0.22222222 0. ]
[0.11111111 0. 0.33333333]]
Таким образом, параметр sample_weight
позволяет контролировать влияние отдельных наблюдений на матрицу ошибок, что особенно важно в ситуациях с дисбалансом классов, разной стоимостью ошибок или другими приоритетами. Этот инструмент позволяет сделать вашу модель не только более точной, но и более релевантной для реальных задач.
Мой углублённый курс о метриках качества для задач классификации размещён на платформе Stepik: https://stepik.org/a/216263
Также на Stepik есть несколько моих курсов по темам визуализации, pandas, машинного обучения