
情緒檢測或表情分類在深度學習領域中有著廣泛的研究。使用相機和一些簡單的代碼我們就可以對情緒進行實時分類,這也是邁向高級人機交互的一步。
前言
本期我們將首先介紹如何使用Keras 創建卷積神經網絡模型,再使用攝像頭獲取圖片進行情緒檢測。
為了更好的閱讀體驗,我們最好具備一下知識:
? Python
? OpenCV的
? 卷積神經網絡(CNN)
? numpy
(注意:我們使用的Tensorflow是1.13.1版本、keras是 版本2.3.1)
模型制作
首先,我們將創建模型代碼并解釋其中的含義。代碼的創建總共分為以下5個部分。
任務1:
導入該項目所需的必需模塊。
import kerasfrom keras.preprocessing.image import ImageDataGeneratorfrom keras.models import Sequentialfrom keras.layers import Dense,Dropout,Activation,Flatten,BatchNormalizationfrom keras.layers import Conv2D,MaxPooling2Dimport os
現在讓我們定義一些變量,這些變量將節省手動輸入的時間。
num_classes=5img_rows,img_cols=48,48batch_size=32
以上變量的說明如下:
? num_classses = 5:訓練模型時要處理的類即情感的種類數。
? img_rows=48,img_cols = 48:饋送到神經網絡中的圖像陣列大小。
? batch_size = 32:更新模型之前處理的樣本數量。epochs 是完整通過訓練數據集的次數。batch_size必須大于等于1并且小于或等于訓練數據集中的樣本數。
任務2:
現在讓我們開始加載模型,這里使用的數據集是fer2013,該數據集是由kaggle托管的開源數據集。數據集共包含7類,分別是憤怒、厭惡、恐懼、快樂、悲傷、驚奇、無表情,訓練集共有28,709個示例。該數據集已從網站上刪除,但我們在以下鏈接中可以找到相關代碼和數據集。https://github.com/karansjc1/emotion-detection
數據集的存儲庫中
我們將數據儲存在特定文件夾中。例如,“憤怒”文件夾包含帶有憤怒面孔等的圖片。在這里,我們使用5類,包括“憤怒”,“快樂”,“悲傷”,“驚奇”和“無表情”。使用24256張圖像作為訓練數據,3006張圖像作為檢測數據。
現在讓我們將數據加載到一些變量中。
train_data_dir='fer2013/train'validation_data_dir='fer2013/validation'
以上兩行導入了檢測和訓練數據。該模型是在訓練數據集上進行訓練的;在檢測數據集上檢測該模型性能,檢測數據集是原始數據集的一部分,從原始數據集上分離開來的。
任務3:
現在,我們對這些數據集進行圖像增強。圖像數據增強可以擴展訓練數據集大小,改善圖像質量。Keras深度學習神經網絡庫中的ImageDataGenerator類通過圖像增強來擬合模型。
train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=30, shear_range=0.3, zoom_range=0.3, width_shift_range=0.4, height_shift_range=0.4, horizontal_flip=True, fill_mode='nearest')validation_datagen = ImageDataGenerator(rescale=1./255)
train_datagen變量以下方法人為地擴展數據集:
? rotation_range:隨機旋轉,在這里我們使用30度。
? shear_range:剪切強度(逆時針方向的剪切角,以度為單位)。在這里我們使用0.3作為剪切范圍。
? zoom_range:隨機縮放的范圍,這里我們使用0.3作為縮放范圍。
? width_shift_range:在圖像的整個寬度上移動一個值。
? height_shift_range:這會在整個圖像高度上移動一個值。
? horizontal_flip:水平翻轉圖像。
? fill_mode:通過上述使用的方法更改圖像的方向后填充像素,使用“最近”作為填充模式,即用附近的像素填充圖像中丟失的像素。
在這里,我只是重新保存驗證數據,而沒有執行任何其他擴充操作,因為我想使用與訓練模型中數據不同的原始數據來檢查模型。
train_generator = train_datagen.flow_from_directory( train_data_dir, color_mode='grayscale', target_size=(img_rows,img_cols), batch_size=batch_size, class_mode='categorical', shuffle=True)validation_generator = validation_datagen.flow_from_directory( validation_data_dir, color_mode='grayscale', target_size=(img_rows,img_cols), batch_size=batch_size, class_mode='categorical', shuffle=True)
上面代碼的輸出將是:
Found 24256 images belonging to 5 classes.Found 3006 images belonging to 5 classes.
在上面的代碼中,我正在使用flow_from_directory()方法從目錄中加載我們的數據集,該目錄已擴充并存儲在train_generator和validation_generator變量中。flow_from_directory()采用目錄的路徑并生成一批擴充數據。因此,在這里,我們為該方法提供了一些選項,以自動更改尺寸并將其劃分為類,以便更輕松地輸入模型。
給出的選項是:
? directory:數據集的目錄。
? color_mode:在這里,我將圖像轉換為灰度,因為我對圖像的顏色不感興趣,而僅對表達式感興趣。
? target_size:將圖像轉換為統一大小。
? batch_size:制作大量數據以進行訓練。
? class_mode:在這里,我將“類別”用作類模式,因為我將圖像分為5類。
? shuffle:隨機播放數據集以進行更好的訓練。
任務4:
數據集的修改已完成,現在是該模型的大腦即CNN網絡。
因此,首先,我將定義將要使用的模型的類型。在這里,我使用的是Sequential模型,該模型定義網絡中的所有層將依次相繼并將其存儲在變量模型中。
model = Sequential()
該網絡由7個塊組成:(后面我們將逐層解釋)
#Block-1model.add(Conv2D(32,(3,3),padding='same',kernel_initializer='he_normal', input_shape=(img_rows,img_cols,1)))model.add(Activation('elu'))model.add(BatchNormalization())model.add(Conv2D(32,(3,3),padding='same',kernel_initializer='he_normal', input_shape=(img_rows,img_cols,1)))model.add(Activation('elu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.2))#Block-2model.add(Conv2D(64,(3,3),padding='same',kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(Conv2D(64,(3,3),padding='same',kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.2))#Block-3model.add(Conv2D(128,(3,3),padding='same',kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(Conv2D(128,(3,3),padding='same',kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.2))#Block-4model.add(Conv2D(256,(3,3),padding='same',kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(Conv2D(256,(3,3),padding='same',kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.2))#Block-5model.add(Flatten())model.add(Dense(64,kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(Dropout(0.5))#Block-6model.add(Dense(64,kernel_initializer='he_normal'))model.add(Activation('elu'))model.add(BatchNormalization())model.add(Dropout(0.5))#Block-7model.add(Dense(num_classes,kernel_initializer='he_normal'))model.add(Activation('softmax'))
運行以上代碼,如果使用的是舊版本的tensorflow,則會收到一些警告。
在這里,我使用了存在于keras.layers中的7種類型的層。
這些層是:
?Conv2D(
filters, kernel_size, strides=(1, 1), padding=’valid’, data_format=None,
dilation_rate=(1, 1), activation=None, use_bias=True,
kernel_initializer=’glorot_uniform’, bias_initializer=’zeros’,
kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None,
kernel_constraint=None, bias_constraint=None, **kwargs)
? Activation(activation_type)
? BatchNormalization()
? MaxPooling2D(pool_size, strides,padding, data_format, **kwargs)
? Dropout(dropout_value)
? Flatten()
? Dense(
units,
activation=None,
use_bias=True,
kernel_initializer=”glorot_uniform”,
bias_initializer=”zeros”,
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None,
**kwargs)
Block-1層的出現順序如下:
? Conv2D層-此層為網絡創建卷積層。我們創建的該層包含32個大小為(3,3)濾波器,其中使用padding ='same'填充圖像并使用內核初始化程序he_normal。添加了2個卷積層,每個層都有一個激活層和批處理歸一化層。
? 激活層-使用elu激活。
? BatchNormalization(批處理歸一化)-歸一化每一層的激活,即將平均激活值保持在接近0并將激活標準偏差保持在接近1。
? MaxPooling2D層-通過沿pool_size定義的沿特征軸的每個尺寸的窗口上的最大值,對輸入表示進行下采樣。在此, pool_size大小為(2,2)。
? Dropout:是一種在訓練過程中忽略隨機選擇的神經元的技術。在這里,我將dropout設為0.5,這意味著它將忽略一半的神經元。
Block-2層的出現順序如下:
? 與block-1相同的層,但是卷積層具有64個濾波器。
Block-3層的出現順序如下:
? 與block-1相同的層,但是卷積層具有128個濾波器。
Block-4層的出現順序如下:
? 與block-1相同的層,但是卷積層具有256個濾波器。
Block-5層的出現順序如下:
? 展平層-將前一層的輸出展平,即轉換為矢量形式。
? 密集層-該層中每個神經元都與其他每個神經元相連。在這里,我使用帶有內核的程序初始化64個單元或64個神經元-he_normal。
? 這些層之后使用elu激活,批處理歸一化,最后以dropout為50%選擇忽略。
塊6層的出現順序如下:
? 與模塊5相同的層,但沒有展平層,因為該模塊的輸入已展平。
塊7層的出現順序如下:
? 密集層-網絡的最后一個塊中,我使用num_classes創建一個密集層,該層具有he_normal初始值設定項,其unit =類數。
? 激活層-在這里,我使用softmax,該層多用于分類。
現在檢查模型的整體結構:
print(model.summary())
輸出將是:
Model: "sequential_1"_________________________________________________________________Layer (type) Output Shape Param # =================================================================conv2d_1 (Conv2D) (None, 48, 48, 32) 320 _________________________________________________________________activation_1 (Activation) (None, 48, 48, 32) 0 _________________________________________________________________batch_normalization_1 (Batch (None, 48, 48, 32) 128 _________________________________________________________________conv2d_2 (Conv2D) (None, 48, 48, 32) 9248 _________________________________________________________________activation_2 (Activation) (None, 48, 48, 32) 0 _________________________________________________________________batch_normalization_2 (Batch (None, 48, 48, 32) 128 _________________________________________________________________max_pooling2d_1 (MaxPooling2 (None, 24, 24, 32) 0 _________________________________________________________________dropout_1 (Dropout) (None, 24, 24, 32) 0 _________________________________________________________________conv2d_3 (Conv2D) (None, 24, 24, 64) 18496 _________________________________________________________________activation_3 (Activation) (None, 24, 24, 64) 0 _________________________________________________________________batch_normalization_3 (Batch (None, 24, 24, 64) 256 _________________________________________________________________conv2d_4 (Conv2D) (None, 24, 24, 64) 36928 _________________________________________________________________activation_4 (Activation) (None, 24, 24, 64) 0 _________________________________________________________________batch_normalization_4 (Batch (None, 24, 24, 64) 256 _________________________________________________________________max_pooling2d_2 (MaxPooling2 (None, 12, 12, 64) 0 _________________________________________________________________dropout_2 (Dropout) (None, 12, 12, 64) 0 _________________________________________________________________conv2d_5 (Conv2D) (None, 12, 12, 128) 73856 _________________________________________________________________activation_5 (Activation) (None, 12, 12, 128) 0 _________________________________________________________________batch_normalization_5 (Batch (None, 12, 12, 128) 512 _________________________________________________________________conv2d_6 (Conv2D) (None, 12, 12, 128) 147584 _________________________________________________________________activation_6 (Activation) (None, 12, 12, 128) 0 _________________________________________________________________batch_normalization_6 (Batch (None, 12, 12, 128) 512 _________________________________________________________________max_pooling2d_3 (MaxPooling2 (None, 6, 6, 128) 0 _________________________________________________________________dropout_3 (Dropout) (None, 6, 6, 128) 0 _________________________________________________________________conv2d_7 (Conv2D) (None, 6, 6, 256) 295168 _________________________________________________________________activation_7 (Activation) (None, 6, 6, 256) 0 _________________________________________________________________batch_normalization_7 (Batch (None, 6, 6, 256) 1024 _________________________________________________________________conv2d_8 (Conv2D) (None, 6, 6, 256) 590080 _________________________________________________________________activation_8 (Activation) (None, 6, 6, 256) 0 _________________________________________________________________batch_normalization_8 (Batch (None, 6, 6, 256) 1024 _________________________________________________________________max_pooling2d_4 (MaxPooling2 (None, 3, 3, 256) 0 _________________________________________________________________dropout_4 (Dropout) (None, 3, 3, 256) 0 _________________________________________________________________flatten_1 (Flatten) (None, 2304) 0 _________________________________________________________________dense_1 (Dense) (None, 64) 147520 _________________________________________________________________activation_9 (Activation) (None, 64) 0 _________________________________________________________________batch_normalization_9 (Batch (None, 64) 256 _________________________________________________________________dropout_5 (Dropout) (None, 64) 0 _________________________________________________________________dense_2 (Dense) (None, 64) 4160 _________________________________________________________________activation_10 (Activation) (None, 64) 0 _________________________________________________________________batch_normalization_10 (Batc (None, 64) 256 _________________________________________________________________dropout_6 (Dropout) (None, 64) 0 _________________________________________________________________dense_3 (Dense) (None, 5) 325 _________________________________________________________________activation_11 (Activation) (None, 5) 0 =================================================================Total params: 1,328,037Trainable params: 1,325,861Non-trainable params: 2,176_________________________________________________________________None
上面的輸出顯示了該網絡中使用的所有層。這是一個大型網絡,包含1,328,037個 參數。
任務5:
最后一步:編譯和訓練
現在剩下的事情就是編譯和訓練模型。但是首先讓我們導入更多的依賴。
from keras.optimizers import RMSprop,SGD,Adamfrom keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
在編譯之前,我將使用keras.callbacks類創建以下3個東西:
Checkpoint(函數— ModelCheckpoint())
它將監視驗證損失,并使用mode ='min'屬性嘗試將損失降至最低。到達檢查點時,它將保存訓練有素的最佳大小。Verbose = 1僅用于代碼創建檢查點時的可視化。這里我使用以下參數:
? file-path:保存模型文件的路徑,這里我保存的模型文件名為EmotionDetectionModel.h5
? monitor:要監視的數量。在這里,我正在監視驗證損失。
? mode:{自動,最小,最大}之一。如果save_best_only = True,則基于監視數量的最大化或最小化來決定覆蓋當前保存文件。
? save_best_only:如果save_best_only = True,則根據監視數量的最新最佳模型將不會被覆蓋。
? verbose:1:更新數據,0:不變。
提前停止(功能— EarlyStopping())
通過檢查以下屬性,以提前結束運行。
? monitor:要監視的數量。在這里,我正在監視驗證損失。
? min_delta:被監視的數量的最小變化有資格作為改進,即絕對變化小于min_delta將被視為沒有任何改進。在這里我給了0。
? patience:沒有改善的時期數,此后將停止訓練。我在這里給了它3。
? restore_best_weights:是否從時期以受監視數量的最佳值恢復模型權重。如果為False,則使用在訓練的最后一步獲得的模型權重。
? verbose:1:更新數據,0:不變。
降低學習率(函數— ReduceLROnPlateau())
一旦學習停滯,模型通常會受益于將學習率降低2-10倍?;卣{監視數量,并且如果沒有發現patience的改善,則學習率會降低,為此使用了以下屬性。
? monitor:監視特定損失。在這里,我正在監視驗證損失。
? factor:降低學習率的因素。new_lr = lr *因子。在這里我使用0.2作為系數。
? patience:沒有改善的時期數,之后學習率將降低。我在這里使用3。
? min_delta:測量新的最佳閾值,僅關注重大變化。
? verbose:1:更新數據,0:不變。
現在是時候到最后使用編譯模型model.compile()和適合訓練數據集的模型model.fit_generator()
model.compile()
具有以下參數:
? loss:此值將確定要在代碼中使用的損失函數的類型。在這里,我們有5個類別或類別的分類數據,因此使用了“ categorical_crossentropy”損失。
? optimizer:此值將確定要在代碼中使用的優化器功能的類型。這里我使用的學習率是0.001的Adam優化器,因為它是分類數據的最佳優化器。
? metrics:metrics參數應該是一個列表,模型可以有任意數量的metrics。它是模型在訓練和測試過程中要評估的metrics列表。這里我們使用了精度作為度量標準。
model.fit_generator()
使模型適合Python逐批生成的數據。
它具有以下參數:
? generator:我們之前創建的train_generator對象。
? steps_per_epochs:在一個紀元內接受訓練數據的步驟。
? epoch:一次通過整個數據集。
? callbacks:包含我們之前創建的所有回調的列表。
? validation_data:我們之前創建的validation_generator對象。
? validation_steps:在一個時期內采取驗證數據的步驟。
model.compile(loss='categorical_crossentropy', optimizer = Adam(lr=0.001), metrics=['accuracy'])nb_train_samples = 24176nb_validation_samples = 3006epochs=25history=model.fit_generator( train_generator, steps_per_epoch=nb_train_samples//batch_size, epochs=epochs, callbacks=callbacks, validation_data=validation_generator, validation_steps=nb_validation_samples//batch_size)
完成!
現在,可以使用此模型創建情緒檢測器,從而完成模型生成。
驅動程式碼
現在,我們將使用在上一節中創建的模型來說明用于情感檢測的代碼。
首先,讓我們再次導入一些運行代碼所需的模塊。
from keras.models import load_modelfrom keras.preprocessing.image import img_to_arrayfrom keras.preprocessing import imageimport cv2import numpy as np
現在,讓我們加載模型,并加載我用來檢測攝像頭前方人臉的分類器。使用haarcascade_frontalface_default分類器。Haar Cascade是一種機器學習對象檢測算法,用于識別圖像或視頻中的對象,并基于Paul Viola和Michael Jones在其論文《使用簡單特征的增強級聯進行快速對象檢測》中提出的特征概念。2001。haarcascade_frontalface_default分類器可檢測圖像或連續視頻源中人的正面。
face_classifier=cv2.CascadeClassifier('/haarcascade_frontalface_default.xml')classifier = load_model('/EmotionDetectionModel.h5')
現在,我將定義一個變量class_labels來存儲類的名稱或我們要預測的情緒類型,還定義一個變量cap來存儲cv2.VideoCapture方法返回的值。在此,VideoCapture中的值0用于指示該方法使用便攜式計算機的主要網絡攝像頭。
class_labels=['Angry','Happy','Neutral','Sad','Surprise']cap=cv2.VideoCapture(0)
結論
因此,在這里我已經解釋了使用OpenCV和Keras創建情緒檢測的過程。通過以下鏈接可以查看完整的代碼以及數據集。
https://github.com/karansjc1/emotion-detection
實驗結果如下:
數據分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
解碼數據基因:從數字敏感度到邏輯思維 每當看到超市貨架上商品的排列變化,你是否會聯想到背后的銷售數據波動?三年前在零售行 ...
2025-05-23在本文中,我們將探討 AI 為何能夠加速數據分析、如何在每個步驟中實現數據分析自動化以及使用哪些工具。 數據分析中的AI是什么 ...
2025-05-20當數據遇見人生:我的第一個分析項目 記得三年前接手第一個數據分析項目時,我面對Excel里密密麻麻的銷售數據手足無措。那些跳動 ...
2025-05-20在數字化運營的時代,企業每天都在產生海量數據:用戶點擊行為、商品銷售記錄、廣告投放反饋…… 這些數據就像散落的拼圖,而相 ...
2025-05-19在當今數字化營銷時代,小紅書作為國內領先的社交電商平臺,其銷售數據蘊含著巨大的商業價值。通過對小紅書銷售數據的深入分析, ...
2025-05-16Excel作為最常用的數據分析工具,有沒有什么工具可以幫助我們快速地使用excel表格,只要輕松幾步甚至輸入幾項指令就能搞定呢? ...
2025-05-15數據,如同無形的燃料,驅動著現代社會的運轉。從全球互聯網用戶每天產生的2.5億TB數據,到制造業的傳感器、金融交易 ...
2025-05-15大數據是什么_數據分析師培訓 其實,現在的大數據指的并不僅僅是海量數據,更準確而言是對大數據分析的方法。傳統的數 ...
2025-05-14CDA持證人簡介: 萬木,CDA L1持證人,某電商中廠BI工程師 ,5年數據經驗1年BI內訓師,高級數據分析師,擁有豐富的行業經驗。 ...
2025-05-13CDA持證人簡介: 王明月 ,CDA 數據分析師二級持證人,2年數據產品工作經驗,管理學博士在讀。 學習入口:https://edu.cda.cn/g ...
2025-05-12CDA持證人簡介: 楊貞璽 ,CDA一級持證人,鄭州大學情報學碩士研究生,某上市公司數據分析師。 學習入口:https://edu.cda.cn/g ...
2025-05-09CDA持證人簡介 程靖 CDA會員大咖,暢銷書《小白學產品》作者,13年頂級互聯網公司產品經理相關經驗,曾在百度、美團、阿里等 ...
2025-05-07相信很多做數據分析的小伙伴,都接到過一些高階的數據分析需求,實現的過程需要用到一些數據獲取,數據清洗轉換,建模方法等,這 ...
2025-05-06以下的文章內容來源于劉靜老師的專欄,如果您想閱讀專欄《10大業務分析模型突破業務瓶頸》,點擊下方鏈接 https://edu.cda.cn/g ...
2025-04-30CDA持證人簡介: 邱立峰 CDA 數據分析師二級持證人,數字化轉型專家,數據治理專家,高級數據分析師,擁有豐富的行業經驗。 ...
2025-04-29CDA持證人簡介: 程靖 CDA會員大咖,暢銷書《小白學產品》作者,13年頂級互聯網公司產品經理相關經驗,曾在百度,美團,阿里等 ...
2025-04-28CDA持證人簡介: 居瑜 ,CDA一級持證人國企財務經理,13年財務管理運營經驗,在數據分析就業和實踐經驗方面有著豐富的積累和經 ...
2025-04-27數據分析在當今信息時代發揮著重要作用。單因素方差分析(One-Way ANOVA)是一種關鍵的統計方法,用于比較三個或更多獨立樣本組 ...
2025-04-25CDA持證人簡介: 居瑜 ,CDA一級持證人國企財務經理,13年財務管理運營經驗,在數據分析就業和實踐經驗方面有著豐富的積累和經 ...
2025-04-25在當今數字化時代,數據分析師的重要性與日俱增。但許多人在踏上這條職業道路時,往往充滿疑惑: 如何成為一名數據分析師?成為 ...
2025-04-24