☰
基于CNN的动物疲劳识别:从图像分类到深度学习全流程实战
2026/10/17 9:53:09 网站建设 项目流程

做毕设选题的时候,很多同学一听到“动物疲劳识别”第一反应是“这东西怎么做”,第二反应是“是不是太偏了”。实际上这个题目放到计算机视觉的框架里,就是一个典型的图像二分类问题:给一张动物图像,判断它处于疲劳状态还是正常状态。没有目标检测,没有语义分割,没有姿态估计,门槛非常友好,但它又足够完整,能让你把数据采集、标注、模型训练、评估、部署这一整套深度学习流程全部走一遍。这篇文章就以我实际带过的一个模拟项目X为例,把基于CNN卷积网络的动物疲劳识别从零到落地讲清楚,适合正在准备毕业设计、打算入门图像分类的读者直接参考。

我先把话放在前面:这个项目的核心价值不在于“识别准确率能有多高”,而在于你能不能把每一个环节都处理好。数据定义是否合理、标注是否一致、训练过程是否可复现、评估指标是否选对了,这些才是答辩和高分的关键。下面我按整个项目推进的时间线来拆解,每一部分都会给出我实际操作中的具体参数、踩过的坑和修正经验。

1. 选题分析与整体技术路线

1.1 这个题目为什么会成为一个好的毕设选题

很多同学选毕设题目时会陷入两个极端:要么选一个非常宏大但根本做不完的方向,比如“基于深度学习的智能养殖系统”,听起来很唬人,但涉及检测、跟踪、识别、告警好几块,一个人短期根本搞不定;要么选一个过于简单的东西,比如用现成的模型跑一下公开数据集,写个报告就完事,答辩时很容易被问住。

“动物疲劳识别”恰好卡在中间位置。从技术难度看,它不需要你做复杂的目标定位,只要图像里动物主体相对明确,CNN就能直接端到端学习疲劳特征。从算法深度看,它涉及到图像分类任务的典型难点——类间差异小、类内差异大、样本不均衡,这些都是可以写进论文的实质内容。从应用价值看,疲劳状态监测在畜牧业、宠物健康监护、实验动物状态评估等领域都有真实需求,不是凭空捏造的场景。

我当时给A同学建议这个题目的时候,特别强调了一点:不要试图一步到位做一个“全自动畜牧疲劳监测系统”,那是产品级目标。毕设的核心是把“基于CNN的动物疲劳识别”这个算法链路做扎实,最后能给出一个可复现的模型、一组可靠的评估数据、一个简单的推理调用方式,就非常完整了。

这个定位很重要,因为很多人的毕设最后烂尾,不是技术能力不够,而是目标定得太大,时间被各种边角料吞掉了。

1.2 疲劳状态怎么定义,数据从哪来

这个题目的第一个坑,就是对“疲劳”的定义。疲劳不是一只动物站在那里写着“我很累”,它是一种需要人为约定的视觉状态。如果不把定义先想清楚,后面所有的标注都会乱套。

在实际操作里,我们采用的是“行为学特征 + 影像特征”结合的定义方式。针对常见的哺乳动物(以某实验农场的一批动物为样本),我们约定以下可辨识特征作为疲劳状态的判断依据:

  • 眼睛闭合程度:疲劳状态下,眼睛闭合面积明显超过正常状态,或者出现持续性半闭;
  • 耳朵姿态:健康状态下耳朵经常转动、竖起,疲劳状态下耳朵低垂,活动频率降低;
  • 头部角度:疲劳的动物头部经常低垂,少见主动环顾四周的动作;
  • 反应速度:这个特征在单张静态图像里无法直接体现,所以我们只作为辅助条件,不作为主要标注依据。

你可以看到,这个定义里面“眼睛闭合程度”“耳朵姿态”“头部角度”都是可以在单张图像上观察到的,这就保证了人工标注的可操作性。疲劳状态是二分类标签,如果后续你想做成三分类(正常、轻度疲劳、重度疲劳),可以在此基础之上扩展,但毕设阶段建议先做二分类。

数据来源方面,我们用了两条腿走路:一部分来自公开的动物行为图像集,另一部分是我们自己在模拟场景下拍摄的。公开数据的好处是量大、覆盖场景广,坏处是标注标准不一定和我们的定义一致;自采数据的好处是标注可控,坏处是需要花时间和精力。对于毕设来说,我建议公开数据占比大概七成、自采数据三成,这样既能保证数据量,又能保证评价时有自己可控的验证集。需要注意的是,公开数据里如果有非常不清晰的图像,宁可不要,也不要让它污染训练集。

1.3 整体技术路线

整个项目的技术路线可以归纳为五步:数据采集与清洗、疲劳标签标注、图像预处理与增强、CNN模型训练与调优、模型评估与推理封装。不要跳过任何一步,尤其是数据清洗和标注一致性检查,很多人为了赶进度在这一块偷工减料,后期训练时各种奇怪问题都会冒出来。

模型方面,我们采用的是经典的卷积神经网络结构。从零搭建了一个小规模的CNN,同时也做了基于预训练残差网络的迁移学习实验,最后对比发现两种方案在不同数据规模下的表现差异。对于有GPU环境的同学,我建议两条路线都跑一下,答辩时这是一个很好的工作量体现;如果设备限制比较大,可以从零搭建的小网络开始,效果也完全够用。

我把整个流程的时间分配建议写在下面表格里,你可以根据自己的周期调整:

阶段时间占比说明
数据采集与标注30%决定模型上限,最容易被低估
预处理与增强15%清洗、统一尺寸、增强策略
模型搭建与训练30%结构设计和调参迭代
评估与分析15%指标、混淆矩阵、误判案例
模块封装与文档10%推理脚本、README、答辩材料

这个时间分配不是我随便写的。我见过太多同学在模型训练上死磕,却忽略了数据标注的质量,最后训练出来的模型在测试集上准确率很高,一拿到自己拍的照片上就崩。原因往往不是模型不行,而是训练数据和真实场景差异太大,或者标注标准不统一导致模型学到的特征混乱。

2. 数据集构建与预处理

2.1 数据采集的实操细节

数据采集听起来简单,不就是拍照片吗?但在实际操作中,有几个细节直接决定了数据的可用性。

首先是拍摄角度的一致性。在自采数据时,我强烈建议固定摄像头的安装高度和角度,尽量避免出现俯拍、仰拍混杂的情况。CNN虽然有一定的尺度和平移鲁棒性,但角度差异过大时,模型很容易学到“角度特征”而不是“疲劳特征”。我们在模拟场景中把摄像头固定在侧上方约45度角的位置,保证动物主体在画面中央且完整。

其次是光线条件。疲劳状态判断非常依赖眼睛和耳朵的细节,光线过暗时这些特征会被噪声淹没。自采数据的时候,尽量在自然光充足的条件下拍摄,不要使用会导致偏色的彩色光源。如果你之后要对公开数据进行清洗,优先选择那些主体清晰、光照均匀的图像,模糊的、严重过曝的、主体被遮挡的图像直接舍弃。

第三是样本的去重问题。很多人从视频里抽帧,如果直接连续抽帧,相邻帧之间高度相似,这些近重复样本会被训练集和验证集同时包含,导致验证准确率虚高。我们当时抽帧策略是每隔若干帧抽一帧,并且抽完以后计算相邻帧的像素相似度,相似度超过阈值的保留一张,其余的丢弃。这个操作虽然简单,但能有效避免数据泄漏,在答辩时也是可以讲的细节。

最后要检查的是类别平衡性。疲劳状态在自然场景里本来就是少数情况,所以采集到的正常样本往往比疲劳样本多很多。我们的初始数据集里正常和疲劳的比例大概是3.5比1,这个比例如果不处理,模型会倾向于把所有样本都预测为正常,因为这样准确率也能到70%以上。后面我会讲具体的处理办法,但至少你要先心里有数:你的数据不平衡程度有多严重,各是多少张图。

2.2 标注标准的制定与一致性检查

标注是项目中最容易被轻视的环节,也是影响最终结果最大的环节。一张图像到底算疲劳还是正常,不同人看可能结果不同,所以必须要把标准提前固化下来。

我们当时做了一套标注规范文档,里面写明了各种边界情况的判定规则。比如:眼睛半闭但耳朵竖直,怎么算?答案是只要眼睛闭合面积超过整个眼球面积的一半,就倾向判定为疲劳,耳朵姿态作为辅助修正。头部低垂但眼睛完全睁开,怎么算?答案是以眼睛状态为主,判定为正常。这套规则看起来非常死板,但它保证了不同标注者之间的一致性。

实际操作中,我们让三个人独立对同一批图像进行标注,最后取多数票作为最终标签。三个人标签不一致的图像单独挑出来讨论,讨论后仍无法达成一致的,直接弃用。你可能会觉得三个人标注太奢侈,但对于毕设来说,哪怕只有你自己一个人标注,也建议隔一段时间后重标一次,然后对比两次标注的一致性。我们当时第二遍重标的时候发现,有接近5%的图像两次标注结果不一致,这些图像基本集中在模糊和遮挡比较严重的区域。把它们剔除之后,模型的训练过程明显稳定了很多。

另外一个我强烈建议的操作是:在标注完成之后,按照类别分别随机抽取一部分图像,人工快速浏览一遍,确认是否有标错的异常值。这个检查看起来没有技术含量,但能阻止脏数据进入训练流程。很多同学把时间花在调学习率上,却不愿意花半小时做数据检查,这完全搞反了优先级。

2.3 数据增强怎么做才不会引入问题

数据增强是图像分类任务里绕不开的环节。它的本质是对原始图像做一系列随机变换,生成更多样的训练样本,从而提升模型的泛化能力。对于动物疲劳识别这个场景,我们用到的增强操作包括:

  • 水平翻转:这个操作对于动物图像来说是安全的,因为左右翻转不会改变疲劳语义;
  • 随机裁剪:在原始图像上随机裁剪一部分后缩放到固定尺寸,可以模拟不同的取景范围;
  • 亮度与对比度扰动:适当模拟光线条件变化;
  • 轻微旋转:在正负15度范围内随机旋转,模拟拍摄角度的小幅波动。

这里有一个很重要的原则:增强操作不能改变疲劳语义。比如垂直翻转就不能用,因为动物如果倒过来,特征分布完全没有意义了。还比如过度的随机裁剪,如果裁掉了动物主体,模型学到的就是背景特征,这也会出问题。所以我们裁剪的范围通常控制在原始图的0.7到1.0之间,并且要保证裁剪后图像里动物主体仍然可见。

增强策略还要注意训练集和验证集的区别。验证集和测试集一定不要做任何数据增强,最多做一个尺寸归一化和归一化处理,否则你评估的就是增强后的数据性能,而不是模型本身的能力。这是一个很基础但容易被忽略的细节。

预处理方面,统一的图像尺寸我们定为224乘224像素,这也是主流图像分类模型的标准输入大小。像素值归一化到0到1区间,方便模型训练时梯度稳定。如果你用的是预训练模型,建议直接用它在ImageNet上统计的均值和标准差做标准化,而不是自己重新统计,这样迁移效果更稳。

3. CNN模型设计与核心参数选择

3.1 从零搭建还是用预训练模型

这是每个做图像分类的人都会面临的选择题。我给出的建议是:如果你手里只有几千张图像,优先使用预训练模型做迁移学习;如果数据集到了几万张的规模,从零搭建的CNN也能有不错的表现。

为什么这么说?因为预训练模型在大型通用图像数据集上学到过丰富的底层特征,比如边缘、纹理、形状。这些特征对动物图像同样有效,你只需要让模型在动物疲劳数据上微调高层特征即可。从零搭建的CNN需要从随机初始化开始学习所有特征,在数据量不足时容易欠拟合或过拟合。

不过从毕设角度出发,我还是建议把两种方案都跑一遍,做一个对比实验。这个对比本身就是工作量,也是论文里可以写的一章内容。我们的实验中,预训练残差网络在验证集上的准确率比从零搭建的小CNN高了大约4到5个百分点,尤其是在疲劳类别上的召回率提升更明显。

如果你设备资源有限,也不需要担心。预训练模型可以用CPU训练,只是慢一些。很多同学以为深度学习必须要有好显卡,其实对于二分类任务、小数据集来说,CPU也完全可以跑,只不过一次训练可能要几个小时。我后面给出的训练策略里会提到,如果速度太慢,可以减少训练轮次、降低输入分辨率,这些都是可接受的折中方案。

3.2 具体网络结构示例

如果你想从零搭建一个轻量的CNN,我给你一个我实测过非常稳的结构方案。输入是224乘224的RGB图像,经过下面这些层:

卷积层1:输出通道32,卷积核大小3乘3,填充1,激活函数ReLU,后面接2乘2最大池化; 卷积层2:输出通道64,卷积核大小3乘3,填充1,激活函数ReLU,后面接2乘2最大池化; 卷积层3:输出通道128,卷积核大小3乘3,填充1,激活函数ReLU,后面接2乘2最大池化; 展平后接全连接层256维,加ReLU和Dropout;最后接一个2节点的全连接分类层,输出对应正常和疲劳两个类别的逻辑回归值。

这个结构的整体参数量在百万量级,对毕设来说完全够用。如果你用PyTorch实现,核心代码大概长这样:

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

注意这里全连接层的输入维度是根据前面特征图尺寸算出来的。224输入经过三次2倍池化之后变成28乘28,再乘上卷积通道数128,就是128乘28乘28。很多同学在复现时最容易在这里报错,所以一开始就把计算过程理清楚。

如果你选择预训练模型路线,可以把模型最后一层全连接替换成输出维度为2的新全连接层,其他部分沿用预训练权重。这些模型在深度学习框架里都有直接加载的方式,不需要自己重新实现。训练时冻结前几层参数、只微调后面几层的方式,在数据量比较小的时候效果更稳。

3.3 损失函数、优化器与超参数设定

二分类任务的损失函数首选交叉熵损失。PyTorch里的CrossEntropyLoss已经结合了Softmax,不需要在网络最后一层再手动加激活函数。如果类别不平衡,可以给损失函数传入类别权重,让少数类样本的错误产生更大的梯度。我们当时的正常样本数量大约是疲劳样本的3.5倍,所以在CrossEntropyLoss里给疲劳类设置的权重是3.5,给正常类的权重是1.0。这个简单的操作让疲劳类别的召回率明显提升。

优化器方面,我推荐使用Adam,初始学习率设置为0.001。Adam自带自适应学习率调整,对于第一次做深度学习项目的同学来说,使用门槛比SGD低很多,不需要手动调整动量等参数。如果用的是预训练模型迁移学习,学习率可以降到0.0001,因为模型已经在一个比较好的初始点上,学习率太大会破坏已经学好的特征。

批次大小我设置为32。这个数值在大部分显卡甚至CPU上都能跑得动,同时梯度更新也比较稳定。不要为了追求速度把批次设得过大,对于这种小数据集,批次64以下效果差异不大,但一旦内存溢出就很麻烦。

训练轮次我们先设定为30到50轮,配合早停机制使用。所谓早停,就是每一轮训练后在验证集上计算准确率,如果连续若干轮验证准确率不再提升,就提前终止训练,防止过拟合。这个机制在实际训练中非常有用,我们在第35轮左右观察到了明显的验证损失上升趋势,如果不早停,继续训练到50轮,模型在测试集上的表现反而会下降。

如果你想完全复现我的实验条件,还需要设置随机种子。PyTorch和Python的随机数生成器、NumPy的随机数生成器都要设置同一个种子。这样不同人运行同一个脚本能得到一模一样的结果,这在毕业论文里是一个可复现性加分项。代码很简单,就是固定seed为某个整数,然后设置环境变量和各个库的种子,这里不展开。

4. 训练实操与避坑记录

4.1 数据划分与训练策略

数据划分方式会直接影响你对模型效果的判断。我们当时把数据集按7比2比1划分为训练集、验证集、测试集。很多同学只划分训练集和测试集,没有单独的验证集,然后在测试集上调参数,这是非常大的错误。调参过程本身已经利用了测试集的信息,最后得到的准确率是偏乐观的,不能真实反映模型泛化能力。分出一个验证集专门用来调参和早停,测试集只在最终评估时使用一次,结果才可信。

划分的时候还要注意按个体划分,而不是简单随机划分。什么意思呢?如果同一个动物的多张图像同时出现在训练集和测试集里,模型可能记住了这个动物的特征,而不是学到通用的疲劳特征,测试集准确率会虚高。我们当时按动物个体先分好组,确保同一个体的图像只出现在一个集合里。这个细节乍一看不重要,但答辩时如果评委问“你的模型是不是真的泛化到了新动物”,你就能给出一个站得住的答案。

训练过程中,我习惯每一轮都打印训练损失、训练准确率、验证损失、验证准确率四组数据。不要只看准确率,损失下降曲线更能反映模型是否收敛。很多情况下准确率看起来没变化,但损失还在缓慢下降,说明模型仍在学习;反过来,准确率很高但验证损失开始上升,说明已经在过拟合了。

如果资源允许,每次训练结束以后保存最优模型权重而不是最后一轮的权重。因为最后一轮可能已经过拟合或者开始波动,最优权重通常是验证准确率最高的那个时刻的权重。我在训练脚本里加入了一个简单的判断:当验证准确率超过历史最佳值时,保存当前模型参数。这个策略简单但非常有效。

4.2 训练曲线怎么看

训练曲线是诊断模型状态最重要的工具。我一般会在训练结束后把损失曲线和准确率曲线画在一张图上,直观地观察模型行为。

常见的情况有几种。第一种是训练损失下降,验证损失也在下降,这是最健康的状态,说明模型在学习且没有过拟合。第二种是训练损失下降,验证损失先降后升,说明模型开始过拟合了。这时候可以回调早停、增加Dropout、增加数据增强或者减少模型参数。第三种是训练损失和验证损失都几乎不降,这说明模型容量不足或者学习率设置不合理,需要增加网络深度、宽度或者调大学习率。

第四种情况比较隐蔽:训练准确率很高但验证准确率一直在某个较低水平徘徊。这通常是数据划分出了问题,比如训练集和验证集分布不一致,或者验证集数据量太小、噪声太大。我遇到过类似的情况,后来检查发现验证集里有几张图像存在标注错误,把它们剔除之后验证准确率正常了。所以看到曲线异常,先怀疑数据和标注,再去动模型。

我强烈建议你养成记录每次实验的参数和结果的习惯。我当时用了一个简单的表格,记录学习率、模型结构、是否用预训练、数据增强方式、验证准确率等关键信息。这样做的好处是调参不再靠感觉,每次修改都有据可查。很多同学调参没过一会儿就忘了自己试过什么,后期总结实验部分的时候才知道有多痛苦。

4.3 常见问题与排查方法

训练过程中我遇到的典型问题,整理成表格给你参考:

症状可能原因排查方向
损失一直不下降学习率过大或过小尝试0.01、0.001、0.0001梯度
训练准确率正常,验证准确率低过拟合检查是否缺验证集、增强策略、Dropout
正常类别准确率高,疲劳类别很低类别不平衡给损失函数加类别权重,或对少数类过采样
换一批图像后效果崩掉训练数据和实际场景差异大增加自采数据,统一拍摄条件
预测结果总偏向某一个类别阈值或类别权重问题调整决策阈值,或重新设计损失权重

还有一个容易被忽视的问题:图像的EXIF信息里的方向标记。手机拍摄的照片有时会带有旋转标记,直接读图时一些库不会自动处理方向,导致图像被旋转了90度。训练和测试时旋转方向不一致,模型自然表现不稳定。解决办法是在数据预处理阶段统一将所有图像转换成RGB并校正方向,输出为标准的三通道图像。

推理阶段也常遇到一个问题:输入图像的尺寸和训练时不一致。如果你训练时统一缩放到224乘224,那么推理时也必须走同样的预处理流程,不能直接拿一张1920乘1080的原图丢给模型。我的做法是把预处理逻辑写成一个独立的函数,训练和推理共用同一个函数,避免两边出现差异。这些都是很小的细节,但往往就是这些细节决定了最终成绩。

5. 模型评估与结果分析

5.1 评价指标怎么选

二分类任务的评价指标不只有准确率。在动物疲劳识别场景下,准确率甚至不是最重要的指标。设想一下,如果目的是筛选出疲劳状态的动物,我们更关心的是“把疲劳动物漏掉”的概率有多低,也就是疲劳类别的召回率。反过来,如果资源有限、不可能对所有标记疲劳的个体都查验,那么精确率也很重要。

所以我建议你至少报告四个指标:准确率、精确率、召回率、F1值,并且分别计算正常类别和疲劳类别各自的数值,而不是只给一个总的准确率。混淆矩阵也要画出来,它能直观地展示模型在哪一类上犯错更多。

我们在测试集上的最终结果显示,预训练模型的整体准确率约为93%,但把混淆矩阵拆开看,正常类的召回率是96%,疲劳类的召回率只有88%。这说明疲劳类被漏检的概率仍然偏高。针对这个情况,我们通过调整分类阈值,将决策边界向疲劳类别偏移,在疲劳类召回率提升到92%的同时,正常类召回率小幅下降到93%,整体F1值更均衡了。这个调整操作在论文里可以写得很有说服力,因为你已经从“模型训练”进入了“模型决策”层面的优化。

5.2 测试结果实测记录

这里给出我们一组实际的测试记录,数据来自一个模拟场景下的动物图像集,类别是二分类。需要注意这组数据仅供你理解评估流程,不同数据分布下结果一定会有差异,你的任务是把评估方法做对,而不是套用某一个准确率数值。

在测试集共400张图像上,正常类200张,疲劳类200张(这里为了展示效果,我们做了类别均衡处理,实际中不需要强行均衡测试集,保持原始分布更真实)。

指标数值
整体准确率92.5%
正常类精确率94.1%
正常类召回率96.0%
疲劳类精确率89.2%
疲劳类召回率88.0%
宏平均F10.915

可以看到疲劳类精确率明显低于正常类。这说明模型把一部分正常图像误判成了疲劳。我逐个查看误判图像后发现,最常见的情况是图像里动物正在闭眼休息或者低头吃食,单从视觉特征上看确实和疲劳比较接近。这种情况下模型犯错其实情有可原,因为只凭单张静态图像,人类也不能做到百分之百准确。这个观察让我意识到,如果后续想要进一步提升效果,引入视频时序信息会是一个合理方向。当然毕设阶段做到这一步已经很完整了,时序模型可以作为展望章节的内容来写。

5.3 误判案例拆解

误判案例分析是论文里很出彩的部分,因为它能体现你对问题本质的理解。我举一个我们遇到的典型例子:一张动物的侧面照,眼睛睁得很大,按标注规则应该判为正常,但模型输出了疲劳类别。我仔细对比了同类图像发现,模型学到的疲劳特征里可能包含“头部低垂+耳朵下垂”的组合,而这张图像里动物的耳朵恰好是向后压的,物理位置上和疲劳样本比较像,因此模型发生了混淆。

这类问题很难单纯靠调参解决,更可行的路径是增加类似硬样本的训练数据,或者在预处理阶段把耳朵区域单独裁剪出来作为辅助输入。当然,这些都是毕设之后可以继续做的方向,不需要在一开始的版本里实现。

与之相反的另一类误判是:图像里动物眼睛明显半闭,但模型判成了正常。这类误判更危险,因为它是漏检。我分析后发现,这些图像里动物处于侧后方角度,眼睛区域被毛发的阴影覆盖,视觉特征非常弱。说明模型对眼睛局部区域的敏感性还不够。解决思路有两个,一是用注意力机制让模型更关注眼睛和耳朵区域,二是使用目标检测先定位头部区域再做分类。这两种方法都会显著增加项目的复杂度,所以我当时没有在毕设主流程中加入,而是留在了改进方案里。

6. 模型部署与后续扩展

6.1 模型导出与推理封装

毕设如果只停留在训练脚本和准确率报告里,总觉得少了点东西。我建议至少做一个简单的推理脚本,让老师或者同学能直接拿一张新图像,传入模型,输出分类结果和置信度。这个动作不复杂,但会让整个项目的完整度提升一个档次。

我用PyTorch为例说一下导出流程。训练结束后把模型状态字典加载回模型结构,然后调用eval模式。需要特别注意的是,一定要在eval模式下推理,否则BN层和Dropout层的行为会和训练时不一致。我在没加eval模式的时候,推理结果忽好忽坏,查了半天才发现是这地方的小问题。

如果想让推理速度更快、脱离深度学习框架环境运行,可以把模型导出为ONNX格式,然后使用ONNX Runtime加载。ONNX是一个通用的模型交换格式,很多平台都能直接运行。部署到边缘设备的时候也可以转成更轻量的格式,但毕设阶段做到ONNX这一步已经完全够用。一个最简单的推理流程大概是:读取图像,统一缩放和归一化,输入模型得到两个类别的得分,再经过Softmax转成概率,取概率较大的类别作为输出,同时打印出置信度。

我还建议在推理脚本里加入一个边界情况判断:如果输入图像是灰度图,先转换成三通道RGB;如果图像里没有动物主体,直接提示用户重新输入,不要硬跑模型输出一个没有意义的结果。这些交互细节看似不起眼,但对于一个需要演示的项目来说非常重要,因为现场演示出问题的概率其实很高,提前加好防御逻辑能帮你避免尴尬。

6.2 后续还能怎么扩展

毕设答完以后,如果你对这个方向还有兴趣,可以沿着几个方向继续深入。最直接的方向是增加类别,把二分类扩展为“正常、轻度疲劳、重度疲劳”三分类,配合更细的数据标注规范。第二个方向是引入时序信息,用视频片段而不是单张图像来判断疲劳状态,通过帧序列捕捉动物的动态行为特征,比如反应速度、活动频率,这些在静态图像里完全无法体现。第三个方向是把区域感知和分类结合,先检测动物头部、眼睛区域,然后对这个局部区域做疲劳判断,理论上能降低背景干扰。

还有一个我比较看好的方向是迁移到不同动物种类上。你可以在一个动物种类上训练好模型,然后通过少量数据和微调把它适配到另一个种类,这样可以大幅降低人工标注成本。这个方向在真实应用场景中价值很高,也适合作为后续研究内容写进论文展望。

做这个项目的整个过程里,我个人最大的体会是:模型的代码实现反而是整个流程里最简单的一环,真正花时间的、决定成败的,永远是数据定义、数据处理和评估设计。很多同学一上来就想用最高级的网络结构、最流行的训练技巧,结果忽略了最基本的数据质量检查,最后模型泛化能力一塌糊涂。如果你准备做这个题目,我真心建议你把一半以上的时间花在数据上,先把数据和评估体系做扎实,再谈模型结构,你会发现问题少很多。

最后分享一个小技巧:把所有实验的超参数、结果、踩坑记录都写在一个单独的文档里,包括失败了的东西也记下来。答辩的时候评委问“你有没有尝试过其他方案”,你就能自然地讲出来“试过,效果不好,原因是什么”。这种回答展示的是真实的研究过程,远远比只讲成功的部分更有说服力。希望这篇内容能帮你在毕设路上少走几个弯路,也祝你最后能交出一份自己满意的项目。

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

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

立即咨询