基於卷積神經網路的人臉表情識別應用--AR川劇變臉(一)

鹽亭的森林發表於2023-01-08

1、摘要

本專案將在Android上實現一種透過識別表情類別,從而給人臉戴上不同樣式臉譜的AR軟體,效果如下:

透過深度學習和Keras訓練一個人臉表情識別的卷積神經網路,然後使用TensorFlow Lite轉換為tflite檔案,部署到Android平臺。

想要實現這樣一個軟體,核心就是兩部分:
1)使用卷積神經網路訓練一個人臉表情識別模型,
2)將訓練好的模型移植到Android平臺,同時在Android實現臉譜AR效果,並結合表情識別模型的識別結果,渲染不同的臉譜樣式

本文講第一部分,如何使用Keras訓練一個人臉表情識別的卷積神經網路。

第二部分見:基於卷積神經網路的人臉表情識別應用--AR川劇變臉(二)

2、資料集處理

資料集我們使用FER2013PLUS人臉表情識別資料集,大概有35000張圖片,每張圖片是48*48解析度的灰度圖。

在這裡插入圖片描述

下圖展示7個表情類別的資料集樣本量,可以看出類別 angry、happy、sad、surprised、normal 這 5 種表情數量較多,而 disgust、fear這 2 種表情較少。
在這裡插入圖片描述

在實驗中為了防止出現過擬合現象需要進行資料集擴充,提高模型的泛化能力。資料增強是指對輸入的影像進行隨機的影像處理,比如旋轉、位移、光照強度、裁剪等,對於計算機而言,經過位移後的影像和原影像是兩幅不同的影像,但具有相同的標籤,從而擴充了已有的資料集。而透過使用大量不同的資料集來訓練網路,進一步提高深度模型的泛化能力,使得網路即不過擬合也不欠擬合,達到最佳狀態。

模型的實際使用常見是自由場景,自由場景中光照亮度的不同會影響模型的效能,新增隨機光照,同時為了擴充資料集和對非正常角度人臉的識別增加隨機旋轉、隨機聚焦和水平翻轉。下圖 展示對一張 48*48 尺寸,表情為 happy 的灰度圖進行隨機亮度、隨機旋轉、隨機聚焦和隨機水平翻轉的效果。可以看出,小型資料集藉此技術可以實現數十倍的規模擴充,而資料集規模的提升可以提升網路的泛化能力和魯棒性,提高網路在場景下的識別準確率。
在這裡插入圖片描述

在Keras中,我們需要使用資料載入器載入資料,最大化硬體利用率,幸運的是Keras已經提供了方法,我們直接使用即可:

def getDenerator(fer2013plus_path="datasets/fer2013plus", batch_size=32):
    """
	我們傳遞資料集的位置和batch_size,然後函式返回用於訓練和測試的資料載入器,用於訓練時投餵資料
    :param fer2013plus_path:fer2013plus資料集目錄
    :param batch_size:
    :return:
    """
    train_datagen = ImageDataGenerator(    # 定義資料增強的方式
        brightness_range=(0.7, 1.3),  # 隨機亮度
        featurewise_center=False,
        featurewise_std_normalization=False,
        rotation_range=10,  # 隨機旋轉
        zoom_range=(0.7, 1.3),  # 聚焦
        horizontal_flip=True)  # 水平翻轉
        
    train_generator = train_datagen.flow_from_directory(fer2013plus_path+"/train",  # 告訴Keras圖片的位置
                                                        target_size=(48, 48),		# 將圖片縮放到指定大小
                                                        color_mode="grayscale",		# 因為是灰度圖
                                                        batch_size=batch_size,		# 一次性載入多少圖片,越大訓練越快,但是需要更多的視訊記憶體
                                                        class_mode='categorical')    

    test_datagen = ImageDataGenerator()  # 水平翻轉
    test_generator = test_datagen.flow_from_directory(fer2013plus_path+"/test",
                                                      target_size=(48, 48),
                                                      color_mode="grayscale",
                                                      batch_size=batch_size,
                                                      class_mode='categorical')

    return train_generator,test_generator

3、模型設計

模型設計參考VGG網路,也就是Conv+Conv+Pool式的堆疊。

如下圖是VGG的結構,可以看出就是Conv+Conv+Pool式的堆疊,最後使用全連線層,實現1000類別的分類。
在這裡插入圖片描述
但是我們人臉表情識別資料集比較小,僅有3萬多張圖片,模型太大容易過擬合,所以以VGGNet為藍本,設計一個比較小的模型,如下圖所示,也是conv+conv+pool式,但是更小。

因為我們的表情是7中分類,所以輸出的全連線層長度是7。

在這裡插入圖片描述

在Keras中定義模型結構其實而很簡單,程式碼如下:

def getVXSlim():
    model = Sequential()

    # block1
    model.add(Conv2D(64, (5, 5), input_shape=(48, 48, 1), activation='relu', padding='same'))
    model.add(Conv2D(64, (5, 5), activation='relu', padding='same'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))

    # block2
    model.add(Conv2D(128, (5, 5), activation='relu', padding='same'))
    model.add(Conv2D(128, (5, 5), activation='relu', padding='same'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))

    # block3
    model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))

    # single conv
    model.add(Conv2D(64, (3, 3), padding='same'))

    # classifier
    model.add(Flatten())		# 將多維的資料展成一列,可以理解為將影像展開成一列
    model.add(Dense(7))			# 堆疊一個全連線層,長度為7
    model.add(BatchNormalization())
    model.add(Dense(7))			# 堆疊一個全連線層,長度為7

    # config
    model.add(Activation('softmax'))	# 最後使用softmax啟用函式,將輸出轉換為機率
    model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')  # 定義最佳化器

    return model

在上面的程式碼中使用了Conv2D、BatchNormalization、MaxPooling2D、Dense:

  • Conv2D:提取特徵,將影像分解為多個特徵的組合
  • BatchNormalization:將資料的分佈變換為正態分佈,再對映到其他分佈,可以加快模型的訓練速度
  • MaxPooling2D:可以縮減影像的尺寸
  • Dense:全連線層,用於綜合所有特徵進行判斷類別

4、訓練

其實到這裡,最重要的資料載入和模型定義就寫好了,就可以開始訓練了。訓練了100個輪次,然後開始訓練。
在這裡插入圖片描述
從圖中可以看到,訓練集精度和損失在 45epoch 左右進入緩慢最佳化階段,在 70epoch左右趨於穩定。測試集精度和損失在 45epoch 左右趨於穩定,最終測試集精度達到 83%,損失低於 0.3,取得了較好的表情識別效果。

都說深度學習是黑盒,我們來看看訓練出來的模型的視覺化效果。
在這裡插入圖片描述
在視覺化中間輸出部分,透過卷積網路的每層都會輸出結果,透過將結果視覺化可以看到輸入圖片在網路中的流動形式,在這裡展示第 3 層的 max_pooling2d、第 4 層的conv2d_2 和第 8 層的 conv2d_4 的輸出結果。在圖 中可以看出,在開始階段實際上是各種邊緣檢測器的集合,在這一階段,啟用幾乎保留了輸入影像中所有的資訊。在第二幅中,雖然仍有邊緣檢測的痕跡,但是此時更加像是對某些區域性部位的特徵檢測,隨著層數的加深,中間啟用的結果變得越來越抽象,也更加難以直觀地理解。在第三層中幾乎無法和原圖聯絡起來。所以抽象是卷積神經網路的一大能力,而這種能力也正是人類所擁有的。

我們將本文設計的模型和VGG9,以及mini_Xception進行對比:
在這裡插入圖片描述
從圖中可以看出,本文的VXSlim網路的驗證集精度最高,達到了 83%,VGG9 達到了 82%,mini_Xception 最低,只有79%

那麼本文的模型在7種表情類別上的效果如何?可以看到最好的是開心類別,正確率達到了91%!
在這裡插入圖片描述

我們把每一個類的正確識別率用曲線表示,可以看到本文設計的模型在7種類別上的正確率都是最高的,很nice!
在這裡插入圖片描述

5、效果展示

透過攝像頭進行表情識別效果展示

程式碼資源:
連結:https://pan.baidu.com/s/1Y0xWDmz_lg04PTiukoBVgQ?pwd=ozn5
提取碼:ozn5

相關文章