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

Пример:

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

Решение / подход:

Введение (зачем это нужно) - ›

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

Шаг №1 - ›

Импорт необходимых библиотек Python, таких как numpy, seaborn, matplotlib, tensorflow

Python3

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import utils
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Dense, Input, Dropout,Flatten, Conv2D
from tensorflow.keras.layers import BatchNormalization, Activation, MaxPooling2D
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau
from tensorflow.keras.utils import plot_model
from IPython.display import SVG, Image
from livelossplot.inputs.tf_keras import PlotLossesCallback
import tensorflow as tf
print("Tensorflow version:", tf.__version__)

В приведенном выше коде мы импортировали различные модули из keras, который является оболочкой для tenorflow. Мы импортировали именно те модули из keras, которые помогут в построении модели CNN. В последней строке в приведенном выше коде мы распечатали текущую версию тензорного потока в системе.

Обратите внимание, что - › требуемая версия тензорного потока должна быть больше или равна 2,0

Шаг 2 - ›

Теперь мы получим набор данных, на котором будет обучаться наша модель, и проверим, насколько хорошо или плохо наша модель работает с этим конкретным набором данных, чтобы мы могли улучшить показатель точности. Я загрузил используемый мной набор данных на Kaggle, чтобы любой мог получить к нему доступ. Ссылка на набор данных: - https://www.kaggle.com/aadityasinghal/facial-expression-dataset

В наборе данных есть семь категорий выражений.

Шаг 3 - ›

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

Python3

for expression in os.listdir("PATH OF TRAIN FOLDER"):
    print(str(len(os.listdir("PATH OF TRAIN FOLDER" + expression))) + " " + expression + " images")

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

Шаг 4 - ›

Теперь мы будем сгенерировать пакеты обучения и тестирования (проверки), чтобы наша модель могла быть обучена и оценена / проверена на тестовых данных. Это очень важный шаг, поскольку без него у нас не может быть точной модели, а также без обучения модель также не знает, что ей нужно искать, а также прогнозировать.

Python3

img_size = 48
batch_size = 64
datagen_train = ImageDataGenerator(horizontal_flip=True)
train_generator = datagen_train.flow_from_directory("PATH OF TRAIN FOLDER",
                                                    target_size=(img_size,img_size),
                                                    color_mode="grayscale",
                                                    batch_size=batch_size,
                                                    class_mode='categorical',
                                                    shuffle=True)
datagen_validation = ImageDataGenerator(horizontal_flip=True)
validation_generator = datagen_validation.flow_from_directory("PATH OF TEST FOLDER",
                                                    target_size=(img_size,img_size),
                                                    color_mode="grayscale",
                                                    batch_size=batch_size,
                                                    class_mode='categorical',
                                                    shuffle=False)

В приведенном выше коде мы определили размер изображения 48, поэтому каждое изображение будет уменьшено до размера 48x48. После этого мы определили размер пакета, равный 64, что означает, что в каждую эпоху, т. Е. Когда модель проходит через набор обучающих данных в каждом цикле, количество передаваемых изображений будет 64. Это означает, что модель возьмет первые 64 изображения для обучения через первую эпоху и будет продолжать работу до тех пор, пока все эпохи не будут завершены.

После этого мы использовали ImageDataGenerator из модуля keras, который генерирует пакеты данных тензорного изображения с увеличением данных в реальном времени. Здесь мы сохранили горизонтальное отражение True, что означает, что входные изображения будут случайным образом переворачиваться по горизонтали.

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

Сначала мы займемся данными поездов. Здесь мы использовали функцию из datagen_train, которая взята из ImageDataGenerator, то есть flow_from_directory, которая принимает несколько параметров, таких как путь к набору данных, target_size (размер выходного изображения), color_mode (цвет выходных изображений, мы установили gayscale, который дает серые изображения), batch_size, class_mode (определяет тип возвращаемых массивов меток, мы указали категориальный), и у нас есть перемешивание False.

Теперь мы сделаем вышеупомянутое то же самое для создания изображений тестового набора данных. Единственным изменением будет путь к тестовой папке.

Шаг 5 - ›

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

Python3

# Initialising the CNN
model = Sequential()
# 1 - Convolution
model.add(Conv2D(64,(3,3), padding='same', input_shape=(48, 48,1)))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 2nd Convolution layer
model.add(Conv2D(128,(5,5), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 3rd Convolution layer
model.add(Conv2D(512,(3,3), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 4th Convolution layer
model.add(Conv2D(512,(3,3), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# Flattening
model.add(Flatten())
# Fully connected layer 1st layer
model.add(Dense(256))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.25))
# Fully connected layer 2nd layer
model.add(Dense(512))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(Dense(7, activation='softmax'))
opt = Adam(lr=0.0005)
model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()

В приведенном выше коде мы создали базовую структуру того, как будет выглядеть наша нейронная сеть. Для этого мы воспользовались помощью модуля keras, который является отличной оболочкой библиотеки тензорного потока и помогает сократить нашу работу. Прежде всего, мы инициализировали модель CNN, используя последовательную () функцию. После этого мы создали первые четыре слоя нейронной сети, которые являются слоем свертки.

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

Сверточный слой можно добавить в нашу модель с помощью model.add (). Он принимает несколько параметров, например Conv2D, который указывает, что добавляемый слой является сверточным. Этот метод также принимает два параметра: первый - количество фильтров и объединение. После этого мы применяем Пакетную нормализацию, которая применяет преобразование, которое поддерживает средний выходной сигнал близким к 0, а стандартное отклонение выходного сигнала - близким к 1. После этого активация Добавлена ​​функция, которая выполняет нелинейное преобразование, которое мы делаем над входным сигналом. Эти преобразованные выходные данные затем отправляются на следующий уровень нейронов в качестве входных данных. И после этого мы применили MaxPolling2D, который извлекает из пула только максимум. И, наконец, мы добавляем dropout, который предотвращает переоснащение модели. На этом мы завершаем построение сверточного слоя.

Здесь для всех четырех слоев свертки все параметры, кроме Conv2D, остаются неизменными. для первого Conv мы также упомянули размер ввода, размер получаемого изображения.

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

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

Для всех слоев до сих пор мы сохранили функцию активации ReLU, потому что ReLU выпрямлен наполовину (снизу). f (z) равно нулю, когда z меньше нуля, и f (z) равно z, когда z больше или равно нулю.

Но после всех вышеперечисленных слоев мы наконец добавим слой Dense Layer с активацией, установленной как Softmax, который превращает числа он же входит в вероятности, которые в сумме равны единице. Функция Softmax выводит вектор, представляющий распределения вероятностей списка потенциальных результатов.

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

После этого мы использовали model.compile для компиляции модели. Он принимает некоторые параметры, такие как optimizer, которые оптимизируют входные веса, сравнивая прогноз и функцию потерь. Мы оставили оптимизатор для Адама с указанной скоростью обучения (lr). После этого мы добавили убыток к категориальной_кросцентропии и сохранили метрики (, который используется для оценки производительности модели, равной ) точности. Наконец, мы получили сводку модели с помощью model.summary ().

Шаг 6 - ›

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

Python3

plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True)
Image('model.png',width=400, height=200)

В приведенном выше коде мы использовали plot_model, который также является функцией библиотеки keras. мы присвоили ему такие же параметры, как название модели, имя файла, т. е. to_file, show_shapes, который отображает информацию о форме, и show_layer_names: отображает имена слоев.

После этого мы использовали функцию библиотеки Ipython Image для отображения архитектуры модели на выходе. Для отображения требуется несколько аргументов, например имя файла, ширина и высота изображения. Результат этого (изображение) показан ниже.

Шаг № 7 - ›

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

Python3

%%time
epochs = 15
steps_per_epoch = train_generator.n//train_generator.batch_size
validation_steps = validation_generator.n//validation_generator.batch_size
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1,
                              patience=2, min_lr=0.00001, mode='auto')
checkpoint = ModelCheckpoint("model_weights.h5", monitor='val_accuracy',
                             save_weights_only=True, mode='max', verbose=1)
callbacks = [PlotLossesCallback(), checkpoint, reduce_lr]
history = model.fit(
    x=train_generator,
    steps_per_epoch=steps_per_epoch,
    epochs=epochs,
    validation_data = validation_generator,
    validation_steps = validation_steps,
    callbacks=callbacks
)

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

Прежде всего, мы установили количество эпох, где одна эпоха - это когда ВСЕ набор данных передается вперед и назад через нейронную сеть только ОДИН РАЗ. Поскольку одна эпоха слишком велика, чтобы передать ее в компьютер за один раз, мы делим ее на несколько меньших пакетов. По мере увеличения количества эпох вес меняется все чаще. в нейронной сети, и кривая переходит от недостаточного соответствия к оптимальной кривой.

Важное примечание: - Я сохранил количество эпох равным 15, вы можете увеличить его, чтобы получить большую точность и получить отличные результаты.

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

После этого мы установили ReduceLROnPlateau, который регулирует скорость обучения при обнаружении плато в производительности модели, например без изменений для данного количества тренировочных эпох. После этого мы установили ModelCheckpoint, который позволяет вам определять, где проверять веса модели, как файл следует назвать и при каких обстоятельствах производить КПП из модели. После этого мы установили обратные вызовы с помощью PlotLossesCallback (), который дает отчет в реальном времени о том, как проходит обучение. on, и мы также добавили контрольные точки и reduce_lr.

Наконец, мы выполнили model.fit (), который запускает обучение и проверку

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

Log-loss (cost function):
training   (min:    0.866, max:    1.786, cur:    0.866)
validation (min:    0.970, max:    1.705, cur:    0.970)
accuracy:
training   (min:    0.313, max:    0.675, cur:    0.675)
validation (min:    0.381, max:    0.643, cur:    0.643)
Epoch 00015: saving model to model_weights.h5
448/448 [==============================] - 27s 60ms/step - loss: 0.8659 - accuracy: 0.6748 - val_loss: 0.9700 - val_accuracy: 0.6426
CPU times: user 6min 50s, sys: 57.4 s, total: 7min 47s
Wall time: 6min 46s

Мы получили точность набора данных почти 68%, обработав всего 15 эпох. Вы можете пробежать больше эпох и можете изменить некоторые другие параметры, чтобы получить более высокую точность.

Шаг № 8 - ›

Теперь, когда у нас есть обученная модель, давайте сохраним модель в формате json с ее веса также сохранены.

Python3

model_json = model.to_json()
model.save_weights('model_weights.h5')
with open("model.json", "w") as json_file:
    json_file.write(model_json)

В приведенном выше коде мы сначала преобразовали модель в формат json. После этого мы сохранили веса модели в формате .h5 . После этого мы открыли файл model.json в режиме записи и записали в этот файл модель, которая была преобразована в формат json. Теперь у нас есть два файла с именами model.json и model_weights.h5 для модели и ее веса соответственно, которые можно использовать где угодно для составления прогнозов.

Шаг 9 - ›

Теперь мы напишем код Python для загрузки модели и весов и создания прогнозов.

Python3

from tensorflow.keras.models import model_from_json
class FacialExpressionModel(object):
    EMOTIONS_LIST = ["Angry", "Disgust",
                    "Fear", "Happy",
                    "Neutral", "Sad",
                    "Surprise"]
    def __init__(self, model_json_file, model_weights_file):
        # load model from JSON file
        with open(model_json_file, "r") as json_file:
            loaded_model_json = json_file.read()
            self.loaded_model = model_from_json(loaded_model_json)
        # load weights into the new model
        self.loaded_model.load_weights(model_weights_file)
        self.loaded_model.make_predict_function()
    def predict_emotion(self, img):
        self.preds = self.loaded_model.predict(img)
        return FacialExpressionModel.EMOTIONS_LIST[np.argmax(self.preds)]

В приведенном выше коде мы сначала импортировали функцию model_from_json, которая помогает нам импортировать модель из файла json. После мы написали класс python, в котором сначала есть список эмоций, содержащийся в нашем наборе данных. После этого мы определили метод инициализации, который принимает файл model.json и файл весов модели, который является в формате .h5. После этого мы читаем файл json и используем функцию model_from_json для загрузки модели. После этого мы загружаем веса в модель.

После этого в классе мы определили метод с именем pred_emotion, который дает предсказание изображения. Сначала он использует метод .predict для прогнозирования, после чего мы используем numpy argmax, чтобы получить целое число ч / б 0–6, представляющее соответствующую эмоцию в списке. И, наконец, мы возвращаем название этой конкретной эмоции.

Шаг 10 - ›

Теперь, когда у нас есть режим кода для загрузки весов в модель, теперь мы создадим кадры видео и выполним для них прогнозы.

Python3

import cv2
facec = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
model = FacialExpressionModel("model.json", "model_weights.h5")
font = cv2.FONT_HERSHEY_SIMPLEX
class VideoCamera(object):
    def __init__(self):
        self.video = cv2.VideoCapture(0)
    def __del__(self):
        self.video.release()
    # returns camera frames along with bounding boxes and predictions
    def get_frame(self):
        _, fr = self.video.read()
        gray_fr = cv2.cvtColor(fr, cv2.COLOR_BGR2GRAY)
        faces = facec.detectMultiScale(gray_fr, 1.3, 5)
        for (x, y, w, h) in faces:
            fc = gray_fr[y:y+h, x:x+w]
            roi = cv2.resize(fc, (48, 48))
            pred = model.predict_emotion(roi[np.newaxis, :, :, np.newaxis])
            cv2.putText(fr, pred, (x, y), font, 1, (255, 255, 0), 2)
            cv2.rectangle(fr,(x,y),(x+w,y+h),(255,0,0),2)
        return fr

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

Каскадные классификаторы Хаара, которые мы использовали, можно найти ЗДЕСЬ.

После этого мы вызвали модель, передав файл model.json и файл весов. После этого мы установили шрифт для CV2. После этого мы написали класс python. В классе прежде всего мы объявили метод init, который использует метод cv2 VideoCapture для доступа к камере или видеофайл, для которого вы хотите получить прогнозы.

Важное примечание: -

В приведенном выше коде мы передали 0 в качестве аргумента в VideoCapture.. Вы можете изменить 0 на путь к видеофайлу, чтобы делать прогнозы для видеофайла. Здесь 0 означает, что CV2 будет получать видео с веб-камеры вашего ПК.

После этого мы объявили деструктор для класса, который освобождает видео и останавливает методы, когда вы этого хотите.

После этого мы объявили метод get_frame, который первым читает видео. После этого мы использовали метод cv2.cvtColor (), который используется для преобразования изображения из одного цветового пространства в другое. Здесь мы использовали cv2.COLOR_BGR2GRAY, который преобразует изображение в серый цвет, поскольку наша модель обучалась на изображениях с серым цветом. После этого мы использовали detectMultiScale (), который обнаруживает объекты разных размеров во входном изображении. Обнаруженные объекты возвращаются в виде списка прямоугольников. После этого мы перешли к различным координатам возвращенного изображения и изменили размер изображения с помощью функции CV2.resize (). Наконец, мы использовали model.predict_emotion, чтобы получить прогнозируемую эмоцию. После этого мы помещаем текст в рамку изображения, которая показывает прогнозируемую эмоцию, а также имеем поместите прямоугольную рамку вокруг области, где было обнаружено лицо. И, наконец, мы вернули этот фрейм вместе с предсказанным полем и текстом.

Шаг 11 - ›

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

Python3

def gen(camera):
    while True:
        frame = camera.get_frame()
        cv2.imshow('Facial Expression Recognization',frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cv2.destroyAllWindows()

В приведенном выше коде мы объявили функцию Python с именем gen, которая принимает в качестве параметра камеру. Здесь мы запускаем цикл while для True, непрерывно. И сначала мы вызываем функцию get_frame для VideoCamera. После этого мы используем метод imshow CV2. чтобы показать видео как выходной. После этого мы написали код остановки кода. Мы добавили условие if, что при нажатии клавиши 'q' цикл будет прерван, а экран вывода будет уничтожен с помощью destroyAllWindows () .

Шаг 12 - ›

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

Python3

gen(VideoCamera())

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

ВЫХОД:

Важные моменты: -

  • Чтобы получить набор данных, нажмите ЗДЕСЬ
  • Чтобы получить файл Каскадные классификаторы Хаара, нажмите ЗДЕСЬ
  • Чтобы получить весь код с файлом модели, таким как JSON и веса, и всем остальным кодом, ССЫЛКА НА GITHUB находится ЗДЕСЬ
  • Ссылка на Блокнот Kaggle: ЗДЕСЬ