☰
基于Python的CNN猫表情识别:从数据标注到答辩的完整毕设实战
2026/10/22 13:29:38 网站建设 项目流程

每个来找我聊毕设选型的人,开场白都差不多:想做深度学习方向,最好能用 python 实现,但又不确定什么题目能做到“做完、讲清、拿得出手”。我很少一开始就推荐具体题目,而是先反问一句:你平时有没有拍过猫?养猫的同学做基于 python 的 CNN 卷积神经网络去训练识别猫的表情,光是标注数据这一关就能笑着挺过去;没养过猫的,把各种猫脸萌图翻一遍也会觉得有意思。这个题目看起来像是个趣味项目,实际上它覆盖了深度学习图像分类的完整链路:数据采集、清洗、标注、模型设计、训练调参、评估分析、部署演示。作为计算机毕业设计案例,它的性价比相当高,工程量和学术深度都容易控制。这篇内容就按我这些年带过的类似项目流程,把从选题到答辩每个环节的决策逻辑、实战步骤和踩过的坑完整拆开讲。

1. 选这个题,是算过账的:猫表情识别的毕设性价比分析

1.1 一个题目好不好,先看这四个指标

判断一个毕业设计题目能不能做,我通常会先看四个指标:数据好不好拿、技术栈主不主流、一个人工作量是否可控、最后演示效果够不够直观。

猫表情识别在这四项上几乎是全优。图像类公开数据集里,猫脸相关的资源虽然没有人脸的那么泛滥,但足够支撑一个本科或硕士级别的项目。数据采集还可以自己动手,猫的表情变化比人更外放,放松、警觉、生气、张嘴叫,差异肉眼可见,标注门槛低。技术栈就更不用说了,基于 python 的 CNN 卷积神经网络训练识别猫的表情,用的全是深度学习领域最标准的工具链,PyTorch 或 TensorFlow 都行,这套技能写进简历也完全站得住脚。

工作量层面,这个题目可深可浅。浅的做法是拿现成的预训练模型做迁移学习,训练一个几类的分类器,大概几百行代码就能跑通,作为毕设核心工作量已经够了。深的做法可以自己从零设计 CNN 结构,做数据增强策略对比,加注意力模块,甚至用类激活热图解释模型行为,做消融实验。也就是说,这个题目能精确匹配不同学校对毕设工作量的不同要求,做到“上不封顶,下有余量”。

演示效果这一点常常被低估。答辩现场如果只能给老师看训练曲线和准确率,互动感太弱。猫表情识别的模型一旦和摄像头联动,能够实时显示猫脸框、预测标签和各类别概率,现场效果非常直观,哪怕老师不养猫,也会觉得这个系统“真的能用”。

1.2 它的真正难度:表面是模型,背后是数据与工程

很多人一听图像分类,就觉得核心是调模型,其实这个题目的难点从来不在卷积网络本身,而在三个不那么显眼的地方。

第一个是表情类别的定义。猫没有笑容这个概念,所谓表情本质上是耳朵、眼睛、嘴巴、胡须和头部姿态的组合状态。同一只猫放松时半眯眼睛,另一只猫天生眼睛就细长,算法该怎么区分“放松”和“困倦”?这些边界问题如果不在标注阶段定义清楚,后面模型再强也白搭。

第二个是数据标注的一致性问题。一个人连续标注五百张图,前后标准容易漂移,前面觉得是“生气”的图,后面可能就觉得是“警觉”。标注不稳定,训练出来的模型指标会非常难看,而且你还很难定位是模型的问题还是标注的问题。

第三个是端到端工程闭环。训练好模型只是第一步,毕设要完整,得有数据预处理脚本、训练脚本、评估脚本、可视化脚本,最好还有一个能跑起来的演示界面。很多同学模型训练完觉得万事大吉,结果到了答辩前一周发现摄像头采集的画面和训练时的图片分辨率、光照分布完全不一致,预测结果一塌糊涂,才慌慌张张回来补数据适配。

把这三点想清楚了,这个题目才算是真正选明白了。接下来我从数据这条线开始,讲每一步具体怎么落地。

2. 数据是命根子:猫脸数据集、类别体系与增强策略的落地方案

2.1 数据从哪来:公开集与自建各有打法

做猫表情识别,数据来源一般分两条路:公开数据平台和自建数据集。

公开数据平台可以找到不少猫脸相关的图像数据,但有两个麻烦。一是标注质量参差不齐,有些数据集是目标检测框,没有表情标签;有些带标签但类别体系混乱,比如把“张嘴”和“开心”混在一起;二是版权和使用协议理不清,毕设论文里没法交代清楚数据来源。我的建议是,公开数据只做补充,千万别当唯一来源。

自建数据集的路径更可控。有猫的同学直接手机拍猫,一天拍几十个短视频,按帧抽图就能拿到几百张不同角度的猫脸。没猫的同学可以找朋友家的猫,或者去宠物咖啡馆拍摄。拍摄时尽量覆盖不同光线、不同距离、不同姿态,这些都是模型泛化能力的关键。另外提醒一句,自建数据一定要保留原始视频或大图,不要拍完就直接缩成 224x224,因为后面做清洗、裁剪对齐、增强都要在原始分辨率上操作。

数据量方面,做 4 到 5 个类别,每个类别最少准备 150 到 200 张有效猫脸图,整体 800 到 1000 张是一个比较现实的起点。低于这个量,模型很难学到稳定的表情特征,完全依赖预训练权重也不是个事。如果条件允许,做到每类 300 张以上,效果会有一个明显提升。

这里有一个容易被忽略的“黄金细节”:拍摄时同时记录猫的状态标签。比如在猫舔毛放松时拍到的图标“放松”,在猫看到窗外小鸟警觉时拍到的图标“警觉”。现场记录比事后看照片猜状态可靠得多,这能直接减少标注噪声。

2.2 类别定四类还是五类:表情状态怎么划分才不打架

表情类别不是越多越好,而是要保证类间差异清晰、类内差异可控、标注时能快速决策。我做过几次对比测试之后,比较推荐四类或五类方案。

四类方案可以划分为:放松(眯眼、身体松弛)、警觉(竖耳、瞪眼、瞳孔收缩)、生气(耳朵后压、胡须前伸、可能有低吼姿态)、发声(张嘴叫,可能伴随头部上扬)。五类方案可以在四类基础上加一个“委屈”或“困惑”状态,但说实话,这类表情在数据上的代表性往往不足,容易和警觉类混淆,做的时候要特别小心。

下面是一张我在项目中实际使用的类别定义参考表:

类别核心视觉特征容易混淆的状态标注建议
放松半眯眼、耳朵自然下垂或朝前、身体舒展困倦、部分短毛猫的正常眼型嘴部放松、胡须位置居中
警觉圆眼、竖耳、瞳孔可能收缩、头部向前探惊讶、好奇优先看耳朵角度与瞳孔状态
生气耳朵后压、胡须向前、瞳孔可能扩张恐惧、防御状态结合身体弓起姿态判断
发声嘴巴张开、下颌下压、可能露出牙齿放松打哈欠、开心吐舌尽量选有明显叫声状态的照片
委屈眉毛内侧抬高、眼白明显、头部低垂警觉、困惑数据不足时建议放弃该类

定义好类别之后,强烈建议在正式标注前先做一次“试标”:随机抽 50 张图,标注完隔一天再标一遍,算一下自我一致性。如果同一张图两次标注的类别不一致率超过 5%,说明类别定义还有歧义,先调整定义再继续标注,否则后面所有工作都在一个不稳的地基上。

2.3 清洗、对齐与增强:把几百张图变成能训练的标准输入

原始拍到的照片不能直接丢进卷积网络,中间要过三关:清洗、对齐、增强。

清洗这一步做三件事。第一,删掉模糊严重的图,判断方法很简单,肉眼看着边缘发虚的直接删。第二,删掉猫脸占比太小的图,比如猫蹲在画面角落只占不到十分之一,这种图即使有表情也难以学到特征。第三,删掉多猫图,两只以上猫在同一画面会导致标注对象不明确,除非你额外做目标检测,否则训练阶段建议只用单猫图。

对齐这一步比较关键。卷积网络对输入图片的内容位置有一定敏感性,虽然池化层能提供一些平移鲁棒性,但如果猫脸一会儿在画面左边,一会儿在右边,网络会把大量容量浪费在位置拟合上。标准做法是先用猫脸检测算法定位出猫脸框,然后裁剪并缩放到统一尺寸。我用的尺寸是 224x224,这个尺寸是图像分类领域的事实标准,后续做迁移学习时可以直接对接预训练模型输入要求,不用额外改尺寸。

增强这一步决定了模型泛化能力的上限。基础增强包括水平翻转、小角度旋转(正负 10 到 15 度)、亮度对比度微调、随机缩放与裁剪。需要特别说明的是,猫脸的表情判断对上下方向非常敏感,垂直翻转是绝对不要做的,一只倒过来的猫表情含义会完全改变。此外,颜色增强要克制,因为表情状态和整体色调相关性不大,过度调色反而会让模型学偏。

我还做过一个尝试:用 MixUp 或者 CutMix 这类混合增强技巧增强表情类别间的过渡样本。实测下来,在猫表情这种类间差异本就微妙的任务上,MixUp 会带来一点精度提升,但提升幅度不大,而且会让训练收敛变慢。如果你的项目时间充裕,可以做一组消融对比写进论文;时间紧的话,基础增强已经足够。

3. 模型架构:两条路线,参数都给你算好

3.1 从零搭的小型 CNN:结构设计与参数量估算

如果是计算机毕业设计案例,从零搭建 CNN 往往比直接调现成模型更能体现你对卷积原理的理解。毕竟答辩老师大概率会问:模型结构是你自己设计的吗?这里的“设计”不是凭空创造,而是按卷积网络的经典套路组装并解释清楚理由。

我在项目中用的小型 CNN 结构如下:

网络层输出尺寸关键参数与说明
输入224x224x3统一缩放后的 RGB 猫脸图
Conv+BatchNorm+ReLU224x224x32卷积核 3x3,padding=1,提取低级边缘纹理
MaxPool112x112x322x2 池化,下采样,降低分辨率
Conv+BatchNorm+ReLU112x112x64卷积核 3x3,通道翻倍,提取五官结构
MaxPool56x56x642x2 池化
Conv+BatchNorm+ReLU56x56x128卷积核 3x3,继续抽象特征
MaxPool28x28x1282x2 池化
全局平均池化1x1x128用全局平均池化替代全连接层,大幅减少参数
全连接层 + Dropout64Dropout=0.5,防止过拟合
输出层4 或 5Softmax 类别输出

为什么通道数按 32 到 64 到 128 翻倍?因为越往后的层感受野越大,需要更多的通道数来编码不同类别的语义组合,同时池化不断降低空间分辨率,通道翻倍能保持信息量不衰减。为什么用全局平均池化而不直接 Flatten?因为一张 28x28x128 的特征图如果直接拉平,会得到约 10 万个神经元输入,再接全连接层参数量会膨胀到几千万,全局平均池化把每个通道压缩成一个值,参数量瞬间降到千级别,还能有效缓解过拟合。

参数量可以粗略估算一下。第一个卷积层输入 3 通道输出 32 通道,3x3 卷积核,参数量是 3x32x3x3=864 个,加上 BatchNorm 的均值和方差参数可以忽略不计。第二层是 32x64x3x3=18432 个,第三层 64x128x3x3=73728 个。最后全连接层 128x64=8192 个。总的可训练参数量大概在 10 万这个量级,不到主流大模型的零头。这个规模用家用显卡跑,几百张图几分钟一个 epoch,完全没有压力。

用代码表述,这个模型在 PyTorch 里长这样:

import torch.nn as nn class CatExpressionCNN(nn.Module): def __init__(self, num_classes=4): super().__init__() self.features = nn.Sequential( nn.Conv2d(3, 32, kernel_size=3, padding=1), nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(2), nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.BatchNorm2d(128), nn.ReLU(inplace=True), nn.MaxPool2d(2), ) self.classifier = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(128, 64), nn.ReLU(inplace=True), nn.Dropout(0.5), nn.Linear(64, num_classes), ) def forward(self, x): return self.classifier(self.features(x))

3.2 迁移学习路线:用预训练模型省时间还提精度

另一条路线是迁移学习。拿一个在大型通用图像数据集上预训练过的卷积骨干网络,去掉它原有的分类层,接上自己的分类头,然后做两种训练策略之一:只训练分类头,或者整个网络做微调。

骨干网络的选择建议从 ResNet18 或者 EfficientNet-S 这类轻量级模型入手。别一上来就塞 ResNet50 这种大规模模型,毕设数据量也就是几百到一千张图,太重的模型反而会因为容量过大而严重过拟合。我实测过同样的数据,轻量级预训练模型加上只冻骨干、只训练分类头,准确率可以比自研小型 CNN 高出 5 到 10 个百分点,而且训练时间更短。这对追求指标的同学来说是很划算的方案。

冻结骨干有个重要陷阱:BatchNorm 层的状态。预训练模型的 BatchNorm 层里保存着训练集的均值和方差统计量,在冻住骨干参数的时候,BatchNorm 默认会继续更新这些统计量,但如果你的批次很小、数据分布和预训练数据差得远,统计量会被带偏。处理办法是在训练时把骨干里的 BatchNorm 也一并冻结,或者使用足够大的批次让统计量稳定更新。我在项目里遇到过一次诡异的现象:分类头损失在降,验证准确率却波动很大,排查了半天,最后发现是 BatchNorm 统计量没冻结导致的问题。

换分类头的代码非常简单,以 ResNet18 为例:

import torchvision.models as models backbone = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) for param in backbone.parameters(): param.requires_grad = False in_features = backbone.fc.in_features backbone.fc = nn.Linear(in_features, num_classes) model = backbone

把最后一个全连接层换成本项目的分类头,其余层的参数不参与梯度更新,这样训练起来又快又稳。如果后续数据量上来了,再逐层解冻做微调,比如先用 1e-4 的小学习率全量训练五到十个 epoch,通常还能再涨一点。

3.3 两条路线的对比,以及我为什么这么选

我见过很多同学在自研模型和迁移学习之间反复横跳。这里直接给一个对比:

对比项自研小型 CNN迁移学习 + 预训练骨干
代码量较多少
参数量约 10 万级千万级但大部分冻结
前期精度较低,约 80% 上下较高,通常 85% 以上
训练时间快,CPU 也能跑略慢,但 GPU 下更快收敛
答辩解释性强,结构都是自己算的较弱,需要解释预训练原理
过拟合风险低数据太少时也有风险

我的建议是,毕设想突出工程能力,就两条腿走路:主方案用预训练模型把指标做上去,再用自研小型 CNN 做一组对比实验,说明“在同等数据下,容量更大的预训练模型确实更有效”。这段对比本身就是论文里很好的分析素材,答辩时也能体现你不是只会调包。

如果你就是想省事,直接走迁移学习路线也能顺利毕业,但自研 CNN 这组实验建议至少要留一份,因为它是你理解卷积层、池化层、感受野这些概念的最直接证明。

4. 训练不玄学:损失函数、优化器与过拟合干预的实际调参记录

4.1 损失函数与优化器:分类任务的底盘

图像分类任务损失函数的默认选择是交叉熵。为什么用它而不是均方误差?因为分类任务的输出本质是一个概率分布,交叉熵度量的是预测概率分布和真实标签分布之间的距离。均方误差对概率输出不敏感,小偏差和大偏差的惩罚尺度差不多,梯度更新不够高效,训练收敛也慢。

在 PyTorch 里,交叉熵损失直接调用nn.CrossEntropyLoss(),它内部已经包含 Softmax 操作,所以模型的输出层不应该再手动加 Softmax,否则会做两次归一化,损失函数数值范围会变得很奇怪。我在代码里踩过一次这个坑,损失函数数值看起来很小,但模型预测全部挤到同一个类,原因就是输出层多加了一次 Softmax。正确做法是训练时直接用 logits 算交叉熵,推理时再对 logits 取 Softmax 得到各项概率。

优化器我用的是 Adam,初始学习率 1e-3。Adam 的优势在于对学习率的敏感度低于 SGD,不用太精细地手动调整就能稳定收敛。如果追求更优结果,可以换成带动量的 SGD 配合余弦退火学习率,但那是锦上添花的事,不建议在毕设里为了调这些消耗太多时间。

4.2 批次大小、学习率调度与随机种子:稳定训练质量的细节

批次大小的选择要考虑显存和 BatchNorm 两方面的约束。我常用的配置是 16 或 32,在分类头全连接层和 BatchNorm 同时存在时,批次不要低于 8,否则 BatchNorm 的统计量会在每个 step 之间剧烈抖动。显存不够时,优先减小输入尺寸而不是缩小批次。

学习率调度我推荐先用ReduceLROnPlateau:验证集指标连续 3 到 5 个 epoch 不涨,就把学习率乘以 0.5 或 0.3。这个策略的意义在于,训练初期需要大步探索,接近最优区域时则需要小步精调。固定学习率会让模型在最优值附近震荡,最终精度上不去。

随机种子是一个容易被忽略的稳定性保障。深度学习框架里有很多随机过程:数据加载器打乱顺序、Dropout 随机失活、权重的随机初始化、甚至部分算子在不同平台上的随机行为。如果不固定随机种子,同样的代码每次训练结果都不同,论文里写的可复现性就无从谈起。我通常在数据划分和训练两个入口都设置种子:

import random import numpy as np import torch def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)

注意,即使固定了种子,在 CUDA 环境下仍然可能出现微小波动,这是正常的。所以论文里应该写“随机种子固定后可复现实验结果”,而不是“完全一致复现”。

4.3 过拟合信号与干预:训练曲线会说话

训练过程中我最常看的就是两个曲线:训练集损失和验证集准确率。这两条线能讲出的信息比任何高级可视化都多。

如果训练损失持续下降,验证损失先降后升,这就是教科书级的过拟合信号。说明模型容量已经把训练样本背下来了,但学到的东西不能泛化。干预手段顺序一般是:加数据增强、加大 Dropout 比例、加权重衰减、缩小模型容量。注意顺序,别一上来就砍模型,数据增强通常是最有效且几乎不损失表达能力的方案。

如果验证集准确率在训练前期就剧烈震荡,上下差五六个百分点,大概率是学习率太大了。这时候把初始学习率降到 3e-4 左右,震荡幅度会明显收敛。如果训练损失降到很低但验证准确率就是上不去,可能是类别定义存在歧义,回去检查标注一致性,而不是继续调参。

我记录过一组实际数字:一个五类猫表情任务,自研 CNN 从零训练大约 30 个 epoch 后训练准确率达到 98%,验证准确率停在 82% 附近。加上更强的数据增强和 Dropout 之后,验证准确率提升到 85% 左右。再把骨干换成 ResNet18 冻结微调,验证准确率直接跳到 90%。这个递进过程非常有说服力,写在论文里就是一条清晰的改进曲线。

4.4 一套实际可行的训练主循环代码

训练主循环不需要写得多花哨,关键在于把模型切换、保存最优、早停这几件最基本的逻辑做对。下面是一套我在项目里实际使用的训练框架,注释标明了每一个关键动作的意图:

best_val_acc = 0.0 patience_counter = 0 max_patience = 5 for epoch in range(1, max_epochs + 1): model.train() running_loss = 0.0 for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * images.size(0) model.eval() val_acc, _ = evaluate(model, val_loader, device) scheduler.step(val_acc) print(f"Epoch {epoch:02d}, Loss: {running_loss / len(train_loader.dataset):.4f}, ValAcc: {val_acc:.4f}") if val_acc > best_val_acc: best_val_acc = val_acc torch.save(model.state_dict(), "best_model.pth") patience_counter = 0 else: patience_counter += 1 if patience_counter >= max_patience: print("Early stop triggered.") break

model.train()和model.eval()之间的切换一定要养成肌肉记忆。train()模式下 Dropout 在随机置零,BatchNorm 在更新统计量;eval()模式下 Dropout 关闭,BatchNorm 使用缓存的统计量。如果推理时忘了切eval(),模型输出会不稳定,有时两次预测同一个输入得到不同结果。

5. 光有准确率不够:混淆矩阵、错例复盘和可演示的完整流程

5.1 指标怎么选:别只用准确率

很多毕设论文只写一个总体准确率,这其实很可惜。猫表情识别是典型的多分类问题,不同类别的样本量天然不均衡,比如放松状态的照片特别多,生气状态的照片特别少。这种时候总体准确率容易被多数类主导,哪怕模型把生气类全部预测错,准确率可能还是很高,你根本发现不了问题。

正确的评估方式至少要看三样东西:每个类别的精确率、召回率、F1 值,以及一张混淆矩阵热力图。精确率回答“模型预测为某类的结果里有多少是对的”,召回率回答“某类的真实样本里有多少被找出来了”,F1 是两者的调和平均。如果某一类的精确率特别低,说明模型经常把别的东西误判成这一类,通常是类间边界定义有问题;如果召回率特别低,说明这一类样本量不够,或者特征太不突出。

混淆矩阵则在五类场景下能直接指认模型最常犯的错:哪两类之间互相误判,一眼就能看出来。我在项目里遇到的高频混淆是“生气”和“警觉”,因为两者的耳朵角度和瞳孔状态有时候在二维照片上区分度不够;另一个是“发声”和“放松”,因为它们都可能出现嘴巴张开的瞬间。这些发现反过来指导类别定义和数据补充。

5.2 错例长什么样:模型被什么迷惑了

统计指标之后,一定要亲手翻一遍错例。操作方法是写一个脚本,把验证集里预测错误的图片连同真实标签、预测标签、置信度一起输出到文件夹,然后用人眼逐张查看。这一步会让你意外地发现很多隐藏问题。

常见的错例可以归成几类。第一类是数据本身标注错误,我抽查过一百张错例,发现大概有四到五张是标注阶段自己标错了,这类错例和模型无关。第二类是猫脸检测框漂移,比如框没框住耳朵,只框了面部中心,导致模型缺少关键的耳朵姿态信息。第三类是光照和角度造成的视觉歧义,猫从下往上仰视时,耳朵后压的姿态会非常像生气,但它可能只是在观察高处的东西。第四类是品种差异,扁脸猫和长毛猫的放松表情在人看来都有明显差异,模型学到的特征也会因此受影响。

错例分析的结果直接变成两个行动:要么补数据,针对性补充容易混淆的角度的照片;要么调整后处理策略,比如在演示系统里设置置信度阈值,低于某一概率时提示“无法判断”,而不是硬给一个可能错误的结果。后者在毕业设计答辩演示时很讨巧,显得你对模型边界有清晰认知。

5.3 把模型变成能演示的功能:摄像头实时识别

训练完成的模型如果只停留在脚本里,说服力差很多。我建议用 Tkinter 或 PyQt 做一个极简界面,用 OpenCV 读取摄像头或本地视频,实时检测猫脸并预测表情,界面显示当前的置信度条。这个演示环节在答辩现场几乎是一次“加分表演”。

实现逻辑不复杂:读帧、检测猫脸区域、把区域缩放到 224x224、做同样的归一化预处理、喂进模型、取 Softmax 结果、画框和标签。需要特别注意的是,训练时的预处理和推理时的预处理必须完全一致。比如训练时用了 RGB 通道均值归一化,推理时也要用同一套均值;训练时做了随机裁剪,推理时就必须改成中心裁剪。我在演示阶段经常看到同学训练指标很好,一接摄像头就乱预测,排查到最后都是预处理不一致导致的。

实时检测的帧率不用太苛刻,每秒 5 到 10 帧足够,因为猫不会一秒变三次脸。如果觉得画面太卡,可以每三帧检测一次,中间帧直接沿用上一帧的结果,体感会流畅很多。这一步完成之后,整个项目就不是“一个训练脚本”,而是“一个可交互的猫表情识别系统”,从工程完整性上来说已经是完整的闭环了。

6. 从代码到答辩:一个能打的毕设项目是怎么组织出来的

6.1 工程目录:像正经项目而不是实验脚本

到后期经常有人拿着一个孤零零的训练脚本来找我,说代码写了但感觉不像个项目。工程组织能力在毕设里其实是被忽视的隐性分数。一个像样的项目目录至少应该长这样:

cat_expression/ ├── data/ │ ├── train/ │ ├── val/ │ └── test/ ├── scripts/ │ ├── prepare_data.py │ ├── train.py │ ├── evaluate.py │ └── demo.py ├── models/ │ └── resnet_cnn.py ├── utils/ │ ├── dataset.py │ ├── transforms.py │ └── visualize.py ├── checkpoints/ └── README.md

数据、脚本、模型、工具函数、模型权重分目录放,每个脚本只做一件事。README 里写清楚环境依赖、数据准备方法、训练命令、评估命令、演示命令。这不仅是给评委看的,也是给自己看的——两个星期后你再来跑这个项目,能不能照着 README 十分钟内复现结果,是检验工程质量的硬标准。

写代码的时候还有一个习惯值得培养:把关键超参数集中放在一个配置文件或者config.py里,不要散落在各个脚本中。比如图片尺寸、批次大小、初始学习率、类别数量、随机种子,这些参数集中管理之后,做实验记录会轻松很多,打印日志时也更容易回溯。

6.2 论文结构、消融实验与答辩问题准备

论文结构上,除了通常的背景、相关工作、方法、实验、总结之外,我比较推荐在方法部分重点写两件事:一是类别体系的定义过程和标注规范,二是模型结构的选型理由。这两部分是很多毕业论文缺的,写清楚之后会让工作在评审眼里显得严谨。

实验部分除了最终模型在主测试集上的数字,最好有一张消融实验表。我提供一套可以直接借鉴的三行式消融设计:

设置数据增强迁移学习测试准确率
基线仅缩放无78% 左右
+增强全量增强无82% 左右
+迁移全量增强ResNet18 冻结90% 左右

每一行的提升都能讲出一个原因,这比只给一个总准确率有说服力得多。消融实验也方便答辩老师追问细节,而你有每一条对应的数据支撑。

答辩问题也不是完全不可预测。这个题目高频问题基本集中在:为什么用卷积神经网络而不是别的模型?模型参数量大概多少?数据不平衡怎么处理?猫脸检测框不准怎么办?换一个品种的猫会不会掉点?这些问题在本文前几个部分都有对应的解释逻辑。我习惯让学生准备一张“模型能力边界”的页,明确说明当前方案在单猫、正面角度、可见光条件下表现最好,多猫、遮挡、极端角度会下降,这样反而显得诚实且专业。

6.3 后续还能怎么扩展

如果时间和精力有富余,这个题目还有不少扩展方向能让项目上一个台阶。第一个方向是在模型层面引入注意力机制,比如挤压激励模块甚至 Transformer 结构,观察对“生气”和“警觉”这类细微差异类别的改善。第二个方向是做多任务学习,同时输出品种、表情、头部姿态,多任务共享骨干网络往往能互相增强。第三个方向是模型轻量化,把训练好的模型做剪枝或量化,压缩到能在轻量级推理框架上运行,这样可以往移动端部署方向写。

不过扩展方向不能贪多,毕设的核心还是把主链路做扎实。先把一个能跑、可复现、能演示的完整系统做出来,再根据时间和精力挑选一个扩展点做深入探索,已经是很高质量的工作了。

我做这类项目做到现在的体会是:猫表情识别这个题目真正的价值,不在于让模型认识一只猫的情绪,而在于它强迫你把一个完整项目从数据到演示的所有环节都走一遍。这个过程练出来的数据意识、调参直觉和工程组织能力,才是比“准确率 90%”更值钱的东西。如果你正卡在这个选题上,别急着把时间花在更复杂的网络结构上,先回去看看你的数据、标签和预处理,很多问题都会迎刃而解。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询