Всем привет!

Это было долго. Итак, позвольте мне начать с краткого обзора того, чем я занимался в это время. Я переехал в красивый город Боулдер, штат Колорадо, США, чтобы получить степень магистра наук в Университете Колорадо в Боулдере!

Я начал работать над проектом взаимодействия человека и робота в Лаборатории совместного искусственного интеллекта и робототехники в моем университете и за очень короткое время познакомился со множеством удивительных людей!

Хорошо, хватит, перейдем к текущей задаче. Это сообщение, надеюсь, ответит на вопрос -

Как распознать эмоции лица с помощью сверточной нейронной сети?

Прежде чем мы начнем с подробностей, давайте начнем с основ!

Что такое сверточная нейронная сеть?

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

  1. Скрытые слои / Часть извлечения объектов
  • извилины
  • объединение

2. Классификатор.

Хорошо, но что за свертка?

Свертка - это математическая операция, которая включает комбинацию двух функций для получения третьей функции. В CNN свертка выполняется для входных данных с использованием фильтра для создания карты характеристик.

Но вы тоже упомянули о том, что называется объединением?

Слой объединения добавляется после слоя свертки. Он выполняет непрерывное уменьшение размерности, то есть уменьшает количество параметров и вычислений, тем самым сокращая время обучения и контролируя переобучение. Один из таких методов объединения называется max-pooling, который принимает максимальное значение в каждом окне, что уменьшает размер карты функций, сохраняя при этом важную информацию.

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

Все еще не понимаете?

Не волнуйтесь, просто прочтите этот замечательный пост от Дафны Корнелисс, и вы все поймете.

Теперь приступим к кодированию!

Мы будем работать с набором данных Kaggle FER2013, который можно загрузить, щелкнув ссылку, и извлечь CSV-файл.

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

Это довольно простой шаг, который включает в себя получение данных и их хранение в удобном для нас виде.

Строка 1–7 - Импорт библиотек и чтение файла CSV.

Строка 8–3 - Получение обучающих функций X и меток y из pixels и emotion столбцов CSV соответственно и преобразование их в многочисленные массивы. Мы также добавляем дополнительное измерение к нашему вектору признаков с помощью функции np.expand_dims(), это делается для того, чтобы входные данные подходили для нашей CNN, которую мы разработаем позже. И функции, и метки сохраняются в виде файлов .npy, которые будут использоваться позже.

После того, как мы выполним приведенный выше код, наш результат будет выглядеть примерно так:

Preprocessing Done
Number of Features: 48
Number of Labels: 7
Number of examples in dataset:35887
X,y stored in fdataX.npy and flabels.npy respectively

Теперь приступим к разработке нашей модели. Я разделю процесс на несколько этапов, чтобы он не был слишком утомительным.

Строка 1–11 - Импорт необходимых библиотек для нашей CNN.

Строка 12-23 - Хорошо, здесь много всего происходит, сначала мы объявляем переменные, которые нам понадобятся для обучения нашей CNN. У нас разрешение 48x 48 пикселей, поэтому ширина и высота равны 48. Затем у нас есть 7 эмоций, которые мы прогнозируем, а именно (0 = гнев, 1 = отвращение, 2 = страх, 3 = счастье, 4 = грусть, 5 = сюрприз. , 6 = нейтральный), поэтому у нас есть 7 меток. Мы будем обрабатывать наши входные данные с размером пакета 64.

Затем мы загружаем функции и метки в x и y соответственно и стандартизированные x путем вычитания средних значений и деления на стандартное отклонение.

Строка 24–35 - Первые четыре строки просто печатают изображения с использованием значений пикселей. После этого мы разделяем данные на набор для обучения и тестирования с помощью функции sklearn train_test_split() и сохраняем тестовые функции и метки для использования в дальнейшем. Мы также выполняем еще одно деление наших обучающих данных, чтобы получить данные проверки, которые будут использоваться позже в коде.

Теперь перейдем к следующему фрагменту кода.

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

  1. Sequential() - Последовательная модель - это просто линейный стек слоев, в котором слои накладываются друг на друга по мере того, как мы продвигаемся от входного уровня к выходному слою. Подробнее об этом можно прочитать здесь.
  2. model.add(Conv2D()) - это сверточный 2D-слой, который выполняет операцию свертки, как описано в начале этого сообщения. Цитируя Документацию Keras: Этот слой создает ядро ​​свертки, которое свертывается с входом слоя для создания тензора выходных данных. Здесь мы используем размер ядра 3x3 и выпрямленную линейную единицу (ReLU) в качестве нашей функции активации.
  3. model.add(BatchNormalization()) - Он выполняет операцию пакетной нормализации для входных данных следующего уровня, так что наши входные данные находятся в заданном масштабе, скажем, от 0 до 1, а не разбросаны повсюду.
  4. model.add(MaxPooling2D()) - Эта функция выполняет операцию объединения данных, как описано в начале сообщения. В этой модели мы берем окно объединения 2x2 с шагом 2x2. Если вы хотите узнать больше о MaxPooling, вы можете обратиться к Документации Keras или упомянутому выше сообщению.
  5. model.add(Dropout()) - Как объяснялось выше, Dropout - это метод, при котором случайно выбранные нейроны игнорируются во время обучения. Они «выпадают» случайным образом. Это снижает переоснащение.
  6. model.add(Flatten()) - Это просто сглаживает ввод от ND до 1D и не влияет на размер пакета.
  7. model.add(Dense()) - Согласно документации Keras, Dense реализует операцию: output = activation(dot(input, kernel) где activation - это функция поэлементной активации, переданная как аргумент activation, kernel - это матрица весов, созданная слоем. Проще говоря, это последний гвоздь в гробу, который использует особенности, полученные с помощью слоев, и отображает их на этикетке. Во время тестирования этот слой отвечает за создание окончательной метки для обрабатываемого изображения.

После выполнения функции model.summary() результат будет выглядеть примерно так:

К следующему фрагменту!

Это довольно простой фрагмент кода, в котором сначала модель компилируется с categorical_crossentropy в качестве функции потерь и с использованием Оптимизатора Адама. Мы используем точность в качестве метрики для проверки.

Затем мы настраиваем модель с фиксированным размером пакета (здесь 64), эпохами (здесь 100) и данными проверки, которые мы получили путем разделения данных обучения ранее. И, наконец, мы сохраняем модель для некоторых пользовательских тестов, о которых я расскажу позже.

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

Train on 29068 samples, validate on 3230 samples
Epoch 1/100
29068/29068 [==============================] — 34s 1ms/step — loss: 2.0047 — acc: 0.2124 — val_loss: 1.8123 — val_acc: 0.2817
Epoch 2/100
29068/29068 [==============================] — 31s 1ms/step — loss: 1.7918 — acc: 0.2692 — val_loss: 1.6796 — val_acc: 0.3195
Epoch 3/100
29068/29068 [==============================] — 31s 1ms/step — loss: 1.7021 — acc: 0.3148 — val_loss: 1.5516 — val_acc: 0.3957
.
.
.
Epoch 100/100
29068/29068 [==============================] — 31s 1ms/step — loss: 0.3083 — acc: 0.9049 — val_loss: 1.3855 — val_acc: 0.6666
Saved model to disk

Мы видим, что точность проверки составила 66,6%, что на самом деле неплохо! Давайте сделаем шаг вперед и протестируем модель на данных тестирования, которые мы сохранили ранее, запустив файл fertest.py. Мы получим такой вывод:

Loaded model from disk
Accuracy on test set :66.3694622458

Это впечатляющий результат, потому что модель, победившая в конкурсе имела точность 71,1%, что означает, что этот результат ставит нас на 5-е место! Разве это не круто!

Теперь я также создал матрицу путаницы, чтобы выяснить, какие эмоции обычно путают друг с другом чаще, и это выглядело примерно так:

Посмотрите, как гнев и отвращение перепутали друг с другом, поскольку это очень похожие отрицательные эмоции. Нечто подобное произошло с Fear and Sadness.

Основываясь на этом результате, я делю эмоции на 3 категории (положительные, нейтральные и отрицательные) для своего следующего проекта, который предполагает предоставление роботу возможностей распознавания эмоций лица во время навигации!

Вы можете создать свою собственную матрицу путаницы, запустив программу confmatrix.py из репозитория.

Чтобы было веселее, я протестировал модель на лицах актеров из популярного сериала F.R.I.E.N.D.S, и результаты были довольно хорошими!

Имейте в виду, это реальные предсказанные эмоции. Вы можете сделать то же самое со своим пользовательским тестовым изображением или использовать эту модель в своем собственном проекте, разветвив и клонировав репозиторий и запустив файл thefertestcustom.py!

Я думаю, что на этом все закончилось очень хорошо. Как всегда, поездка была отличной.

Прощай!