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

Обычно для бинарной классификации она выглядит так:

где:

  • 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, машинного обучения

Комментарии (0)