该项目利用ResNet50卷积神经网络实现微表情分类并部署至手机端。数据集源自CASMEII,含7类微表情,按6:2:2划分为训练(2464张)、验证(822张)、测试(826张)集。基于Paddle框架训练,用Momentum优化器,验证集top1准确率达98.91%。通过Paddle-Lite部署到安卓手机,支持本地图片和摄像头预测,适用于情感识别等场景。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

您是否有过以下困惑:
第一次约会中,内心小鹿乱撞,却摸不清他(她)对我的真实情感,是喜欢、犹豫还是......我应该怎么做,才能得到他(她)的真心呢?
青春期的孩子最为敏感,最为复杂,为人父母,管教过于温和怕起不到效果,过于激烈又会伤了孩子的自尊心,探寻他们内心深处的真实情感,才能对症下药。
现在,只需要fork本项目,安装一个微表情识别软件的手机,就能为你解决以上困惑,为你追求真爱,为你家庭和睦助上一臂之力。
任务:本次实验是一个图像多分类问题,利用ResNet50卷积神经网络实现微表情分类,再部署至手机端。
实践平台:百度AI实训平台-AI Studio、Python3.7+ Paddle2.2、Android Studio、华为手机。
该项目是本人为完成学校课程而创建的,数据集不能公开,在此深表歉意。
但模型文件(MicroExpression.nb)文件有,需要的朋友拿去手机端部署就ok,部署直接看第五版块。
针对此项目还存在的改进空间,以及这类场景在飞桨框架下的实现,希望大家多交流观点,fork优化,相互关注,共同学习进步个人主页。
人类接触外界的过程通常伴随一系列复杂的心理活动,由此诱发情感,也称情绪。研究表明,情绪变化会在脸部产生表情,两者存在绝对的对应关系。 近年来,在人脸表情范畴下,新兴起一个细化的分支——微表情( Micro-Expression)。
猜猜左边这位大哥的微表情,看似一脸认真,其实他的标签是高兴哈哈哈哈哈
仔细观察右边这位小姐姐,眉头微微皱起,嘴角轻轻上扬,如此迷惑,其实她代表微表情的是压抑
由此可见,未经正规训练过的人很难识别微表情所代表的真实情感。
不同于传统的宏观表情,微表情具有如下特点:
真实的情绪,复杂心理活动
非自主控制,不自觉流露
持续时间短,强度弱,不易察觉
其实,微表情研究在侦察讯问、安全司法、临床医学都有很好的应用前景,因此开发一套自动化设备,对微表情进行识别研究是很有必要的。
微表情来源于中科院提供的CASMEII数据库,这些样本主要包括七类:高兴、惊讶、恐惧、悲伤、厌恶、压抑和其他。
由于样本量较少,因此训练集、验证集、测试集按照6:2:2划分。
训练样本量| 2,464张
验证样本量| 822张
测试样本量| 826张
加载使用方式|自定义数据集
数据集分为train、valid、test三个文件夹,每个文件夹内包含7个分类文件夹,每个分类文件夹内是具体的样本图片。
!tree''' ├── classification2 │ ├── test │ │ ├── disgust │ │ │ ├── 1110013.jpg │ │ │ ├── 1110016.jpg │ │ │ ├── 111004.jpg '''
config.py
__all__ = ['CONFIG', 'get']CONFIG = { 'model_save_dir': "./output/MicroExpression", 'num_classes': 7, 'total_images': 4112, 'epochs': 20, 'batch_size': 32, 'image_shape': [3, 224, 224], 'LEARNING_RATE': { 'params': { 'lr': 0.00375
}
}, 'OPTIMIZER': { 'params': { 'momentum': 0.9
}, 'regularizer': { 'function': 'L2', 'factor': 0.000001
}
}, 'LABEL_MAP': [ "disgust", "others", "sadness", "happiness", "surprise", "repression", "fear"
]
}
def get(full_path): for id, name in enumerate(full_path.split('.')): if id == 0: config = CONFIG
config = config[name]
return configimport ioimport osfrom PIL import Imagefrom config import get# 数据集根目录DATA_ROOT = 'classification2'# 标签ListLABEL_MAP = get('LABEL_MAP')# 标注生成函数def generate_annotation(mode):
# 建立标注文件
with open('{}/{}.txt'.format(DATA_ROOT, mode), 'w') as f: # 对应每个用途的数据文件夹,train/valid/test
train_dir = '{}/{}'.format(DATA_ROOT, mode) # 遍历文件夹,获取里面的分类文件夹
for path in os.listdir(train_dir): # 标签对应的数字索引,实际标注的时候直接使用数字索引
label_index = LABEL_MAP.index(path) # 图像样本所在的路径
image_path = '{}/{}'.format(train_dir, path) # 遍历所有图像
for image in os.listdir(image_path): # 图像完整路径和名称
image_file = '{}/{}'.format(image_path, image)
try: # 验证图片格式是否ok
with open(image_file, 'rb') as f_img:
image = Image.open(io.BytesIO(f_img.read()))
image.load()
if image.mode == 'RGB':
f.write('{}\t{}\n'.format(image_file, label_index)) except: continuegenerate_annotation('train') # 生成训练集标注文件generate_annotation('valid') # 生成验证集标注文件generate_annotation('test') # 生成测试集标注文件接下来我们使用标注好的文件进行数据集类的定义,方便后续模型训练使用。
我们数据集的代码实现是在dataset.py中。
import paddleimport paddle.vision.transforms as Timport numpy as npfrom config import getfrom PIL import Image
__all__ = ['MicroExpression']# 定义图像的大小image_shape = get('image_shape')
IMAGE_SIZE = (image_shape[1], image_shape[2])class MicroExpression(paddle.io.Dataset):
"""
微表情数据集类的定义
"""
def __init__(self, mode='train'):
"""
初始化函数
"""
assert mode in ['train', 'test', 'valid'], 'mode is one of train, test, valid.'
self.data = [] with open('classification2/{}.txt'.format(mode)) as f: for line in f.readlines():
info = line.strip().split('\t') if len(info) > 0:
self.data.append([info[0].strip(), info[1].strip()]) if mode == 'train':
self.transforms = T.Compose([
T.RandomResizedCrop(IMAGE_SIZE), # 随机裁剪大小
T.RandomHorizontalFlip(0.5), # 随机水平翻转
T.ToTensor(), # 数据的格式转换和标准化 HWC => CHW
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 图像归一化
]) else:
self.transforms = T.Compose([
T.Resize(256), # 图像大小修改
T.RandomCrop(IMAGE_SIZE), # 随机裁剪
T.ToTensor(), # 数据的格式转换和标准化 HWC => CHW
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 图像归一化
])
def __getitem__(self, index):
"""
根据索引获取单个样本
"""
image_file, label = self.data[index]
image = Image.open(image_file) if image.mode != 'RGB':
image = image.convert('RGB')
image = self.transforms(image) return image, np.array(label, dtype='int64') def __len__(self):
"""
获取样本总数
"""
return len(self.data)import paddleimport numpy as npfrom config import get paddle.__version__
'2.2.2'
from dataset import MicroExpression
根据所使用的数据集需求实例化数据集类,并查看总样本量。
train_dataset = MicroExpression(mode='train')
valid_dataset = MicroExpression(mode='valid')print('训练数据集:{}张;验证数据集:{}张'.format(len(train_dataset), len(valid_dataset)))训练数据集:2464张;验证数据集:822张
# 模型选择和开发network = paddle.vision.models.resnet50(num_classes=get('num_classes'), pretrained=True)model = paddle.Model(network)
model.summary((-1, ) + tuple(get('image_shape')))''''
-------------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===============================================================================
Conv2D-1 [[1, 3, 224, 224]] [1, 64, 112, 112] 9,408
BatchNorm2D-1 [[1, 64, 112, 112]] [1, 64, 112, 112] 256
ReLU-1 [[1, 64, 112, 112]] [1, 64, 112, 112] 0
MaxPool2D-1 [[1, 64, 112, 112]] [1, 64, 56, 56] 0
''''其中关键API介绍如下:
https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/lr/CosineAnnealingDecay_cn.html#cosineannealingdecay
https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/Momentum_cn.html#momentum
EPOCHS = get('epochs')
BATCH_SIZE = get('batch_size')def create_optim(parameters):
step_each_epoch = get('total_images') // get('batch_size')
lr = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=get('LEARNING_RATE.params.lr'),
T_max=step_each_epoch * EPOCHS) return paddle.optimizer.Momentum(learning_rate=lr,
parameters=parameters,
weight_decay=paddle.regularizer.L2Decay(get('OPTIMIZER.regularizer.factor'))) #正则化来提升精度# 模型训练配置model.prepare(create_optim(network.parameters()), # 优化器
paddle.nn.CrossEntropyLoss(), # 损失函数
paddle.metric.Accuracy(topk=(1, 5))) # 评估指标# 训练可视化VisualDL工具的回调函数visualdl = paddle.callbacks.VisualDL(log_dir='visualdl_log')# 启动模型全流程训练model.fit(train_dataset, # 训练数据集
valid_dataset, # 评估数据集
epochs=EPOCHS, # 总的训练轮次
batch_size=BATCH_SIZE, # 批次计算的样本量大小
shuffle=True, # 是否打乱样本集
verbose=1, # 日志展示格式
save_dir='./chk_points/', # 分阶段的训练模型存储路径
callbacks=[visualdl]) # 回调函数使用'''
Epoch 20/20
step 77/77 [==============================] - loss: 0.3286 - acc_top1: 0.9257 - acc_top5: 0.9984 - 139ms/step
save checkpoint at /home/aistudio/chk_points/19
Eval begin...
step 26/26 [==============================] - loss: 0.0287 - acc_top1: 0.9891 - acc_top5: 1.0000 - 86ms/step
Eval samples: 822
save checkpoint at /home/aistudio/chk_points/final
'''top1 表示预测的第一个答案就是正确答案的准确率
top5 表示预测里面前五个包含正确答案的准确率
预测可视化:
# 将我们训练得到的模型进行保存,以便后续评估和测试使用。model.save(get('model_save_dir'))# 模型评估和测试predict_dataset = MicroExpression(mode='test')print('测试数据集样本量:{}'.format(len(predict_dataset)))测试数据集样本量:826
from paddle.static import InputSpec# 网络结构示例化network = paddle.vision.models.resnet50(num_classes=get('num_classes'))# 模型封装model_2 = paddle.Model(network, inputs=[InputSpec(shape=[-1] + get('image_shape'), dtype='float32', name='image')])# 训练好的模型加载model_2.load(get('model_save_dir'))# 模型配置model_2.prepare()# 执行预测result = model_2.predict(predict_dataset)Predict begin... step 826/826 [==============================] - 17ms/step Predict samples: 826
from PIL import Imageimport matplotlib.pyplot as plt# 样本映射LABEL_MAP = get('LABEL_MAP')def show_img(img, predict):
plt.figure()
plt.title('predict: {}'.format(LABEL_MAP[predict_label]))
image_file, label = predict_dataset.data[idx]
image = Image.open(image_file)
plt.imshow(image)
plt.show()# 随机取样本展示indexs = [50,150 , 250, 350]for idx in indexs:
predict_label = np.argmax(result[0][idx])
real_label = predict_dataset[idx][1]
show_img(real_label,predict_label ) print('样本ID:{}, 真实标签:{}, 预测值:{}'.format(idx, LABEL_MAP[real_label], LABEL_MAP[predict_label]))'''
<Figure size 432x288 with 1 Axes>
样本ID:50, 真实标签:happiness, 预测值:happiness
<Figure size 432x288 with 1 Axes>
样本ID:150, 真实标签:disgust, 预测值:disgust
'''Paddle-Lite 是飞桨推出的一套功能完善、易用性强且性能卓越的轻量化推理引擎。 轻量化体现在使用较少比特数用于表示神经网络的权重和激活,能够大大降低模型的体积,解决终端设备存储空间有限的问题,推理性能也整体优于其他框架。本部分以本案例的微表情数据集的ResNet50模型为例,介绍怎样使用Paddle-Lite,在移动端(基于华为麒麟985npu的安卓开发平台)对进行模型速度评估。
华为麒麟NPU部署参考链接: https://paddlelite.paddlepaddle.org.cn/v2.10/demo_guides/huawei_kirin_npu.html
由下图可见,NPU上的运算速率是CPU的数倍:
说明:PaddleLite v2.10不支持鸿蒙系统下使用npu,若有需要,可按照上述链接改为EMUI系统。
# 模型部署model_2.save('infer/MicroExpression', training=False)文件:infer文件夹中的 .pdmodel .pdiparams 和下载到本地的image_classification_demo文件,下载链接如下:
https://github.com/PaddlePaddle/Paddle-Lite-Demo
工具:Android Studio、华为手机(开启USB调试模式)、USB数据线
Android Studio安装: https://blog.csdn.net/qq_41976613/article/details/91432304
通过以下命令,将.pdmodel 和 .pdiparams两个模型文件生成Android Studio可执行的.nb文件。
# 准备PaddleLite依赖!pip install paddlelite==2.10
# 准备PaddleLite部署模型#--valid_targets中参数(arm)用于传统手机,(huawei_kirin_npu,arm )用于华为带有npu处理器的手机!paddle_lite_opt \
--model_file=infer/MicroExpression.pdmodel \
--param_file=infer/MicroExpression.pdiparams \
--optimize_out=./infer/ResNet \
--optimize_out_type=naive_buffer \
--valid_targets=huawei_kirin_npu,armPaddle-Lite-Demo-master\Paddle-Lite-Demo-master\PaddleLite-android-demo\image_classification_demo\app\src\main\assets
制作标签文件见 classfication2/train.txt
序号和类别的对应关系千万不能弄错了!
创建子文件夹resnet_for_npu,将上述步骤生成的.nb文件下载到本地,并改名为model.nb
位置:Paddle-Lite-Demo-master\Paddle-Lite-Demo-master\PaddleLite-android-demo\image_classification_demo
手机会自动出现以下画面
说明:由于我没有再把鸿蒙系统改为EMUI系统,故默认使用CPU进行预测,从预测时间几百ms可以看出,符合上文中官方给出的推理时间。
以上就是微表情——直抵内心深处最真实的情感的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号