ARTICLE DETAIL

资讯详情

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

基于图像的情绪分析:AI导论大作业从原理到代码实战

基于图像的情绪分析:AI导论大作业从原理到代码实战 简介这是人工智能导论课程‘基于图像的情绪分析’大作业的完整方案源码、文档与实验报告齐备面向课程设计、大作业汇报或答辩场景适合高校相关专业学生参考也适合初学者学习表情识别与图像分类流程。压缩包共25个文件、22.54MB含11个Python脚本涵盖CNN、VGG、ResNet多个模型的训练、测试与效果对比以及数据划分、数据视图、人脸检测、情绪映射等功能代码另有6个Markdown说明、人脸检测XML、演示视频以及开题报告、进展报告和期末课程项目报告等材料并附使用说明目录按功能分层便于快速上手和定向检索。资源内集成3个参考工程压缩包可作为二次开发基础。已有251人浏览/学习遇到运行问题可私信作者支持远程教学。对需要完成课程设计、准备答辩或梳理完整项目链路的读者这份资料能从开题、数据处理、模型搭建、效果对比到报告撰写提供全流程参考。1. 人工智能导论大作业基于图像的情绪分析到底在做一个什么问题人工智能导论大作业里基于图像的情绪分析是一个特别适合练手的题目它不要求你发明算法却要求你把“图像输入到情绪标签输出”这条链路真正跑通并交出源代码、文档说明和实验报告。很多同学会误以为这只是“下载一个模型跑一遍”实际上还要处理数据集、选网络、调参数、做评估最后把过程写成老师看得懂的实验报告。这背后需要你掌握图像分类、迁移学习、模型验证、工程目录组织等一堆知识点但每一个点又都不会深到让你在导论课阶段就放弃。这篇内容适合正在写人工智能导论大作业、或者想用一个小项目入门图像分类的从业者和学生。我会按一套能交付的工程来拆解包括技术选型、代码结构、文档组织以及我踩过的坑。2. 先把原理立住情绪分析为什么不是“看图说话”这么简单2.1 人脸检测与情绪分类的流水线先拆任务再选模型基于图像的情绪分析严格说包含两个层级的任务第一在图像里找到人脸第二把每个人脸区域划分为预先定义好的情绪类别。传统教材里这套流程叫“级联结构”——先用目标检测把人脸框出来再用分类网络判断情绪。如果你跳过检测直接把整张图扔给分类模型模型会被背景、光照、多个人脸干扰很难收敛。所以大作业的第一件事是把这个问题拆成“检测分类”两个子问题。检测环节最稳妥的做法是用 OpenCV 的 Haar Cascade 或 MTCNN。Haar Cascade 不需要额外训练调用一行就出来缺点是漏检率偏高尤其是侧脸和暗光。MTCNN 效果好但需要额外安装库。我一般建议大作业先上 Haar Cascade把流程跑通再决定要不要换检测器。检测器的输出是一个矩形框 (x, y, w, h)后续情绪分类只处理这个裁剪区域。这一步决定了后面分类器的输入质量如果框大了会把头发、背景都包进来框小了会切掉眉毛和嘴情绪特征不完整。分类环节要定义情绪标签。公开数据集里最常见的七类表情是生气、厌恶、恐惧、高兴、悲伤、惊讶和中性。注意这七个类别之间的边界很模糊比如“恐惧”和“惊讶”在眼睛瞪大时特别容易混“中性”经常被误判成“悲伤”。这不是你的模型有问题而是情绪本身存在主观性。大作业里最好明确使用数据集官方标签不要自己重新标注否则报告里没法说服老师。2.2 从传统特征到预训练CNN大作业选型的三条路线如果只按照导论课讲过的知识选型你会面对三条路线各有取舍。第一条是传统特征加经典分类器提取 HOG 特征训练 SVM 分类器。这条路代码量小几十行就能跑CPU 也能训。但 HOG 特征对表情这种局部形变很不敏感在 FER2013 这类真实情绪数据集上通常只有 50% 出头的准确率和随机猜测的 14% 比有提升但拿到答辩现场不够好看。除非老师要求“不能只背深度学习”否则不建议主推。第二条是小型卷积神经网络自己写一个 4 到 6 层的 CNN输入 48x48 灰度图。这能体现你理解卷积、池化、全连接这些基础概念而且训练参数量不大普通笔记本 CPU 也能在半小时内跑完一个 epoch。缺点是收敛状态很看运气同样的网络结构换一个初始化结果可能差 5 个百分点。“玄学”往往就出现在这条路上。第三条是迁移学习加载 ImageNet 预训练模型例如 ResNet18把最后一层换成 7 分类冻结前面几层只微调后三层和分类头。这条路线我最推荐因为它在导论课作业里不算超纲——你只要说明“预训练权重帮模型在小样本上更快收敛”老师就能理解。实际效果在 FER2013 上通常比小型 CNN 高出 10 到 15 个点而且调试时间短。下表对比三条路线的落地感受。路线代码量训练耗时在FER2013参考准确率答辩亮点风险HOGSVM少分钟级约50%-60%经典ML过程完整效果说服力弱小CNN中半小时/epoch约60%-70%网络结构理解清晰调参看运气迁移学习ResNet18中几分钟/epoch约70%-75%能讲清fine-tune原理需解释为何不用更大网络这里的参考准确率是我用常见配置跑出来的经验区间不是绝对标准。准确率受数据切分影响很大同一个模型换个划分就可能浮动 2 到 3 个点。汇报时不要单看准确率而是把准确率配合混淆矩阵一起讲这样老师会认为你有评估意识。选型理由上迁移学习还有一个隐藏好处它能帮你少碰梯度消失。导论课作业通常不要求你别出心裁设计网络直接微调 ResNet18 既安全又有一定创新度——你在报告里多写一句“只替换卷积首层适配灰度图并微调到最后两层”就足以展现思考深度。2.3 数据集与标签情绪类别怎么定正负样本别踩坑公开最常用的情绪分类数据集是 FER2013它把每张人脸表情保存成 48x48 的灰度像素总样本量 35887 张分训练集、公开测试集和私有测试集。另一个常用的是 CK图数少但标注干净多用于验证集。导论课大作业不建议自己爬图第一费时间第二标注很难做到一致。用现成数据集报告里可以写清楚来源、数量、格式这部分是老师一定会看的。FER2013 有一个需要提前知道的坑类别严重不平衡。happy 和 neutral 数量多disgust 数量特别少可能只有整个数据集的 2% 到 3%。如果你不做任何处理模型会对多数类过拟合最终测试准确率虽然能看但每个类别的 recall 相差悬殊。解决不平衡最常见的有三种对训练集做重采样让每个类别样本数接近给损失函数加类别权重或者把过少且容易混淆的类别合并。我一般会用 sklearn 的 compute_class_weight 直接算权重然后塞进 CrossEntropyLoss 的 weight 参数。这比改代码更省事。还要注意情绪类别定义。FER2013 的 7 类里disgust 和 fear 经常样本少且和 anger 混淆。如果你的大作业只有两周建议保留原始 7 类用类别权重兜底但如果最后测试集上 disgust 的 F1 还是不到 0.2可以在报告的“改进方向”里写“将 disgust 与 fear 合并成 negative 类”并补做一个 6 类实验让老师看到你不是只会照跑而是会分析问题。数据预处理要固定一个流程灰度图 → 压缩到统一尺寸 → 归一化。不要用原图尺寸直接训练因为不同图片宽高比不同全连接层维度会崩。归一化用 mean0.5、std0.5 比较省事等价于把像素值压到 -1 到 1 之间配合大多数网络都能稳定训练。如果走迁移学习第一层输入是三通道需要把灰度图复制成三通道并使用 ImageNet 的 mean/std0.485, 0.456, 0.406 / 0.229, 0.224, 0.225。这两种归一化都常见关键是要保证训练和推理用同一个参数否则模型推理结果会“翻车”。3. 把代码跑通从数据集到训练再到推理的最小工程3.1 项目目录与依赖一份能直接交的源码结构动手写代码前先把目录组织好比写到一半再整理要省力得多。我见过很多同学把训练脚本、数据集、报告全堆在桌面上最后提交时补一个压缩包文件名还是“新建文档”。老师打开这种项目第一印象就是你缺乏工程规范。一套简单的目录结构如下emotion_analysis/ ├── data/ │ ├── raw/ # 原始数据集按标签存子目录 │ └── processed/ # 预处理后的图片训练时可读取 ├── src/ │ ├── dataset.py # 数据集加载、增强、归一化 │ ├── train.py # 训练主脚本保存最优权重 │ ├── inference.py # 测试集推理、画混淆矩阵、样本预测图 │ └── utils.py # 随机种子、类别权重等公共函数 ├── docs/ │ ├── README.md # 文档说明怎么跑起来 │ └── experiment_report.md # 实验报告正文 ├── checkpoints/ # 训练好的模型权重 └── requirements.txt # 依赖清单这个目录的用意是让源码、数据、文档分离src 只放代码data 放原始和处理后的数据docs 放文档说明和实验报告checkpoints 放权重。大作业只需要交源码加文档时把 checkpoints 里的大权重文件删掉或另存网盘压缩包体积就很小。如果老师要求“源代码文档说明实验报告”三件套这个目录直接对应三件事不要额外拼凑。依赖清单 requirements.txt 也别随手写。最稳妥的是列出你实际 import 过的核心库不要用pip freeze requirements.txt把几十个间接依赖全塞进去。课设环境通常不好复现越精简越容易装上。下面是一份够用的依赖清单opencv-python4.6.0 torch1.13.0 torchvision0.14.0 numpy1.21.0 matplotlib3.5.0 scikit-learn1.0.0 pandas1.3.0说明opencv 负责读取图像和人脸检测torch/torchvision 负责模型和预训练权重matplotlib 画混淆矩阵和样本图scikit-learn 提供 classification_report 和类别权重计算pandas 用来读 csv 格式的标签文件。版本写下限不写死是为了在同学之间传递压缩包时不容易因为“缺某个小版本”而崩掉。3.2 数据加载与预处理搞定灰度图、归一化和数据增强先写 Dataset 类这是 PyTorch 工程里最容易被忽略但最关键的一环。它决定了每条训练样本是怎么从磁盘变成张量的。下面这套代码可以直接适应按标签存子目录的图片结构。# src/dataset.py import os import torch from torch.utils.data import Dataset from torchvision import transforms from PIL import Image class EmotionDataset(Dataset): def __init__(self, img_dir, label_dict, trainTrue): self.img_dir img_dir self.samples list(label_dict.keys()) self.labels label_dict self.train train if train: self.transform transforms.Compose([ transforms.RandomHorizontalFlip(p0.5), transforms.RandomAffine(degrees8, translate(0.05, 0.05)), transforms.Resize((48, 48)), transforms.ToTensor(), transforms.Normalize(mean[0.5], std[0.5]) ]) else: self.transform transforms.Compose([ transforms.Resize((48, 48)), transforms.ToTensor(), transforms.Normalize(mean[0.5], std[0.5]) ]) def __len__(self): return len(self.samples) def __getitem__(self, idx): img_name self.samples[idx] img Image.open(os.path.join(self.img_dir, img_name)).convert(L) img self.transform(img) label self.labels[img_name] return img, label这段代码的核心是 transform 的差异训练时随机水平翻转和轻微仿射变换用于扩大样本多样性抑制过拟合测试时不加增强只做 resize 和归一化。之所以只做 8 度旋转和 5% 平移是因为情绪分类对姿态变化敏感角度太大的增强会把人脸五官形状扭曲到失去语义。这里要特别说明灰度图处理convert(L)把图片读成单通道后面 Normalize 用mean[0.5], std[0.5]两个参数对应灰度图。如果你改成三通道迁移学习要么把convert(L)改成convert(RGB)Normalize 换成 ImageNet 的三通道均值方差要么像我在训练脚本里那样把模型第一层改成单通道输入。两种改法都能跑但别混用——训练时用单通道推理时如果忘了convert(L)模型会直接报通道数错误或者输出概率一团糟这种“玄学报错”通常是预处理不一致造成的。3.3 训练脚本关键参数epoch、batch size、学习率怎么设训练脚本是整套作业的心脏。这里以 ResNet18 迁移学习为例子给出了可运行的最小骨架。# src/train.py import torch import torch.nn as nn from torch.optim import AdamW from torch.utils.data import DataLoader from torchvision import models from dataset import EmotionDataset # 超参数 BATCH_SIZE 64 EPOCHS 30 LR 1e-3 NUM_CLASSES 7 DEVICE torch.device(cuda if torch.cuda.is_available() else cpu) model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.conv1 nn.Conv2d(1, 64, kernel_size7, stride2, padding3, biasFalse) model.fc nn.Linear(model.fc.in_features, NUM_CLASSES) model.to(DEVICE) train_ds EmotionDataset(data/processed/train, train_label_dict, trainTrue) val_ds EmotionDataset(data/processed/val, val_label_dict, trainFalse) train_loader DataLoader(train_ds, batch_sizeBATCH_SIZE, shuffleTrue, num_workers2) val_loader DataLoader(val_ds, batch_sizeBATCH_SIZE, shuffleFalse, num_workers2) criterion nn.CrossEntropyLoss() optimizer AdamW(model.parameters(), lrLR, weight_decay1e-4) for epoch in range(EPOCHS): model.train() total_loss 0.0 for imgs, labels in train_loader: imgs, labels imgs.to(DEVICE), labels.to(DEVICE) optimizer.zero_grad() preds model(imgs) loss criterion(preds, labels) loss.backward() optimizer.step() total_loss loss.item() avg_loss total_loss / len(train_loader) print(fepoch {epoch1}/{EPOCHS}, loss: {avg_loss:.4f}) # 验证 model.eval() correct 0 total 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(DEVICE), labels.to(DEVICE) preds model(imgs).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fval acc: {correct / total:.4f})关键参数我有几个自己的设置习惯。BATCH_SIZE 取 64是因为 48x48 的灰度图内存占用小64 和 128 在训练速度上差异不大但 64 对 BN 层的稳定性更好。EPOCHS 设 30 而非导论课里常见的 10是因为迁移学习在微调阶段前几轮还在“适应”新分类头收敛比从零训练更慢但也用不着 100 轮。LR 初始 1e-3 配合 AdamW 足够如果你发现验证损失在涨就把 LR 降到 1e-4 继续微调。weight_decay1e-4 算是标准的 L2 正则能压住一些噪声大的样本对权重的影响。代码里有一点需要特别注意验证阶段用torch.no_grad()避免记录梯度否则验证会占一倍的显存model.eval()和model.train()切换也很关键否则 BN 层在验证时还在用训练集统计量导致准确率偏“虚高”。每次 epoch 结束后记得把验证准确率记到日志里后面写实验报告需要这个数据。3.4 推理与可视化出图、出报告老师看的不是代码是结果老师评审这份大作业不会一行行读你的训练代码但一定会看你输出的混淆矩阵、分类报告和几张“预测成功/失败”的样本图。所以推理脚本要承担“产出证据”的作用。下面这段代码在测试集上跑一遍输出每个类别的 precision、recall、F1并画混淆矩阵保存到 docs 目录。# src/inference.py import torch import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, classification_report CLASSES [angry, disgust, fear, happy, sad, surprise, neutral] def plot_confusion_matrix(cm, save_pathdocs/confusion_matrix.png): plt.figure(figsize(8, 6)) plt.imshow(cm, cmapBlues) plt.xticks(range(len(CLASSES)), CLASSES, rotation45) plt.yticks(range(len(CLASSES)), CLASSES) plt.colorbar() plt.xlabel(Predicted) plt.ylabel(True) plt.tight_layout() plt.savefig(save_path, dpi150) preds_all, labels_all [], [] model.eval() with torch.no_grad(): for imgs, labels in val_loader: imgs imgs.to(DEVICE) preds model(imgs).argmax(dim1).cpu().numpy() preds_all.extend(preds) labels_all.extend(labels.numpy()) cm confusion_matrix(labels_all, preds_all) print(classification_report(labels_all, preds_all, target_namesCLASSES)) plot_confusion_matrix(cm)这段代码有两点值得注意。一是把预测移到 CPU 上再转 numpy避免 GPU 和设备之间频繁拷贝导致显存碎片二是用extend而不是append否则会把每个 batch 当成一个列表元素最后算混淆矩阵时维度对不上。classification_report 会直接给出每个类的指标你在实验报告里可以直接引用这些数字。除了混淆矩阵我还会额外写一个函数随机抽 4 张测试图像把真实标签和预测标签并排打印到一张图上。这能直观展示模型的错误形态答辩时也比只看数字更有说服力。保存图片时注意dpi150清晰度足够插入 Word 或 Markdown 报告。4. 源代码之外文档说明和实验报告才是拿高分的关键4.1 文档说明怎么写从README到环境复现很多同学把“文档说明”理解成把代码复制一遍再加几句注释这其实是在浪费机会。老师需要的是一个陌生人或者两周后的你自己拿到压缩包后能在十分钟内重现你的实验结果。README 就是入口。一份合格的 README 至少要回答三件事这个项目在做什么、怎么安装依赖、怎么跑出报告里的结果。# 基于图像的情绪分析 ## 项目简介 对输入人脸图像进行七类情绪分类使用 ResNet18 迁移学习数据集为 FER2013。 ## 环境要求 - Python 3.8 - PyTorch 1.13 - 见 requirements.txt ## 快速开始 1. 将数据集解压到 data/raw 目录 2. python src/train.py 3. python src/inference.py 4. 结果输出到 docs/ ## 结果 最终验证集准确率70.3%在私有测试集上这个模板看起来朴素但把“复现路径”写清楚了。“结果”一栏的准确率应该来自你最后一次固定种子跑出来的数字不要为了好看写虚高值。老师如果复现不了第一反应是怀疑你的实验造假这比准确率低 2 个点严重得多。所以在 README 里写清楚运行时需要的文件结构如果你的数据来自 FER2013 官方 csv还要注明转换脚本放哪里。4.2 实验报告结构背景、方法、实验、结论四件套实验报告需要跟着课程要求的模板走没有模板时我习惯按以下结构组织。它不是流水账而是用一个问题串起来我为什么要做情绪分析我用了什么方法我证明了什么。报告章节核心内容建议篇幅引言情绪分析的应用场景视频/人机交互/教育约300字相关工作传统 HOGSVM、深度 CNN、迁移学习约500字方法检测分类流水线网络结构预处理训练策略约800字实验数据集说明、评价指标、结果表格、消融对比、混淆矩阵约1000字结论与改进总结达到的效果说明类别混淆和未来方案约300字写“方法”部分时要画一张简单的流程框图输入图像 → 人脸检测 → 裁剪 → 灰度化/缩放 → 情绪分类网络 → 输出标签。能放进报告里的图比一百行文字都有效。如果不会画好看的框图用 PowerPoint 画一个矩形加箭头的图导出 PNG 就可以。“实验”部分要把训练过程中的真实数据放进去。准确率、loss 曲线、混淆矩阵这三样至少要齐全。有同学觉得自己的准确率只有 70% 太低其实老师更看重你能否解释为什么。比如你可以写“在混淆矩阵上sad 和 neutral 最常互混原因是两种表情的嘴部变化不明显”这句话比空泛的“模型还需要改进”有价值得多。4.3 实验记录与对比把“玄学”写成“可控变量”从我的经验看大作业能拉开差距的地方就在这里——能不能把训练过程中那几次“翻车”写成可控的实验变量。比如下面这张表是我在调试时会记录的内容。组别模型数据增强优化器学习率epoch验证准确率备注A小型CNN无Adam1e-32061.4%baselineBResNet18无AdamW1e-32068.9%迁移学习提升明显CResNet18水平翻转仿射AdamW1e-33071.2%增强缓解过拟合DResNet18增强类别权重AdamW1e-33070.6%类别权重小幅波动这张表不是虚构应根据你的实际实验填写。写报告时把每一行“为什么改改了什么效果如何”展开成一段就构成了一节完整的“消融实验”。这对老师的吸引力远大于一次调参尝试的说明说明你真的在控制变量做实验而不是碰运气。要生成这么一张表你训练脚本里最好把每次运行的配置和时间戳记录成 CSV 或 json。最简单的做法是训练结束时把超参数和验证准确率追加到docs/experiment_log.csv之后再整理进报告。这不算额外工作只是把“我记得”改成“数据记得”。4.4 答辩准备老师最常问的三个问题基于图像的情绪分析大作业答辩老师大概率会围绕“选型”和“结果真实性”提问。我整理了三个高频问题提前准备好能省掉很多卡壳。第一“为什么用 ResNet18不试试更深的网络”你回答的核心是预算和收益设备显存有限ResNet18 在 FER2013 这种小规模灰度数据集上已经能有 70% 左右准确率更深的 ResNet34/50 在数据量增加前提下才有优势在几千张训练图上反而容易过拟合。再补一句“我打算在改进方向里加入 ResNet34 对比”效果更好。第二“灰度图为什么能训练出好模型”你可以说人脸表情的主要线索来自纹理和轮廓颜色信息对情绪判断贡献小同时 FER2013 数据集本身就设计成灰度图所以沿用这个设定。如果老师追问三通道预训练模型怎么适配就把替换 conv1 那行代码讲清楚。第三“准确率还能怎么提升”这是一个送分题不要只说“加数据”。你可以提三个方向用 MTCNN 替代 Haar 提升人脸框质量用 Focal Loss 处理类别不平衡加入 SE 注意力模块让模型更关注眼睛和嘴。每一项都能讲出原理说明你思考过如何继续深入。5. 大作业避坑指南情绪分析最容易翻车的五个地方5.1 现象人脸检测框偏移导致情绪全错我见过不少同学提交的代码训练时用的数据是“自己从原图中心裁一块”推理时却用 Haar 检测框去裁两个框的位置不一致导致分类器看到的内容完全不同。比如原图人脸居右中心裁剪裁到了头发和背景情绪自然全错。原因检测框只是定位到人脸但没有经过和训练数据一致的裁剪逻辑。解决方法是把“检测 → 裁剪 → resize”写成同一个函数训练和推理都调用。裁剪时还要在检测框基础上外扩 10% 左右保证额头和下巴不被切掉。检测框的 scaleFactor1.1、minNeighbors5 对 640x480 摄像头图像比较合适框太松就调小 minNeighbors 到 3。5.2 现象loss 一直下降测试集准确率却上不去这个现象几乎每个训练深度学习的人都会遇到。训练 loss 降到 0.2看起来模型已经“背下”了训练集但验证准确率只有 60%。如果再看混淆矩阵几乎所有类别都偏向 happy 和 neutral这就是典型的过拟合加上类别不平衡。原因模型容量大、训练样本少、情绪类别间相似度高三者叠加导致泛化差。解决思路分三层一是增加数据增强用水平翻转、随机裁剪、亮度和对比度抖动二是给 CrossEntropyLoss 加类别权重让少数类错误产生更高损失三是加早停验证准确率连续 5 个 epoch 不提升就停止把最优权重保留下来。记住不要等到 30 个 epoch 全跑完才去看验证曲线那就晚了。5.3 现象别人拿到你的压缩包怎么都跑不起来这是文档说明没做到位也是大作业最常见的“软性丢分”。你的代码在自己的电脑上能跑是因为虚拟环境里装满了历史遗留的库换台机器就容易缺这缺那。最容易翻车的是这几个地方代码里写了绝对路径数据集目录名带中文Python 版本用了 3.11但对方的是 3.7。解决代码里所有路径都用相对路径从项目根目录出发README 里写明 Python 版本和安装命令requirements.txt 只列核心依赖。还要避免使用cv2.imread读中文路径图片它遇到中文路径会静默失败返回 None导致训练数据直接少一半。我习惯在 dataset.py 里加一句assert img is not None没有图片时立刻报错而不是等训练崩到一半才排查。5.4 现象准确率卡在 65% 左右怎么调都上不去如果你已经尝试增大 epoch、调学习率准确率仍停在 65%大概率不是参数问题而是数据集本身和任务定义的问题。FER2013 的不少图像是低分辨率、有遮挡、甚至标注错误的要在这类数据上做到 80%普通 ResNet18 微调很难。解决路径是调整任务不是盲目堆模型。最常见的是把 7 类合并成 6 类比如把 disgust 并入 sad因为这两个类别在数据集中数量少且特征重叠。改标签后准确率往往会涨 2 到 4 个点但注意报告里一定要写“做了标签合并实验”而不是把数据偷偷丢掉。另一个思路是修改模型结构比如在全连接前加一个 SE block让网络更关注眼睛和嘴巴区域。5.5 现象报告里的图表和代码结果对不上最后这个坑很隐蔽但一旦被发现就是诚信问题。有同学训练时输出了 70% 的准确率写报告时又心血来潮改了模型没有重新跑推理直接把旧准确率填进新实验的表格里。或者两次运行没有固定随机种子模型权重不同准确率有自然波动报告里 A 组和 B 组的差异无法复现。解决在 train.py 开头固定随机种子保证每次运行结果可复现。我常用的函数是import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)调用后再开始训练所有随机增强、初始化、数据打乱都变成确定性的。报告里的每一张图表都对应一个训练日志文件评审抽查时你能快速指出这个数字来自哪一行 log。这个习惯让我在多次课设答辩里省掉了“这个结果怎么来的”这种尴尬追问。6. 进阶玩法把大作业做成能演示的实时情绪分析工具如果你的时间允许在交付“源代码文档说明实验报告”之外再加一个摄像头实时演示答辩时的加分效果非常直接。老师看到你从模型训练到工程部署都有了解更容易相信你不只是照着网上代码跑了一遍。实时演示的核心是把训练好的模型加载进来配合 OpenCV 的摄像头读取。一个最小实现长这样import cv2 import torch cap cv2.VideoCapture(0) face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) model.eval() while True: ret, frame cap.read() gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(gray, scaleFactor1.1, minNeighbors5) for (x, y, w, h) in faces: face gray[y:yh, x:xw] face cv2.resize(face, (48, 48)) tensor torch.from_numpy(face).float() / 255.0 tensor (tensor - 0.5) / 0.5 tensor tensor.view(1, 1, 48, 48) with torch.no_grad(): pred model(tensor).argmax(dim1).item() cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(frame, CLASSES[pred], (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(emotion demo, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()注意这里我手动做了一次float() / 255.0再(tensor - 0.5) / 0.5是为了和训练时的Normalize(mean[0.5], std[0.5])保持一致。如果你在训练时用了 ImageNet 的均值方差这里的归一化也要改成对应数值。摄像头画面的光线会一直变我自己的习惯是先对灰度图做一次直方图均衡化cv2.equalizeHist(gray)让模型在面对高噪点和暗光时更稳定。这个技巧不算在作业要求里但写进答辩 PPT 的“优化探索”会显得你很在意真实场景的性能。实时演示还有一个隐含限制Haar Cascade 检测器对模糊和侧脸非常敏感演示时离摄像头 1 米左右、正对镜头效果最好。如果现场翻车也不用慌直接说“这是检测器局限不是分类器问题”然后用提前准备好的测试集图片演示分类效果老师通常能理解。希望这个方向能帮你在人工智能导论大作业里交出一份能讲得清楚、也经得起复现的作品。本文还有配套的精品资源点击获取
返回列表