文章目录
- 前言
- 一、项目背景
- 二、项目任务
- 三、数据说明
- 四、项目步骤
- 1.图片基本情况查看
- 2.图片处理
- 3.模型构建
- 4.模型训练
- 5.验证集验证模型效果
- 6.使用模型进行预测
- 总结
前言
这是我第一次做图像处理的项目,项目基于科大讯飞和Datawhale的一个小比赛,模型效果一般般,可以给初次接触的朋友提供一些图像处理的思路,参考了复旦大学赵老师机器学习课程中利用CNN进行手写体识别、石头剪刀布识别的例子,项目做完后查阅了其他有关情绪识别的思路,在GitHub上作者priya-dwivedi也有类似项目,处理过程大部分一致,附在文末。
环境:python3.8、tensorflow2.3
一、项目背景
人脸表情是传播人类情感信息与协调人际关系的重要方式,表情识别是指从静态照片或视频序列中选择出表情状态,从而确定对人物的情绪与心理变化。在日常生活中人类习惯从面部表情中吸收非言语暗示,那么计算机可以完成类似任务吗?答案是肯定的,但是需要训练它学会识别情绪。
二、项目任务
给定人脸照片完成具体的情绪识别,选手需要根据训练集数据构建情绪识别任务,并对测试集图像进行预测,识别人脸的7种情绪。
三、数据说明
赛题数据由训练集和测试集组成,训练集数据集按照不同情绪的文件夹进行存放。其中:
训练集:2.8W张人脸图像;
测试集:7K张人脸图像;
为了简化任务赛题图像只包含单张人脸,所有图像的尺寸为48*48像素。数据集包括的情绪标签包括以下7类:
angry
disgusted
fearful
happy
neutral
sad
surprised
四、项目步骤
1.图片基本情况查看
完整项目下载链接:https://pan.baidu.com/s/1w72PobVwMfVCnrjRLOh4Sg 提取码:ssmb
文件目录👇
# 有些代码可以正常运行但是会提示警告,使用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)))
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])
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)
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)
3.模型构建
import tensorflow as tf
print(tf.__version__)
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()
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的文件,上面已经存了
补充知识点!(save()和save_weights()的区别)
引用:
使用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()
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)
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)
总结
做完了项目不容易,也尝试去提高了模型准确度,欢迎各位交流,也可以提供我一些指导。提到了priya-dwivedi的情绪检测项目,她训练的是一个六层卷积神经网络模型,GitHub地址:https://github.com/priya-dwivedi/face_and_emotion_detection,我也做了该项目的解读,链接地址:。
个人认为图像处理过程:
Created with Raphaël 2.3.0 开始 图片查看 图片预处理 模型构建 模型训练 模型性能度量 预测 结束
做完项目后的思考:
①训练集的acc可以达到0.90以上,验证集怎么就卡在了0.55左右上不去?
②卷积神经网络任意搭建一个能得到效果一般结果,怎么样搭建网络才能得到特别好的结果?