ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

图像情绪分析课程设计全流程:CNN/VGG/ResNet人脸表情识别复现指南

图像情绪分析课程设计全流程:CNN/VGG/ResNet人脸表情识别复现指南 简介人工智能导论课程设计——基于图像的情绪分析项目包面向人工智能、计算机及相关专业的在校学生适用于课程作业、课程设计、毕业设计或项目初期演示。压缩包共25个文件包含11个Python源文件涉及模型定义、训练与测试、6个Markdown说明文档、2份PDF报告以及XML配置文件、MP4演示视频和Word文档整体体积22.54MB目录结构清晰。源码覆盖CNN、VGG、ResNet等多种主流模型并附有数据划分、可视化、多模型对比等工具脚本还包含人脸检测配置便于理解从人脸定位、特征提取到情绪分类的完整流程。配套文档包括实验报告、开题报告、期末项目报告、进展报告和使用说明另有视频演示辅助代码均经过测试且答辩平均分达96分适合基础薄弱者边看边学也可在此基础上进行二次开发。目前已有251人学习下载适合需要完整参考实现或快速搭建情绪识别项目的读者。1. 图像情绪分析大作业一个能完整复现的课程设计包“人工智能导论”的大作业做图像情绪分析最坑的不是模型选型而是你花了两周调代码最后发现交上去的文档和代码对不上。这一份课程设计资源把源代码、实验报告、开题报告、答辩报告全放在一个包里代码测试过、能跑通评审平均分 96 分——这本身就是一份可以照着复现的完整范例。它解决的问题很明确从人脸检测、数据集划分、CNN/VGG/ResNet 训练到单张图片推理一条链路给你走通。适合计科、人工智能、通信、自动化这些专业的学生拿来复现课程设计也适合基础一般的人先跑通再改自己的数据集。我要提醒一句它并不是一个“调好参数直接出结果”的黑匣子而是把整套工程骨架摊开给你看。你需要自己读代码、改路径、换数据但好在每段都有对应的文档说明踩坑时知道去哪个文件里找答案。接下来我按工程结构、数据准备、模型训练、避坑记录这条线给你拆开讲。2. 工程结构拆解五个模块分别管哪一段2.1 从文件清单看整体设计拿到压缩包先别急着双击运行我习惯的做法是先列一遍文件清单搞清楚每个文件是干什么的。这个项目的文件虽然多但归类后很清晰文件/目录职责model_CNN.py / model_CNN_test.pyCNN 模型的训练与验证脚本model_VGG.py / model_VGG_test.pyVGG 模型的训练与验证脚本model_ResNet.py / model_ResNet_test.pyResNet 模型的训练与验证脚本model_CNN_GPU.pyCNN 的 GPU 加速训练版本model_All_Compare.py加载三个模型做横向对比输出指标data_separation.py划分训练集、验证集、测试集data_view.py可视化数据集样本分布image_emotion_mapping.py单张图片的情绪推理与标签映射haarcascade_frontalface_default.xmlOpenCV 人脸检测的级联分类器face_images/ 与 dataset/人脸图片与原始数据集Face-Annotation-Tool-master.zip自建数据集用的标注工具Fer2013-pytorch-CNN-VGG-Resnet--master.zipFer2013 数据集的参考实现Facial-expression-recognition-main.zip另一个表情识别参考项目video/example_dsh.mp4演示视频可用于答辩展示开题报告.pdf / 进展报告.doc / 期末课程项目报告.pdf全过程文档材料README.md / 使用说明.md项目说明与运行指引从这套结构能看出来它不是只给你一个训练脚本就完事而是把“课程设计”从头到尾的产物都整理好了。文档部分尤其值得注意开题报告、进展报告、期末报告分别对应课程设计的三个时间节点这比单纯跑通代码更值钱。2.2 训练脚本与验证脚本的对应关系三个模型各有一对训练/验证脚本这是我在这类项目里比较认可的设计。训练脚本负责加载数据、定义损失函数、训练并保存权重验证脚本负责加载训练好的权重在测试集上计算准确率。两者分工明确避免了把训练和评估逻辑揉在一起导致的“验证时用了训练集”这种低级错误。model_CNN_GPU.py 是单独拎出来的 GPU 版本。如果你本机只有 CPU就用 model_CNN.py如果有 NVIDIA 显卡并且装好了 CUDA 版 PyTorch就用 GPU 版本训练速度会快一个量级。model_All_Compare.py 的作用更直接它把三个模型加载到同一份测试数据上输出准确率对比答辩时直接拿这张对比表讲“为什么 ResNet 深度更深但效果不一定最好”比空口说理论有说服力得多。2.3 文档材料的使用顺序我建议你按照“开题报告 → 进展报告 → 期末报告”的顺序读而不是先读代码。开题报告告诉你当时为什么选情绪分析这个题目、预期目标是什么进展报告记录的是中途做了什么、遇到什么问题期末报告才是最终的完整技术方案和实验结果。先把这三份文档过一遍再回去看代码你会知道每个模型脚本为什么要这样写而不是停留在“能跑就行”的层面。README.md 是入口使用说明.md 是操作手册文档里通常会写明数据放在哪个目录、训练命令是什么、权重保存在哪。如果你第一次运行就报错先回去看这两份文件大部分路径问题都能解决。3. 人脸检测与数据准备把图片变成能训练的表情数据集3.1 用 haarcascade 做前置人脸检测表情识别的前置步骤是人脸检测。这个项目用的是 haarcascade_frontalface_default.xml这是 OpenCV 自带的传统级联分类器。你可能会问现在深度学习人脸检测一大把为什么课程设计还用这种传统方案原因很实际它不依赖 GPU、不额外装模型文件、单张图片检测只要几十毫秒对课设场景完全够用。而且它与后面的情绪分类是解耦的——先检测人脸裁剪出来再送进分类模型。import cv2 face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml) img cv2.imread(face_images/sample.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSize(48, 48) ) for (x, y, w, h) in faces: face img[y:yh, x:xw] face cv2.resize(face, (48, 48)) # face 就是后续要送给情绪分类模型的输入scaleFactor1.1 表示每次缩放步长为 10%数值越小检测越精细但越慢minNeighbors5 表示每个候选框至少要有 5 个相邻确认调大能减少误检但可能漏检minSize(48, 48) 是为了和 Fer2013 数据集的输入尺寸对齐。这里有个常见误区如果 minSize 设得比实际人脸还大会直接检测不到人脸视频里尤其明显。3.2 data_view.py 与 data_separation.py先看数据再动手训练我刚拿到这套代码时第一件事是先跑 data_separation.py 而不是直接训练。原因很简单——如果不清楚数据集怎么划分的训练完你根本不知道验证集里有没有混入训练样本。data_separation.py 做的就是把 dataset 目录下的图片按比例拆成训练集、验证集、测试集常见的划分比例是 8:1:1 或 7:2:1。# data_separation.py 中的常见划分逻辑 import os import random import shutil source_dir dataset train_dir dataset/train val_dir dataset/val test_dir dataset/test ratio (0.8, 0.1, 0.1) # 首先按类别遍历目录比如 happy/ sad/ angry/ for class_name in os.listdir(source_dir): class_path os.path.join(source_dir, class_name) if not os.path.isdir(class_path): continue images os.listdir(class_path) random.shuffle(images) train_cnt int(len(images) * ratio[0]) val_cnt int(len(images) * ratio[1]) # 前 train_cnt 张进训练集中间进验证集其余进测试集划分的逻辑本身不复杂但要注意两个细节一是必须按类别目录分别划分否则会出现某个类别的测试集为空二是划分前要用 random.shuffle 打乱顺序否则按文件名排序会导致同一批人脸都在同一个集合里。data_view.py 则是把划分后的数据可视化出来让你确认每个类别的样本数量是否均衡。我见过很多人在这一步偷懒最后发现某个类别只有几十张图训练出来的模型对该类别几乎不识别。3.3 Face-Annotation-Tool自建数据集的标注途径如果你不想用 Fer2013 这个现成数据集想用自己的图片训练模型压缩包里的 Face-Annotation-Tool-master.zip 就派上用场了。这是一个图形化标注工具你可以用它框出图片中的人脸并打上对应的情绪标签导出后作为训练数据。使用流程一般是先准备一批包含人脸的图片 → 用工具打开图片 → 框选人脸区域 → 选择情绪类别happy/sad/angry 等→ 保存标注结果到 CSV 或图片目录。标注完成后再通过 data_separation.py 划分数据就可以进入模型训练阶段。这里给你一个参考标准想要训练出能看的模型每个情绪类别至少准备 500 张以上的人脸图且最好覆盖不同角度、不同光照。如果样本太少模型很容易过拟合训练集准确率 99%测试集一塌糊涂。4. 三道模型的训练脚本CNN/VGG/ResNet 差异与训练参数4.1 三种网络结构在代码上的本质差异很多初学者误以为 VGG 和 ResNet 是“另一个算法”其实它们都是卷积神经网络家族差别在于网络深度和连接方式。这个项目在同一份数据集上实现了三种结构正好可以对比它们的效果差异。model_CNN.py 里的网络结构通常是一个基础卷积网络卷积层 激活函数 池化层堆叠最后接全连接层分类。这种结构参数少、训练快但对复杂特征的提取能力有限。我把常见的简化版结构列出来供你对照源码理解import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self, num_classes7): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2) ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(64 * 12 * 12, 128), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))这里的输入是 48×48 的彩色图经过两次 MaxPool2d 后尺寸变成 12×12所以全连接层的输入维度是 64×12×129216。Dropout(0.5) 是为了防止过拟合训练时随机丢弃一半神经元验证时自动关闭。注意如果你自己改了输入尺寸这个全连接层的维度必须同步调整否则会报维度不匹配。model_VGG.py 的思路是“用更深的卷积层堆叠”连续用多个 3×3 卷积核代替大卷积核感受野相同但参数更少、非线性更强。VGG 的参数量比基础 CNN 大很多在小数据集上训练时更需要数据增强来扛过拟合。model_ResNet.py 的核心是残差连接。我摘出关键代码块给你看class BasicBlock(nn.Module): expansion 1 def __init__(self, in_channels, out_channels, stride1): super().__init__() self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse) self.bn1 nn.BatchNorm2d(out_channels) self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stride1, padding1, biasFalse) self.bn2 nn.BatchNorm2d(out_channels) self.shortcut nn.Sequential() if stride ! 1 or in_channels ! out_channels: self.shortcut nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size1, stridestride, biasFalse), nn.BatchNorm2d(out_channels) ) def forward(self, x): out F.relu(self.bn1(self.conv1(x)), inplaceTrue) out self.bn2(self.conv2(out)) out self.shortcut(x) return F.relu(out, inplaceTrue)这个 BasicBlock 是 ResNet 的最小单元。看 forward 里的 out self.shortcut(x)就是把输入 x 通过一个跳连接直接加到卷积输出上。这一加让梯度在反向传播时多了一条直达通道网络可以堆得很深但不会出现梯度消失。ResNet 在图像情绪这个任务上的表现不一定碾压 VGG但训练过程通常更稳定。4.2 训练参数怎么设从脚本里读出关键信息打开任何一个训练脚本重点关注这几个参数——batch_size、学习率、epoch 数和优化器。这类课设项目的常用配置大致如下参数常见取值说明输入尺寸48×48 或 64×64Fer2013 原始尺寸是 48×48batch_size32 或 64显存不够就降到 16学习率0.001 或 0.0001Adam 优化器常用 0.001优化器Adam 或 SGDSGD 需要手动调学习率衰减epoch 数2050小数据集 30 轮左右就能收敛损失函数CrossEntropyLoss多分类标配batch_size 不是越大越好。在课程设计场景GPU 显存通常只有几 Gbatch_size 设 128 容易显存溢出。这时候你要么调小 batch_size要么把图片尺寸从 64×64 改成 48×48。学习率的设置我一般看损失曲线损失震荡不下降就把学习率除以 10下降太慢就适度调大。4.3 model_All_Compare.py 与 model_CNN_GPU.py 的用法model_All_Compare.py 的核心逻辑不复杂加载三个训练好的模型权重分别对同一批测试图片做预测计算准确率最后以表格形式输出对比结果。它的价值在于给你一套可重复的评估流程而不是靠“感觉哪个模型好”。跑这个脚本之前先确认三个模型的权重文件都存在而且类别顺序一致。如果训练脚本里的类别映射和对比脚本里的映射不一致对比结果就是乱的。model_CNN_GPU.py 是 CNN 模型的 GPU 加速版它和 model_CNN.py 的网络结构相同区别在于把数据和张量显式地放到 CUDA 设备上。用 GPU 训练时要注意把模型、输入数据、标签都移动到 GPU漏掉任何一个都会报“device mismatch”错误。如果你的电脑没有 NVIDIA GPU请不要运行这个脚本直接跑 CPU 版本。5. 复现踩坑记录五条最真实的报错与排查5.1 dataset 路径找不到现象运行 data_separation.py 或训练脚本时报错 FileNotFoundError提示找不到 dataset 目录或某个子目录。原因压缩包解压后目录层级发生了偏移。常见的情况是代码在 dataset/ 下找 train 文件夹但实际数据在 dataset/train/train 这种嵌套目录里或者 data_separation.py 还没运行train/val/test 这三个子目录根本不存在。解决先运行 data_separation.py 生成划分后的目录再启动训练。如果仍报错在代码里打印 os.path.exists() 逐一确认路径。我习惯把数据路径在脚本开头统一用绝对路径写死避免换机器后相对路径失效。5.2 图片维度 mismatch现象训练时在 forward 阶段报错提示 Expected 4D tensor as input或者全连接层维度对不上。原因模型定义里写死了输入尺寸和全连接层维度但数据加载时读进来的图片尺寸不一致。比如模型是按 48×48 设计的数据管道里却直接读了原图没有 resize 到 48×48。也可能是图片本身就是灰度单通道但模型第一层 Conv2d 输入通道写的 3。解决在数据加载代码里统一做 resize 和通道转换。灰度图用 cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) 转成三通道再喂给模型。Check 一下 data_view.py 里显示的图片尺寸如果显示的形状不是 (3, 48, 48)优先修数据加载而不是改模型结构。5.3 GPU 显存不足现象跑了几个 epoch 后报 CUDA out of memory程序直接中断。原因图片尺寸大、batch_size 大、模型参数量多三者叠加把显存撑爆了。ResNet 在小显存显卡上尤其容易出现这个问题它虽然参数不算夸张但中间特征图多。解决降 batch_size 到 16 或 8这是见效最快的方法。如果还不行把输入尺寸从 64×64 降到 48×48。再不行换 model_CNN.py 或者用 CPU 版本训练。注意 PyTorch 的显存不一定是占满才报错其它程序占用的显存也算在内。5.4 训练集准确率很高但测试集准确率很低现象训练集准确率 95% 以上测试集只有 60% 出头两个数字差距很大。原因典型过拟合。课堂项目用的数据集通常只有几千张图模型完全记住了训练样本。另一个可能原因是 data_separation.py 划分时没有按类别分层导致测试集里某些类别样本极少。解决先检查数据划分是否符合预期再看训练脚本里有没有数据增强。常见的做法是加 RandomHorizontalFlip、RandomRotation 这类轻量增强并把 Dropout 比例从 0.3 调到 0.5。模型方面可以考虑从 ResNet 换回参数量更小的基础 CNN。5.5 推理结果永远输出同一个类别现象跑 image_emotion_mapping.py 时不管输入哪张图片输出的情绪标签都是同一个。原因大概率是类别映射表错位。训练时类别顺序是 [angry, disgust, fear, happy, neutral, sad, surprise]但推理脚本里按 [happy, sad, angry, ...] 来映射或者 softmax 输出取的是概率最大值但索引对应的标签名写错了。解决把训练脚本里的类别顺序和推理脚本里的类别列表逐项对齐。更稳妥的做法是跑 model_All_Compare.py 看整体准确率如果三个模型对同一批图片都输出同一个标签那基本可以确定是标签映射文件的问题而不是模型没训练好。6. 训练完之后怎么真正用起来单图推理、指标检验与二次微调6.1 用 image_emotion_mapping.py 跑通单张图片闭环训练完成后你要验证的不是训练准确率而是模型的“落地能力”。image_emotion_mapping.py 做的就是这件事输入一张人脸图片输出情绪类别和置信度。它的核心流程是先做人脸检测再送进分类模型最后做 softmax 得到概率分布。有一类常见错误是模型权重文件加载后没有调用 eval()导致 BatchNorm 和 Dropout 仍然处在训练模式推理结果抖动。import cv2 import torch import torch.nn.functional as F model SimpleCNN(num_classes7) model.load_state_dict(torch.load(model/cnn.pth, map_locationcpu)) model.eval() # 关键切换到推理模式 img cv2.imread(test_face.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (48, 48)) tensor torch.tensor(img).permute(2, 0, 1).unsqueeze(0).float() / 255.0 with torch.no_grad(): logits model(tensor) prob F.softmax(logits, dim1) top3 prob.topk(3) # 输出前三个置信度最高的类别和对应概率注意 permute(2, 0, 1) 是把 HWC 格式转成 CHW这是 PyTorch 的输入格式要求除以 255.0 是把像素值归一化到 01。如果你用的预训练模型在训练时做过均值方差标准化推理时也要保持完全一致的前处理。topk(3) 只打印前三个概率方便你判断模型是“很有把握”还是“几个类别都差不多”。6.2 从视频演示到二次微调还能再往前走一步压缩包里的 video/example_dsh.mp4 可以直接拿来演示。把 6.1 的单图推理流程放进一个循环里逐帧读取视频、做人脸检测、做分类、画框、写标签输出成新视频答辩现场放出来比 PPT 讲效果好很多。但要注意 CPU 逐帧推理很慢建议每 35 帧处理一次或者先把视频抽帧处理好再合成而不是实时处理。如果你手里有自己的表情数据集最简单的微调做法是加载项目里训练好的 CNN 权重冻结前面的卷积层只训练最后一个全连接分类层。冻结的方式是设置 requires_gradFalse再把分类层的输出类别改成你自己的类别数。这样做不需要从头训练几十张图片就能把模型调到可用的状态。我自己的习惯是每次拿到别人的课程设计代码一定先把 data_separation.py 跑通确认数据划分没有混样本再进入训练环节。检验一个模型能不能用不是看训练集准确率而是看它在完全没见过的图片上的表现。从那以后我复现这类项目都强制走一遍这个流程省掉了大量返工时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表