подзаголовок здесь

Набор данных Kannada MNIST - отличная недавняя работа (подробности здесь), и я рад, что он также стал общедоступным. Я уверен, что очень скоро сообщество здесь опубликует современные цифры точности в этом наборе данных. Вот почему я делаю что-то другое.

Вместо этого мы попытаемся визуализировать, попытаться увидеть то, что видит модель, оценивать вещи попиксельно. Наша цель - интерпретируемость. В этой статье я начну с «простейшего», самого легкого для интерпретации алгоритма. Надеюсь, в следующей статье я опубликую результаты с другими методами моделирования.

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

Визуализация цифровых данных

Функция для построения одной случайной цифры вместе с ее меткой

def plot_random_digit():
    random_index = np.random.randint(0,X_train.shape[0])
    plt.imshow(X_train[random_index], cmap='BuPu_r')
    plt.title(y_train[random_index])
    plt.axis("Off")
plt.figure(figsize=[2,2])
plot_random_digit()

Просмотр 50 образцов за раз

plt.figure(figsize=[10,6])
for i in range(50):
    plt.subplot(5, 10, i+1)
    plt.axis('Off')
    if i < 10:
        plt.title(y_train[i])
    plt.imshow(X_train[i], cmap='BuPu_r')

Как человеку, не умеющему читать сценарий каннада, мне кажется, что символы в чем-то похожи:

  • 3 и 7
  • 6 и 9

Вначале я ожидал, что предикторы могут быть несколько перепутаны между этими парами. Хотя это не обязательно так - возможно, наша модель может определять цифры лучше, чем я.

Изменение формы наборов данных для построения прогнозных моделей

Индивидуальные примеры - 28 X 28. Для большинства методов прогнозного моделирования в scikit learn нам нужно сгладить примеры до 1D-массива.
Мы будем использовать метод reshape для numpy-массивов.

X_train_reshape = X_train.reshape(X_train.shape[0], 784)
X_test_reshape = X_test.reshape(X_test.shape[0], 784)

Построение и понимание модели логистической регрессии

Давайте построим модель логистической регрессии для нашей задачи многоклассовой классификации.

Еще раз обратите внимание, что мы не будем фокусироваться на достижении максимально возможной производительности, а на том, как понять, чему научилась модель.

Модель логистической регрессии будет простой и интересной для анализа коэффициентов, чтобы понять, чему модель научилась.
Формулировку мультиклассовой классификации в SciKit-learn можно выполнить двумя способами. Они есть -

  • Один против остальных
  • Полиномиальный

1. Один против остальных:

Эта стратегия, также известная как «один против всех», заключается в подборе одного классификатора для каждого класса. Для каждого классификатора класс сопоставляется со всеми другими классами. Одним из преимуществ этого подхода является его интерпретируемость.

Поскольку каждый класс представлен только одним и одним классификатором, можно получить информацию о классе, проверив соответствующий ему классификатор. Это наиболее часто используемая стратегия для мультиклассовой классификации и справедливый выбор по умолчанию.

В нашем случае это означало бы создание 10 разных классификаторов.

Подробнее об этом здесь:
https://scikit-learn.org/stable/modules/generated/sklearn.multiclass.OneVsRestClassifier.html

2. Полиномиальный:

В этой стратегии мы моделируем логарифм вероятности увидеть данный результат с помощью линейного предсказателя.
Для multinomial минимизированные потери - это полиномиальные потери, соответствующие всему распределению вероятностей. Функция softmax используется для нахождения прогнозируемой вероятности каждого класса.

Подробнее об этом читайте здесь:
https://en.wikipedia.org/wiki/Multinomial_logistic_regression#As_a_log-linear_model

Примечание. Это различие важно и требует, чтобы вы по-разному интерпретировали коэффициенты для моделей.

Во-первых, давайте построим нашу модель, используя схему "Один против отдыха".

from sklearn.linear_model import LogisticRegression
lr1 = LogisticRegression(solver="liblinear", multi_class="ovr")

# Fitting on first 10000 records for faster training  
lr1.fit(X_train_reshape[:10000], y_train[:10000])

Оценка производительности на поезде

Прогнозы модели для обучающих данных

from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
y_train_pred = lr1.predict(X_train_reshape[:10000])
cm = confusion_matrix(y_train[:10000], y_train_pred[:10000])

plt.figure(figsize=[7,6])
sns.heatmap(cm, cmap="Reds", annot=True, fmt='.0f')
plt.show()

Это ОЧЕНЬ высокая точность тренировки! Переоснащение?

Кроме того, похоже, что модель НЕ сильно перепутана между 3 и 7, 6 и 9, по крайней мере, не в наборе поездов.

Анализ ошибок: проверка ошибочно классифицированных случаев

Мы обратимся к серии Pandas для простоты индексации, выделим случаи неправильной классификации, построим несколько примеров.

11 случаев были классифицированы неправильно

  • Изучение некоторых случаев
  • Выберем 9 случайных случаев - мы нанесем цифры вместе с истинными и предсказанными метками.

Вы понимаете, почему модель запуталась?
Давайте посмотрим, как модель покажет себя на тестовой выборке.

Матрица неточностей на тестовом наборе

Прогнозы на основе тестовых данных и построение матрицы неточностей.

Глядя на матрицу путаницы и отчет о классификации -

Напомним минимум 3, 7 - модель между ними существенно запуталась. Точно так же путаница между 4 и 5. Кроме того, многие нули ошибочно принимаются за 1 и 3.

Хорошо! Так что, похоже, на тестовом наборе производительность резко упала. Очень велика вероятность, что мы переобучаем поезд.

Мы признаем, что модель может быть улучшена.

Но давайте пока не будем об этом беспокоиться. Давайте сосредоточимся на том, как понять, чему научилась модель.

Интерпретация модели

Понимание вклада каждого пикселя

Коэффициенты, которые мы узнали прямо сейчас для каждого пикселя, основаны на схеме One vs Rest.

Давайте продолжим и проанализируем коэффициенты для нашей модели OVR.

Форма коэффициентов lr1.coef_.shape равна (10 784), т.е. у нас есть 784 коэффициента для каждой метки - коэффициенты для каждого пикселя для каждой цифры!

Положительный коэффициент означает, что высокое значение этого пикселя увеличивает шансы этой метки по сравнению со ВСЕМИ другими классами. Таким образом, коэффициенты говорят нам, как этот пиксель отличает эту метку от всех других меток вместе.

Извлечение пиксельных коэффициентов и нанесение на тепловую карту для метки 0

plt.figure(figsize=[3,3])
coefs = lr1.coef_[0].reshape(28,28)
plt.imshow(coefs,cmap="RdYlGn",vmin=-np.max(coefs),vmax=np.max(coefs)) #setting mid point to 0
plt.show()

Я использовал расходящуюся цветовую схему, чтобы различать положительные и отрицательные знаки.

На изображении выше пиксели зеленого цвета являются пикселями с положительным значением. Изображение говорит нам, что значения в определенных пикселях помогают классифицировать цифру как 0. Как и ожидалось, красный цвет в центре указывает на то, что наличие значений в этом диапазоне означает меньшую вероятность того, что цифра будет нулем. Желтый цвет близок к 0 - это означает, что пиксель никак не помогает различить.

Создание таких пиксельных тепловых карт для всех цифр

Внимательно посмотрите на эти тепловые карты. Это покажет, чему научилась модель. Помните, что у нас есть формулировка «один против остальных», особенно при сравнении с тепловыми картами других цифр.

Теперь давайте построим модель по полиномиальной схеме.

  • Нам нужно указать параметр multi_class как 'multinomial'
  • «Либлинейный» решатель не поддерживает это, поэтому мы выбираем решатель «провисания».
lr2 = LogisticRegression(random_state=42, multi_class="multinomial", solver="sag")
lr2.fit(X_train_reshape[:10000], y_train[:10000])

Оценка производительности на тестовом наборе

Построение матрицы неточностей

Понимание вклада каждого пикселя

У нас есть 784 коэффициента для каждой метки - коэффициенты для каждого пикселя.

Положительный коэффициент будет означать, что делает этот лейбл тем, чем он является! Но если 3 метки имеют одинаковое присутствие в конкретном пикселе, коэффициенты для всех 3 могут иметь одинаковые значения.

Извлечение пиксельных коэффициентов и построение тепловой карты для метки 0

Насколько она отличается / похожа на тепловую карту из модели OVR?
Давайте сделаем тепловые карты для всех пикселей.

Делаем такие пиксельные тепловые карты по всем разрядам

Как эти тепловые карты соотносятся со средними изображениями для каждой метки?

Построение среднего изображения для каждой цифры.

plt.figure(figsize=(10, 4))
for i in range(10):
    plt.subplot(2,5,i+1), plt.title(i)
    plt.imshow(np.mean(X_train[y_train==i],axis=0),cmap='gray')
plt.suptitle('Mean images for each digit')

Собираем их все вместе - хорошо посмотрите.

Упражнение -

Вы видели тепловые карты для метода OVR, а также для полиномиального метода. И у вас также есть среднее изображение для каждой метки.

  • Сравните и сопоставьте тепловые карты со средними изображениями.
  • Как думаете, что происходит? Можете ли вы попытаться понять, что модели узнали для каждой цифры?
  • Почему модели не так хорошо работают с некоторыми цифрами? Может ли тепловая карта помочь понять?

Возможные следующие шаги для тех, кто хочет попробовать больше -

Предлагаю вам попробовать следующее -

  1. Используйте логистическую регрессию с регуляризацией (гребень, лассо, эластичная сеть) и оптимизацию гиперпараметров с помощью перекрестной проверки, чтобы уменьшить переобучение.
  2. Используйте SVD / PCA для шумоподавления и восстановления исходных данных; За ним следует настроенная модель логистической регрессии.

Ну вот и все для нашей маленькой демонстрации! Вскоре я поделюсь большим количеством демонстраций с различными методами моделирования, способами их интерпретации и другими экспериментами с одним и тем же набором данных с использованием контролируемых и неконтролируемых методов.

Нашли это интересным? Следите за новостями, чтобы увидеть больше таких демонстраций.

Делитесь своими замечаниями / комментариями / предложениями!