Моя первая нейросеть: распознаем эмоции человека в кадре
Нейросети и машинное обучение — перспективная отрасль IT, с каждым годом ML-технологии все глубже проникают в разные сферы, включая бизнес, медицину, науку. Понимая все это, я решил выбрать направление машинного обучения в качестве своей профессии. Меня зовут Артем Раздьяконов, я студент курса “Data Scientist PRO” в Skillbox.
Но сегодня хотел бы рассказать не о своей учебе, а о первом крупном проекте — системе распознавания эмоций. Сразу скажу, что это статья для таких же начинающих, как и я сам. Возможно, кто-то из новичков найдет что-то полезное для себя. Ну а если у профи будут советы — то я только рад. Задавайте в комментариях вопросы, постараюсь на все ответить. Ну а пока — поехали!
Да, кстати, я решил создать именно систему распознавания эмоций, а не что-то другое, поскольку мне было интересно в ходе проекта как бы объединить человека и машину. Идея создания алгоритма, который способен детектировать лица в кадре и определять эмоции людей, показалась подходящей для первого серьезного проекта.
Процесс подготовки данных
На первом этапе я подготовил необходимый для нейросети набор данных. Он включал 9 папок, в каждой из которых находился мой снимок, демонстрирующий какую-либо эмоцию. Количество фотографий каждого класса составило около 6000 изображений, выборка сбалансированная. Кроме того, в архив test_kaggle входило 5000 снимков совершенно разных эмоций, то есть изображения не были маркированы каким-либо классом. Общее количество снимков для тренировки — 40000. Для валидации — 10000.
Немного расскажу о процессе подготовки изображений. Я выбрал такой сценарий работы: данные загружались из Google Drive в локальное хранилище блокнота Colab. После этого создавался генератор с указанием валидационной выборки. Затем — параметры для аугментации данных. Поскольку данные изначально были отсортированы по папкам каждого класса, использовался метод flow_from_directory.
Выбор архитектуры моделей
Это один из важнейших этапов. Изначально я решил создать простую сверточную сеть с использованием EarlyStopping и ReduceLROnPlateau во избежание переобучения. Итоговая модель вышла на плато при значениях accuracy на валидации в ~0,45.
Отмечу, что старался максимально использовать существующие наработки. Помню, мой преподаватель, Алексей Мастов, говорил, что для классификации в боевых условиях проще всего использовать архитектуры из зоопарка моделей. Другими словами, велосипед в данном случае изобретать не обязательно; лучше взять готовые решения.
Архитектуру я выбирал при помощи статьи "Image Classification Using Pre-trained models". Решил выбрать три архитектуры: MobileNet V2, VGG19 и ResNet50. Главные критерии при выборе — небольшое время инференса и доступность.
Обучение модели
В случае использования этих трех архитектур обучение реализуется при помощи transfer learning и fine-tuning. Первый касается лишь выходного слоя, который может меняться в соответствии с конкретной задачей. Второй же предполагает обучение либо всех, либо значительной части слоев модели.
Архитектура любой сверточной нейронной сети (а я в своей работе использую именно такую сеть) сконструирована так, чтобы она могла находить и “учить” важные признаки из данных с помощью конволюций или свёрток. Вот интересная ссылка на исследование группы ученых, где наглядно показано, что “видят” нейронные сети. Демонстрация реализована путём визуализации активаций их фильтров на разных слоях. Поначалу фильтры слоев обучаются различать границы или текстуры. По мере продвижения от слоя к слою появляются различия в паттернах, частях объектов, а на финишной прямой — видны и отдельные объекты.
Признаки, которые архитектуры вычленили из ImageNet, отличаются от потенциальных признаков нашего кейса. Так как, скажем, разница между классами «жираф» и «кружка» гораздо больше, чем между эмоциями «радость» и «нейтральное поведение». Дело том, что в первом случае различие между классами может быть заметно уже на этапе границ, а во втором — только на этапе отдельных объектов. У жирафа очень длинная шея и есть 4 ноги, а кружка просто имеет форму цилиндра с ручкой. В свою очередь весёлое и грустное лицо отличаются характерными мимическими признаками, которые проявляются в зависимости от настроения человека. Соответственно, нам необходимо заново обучить все слои, чтобы сеть определила новые признаки.
Именно поэтому я решил тренировать все слои моделей. Тренировка проводилась на базе GPU. Три модели показали следующие значения Accuracy на валидации: VGG19 0.51, ResNet50 0.49 и MobileNet V2 0.46 (кстати, дополнительную информацию о моделях можно получить при сканировании QR-кода).
Я выбрал VGG19 для работы, изучив инференс. Получилось 58-66 мс на 1 кадр против максимально допустимых 33 мс. Для того чтобы ускорить процесс, доведя результат до нормы, я применил пакет TensorRT с собственным "инференс-движком", который относительно недавно был встроен непосредственно в библиотеку TensorFlow. После подобной модификации модели скорость инференса приблизилась к значениям 5-10 мс на 1 кадр.
Ну а потом дело за малым — нужно сформировать файл для отправки на Kagglе, после чего — скачать архив с тестовыми изображениями, прогнать их все через модель, получить предсказания и отформатировать их. Да, есть важный нюанс — форматировать требуется в алфавитном порядке. Так, индекс «0» соответствует названию эмоции, которая идёт раньше всех. Итоговый файл .csv должен был включать строчки вида «название фотографии», эмоция.
Детектируем эмоцию в кадре
Для того, чтобы решить эту задачу, нужно сначала научить нейросеть находить лицо. Проблема в том, что в кадр попадает вовсе не обрезанное изображение, вроде тех что содержались в базе для обучения. Для этого я решил применить детектор из OpenCV. После детекции лица результат отправляется на вход в сеть, а затем над bounding box выводится классифицированная эмоция.
Что в итоге?
С использованием разработанной модели классификации эмоций была создана система детектирования 9 состояний. Для удобства я создал как чисто скриптовую версию, так и версию с исполняемым файлом. Для этого я использовал пакет auto-py-to-exe.
Да, в скрипте я контролирую то, каким образом прописан путь к моделям. Это нужно для того, чтобы корректно отработал auto-py-to-exe в процессе создания исполнения файла, а также на тот случай, если кто-то решит запустить сам скрипт у себя на ПК.
Познакомиться с проектом, скачать его и потестировать возможности можно по ссылке. Ну а вот ссылка на курс “Data Scientist PRO” в Skillbox.