Описание процесса разработки - d-01/graduate-2021-dec GitHub Wiki

Теория

В первую очередь были изучены теоретические материалы (ссылки на конспект):

Обучение модели для распознавания эмоций

Этап 1. Прототип

Colab

Задача: быстро создать прототип, чтобы получить базовую точность (точка отсчета), используя простейший пайплайн и простейшую сверточную сеть. В качестве пайплайна использован tf.data.Dataset, потому что файловая структура предоставленного датасета позволяет создать пайплайн одной командой tf.keras.utils.image_dataset_from_directory(), а для нейросети использована архитектура с тремя сверточными слоями из тюториала с сайта tensorflow.org и обучена в течении 20 минут, результат 0.332000 (public), что выше оценки 0.298800 требуемой для зачета.

Архитектура:

model = tf.keras.Sequential([
  tf.keras.layers.Rescaling(1./255),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(9)
])
  1. Layers: 3*conv32 + 1*fc128 + fc9
  2. Input: 45k images 128x128x1
  3. Output: 9 classes
  4. Parameters: 822,921
  5. Training:
    • 40 epochs
    • 25 min
  • Test accuracy: 0.33400 / 0.33480 (private / public)

model-1-training

Следующая цель: повысить точность (private score) > 0.4.

Этап 2. Быстрый пайплайн для данных

Colab

Решено отказаться от использования tf.data.Dataset в качестве пайплайна для входных данных, т.к. размер датасета позволяет полностью загрузить его в память в виде numpy массива, что многократно упрощает работу с данными и ускоряет процесс обучения.

Этап 3. Изменение архитектуры сверточной сети

Colab

Новая модель:

  1. Сверточная нейросеть с более сложной архитектурой на базе MobileNet (0.25M параметров), время обучения 20 минут, точность = 0.37600

Архитектура:

def create_mobile_net(n_classes=9):
    from tensorflow.keras import layers
    input_shape = (128, 128)
    mobilenet_input_shape = (128, 128, 3)

    i = tf.keras.layers.Input(input_shape, dtype = tf.uint8)
    x = tf.expand_dims(i, axis=-1)
    x = tf.image.grayscale_to_rgb(x)
    x = tf.cast(x, tf.float32)
    x = tf.keras.applications.mobilenet.preprocess_input(x)
    core = tf.keras.applications.MobileNet(mobilenet_input_shape,
                                           alpha=0.25, include_top=False)
    core.trainable = False
    x = core(x)
    x = layers.Flatten()(x)
    x = layers.Dense(n_classes, activation=None)(x)

    model = tf.keras.Model(inputs=[i], outputs=[x])

    return model  # Parameters #: 255417
  1. Parameters: 255,417
  2. Input: 40k images 128x128, gray
  3. Output: 9 classes
  4. Training:
    • Pretrained weights: ImageNet
    • LR: 1e-4
    • Epochs: 20
    • Time: 20 min
  5. Test accuracy: 0.37600 / 0.37160 (private / public)

Training progress:

model-2-training-plots

  1. Переобучение после 5-й эпохи.

Для дальнейшего увеличения точности необходимо решить проблему переобучения с помощью аугментации.

Этап 4. Аугментация

Colab

Рассмотрены два варианта:

  1. Аугментация на CPU, встроенная в пайплайн tf.data.Dataset
  2. Аугментация на GPU, встроенная в модель (единый вычислительный граф)

По результатам тестов на производительность была выбрана аугментация на GPU и разработан кастомный аугментирующий слой (tf.keras.layer).

  1. Добавлена аугментация (1 трансформация: hirizontal flip), архитектура та же, обучение 20 минут, точность = 0.42040
  2. Добавлена аугментация (7 трансформаций), обучение 2ч50м, точность = 0.464400

Модель со встроенной аугментацией (1)

Архитектура такая же как на этапе 3 + аугментирующий слой (горизонтальный флип).

  1. Parameters: 255,417
  2. Input: 40k images 128x128, gray, augmented (horizontal flip)
  3. Output: 9 classes
  4. Training:
    • Pretrained weights: ImageNet
    • LR: 1e-4
    • Epochs: 20
    • Time: 20 min
  5. Test accuracy: 0.42040 / 0.40920 (private / public)

training-2021-11-22

  1. Переобучение после 11-й эпохи.

Модель со встроенной аугментацией (7)

Colab

Аугментация с помощью 7 трансформаций.

  1. Parameters: 255,417

  2. Input: 40k images 128x128, gray, augmented (7 methods)

    • 7 augmentation transformations:

      tf.image.random_contrast(inputs, .2, 2)
      tf.image.random_brightness(inputs, .1)
      tf.keras.layers.RandomRotation(.05, fill_mode='nearest')
      tf.keras.layers.RandomZoom(.2, fill_mode='nearest')
      tf.keras.layers.RandomTranslation(0.1, 0.1, fill_mode='nearest')
      tf.keras.layers.RandomFlip('horizontal')
      RandomJpegQuality(30, 100)  # custom
      
  3. Output: 9 classes

  4. Training:

    • Pretrained weights: ImageNet
    • LR: 1e-4
    • Epochs: 40
    • Time: 2h40m
  • Test accuracy: 0.46080 / 0.45840 (private / public)

training-2021-11-23

Дальнейшее развитие

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

  1. Модифицировать датасет:
    1. Больше данных -- добавить данные из других датасетов (например AffectNet).
    2. Провести чистку данных: с помощью детектора лиц найти и удалить изображения, где нет лиц.
    3. Добавить класс non_face с изображениями без лиц.
  2. Увеличить количество методов аугментации, чтобы сделать модель менее чувствительной к качеству изображений:
    1. Добавить артефакты растяжения изображения разными алгоритмами.
    2. Добавить искажение пропорций (aspect ratio), растяжение-сжатие по одной оси.
    3. Добавить искажения специфичные для фотографий: блики, fisheye.
  3. Препроцессинг входных изображений:
    1. Применять face alignment с помощью 5-point landmark детектора, чтобы компенсировать разные положение, масштаб и наклон лиц на изображениях.
    2. С помощью 68-point landmark детектора отделить лица от бэкграунда, что может повысить точность распознавания за счет удаления несущественных деталей с изображения (шум).
  4. Модифицировать архитектуру модели:
    1. Протестировать более свежие CNN архитектуры (MobileNet-v3, EfficientNet).
    2. Увеличить размер модели (увеличить параметр MobileNet(alpha=1)).
  5. Усовершенствовать процедуру обучения:
    1. Добавить чекпоинты.
    2. Добавить мониторинг.
    3. Использовать learning rate scheduler.

Обучение модели для valence-arousal разложения

Colab

Датасет со значениями valence и arousal меток был получен из датасета для классификации эмоций с помощью системы координат:

img

emot_valence_arousal = dict(
    anger     =(-3.0,  1.0),
    contempt  =(-3.5, -0.5),
    disgust   =(-1.0, -1.8),
    fear      =(-1.0,  2.8),
    happy     =( 4.0,  1.0),
    neutral   =( 0.0,  0.0),
    sad       =(-3.5, -1.8),
    surprise  =( 3.0,  2.0),
    uncertain =( 0.0, -2.0),
)

Кроме того к каждому значению было добавлено случайное смещение (шум) выбранное из равномерного распределения в промежутке [-0.4, 0.4].

Для valence-arousal (приятный-напряженный) предсказания использовались две отдельные модели, полученные на базе классификационной модели для предсказания выражения лица.

У классификационной модели выходной слой был заменен на слой с одним нейроном + выполнена стандартная процедура дообучения (разморозка сверточных слоев и обучение на пониженном LR).

Лосс функция:

  1. MSE

Метрики:

  1. RMSE
  2. SAGR

Sign Agreement Metric (SAGR) -- shows the fraction of cases where the model predicts the right "direction" of emotion, i.e. positive value for pleasant/tense, negative value for unpleasant/calm.

image-20211209170549436

Valence model training:

training-valence

  1. Training time: 50m
  2. SAGR ~ 0.7186 (т.е. в 72% модель верно предсказывает -- лицо выражает приятную или неприятную эмоцию).

Arousal model training:

training-arousal

  1. Training time: 1h40m
  2. SAGR ~ 0.6627 (т.е. в 66% модель верно предсказывает -- выражение лица напряженное или расслабленное).

Демонстрация и тестирование производительности: colab.

Проверка точности с помощью обратного преобразования

Для проверки точности также было выполнено обратное преобразование, VA → emotion, по минимальному евклидову расстоянию до одного из центров (anger, contempt, disgust, etc.).

  1. kaggle score (private/public): 0.30080 / 0.29759
  2. Функция преобразования и генератор файла submisstion_va.csv: colab

Распознавание эмоций на изображении

Распознавание эмоций на произвольном (не подготовленном) изображении состоит из нескольких этапов:

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

Распознавание эмоций на видео-потоке

В качестве видео-потока может выступать как видео-файл, так и поток (stream) с веб-камеры.

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

Выбор детектора лиц

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

Все тесты выполнены в среде Google Colab.

Скорость

Backend FPS (CPU) ms/img (CPU) FPS (GPU) ms/img (GPU)
opencv 59 17 62 16
ssd 10 102 10 97
dlib (deepface) 48 21 48 21
mtcnn 2.5 388 2.5 391
retinaface - - 4.1 242
dlib (hog) 54.5 18 53.6 19
dlib (cnn) 4.2 238 74.2 13
  1. dlib (deepface) -- один из бэкэндов библиотеки deepface использует dlib HOG detector.
  2. Из результатов видно, что mtcnn не использует GPU для ускорения инференса.
  3. dlib (hog) -- функция dlib.get_frontal_face_detector().
  4. dlib (cnn) -- функция dlib.cnn_face_detection_model_v1("mmod_human_face_detector.dat").

Точность

Число обнаруженных лиц из 100 фотографий.

Co-occurance matrix:

ssd retinaface opencv dlib (hog) mtcnn dlib (cnn)
ssd 70 64 64 69 69 70
retinaface 64 70 62 69 70 70
opencv 64 62 90 89 88 90
dlib (hog) 69 69 89 96 94 96
mtcnn 69 70 88 94 97 97
dlib (cnn) 70 70 90 96 97 99
  1. На диагонали -- число детекций из 100 изображений (напр. mtcnn обнаружил 97 лиц из 100).
  2. На пересечении -- число изображений, где оба алгоритма обнаружили лицо.
  3. На текущий момент (2021-11-06) реализация RetinaFace имеет проблемы с обнаружением:

Выводы

  1. Наиболее точный: dlib (cnn).
  2. Наиболее быстрый на CPU: Haar Cascade (opencv), на GPU: dlib (cnn)
  3. Оптимальный выбор:
    1. Для CPU -- dlib (hog):
      1. По точности 3-й после dlib (cnn) и mtcnn.
      2. По скорости на CPU 2-й, после haar cascade (opencv).
    2. Для GPU -- dlib (cnn):
      1. По точности 1-й.
      2. По скорости на GPU 1-й.

Демо. Веб-камера

Colab

Вывод потока с веб-камеры в Google Colab + распознавание эмоций + оверлей с аннотацией.

Демо. Видео-файл

Colab

В качестве источника видео-потока используется mp4 файл.

  • Если не указать путь к файлу (None), то используется поток с веб-камеры.

YouTube

По окончании видео выводится график эмоций на временной шкале:

emotion-timeline

С помощью графика surprise можно выбрать кадры, на которых запечатлены моменты вызвавшие наибольшее удивление:

surprise_moments