温馨提示:文末有 CSDN 平台官方提供的师姐 QQ 名片 :)
1. 项目简介
面部表情识别是计算机视觉领域的一个重要研究方向, 它在人机交互、心理健康评估、安全监控等领域具有广泛的应用。近年来,随着深度学习技术的快速发展, 面部表情识别的准确性和实时性得到了显著提升。本项目以 MobileNetV2 为基础模型构建面向面部表情识别的卷积神经网络, 完成模型的训练、验证和测试,面部表情识别准确率达到 85%以上。并利用 Flask + Bootstrap 框架搭建交互式分析平台,方便用户进行表情的识别。
基于深度学习的面部表情分类识别系统
2. 面部表情数据集读取与预处理
利用 opencv 读取面部表情图像数据,并转换为 numpy 数组,图片为灰度图:
def prepare_data(ori_data):
"""
像素数组转成 numpy array
"""
image_array = np.zeros(shape=(ori_data.shape[0], img_size, img_size))
image_label = np.array(list(map(int, ori_data['emotion'])))
for i, row in ori_data.iterrows():
image = np.fromstring(row['pixels'], dtype=int, sep=' ')
image = np.reshape(image, (img_size, img_size))
image_array[i] = image
return image_array, image_label
读取的数据集,可视化部分样例数据:
3. 数据集制作与样本均衡处理
数据集共包含:生气(Angry)、厌恶(Disgust)、恐惧(Fear)、开心(Happy)、悲伤(Sad)、惊讶(Surprise)和中性(Neutral)七种类型,其样本数量分布如下:
可以看出,样本类别极具不均衡,如不处理样本均衡问题,将影响模型的训练,样本少的类别会得不到充分的学习。
通过对样本进行采样,并切分出训练集、验证集和测试集:
class_data = [data[data['emotion'] == i] for i in range(7)]
# 对每个类别进行过采样,以匹配平均类别数量
oversampled_data = [resample(class_df, replace=True, n_samples=int(average_class_count), random_state=42) for class_df in class_data]
# 将过采样后的数据合并为一个平衡的数据集
balanced_data = pd.concat(oversampled_data)
# 重置索引并直接在原数据框上进行修改
balanced_data.reset_index(drop=True, inplace=True)
# 准备数据,将特征和标签分离
all_x, all_y = prepare_data(balanced_data)
# 重塑特征数据,以匹配输入形状
all_x = all_x.reshape((all_x.shape[0], img_size, img_size, 1))
# 将标签转换为独热编码格式
all_y = to_categorical(all_y)
# 将数据分为训练集和临时集,其中20%用于测试
x_train, x_temp, y_train, y_temp = train_test_split(all_x, all_y, test_size=0.2)
# 将临时集进一步分为验证集和测试集,各占50%
x_val, x_test, y_val, y_test = train_test_split(x_temp, y_temp, test_size=0.5)
print('训练集:{},验证集:{},测试集:{}'.format(x_train.shape[0], x_val.shape[0], x_test.shape[0]))
可以看出,七种类型的样本数量已基本均衡,有利于神经网络的训练。
4. 面部表情识别卷积神经网络构建
4.1 MobileNetV2 基础模型
MobileNetV2 是一种轻量级的深度神经网络模型,由Google在2018年发布,旨在用于移动和边缘设备上的高效图像识别任务。它是MobileNetV1的改进版,继承了其轻量级和高效的特点,并在多个方面进行了优化。以下是MobileNetV2模型的主要特点和结构:
主要特点:
-
深度可分离卷积(Depthwise Separable Convolution): MobileNetV2依然采用了深度可分离卷积来减少模型参数和计算量。这种卷积将标准的卷积分解为两个步骤:深度卷积(depthwise convolution)和逐点卷积(pointwise convolution)。
-
线性瓶颈(Linear Bottlenecks): 在MobileNetV2中,作者引入了线性瓶颈的概念,即在网络的最后几层使用了线性激活函数(ReLU6)而不是传统的ReLU,这有助于减少信息的损失。
-
倒残差结构(Inverted Residuals): MobileNetV2采用了倒残差结构,即先通过一个逐点卷积扩展维度,然后进行深度卷积,最后再用逐点卷积减少维度。这种结构有助于提高网络的表达能力。
-
轻量级: 由于采用了上述结构,MobileNetV2在保持精度的同时大大减少了模型的参数数量和计算量,使其非常适合在资源受限的设备上运行。
4.2 基于迁移学习的卷积神经网络构建
以 MobileNetV2 为 base 模型,加载利用 ImageNet 大规模数据集预训练的 MobileNetV2 模型权重,构建
base_model = tf.keras.applications.MobileNetV2(
weights='./pretrained_models/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_96_no_top.h5',
include_top=False,
input_shape=(96,96,3)
)
# Inputlayer
input = tf.keras.layers.Input(name='0_Input',
shape=(img_size, img_size, 1))
# Preprocessing stage
x = tf.keras.layers.Resizing(name='1_Preprocessing_1',height = 96, width = 96)(input)
x = tf.keras.layers.Rescaling(name='1_Preprocessing_2',
scale = 1/127.0, offset=-1)(x)
x = tf.keras.layers.RandomRotation(name='1_Preprocessing_3',
factor=0.20,
seed=100)(x)
x = tf.keras.layers.RandomFlip(name='1_Preprocessing_4',
mode="horizontal",
seed=100)(x)
......
# Feature extracting stage
x = base_model(x)
x = tf.keras.layers.Flatten(name='3_Classification_1')(x)
# Classification stage
x = tf.keras.layers.Dense(name='3_Classification_2',
units=256,
kernel_regularizer=tf.keras.regularizers.l2(l2=regularization_rate),
kernel_initializer = 'he_uniform',
activation='relu')(x)
......
# Prediction stage
predictions = tf.keras.layers.Dense(name='4_Prediction',
units = 7,
kernel_initializer = 'zeros',
activation=tf.nn.softmax)(x)
model = Model(inputs=input, outputs=predictions)
model.compile(optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate),
loss='categorical_crossentropy',
metrics=['acc'])
model.summary()
5. 模型训练与验证
5.1 模型训练
利用切分的训练集进行模型的训练,验证集进行模型的验证评估,并保存 val_acc 最高的模型权重:
checkpoint = ModelCheckpoint('save_models/best_model.h5', monitor='val_acc', verbose=1, mode='max',save_best_only=True)
early = EarlyStopping(monitor="val_acc", mode="max",restore_best_weights=True, patience=5)
lrp_reducer = ReduceLROnPlateau(monitor='val_loss', factor=lrp_factor, patience=lrp_patience, verbose=1)
callbacks_list = [checkpoint, early, lrp_reducer]
history = model.fit(
x_train, y_train,
batch_size=batch_size,
epochs=epochs,
steps_per_epoch=x_train.shape[0] // batch_size,
verbose=1,
callbacks=callbacks_list,
validation_data=(x_val, y_val),
validation_steps=x_val.shape[0]//batch_size
)
5.2 测试集预测结果的 AUC 得分与 ROC score 分布
模型预测测试集的 AUC 得分,并绘制 ROC 曲线:
# 获取疾病标签名称列表
labels = list(emotions.values())
# 创建一个范围,表示 x 轴上每个标签的位置
x = np.arange(len(labels))
# 设置柱状图的宽度
width = 0.80
fig, ax = plt.subplots(figsize=(20, 8), dpi=120)
rects = ax.bar(x, cate_auc, width, color='#EEC900')
ax.set_ylabel('AUC Score', fontsize=20)
ax.set_xlabel('标签', fontsize=20)
ax.set_title('不同类别模型预测 AUC Score 分布', fontsize=30)
ax.set_xticks(x, labels, fontsize=20)
ax.bar_label(rects, padding=3, fontsize=16)
fig.tight_layout()
plt.show()
5.3 困惑矩阵 Confusionmatrix
from matplotlib.colors import LogNorm
import seaborn as sns
true_labels = np.argmax(y_test, axis=1)
predictions = np.argmax(pred_test, axis=1)
conf_matrix = confusion_matrix(true_labels, predictions)
plt.figure(figsize=(10, 8))
sns.heatmap(conf_matrix, annot=True, cmap='GnBu', fmt='g', xticklabels=[emotions[i] for i in range(len(conf_matrix))], yticklabels=[emotions[i] for i in range(len(conf_matrix))], norm=LogNorm())
plt.title('Confusion Matrix')
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.show()
6. 基于深度学习的面部表情分类识别系统
利用 Flask + Bootstrap 框架搭建响应式布局的交互分析 web 系统,利用 keras load_model 加载训练好的性能最佳的模型,提供标准化 rest api,提供面部表情的在线识别功能。
6.1 系统首页
6.2 面部表情在线识别
通过上传待测试面部表情图片,提交预测后,后端调用模型进行表情预测,预测结果返回给前端进行渲染可视化,展示预测的标签类别,及各类标签预测的概率分布。
7. 结论
本项目以 MobileNetV2 为基础模型构建面向面部表情识别的卷积神经网络, 完成模型的训练、验证和测试,面部表情识别准确率达到 85%以上。并利用 Flask + Bootstrap 框架搭建交互式分析平台,方便用户进行表情的识别。
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。技术交流、源码获取认准下方 CSDN 官方提供的学长 QQ 名片 :)
精彩专栏推荐订阅: