文章目录

  • 前言
  • 一、项目背景
  • 二、项目任务
  • 三、数据说明
  • 四、项目步骤
  • 1.图片基本情况查看
  • 2.图片处理
  • 3.模型构建
  • 4.模型训练
  • 5.验证集验证模型效果
  • 6.使用模型进行预测
  • 总结



前言

这是我第一次做图像处理的项目,项目基于科大讯飞和Datawhale的一个小比赛,模型效果一般般,可以给初次接触的朋友提供一些图像处理的思路,参考了复旦大学赵老师机器学习课程中利用CNN进行手写体识别、石头剪刀布识别的例子,项目做完后查阅了其他有关情绪识别的思路,在GitHub上作者priya-dwivedi也有类似项目,处理过程大部分一致,附在文末。
环境:python3.8、tensorflow2.3


一、项目背景

人脸表情是传播人类情感信息与协调人际关系的重要方式,表情识别是指从静态照片或视频序列中选择出表情状态,从而确定对人物的情绪与心理变化。在日常生活中人类习惯从面部表情中吸收非言语暗示,那么计算机可以完成类似任务吗?答案是肯定的,但是需要训练它学会识别情绪。

情绪识别python 情绪识别图_情绪识别python

二、项目任务

给定人脸照片完成具体的情绪识别,选手需要根据训练集数据构建情绪识别任务,并对测试集图像进行预测,识别人脸的7种情绪。

三、数据说明

赛题数据由训练集和测试集组成,训练集数据集按照不同情绪的文件夹进行存放。其中:

   训练集:2.8W张人脸图像;

   测试集:7K张人脸图像;

为了简化任务赛题图像只包含单张人脸,所有图像的尺寸为48*48像素。数据集包括的情绪标签包括以下7类:

   angry

   disgusted

   fearful

   happy

   neutral

   sad

   surprised

四、项目步骤

1.图片基本情况查看

完整项目下载链接:https://pan.baidu.com/s/1w72PobVwMfVCnrjRLOh4Sg 提取码:ssmb

文件目录👇

情绪识别python 情绪识别图_情绪识别python_02


情绪识别python 情绪识别图_python_03

# 有些代码可以正常运行但是会提示警告,使用filterwarnings过滤
import warnings     
warnings.filterwarnings('ignore')

import os
angry_dir = os.path.join('./train/angry/')
disgusted_dir = os.path.join('./train/disgusted/')
fearful_dir = os.path.join('./train/fearful/')
happy_dir = os.path.join('./train/happy/')
neutral_dir = os.path.join('./train/neutral/')
sad_dir = os.path.join('./train/sad/')
surprised_dir = os.path.join('./train/surprised/')

print('total training angry images:', len(os.listdir(angry_dir)))
print('total training disgusted images:', len(os.listdir(disgusted_dir)))
print('total training fearful images:', len(os.listdir(fearful_dir)))
print('total training happy images:', len(os.listdir(happy_dir)))
print('total training neutral images:', len(os.listdir(neutral_dir)))
print('total training sad images:', len(os.listdir(sad_dir)))
print('total training surprised images:', len(os.listdir(surprised_dir)))
print('total training images:',len(os.listdir(angry_dir))+len(os.listdir(disgusted_dir))+len(os.listdir(fearful_dir))+
     len(os.listdir(happy_dir))+len(os.listdir(neutral_dir))+len(os.listdir(sad_dir))+len(os.listdir(surprised_dir)))

情绪识别python 情绪识别图_机器学习_04

angry_files = os.listdir(angry_dir)
print('angry:',angry_files[:5])

disgusted_files = os.listdir(disgusted_dir)
print('disgusted:',disgusted_files[:5])

fearful_files = os.listdir(fearful_dir)
print('fearful:',fearful_files[:5])

happy_files = os.listdir(happy_dir)
print('happy:',happy_files[:5])

neutral_files = os.listdir(neutral_dir)
print('neutral:',neutral_files[:5])

sad_files = os.listdir(sad_dir)
print('sad:',sad_files[:5])

surprised_files = os.listdir(surprised_dir)
print('surprised:',surprised_files[:5])

情绪识别python 情绪识别图_机器学习_05

import matplotlib.pyplot as plt
import cv2
%matplotlib inline

pic_index = 1

next_angry = [os.path.join(angry_dir,fname) for fname in angry_files[pic_index-1 : pic_index]]
next_happy = [os.path.join(happy_dir,fname) for fname in happy_files[pic_index-1 : pic_index]]
next_disgusted = [os.path.join(disgusted_dir,fname) for fname in disgusted_files[pic_index-1 : pic_index]]
next_fearful = [os.path.join(fearful_dir,fname) for fname in fearful_files[pic_index-1 : pic_index]]
next_neutral = [os.path.join(neutral_dir,fname) for fname in neutral_files[pic_index-1 : pic_index]]
next_sad = [os.path.join(sad_dir,fname) for fname in sad_files[pic_index-1 : pic_index]]
next_surprised = [os.path.join(surprised_dir,fname) for fname in surprised_files[pic_index-1 : pic_index]]

for i, img_path in enumerate(next_angry+next_happy+next_disgusted+next_fearful+next_neutral+next_sad+next_surprised):
    img = cv2.imread(img_path)
    plt.figure(figsize = (1, 1))
    plt.imshow(img)
    plt.axis('Off')
    plt.title(str(img_path).split('/')[2])
    plt.show()
print(img.shape)

情绪识别python 情绪识别图_情绪识别python_06

2.图片处理

from keras_preprocessing.image import ImageDataGenerator 
# ImageDataGenerator()是图片生成器,同时也可以在batch中对数据进行增强,扩充数据集大小,增强模型的泛化能力。比如进行旋转,变形,归一化等等

TRAINING_DIR = './train/'
training_datagen = ImageDataGenerator(
    rescale=1. / 255,  # 值将在执行其他处理前乘到整个图像上
    validation_split=0.25,  # 数据集划分
#     rotation_range=40,  # 整数,数据提升时图片随机转动的角度
#     width_shift_range=0.2,  # 浮点数,图片宽度的某个比例,数据提升时图片随机水平偏移的幅度
#     height_shift_range=0.2,  # 浮点数,图片高度的某个比例,数据提升时图片随机竖直偏移的幅度
#     shear_range=0.2,  # 浮点数,剪切强度(逆时针方向的剪切变换角度)。是用来进行剪切变换的程度
#     zoom_range=0.2,  # 用来进行随机的放大
#     horizontal_flip=True,  # 布尔值,进行随机水平翻转。随机的对图片进行水平翻转,这个参数适用于水平翻转不影响图片语义的时候
#     fill_mode='nearest'  # 'constant','nearest','reflect','wrap'之一,当进行变换时超出边界的点将根据本参数给定的方法进行处理
)

# flow_from_directory()以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据
train_generator = training_datagen.flow_from_directory(    
    TRAINING_DIR,
    subset='training',
    target_size=(48, 48),
    class_mode='categorical'          #  "categorical", "binary", "sparse"或None之一
)
# 由于没有验证集文件夹,所以划分了训练集的25%作为验证集
validation_generator = training_datagen.flow_from_directory(
    TRAINING_DIR,
    subset='validation',
    target_size=(48, 48),
    class_mode='categorical'
)
print(train_generator.class_indices)

情绪识别python 情绪识别图_人脸识别_07

3.模型构建

import tensorflow as tf

print(tf.__version__)

情绪识别python 情绪识别图_python_08

model = tf.keras.models.Sequential([

    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(48, 48, 3)),  # 卷积
    tf.keras.layers.MaxPooling2D(2, 2),   # 池化

    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),  # 卷积
    tf.keras.layers.MaxPooling2D(2, 2),   # 池化

    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),  # 卷积
    tf.keras.layers.MaxPooling2D(2, 2),   # 池化

    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(1024, activation='relu'),  # 全连接
    tf.keras.layers.Dropout(0.5),  # 一种防止神经网络过拟合的手段
    tf.keras.layers.Dense(7, activation='softmax')
])
model.summary()

情绪识别python 情绪识别图_python_09

4.模型训练

from keras.callbacks import ModelCheckpoint

# HDF5 文件一般以 .h5 或者 .hdf5 作为后缀名
model_checkpoint = ModelCheckpoint('./model/face_classify4.h5', 
                                   monitor='loss',         # monitor:'val_acc'|'loss'|'val_loss'|'acc'
                                   verbose=1,              # 如果你喜欢进度条,那就选1,如果喜欢清爽的就选0
                                   mode='min',             # 如果监视器monitor选val_acc, mode就选'max'|如果monitor选acc,mode也可以选'max'|如果monitor选loss,mode就选'min',一般情况下选'auto',
                                   period=1,               # checkpoints之间间隔的epoch数
                                   save_weights_only=False,# 若设置为True,则只保存模型权重,否则将保存整个模型(包括模型结构,配置信息等)
                                   save_best_only=True)    # 只保存最好的模型,也可以都保存

model.compile(loss='categorical_crossentropy', optimizer='Adam', metrics=['accuracy'])  # optimizer参考:

history = model.fit_generator(train_generator, epochs=30, validation_data=validation_generator, verbose=1, callbacks=[model_checkpoint])

# model.save('./model/face_classify4.h5')  # keras的模型一般保存为后缀名为h5的文件,上面已经存了

情绪识别python 情绪识别图_深度学习_10


补充知识点!(save()和save_weights()的区别)

引用:

情绪识别python 情绪识别图_人脸识别_11


使用save_weights()保存的模型不能直接load_model,应该重新把模型结构再描述一遍

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'r', label='Training accuracy')
plt.plot(epochs, val_acc, 'b', label='validation accuracy')
plt.title('Training and validation accuracy')
plt.legend(loc=0)
plt.show()

情绪识别python 情绪识别图_情绪识别python_12

5.验证集验证模型效果

from keras.models import load_model

model = load_model('./model/face_classify4.h5')

#Confution Matrix and Classification Report
Y_pred = model.predict_generator(validation_generator, nb_validation_samples // batch_size+1)  # batch_size = 512
y_pred = np.argmax(Y_pred, axis=1)

print('Confusion Matrix')
print(confusion_matrix(validation_generator.classes, y_pred))
print('Classification Report')
target_names = list(class_labels.values())
print(classification_report(validation_generator.classes, y_pred, target_names=target_names))

plt.figure(figsize=(8,8))
cnf_matrix = confusion_matrix(validation_generator.classes, y_pred)

plt.imshow(cnf_matrix, interpolation='nearest')
plt.colorbar()
tick_marks = np.arange(len(classes))
_ = plt.xticks(tick_marks, classes, rotation=90)
_ = plt.yticks(tick_marks, classes)

情绪识别python 情绪识别图_python_13


情绪识别python 情绪识别图_情绪识别python_14

6.使用模型进行预测

from keras.models import load_model
import pandas as pd
import numpy as np
from tqdm import tqdm  # 进度条
from keras.preprocessing import image

# 在模型训练过程中使用了ModelCheckpoint来保存最优参数的模型,好像使用load_model和load_weights都行
model = load_model("./model/face_classify4.h5")   # keras.models.load_model() 读取网络、权重
# model.load_weights('./model/face_classify4.h5')  # keras.models.load_weights() 仅读取权重,load_model代码包含load_weights的代码,
                                                    # 区别在于load_weights时需要先有网络,
                                                    # 并且load_weights需要将权重数据写入到对应网络层的tensor中。

dataframe = pd.DataFrame(columns=['name','label'])
os.chdir('./test/')   # 原 D:\\python\\情绪识别

for file_name in tqdm(os.listdir()):
    img = image.load_img(file_name,target_size=(48,48))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    images = np.vstack([x])
    classes = model.predict(images, batch_size=10)
    num = np.argmax(classes)

    if num == 0:   # {'angry': 0, 'disgusted': 1, 'fearful': 2, 'happy': 3, 'neutral': 4, 'sad': 5, 'surprised': 6}
        face = 'angry'
    if num == 1:
        face = 'disgusted'
    if num == 2:
        face = 'fearful'
    if num == 3:
        face = 'happy'
    if num == 4:
        face = 'neutral'
    if num == 5:
        face = 'sad'
    if num == 6:
        face = 'surprised'
    dataframe = dataframe.append({'name':file_name,'label':face},ignore_index=True)

dataframe.to_csv('../result/submit4.csv',index=False)

情绪识别python 情绪识别图_python_15


总结

做完了项目不容易,也尝试去提高了模型准确度,欢迎各位交流,也可以提供我一些指导。提到了priya-dwivedi的情绪检测项目,她训练的是一个六层卷积神经网络模型,GitHub地址:https://github.com/priya-dwivedi/face_and_emotion_detection,我也做了该项目的解读,链接地址:。
个人认为图像处理过程:


Created with Raphaël 2.3.0 开始 图片查看 图片预处理 模型构建 模型训练 模型性能度量 预测 结束


做完项目后的思考:
①训练集的acc可以达到0.90以上,验证集怎么就卡在了0.55左右上不去?
②卷积神经网络任意搭建一个能得到效果一般结果,怎么样搭建网络才能得到特别好的结果?