Как я разработал CNN, распознающий эмоции, и попал в топ-10 Kaggle

1656528732 kak ya razrabotal cnn raspoznayushhij emoczii i popal v top 10

Джерин Пол

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

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

PKZ128PL4nQ2RMRJ4MRuU7fLY-RijiHkyIH8
Изображение только для репрезентативных целей.

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

Краткое вступление

«Лучшие и красивые вещи в мире невозможно увидеть или даже прикоснуться. Их нужно чувствовать сердцем» ― Хелен Келлер

Хеллен Келлер отлично описала сущность человеческих эмоций в вышеупомянутой цитате. То, что было зарезервировано для животных, больше не ограничивается ими. Машинное обучение набирает обороты с потрясающими темпами. Появление сверточных нейронных сетей стало прорывом и изменило то, как компьютеры «смотрят» на свет.

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

WxrDi7qPgW7iQIxnx5Ppr3WvpiWrWKID6S8V
Несколько разных типов выражений лица.

Мы будем обучать сверточную нейронную сеть, используя набор данных FER2013, и использовать различные гиперпараметры для точной настройки модели. Мы будем обучать его Google Colab, который является исследовательским проектом, созданным для распространения образования с ML. Они выделят вам некоторые ресурсы, такие как GPU или TPU, и их можно использовать для более быстрого обучения модели. Самое приятное, что это абсолютно бесплатно.

Посмотрите на данные

Мы начнем с загрузки файла FER2013.csv на наш диск, чтобы получить доступ к Google Colab. В этом наборе данных имеется 35888 изображений, которые классифицируются по шести эмоциям. Файл данных содержит три столбца — Класс, Данные изображения и Использование.

класс: является цифрой от 0 до 6 и представляет эмоцию, изображенную на соответствующем рисунке. Каждая эмоция отображается на целое число, как показано ниже.

0 - 'Angry'1 - 'Disgust'2 - 'Fear' 3 - 'Happy' 4 - 'Sad' 5 - 'Surprise'6 - 'Neutral'

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

Использование: обозначает, следует ли использовать соответствующие данные для обучения или тестирования сети.

Разложение изображения.

Как мы все знаем, что изображения состоят из пикселей, и эти пиксели не что иное, как числа. Цветные изображения имеют три цветных канала – красный, зеленый и синий – и каждый канал представлен сеткой (двухмерным массивом). Каждая ячейка в сетке сохраняет число от 0 до 255, обозначающее интенсивность этой ячейки.

uQbNq15Y5ERHePR3iHaXM5Yj-eZpMM5X-srz
То, что вы видите (L) против того, что видит компьютер.

Когда эти три канала выравнены вместе, мы получаем изображения, которые мы видим.

Импорт необходимых библиотек

%matplotlib inlineimport matplotlib.pyplot as plt
import numpy as npfrom keras.utils import to_categoricalfrom sklearn.model_selection import train_test_split
from keras.models import Sequential #Initialise our neural network model as a sequential networkfrom keras.layers import Conv2D #Convolution operationfrom keras.layers.normalization import BatchNormalizationfrom keras.regularizers import l2from keras.layers import Activation#Applies activation functionfrom keras.layers import Dropout#Prevents overfitting by randomly converting few outputs to zerofrom keras.layers import MaxPooling2D # Maxpooling functionfrom keras.layers import Flatten # Converting 2D arrays into a 1D linear vectorfrom keras.layers import Dense # Regular fully connected neural networkfrom keras import optimizersfrom keras.callbacks import ReduceLROnPlateau, EarlyStopping, TensorBoard, ModelCheckpointfrom sklearn.metrics import accuracy_score

Определите механизм загрузки данных

Теперь мы определим функцию load_data(), которая будет эффективно анализировать файл данных и вытаскивать необходимые данные, а затем преобразовать их в удобный для использования формат изображения.

Все изображения в нашем наборе имеют размер 48×48. Поскольку эти изображения имеют оттенки серого, есть только один канал. Мы вытащим данные изображения и упорядочим их в массив 48×48. Затем превратите его в целые числа без знака и разделите на 255, чтобы нормализовать данные. 255 – максимально возможное значение одной ячейки. Разделив каждый элемент на 255 мы гарантируем, что все наши значения находятся в диапазоне от 0 до 1.

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

def load_data(dataset_path):
data = []  test_data = []  test_labels = []  labels =[]
  with open(dataset_path, 'r') as file:      for line_no, line in enumerate(file.readlines()):          if 0 < line_no <= 35887:            curr_class, line, set_type = line.split(',')            image_data = np.asarray([int(x) for x in line.split()]).reshape(48, 48)            image_data =image_data.astype(np.uint8)/255.0                        if (set_type.strip() == 'PrivateTest'):                            test_data.append(image_data)              test_labels.append(curr_class)            else:              data.append(image_data)              labels.append(curr_class)            test_data = np.expand_dims(test_data, -1)      test_labels = to_categorical(test_labels, num_classes = 7)      data = np.expand_dims(data, -1)         labels = to_categorical(labels, num_classes = 7)          return np.array(data), np.array(labels), np.array(test_data), np.array(test_labels)

После того, как наши данные будут разделены, мы расширим размеры данных тестирования и обучения на единицу, чтобы учесть канал. Затем мы закодируем все метки с помощью функции to_categorical() и вернем все списки как numpy массивы.

Мы загрузим данные, вызвав функцию load_data().

dataset_path = "/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/Data/fer2013.csv"
train_data, train_labels, test_data, test_labels = load_data(dataset_path)
print("Number of images in Training set:", len(train_data))print("Number of images in Test set:", len(test_data))

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

Определение модели.

Мы будем использовать Keras для создания последовательной сверточной сети. Это означает, что наша нейронная сеть станет линейным стеком слоев. Эта сеть будет иметь следующие компоненты:

  1. Сверточные слои: эти слои являются строительными блоками нашей сети, и вычисляют точечное произведение между их весом и небольшими регионами, с которыми они связаны. Вот как эти слои узнают некоторые особенности из этих изображений.
  2. Функции активации: это функции, применяемые к выходам всех уровней сети. В этом проекте мы прибегнем к использованию двух функций. Рела и Softmax.
  3. Объединение слоев: эти слои уменьшают выборку операции по размерам. Это помогает снизить объем пространственных данных и минимизировать необходимую мощность обработки.
  4. Плотные слои: эти слои находятся в конце CNN. Они принимают на себя все данные объектов, сгенерированные слоями свертки, и принимают решения.
  5. Слои выпадения: случайным образом выключает несколько нейронов в сети, чтобы предотвратить переоборудование.
  6. Пакетная нормализация: нормализует выход предыдущего уровня активации путем вычитания среднего пакетного значения и деления на стандартное отклонение пакета. Это ускоряет тренировочный процесс.
model.add(Conv2D(64, (3, 3), activation='relu', input_shape=(48, 48, 1), kernel_regularizer=l2(0.01)))model.add(Conv2D(64, (3, 3), padding='same',activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2), strides=(2, 2)))model.add(Dropout(0.5))    model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.5))    model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.5))    model.add(Conv2D(512, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(512, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(512, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.5))    model.add(Flatten())model.add(Dense(512, activation='relu'))model.add(Dropout(0.5))model.add(Dense(256, activation='relu'))model.add(Dropout(0.5))model.add(Dense(128, activation='relu'))model.add(Dropout(0.5))model.add(Dense(64, activation='relu'))model.add(Dropout(0.5))model.add(Dense(7, activation='softmax'))

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

adam = optimizers.Adam(lr = learning_rate)
model.compile(optimizer = adam, loss="categorical_crossentropy", metrics = ['accuracy'])    print(model.summary()

Функции обратного вызова

Функции обратного вызова – это функции, вызываемые после каждой эпохи во время процесса обучения. Мы будем использовать следующие функции обратного вызова:

  1. ReduceLRonPlateau: обучение нейронной сети иногда может останавливаться, и на этом этапе мы перестаем видеть прогресс. Таким образом, эта функция отслеживает потерю подтверждения наличие признаков плато, а затем изменяет скорость обучения на указанный коэффициент, если обнаружено плато.
lr_reducer = ReduceLROnPlateau(monitor="val_loss", factor=0.9, patience=3)

2. Ранняя остановка: иногда прогресс останавливается при обучении нейронной сети, и мы перестаем видеть какое-либо улучшение точности проверки (в этом случае). В большинстве случаев это означает, что сеть не будет сближаться дальше и нет смысла продолжать процесс обучения. Эта функция ожидает определенное количество эпох и прекращает обучение, если не будут найдены изменения в параметре.

early_stopper = EarlyStopping(monitor="val_acc", min_delta=0, patience=6, mode="auto")

3. ModelCheckpoint: Обучение нейронных сетей обычно занимает много времени, и в течение этого периода может произойти все, что может привести к потере всех переменных и весов. Создание контрольных точек хорошая привычка, поскольку она сохраняет вашу модель после каждой эпохи. Если ваше обучение прекращается, вы можете скачать контрольную точку и восстановить процесс.

checkpointer = ModelCheckpoint('/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/Model/weights.hd5', monitor="val_loss", verbose=1, save_best_only=True)

Пора тренироваться

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

epochs = 100batch_size = 64learning_rate = 0.001

Наши данные будут проходить через модель 100 раз и в партиях по 64 изображения. Мы будем использовать 20% наших обучающих данных для проверки модели после каждой эпохи.

model.fit(          train_data,          train_labels,          epochs = epochs,          batch_size = batch_size,          validation_split = 0.2,          shuffle = True,          callbacks=[lr_reducer, checkpointer, early_stopper]          )

Теперь когда сеть тренируется, я предлагаю вам пойти и закончить начатую книгу или побегать. На Google Colab мне понадобилось около часа.

Тестируйте модель

Помните приватный набор, который мы хранили отдельно? Это было именно для этого момента. Это момент истины, и именно здесь мы будем пожинать плоды нашего труда.

predicted_test_labels = np.argmax(model.predict(test_data), axis=1)test_labels = np.argmax(test_labels, axis=1)print ("Accuracy score = ", accuracy_score(test_labels, predicted_test_labels))

Ну, результаты вернулись и мы набрали 63,167%. На первый взгляд это не так много, но мы вышли на девятое место в конкурсе Kaggle по распознаванию эмоций лица.

luDy-IohSf6uUPKMqJEwQfufaNs0r0Bmu3iu
Хотя это не большая проблема

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

Сохраните модель

Быстро сохраните модель с помощью model_from_json от keras.models.

from keras.models import model_from_json
model_json = model.to_json()with open("/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/FERmodel.json", "w") as json_file:    json_file.write(model_json)# serialize weights to HDF5model.save_weights("/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/FERmodel.h5")print("Saved model to disk")

Заворачивая все это

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

Я расширил этот проект, создав программу Python, которая способна распознавать лица и распознавать их эмоции в режиме реального времени. Об этом будет рассказано в следующей публикации.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *