ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于CNN的宠物行为识别实战:从数据准备到模型部署全流程解析

基于CNN的宠物行为识别实战:从数据准备到模型部署全流程解析

看到这个题目的第一眼,多数同学都会冒出同一个念头:这不就是一个图像分类任务吗?用Python搭个CNN卷积神经网络,把宠物图片丢进去,让模型输出“坐着”“趴着”“站立”之类的行为标签,看着好像挺顺理成章。但真正动手做过一次之后,你会发现这个题目比想象中复杂得多——问题不在于CNN本身怎么写,而在于类别怎么定义、数据怎么收集、训练后怎么在真实环境里稳定跑起来。

这篇内容就是围绕一个基于python-CNN卷积神经网络的宠物行为训练识别项目,把从需求分析、数据准备、模型选型、代码实现、训练调参、评估部署到答辩演示的完整链路讲清楚。适合正在做毕业设计、课程设计,或者单纯想练手一个图像分类项目的同学。我会把自己踩过的坑、走过弯路的地方都标出来,尽量让后来的人少花两周冤枉时间。

1. 先想清楚:这个题目到底在解决一个什么问题

1.1 “宠物行为训练识别”拆解后的真实需求

很多人在开题时只写了一句“识别宠物行为”,但导师问一句“具体识别哪些行为、识别出来干什么用”,很多人就卡住了。这个项目里的“训练识别”其实包含两层意思:第一层是识别宠物当前的行为状态,比如坐、趴、走、跑、进食、舔毛;第二层是服务于宠物行为训练场景,比如主人发出口令“坐下”,系统通过摄像头判断宠物是否真的完成了坐下的动作,从而辅助记录训练效果。

所以不要把它只当作一个“看图分类”的Demo。如果做成论文,你的核心价值可以是:设计并实现一个面向行为训练的实时识别系统,能够对视频流中的宠物进行行为分类,并对识别结果给出置信度提示,辅助人工判断训练是否到位。这样一来,选题的立意就从“我会用CNN”变成了“我用CNN解决了一个具体的场景问题”,后者在答辩时明显更有说服力。

1.2 为什么这题难在数据而不是模型

CNN的图像分类在技术层面已经非常成熟,ResNet、MobileNet、EfficientNet这些网络随手就能调起来。但宠物行为识别有一个非常麻烦的属性:同一只狗,同一个动作,换个角度、换个光照、换个背景,看起来可能完全不一样;“坐”和“趴”在部分视角下连人都容易看走眼。宠物本身又不会乖乖配合拍摄,姿态变化、毛发遮挡、运动模糊都会给识别带来干扰。

我见过有人直接用公开的猫狗品种分类数据集来做行为分类,效果自然一塌糊涂,因为品种分类数据集里的标签是“贵宾”“金毛”“暹罗猫”,根本没有行为标注。所以做这个题目的第一个重活就是:你得自己准备一套行为图像数据集,或者找一个包含行为标签的开放数据集作为补充。这块工作往往占掉整个项目一半以上的时间。

1.3 这个项目适合哪些场景延伸

如果只是毕设交差,可以把范围收窄,比如只做“识别宠物是坐着、趴着还是站立”,三类就够写出一套完整流程。如果想让项目有亮点,可以往真实训练场景延伸:用连续视频帧做行为状态判定,只有当模型连续若干帧都输出“坐”且置信度超过阈值时,才判定训练动作成功,这个思路比单纯单帧分类要高级不少,也更容易写出创新点。

我自己的建议是:一开始不要贪多。先做一个稳定的静态图像分类系统,把数据、训练、评估跑通,再考虑加摄像头实时推理、连续帧判定、置信度过滤这些进阶功能。否则一上来就想着端到端全做好,最后大概率什么都做不好。

2. 数据集怎么来、怎么标:决定项目上限的一步

2.1 自采数据为主,公开数据集为辅

适合宠物行为识别的公开数据集数量不多,而且类别体系五花八门,有的平台把“狗坐着”单独作为一个标签,有的平台则是“地面上的狗”“椅子上的狗”这种描述性标签,直接拿来做分类很不顺手。我更推荐的做法是:自采数据做主体,公开数据集做补充。

自采数据的操作并不复杂。用手机或普通摄像头拍摄宠物视频,每个行为类别拍若干段,每段持续30秒以上,然后从视频中抽帧得到图片。比如拍一段狗坐着的视频,每隔10帧取一张图,几分钟视频就能抽出一两百张有效图片。

这里有一个非常关键的原则:同一段视频里相邻帧画面几乎一样,如果全部塞进训练集,模型很可能“记住”了这些几乎重复的图片,而不是学到真正的特征。抽帧时必须隔N帧取一次,并且取完帧后要按时间顺序分配数据,不能让同一段视频的帧同时出现在训练集和验证集里。否则验证集准确率会虚高,真实部署时立刻打回原形。

2.2 类别设计:既要体现工作量,又不能互相混淆

行为类别的设计直接影响后续所有工作。我自己见过很多翻车案例,核心原因就是类别设得模棱两可。比如单独设一个“蹲”和一个“坐”,但实际拍摄时,狗的半蹲和坐几乎无法区分,最后模型在两个类别之间反复横跳。

建议第一版保留六个类别左右,既能体现数据准备的工作量,又不会因为类别过细导致标注和训练难度失控:

类别定义典型画面标注要点
坐臀部着地、前腿直立侧面和正面都常见下腹贴地但前胸抬起
趴四肢及腹部贴地俯视和侧面明显后腿贴近地面,身体压低
站立四肢着地且身体直立全身清晰可见四肢均承重,腹部离地
行走/奔跑四肢交替移动画面有动感前腿和后腿距离变化
进食/饮水头部靠近食盆或水碗通常有食盆等物体口鼻贴近碗沿
理毛/舔舐舌头舔舐身体或毛发头部弯向身体身体产生弯曲动作

真实标注时建议每个类别控制在800到1200张之间。数量太少了模型学不到稳定特征,数量太多了手工标注工作量吃不消,而且数据收集本身就是无穷无尽的。

2.3 标注规范和清洗流程

数据标注不需要用什么复杂工具,最简单的做法就是按类别建文件夹,手动把图片拖进去。目录结构可以这样安排:

pet_dataset/ ├── train/ │ ├── sit/ (800张) │ ├── down/ (820张) │ ├── stand/ (750张) │ ├── walk/ (800张) │ ├── eat/ (780张) │ └── groom/ (790张) ├── val/ (每个类别约150张) └── test/ (每个类别约200张)

清洗这一步容易被忽略,但其实非常重要。抽帧得到的图片里会有相当一部分是模糊的、过曝的、宠物只占画面很小一块的、或者是画面里同时出现多个宠物的,这些图片都应该在标注阶段直接删掉。如果背景信息过于杂乱,模型会偷偷学背景特征而不是宠物姿态。我见过一个离谱的案例,模型最终是靠背景颜色分类的——所有“趴”的图片都在沙发上拍的,所有“坐”的图片都在地面上拍的,模型根本没学到动作本身。

为了避免这种问题,拍摄时要有意识地变化环境:客厅、卧室、室外各拍一部分,不同光照条件、不同拍摄角度都要覆盖。这一点做得好,比后面调什么学习率都管用。

2.4 数据增强:用小数据量训练出能用的模型

数据增强是图像分类项目的标配,尤其在自采数据量有限的情况下。PyTorch中常用的增强策略包括随机裁剪、水平翻转、旋转、颜色抖动。这个配置对大多数宠物行为识别场景都是比较稳妥的起点:

from torchvision import transforms train_transforms = transforms.Compose([ transforms.RandomResizedCrop(224, scale=(0.8, 1.0)), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])

注意,这里的归一化均值方差默认采用ImageNet数据集的统计值。如果你使用了ImageNet预训练模型做迁移学习,就一定要沿用这个归一化参数,因为预训练模型都是在这些参数下训练的。如果你从零训练一个网络,那可以自己重新统计,但一般情况不建议从零训练。

验证集和测试集不要用随机裁剪或翻转这种随机增强,只需要缩放到224×224或者先缩放再中心裁剪就行。否则验证结果每次运行都不一样,很难判断模型好坏。

3. CNN选型与模型结构决策:为什么我最终选择了这个方案

3.1 从LeNet到ResNet:几种常见选择的对比

CNN结构的发展脉络先pass一下。LeNet和AlexNet属于早期结构,用于宠物行为识别已经不太合适,层数太浅,特征抽象能力有限。VGG系列虽然层次清楚,但参数量大,一个VGG16动辄几百MB,对毕设项目和后续部署都不友好。ResNet通过残差连接解决了深层网络退化问题,是目前性价比最高的选择之一。MobileNet系列则在速度和体积上有优势,压缩后比较小,适合移动端。

做个对比表格:

网络结构参数量单张推理耗时(CPU)精度水平适合场景
ResNet18约11M中等较高毕设主力,训练快,结构直观
ResNet50约25M中高高有GPU训练,追求精度
MobileNetV3约4M低中高需要部署到低算力设备
VGG16约138M很高中不推荐,参数多收益低

如果只是做一个毕设,我不建议一上来就选最大最好的网络。ResNet18配合ImageNet预训练权重,往往在中小数据集上就能达到不错的精度,而且训练速度快,硬件要求低。等基础流程跑通之后,如果你的机器条件允许,再把ResNet18换成ResNet50做对比实验,这样就有两组实验数据可以写进论文。

3.2 为什么迁移学习是毕设项目的最优解

宠物行为识别这个任务,靠自采的几千张图片从零训练一个深度网络,效果通常不太行。深层网络动辄几百万个参数,几千张训练图片不足以充分拟合,最后很容易过拟合,训练集准确率很高、验证集一塌糊涂。

迁移学习的思路是:先用海量图像数据训练好的模型,把前面那些卷积层当做一个已经学会“看轮廓、看纹理、看结构”的特征提取器,我们只把后面的分类层换成自己的六类行为分类器,再用自己的行为图片对全部参数做微调。这样相当于站在一个成熟的特征基础上做适配,几千张图片就能训练出可用的模型,所需训练时间也从几天缩减到几十分钟。

用生活中的类比来说,这相当于你请了一个已经认识上万种物体轮廓的实习生,他本来不会区分狗是坐着还是趴着,但只需在你这儿训练半天,学会“臀部是否落地”“腹部是否贴地”这类关键判断即可,而不需要从头教他什么是狗、什么是猫。

3.3 模型结构详解:以ResNet18为例

ResNet18包含四个残差阶段,每个阶段由若干BasicBlock组成,后面接全局平均池化和全连接层。在PyTorch里加载预训练ResNet18并替换分类层的代码如下:

import torch.nn as nn from torchvision import models model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) num_features = model.fc.in_features model.fc = nn.Sequential( nn.Dropout(0.3), nn.Linear(num_features, 6) )

需要解释两句话。第一,model.fc原来是输出1000类的全连接层,因为ImageNet有1000个类别,现在要改成输出6类;第二,前面加了Dropout层,用来在微调时减少过拟合概率。如果原始数据量足够大,不用Dropout也没有太大问题,但加了通常更稳。

这里还要提醒一个细节:旧版本的PyTorch常写models.resnet18(pretrained=True),这个写法在新版本中已经不建议使用,会弹出警告并可能在后续版本移除。新版本推荐使用weights参数,训练代码一定按官方推荐方式写,避免答辩现场被问蒙。

4. 核心代码实现:从数据管道到训练循环

4.1 环境配置清单

环境配置遵循够用原则。推荐Python 3.9以上,PyTorch 2.x,如果你的电脑有NVIDIA显卡就装对应CUDA版本的PyTorch,没有显卡也能在CPU上训练,只是训练时间长一些。对于几千张图片的微调,CPU也能接受,但ResNet18的单轮训练可能要花掉十几分钟甚至更久,体验确实差很多。

建议的核心版本组合:

组件版本说明
Python3.10兼容性好,第三方库支持全面
PyTorch2.1+新API,训练效率提升
torchvision0.16+配套模型库
OpenCV4.x摄像头读取、抽帧处理
ONNX Runtime最新稳定版后续部署加速可选

安装完成之后,先用一行代码确认CUDA是否可用:

import torch print(torch.cuda.is_available())

如果是True,接着做一个小测试:创建随机张量做一次矩阵乘法,确认GPU能正常计算。很多环境装好了库但驱动不匹配,就会出现“PyTorch能看到显卡,但一训练就崩”的尴尬情况。

4.2 数据加载与预处理

数据文件放好之后,用PyTorch的ImageFolder方式自动读取按文件夹分类的数据集,配合DataLoader实现批量加载。

from torchvision import datasets train_dataset = datasets.ImageFolder(root="pet_dataset/train", transform=train_transforms) val_dataset = datasets.ImageFolder(root="pet_dataset/val", transform=val_transforms) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=4)

ImageFolder会根据子文件夹名称自动设定类别映射,生成class_to_idx字典和classes列表。这一点在做推理时特别重要,因为我们打印出来的标签是0、1、2、3、4、5这些数字,要还原成“sit、down”这些可读名称,就必须从train_dataset.classes里取。很多人训练的时候没觉得有什么问题,到了部署阶段突然发现预测输出的数字不知道对应哪个类别,然后才回头找这个映射关系,提前写注释标注清楚,后面能省不少事。

4.3 模型定义与训练脚本

训练脚本是项目的核心。我把去掉细节的主干代码写一下:

import torch import torch.nn as nn from torch.optim import AdamW criterion = nn.CrossEntropyLoss() optimizer = AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4) def train_one_epoch(model, loader, criterion, optimizer): model.train() total_loss = 0.0 total_correct = 0 total_samples = 0 for images, labels in loader: images, labels = images.to(device), labels.to(device) outputs = model(images) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() * images.size(0) total_correct += (outputs.argmax(dim=1) == labels).sum().item() total_samples += images.size(0) avg_loss = total_loss / total_samples acc = total_correct / total_samples return avg_loss, acc def evaluate(model, loader, criterion): model.eval() total_loss = 0.0 total_correct = 0 total_samples = 0 with torch.no_grad(): for images, labels in loader: images, labels = images.to(device), labels.to(device) outputs = model(images) loss = criterion(outputs, labels) total_loss += loss.item() * images.size(0) total_correct += (outputs.argmax(dim=1) == labels).sum().item() total_samples += images.size(0) avg_loss = total_loss / total_samples acc = total_correct / total_samples return avg_loss, acc

为什么用CrossEntropyLoss和AdamW?交叉熵损失是图像多分类任务的默认选择,它把网络输出转成概率分布并与真实标签做比较;AdamW是在Adam基础上加入权重衰减修正,训练微调阶段比普通SGD更容易上手,收敛速度快,不需要手动做太多学习率调整。如果你想追求更稳的结果,可以用SGD加动量,但需要更精细地调学习率,毕设场景不必要先啃这个。

4.4 早停与模型保存策略

训练不是轮数越多越好。更合理的做法是记录验证集准确率,当连续多个epoch验证集没有提升时,提前终止训练并保存验证集上表现最好的那一版模型。

best_val_acc = 0.0 patience = 5 wait = 0 for epoch in range(30): train_loss, train_acc = train_one_epoch(model, train_loader, criterion, optimizer) val_loss, val_acc = evaluate(model, val_loader, criterion) print(f"Epoch {epoch+1} | Train Acc: {train_acc:.4f} | Val Acc: {val_acc:.4f}") if val_acc > best_val_acc: best_val_acc = val_acc torch.save(model.state_dict(), "best_model.pth") wait = 0 else: wait += 1 if wait >= patience: print("Early stopping triggered") break

这个策略叫Early Stopping,翻译过来就是“训练到不想进步了就停”。它最大的好处是节省时间,同时拿到验证集上表现最优的模型。训练完成后生成一个best_model.pth文件,这就是后面做推理、做部署的核心产物。

5. 训练过程中最容易翻车的几个时刻

5.1 损失值不下降:先排查基础问题

我见过太多人一上来训练,发现Loss不降或者降得很慢,就急着改学习率、换优化器。但其实大部分情况下要排查的是一些基础问题。先检查数据加载是否正常,打印一个batch图片看看,图片是不是真的加载了、标签和图片是否对得上。别笑,真的有人把数据集路径写反,导致模型从train目录里做验证,验证集准确率异常高,最后一到真实测试就崩。

第二个常见问题是类别映射错乱。用ImageFolder读取时,文件顺序有可能是字母排序,如果你的文件夹叫sit、down、stand,那类别映射就是把down当成类别0而不是sit,训练阶段自己没察觉,部署阶段打印结果才发现模型预测的“站立”其实是“趴着”,因为文件排序和你脑中的顺序不一致。

第三个常见问题是归一化参数写错。ImageNet归一化均值是[0.485, 0.456, 0.406]、标准差是[0.229, 0.224, 0.225],如果乱写一组数,模型看到的数据分布与预训练时不一致,表现自然灾难般地差。

5.2 过拟合:准确率99%的假象

过拟合是自采数据集上最容易出现的问题。症状是训练集准确率一路飙到99%,验证集却卡在70%左右上不去,甚至下降。

缓解措施有几板斧。第一,增强数据,回到第二节,检查增强策略是否生效;第二,加大Dropout概率,从0.3调到0.5试试;第三,降低训练轮数,一个只有几千张图片的数据集,训练轮数一般20个epoch以内就差不多了,超过反而过拟合;第四,用早停机制,验证集不提升就停下来。

还有更隐蔽的一种过拟合,我把它叫做“跨视频泄漏”。如果你从同一段视频里抽帧做训练集,又从这段视频另外的位置抽帧做验证集,看起来时间上不重叠,但因为背景、光线、甚至宠物项圈完全一样,模型可能学的是场景特征。解决办法是拍视频时就预留不同场景,或者用不同视频分别划分训练和验证集合。这个点如果能在论文里写出来,会显得你确实深入理解了这个项目。

5.3 学习率策略:迁移学习要“小步走”

迁移学习的初始学习率不能太大。我之前在一个类似项目里把学习率设成1e-3训练ResNet18,结果Loss剧烈震荡,模型一直在原地打转。原因在于预训练模型已经处于一个较好的局部最优附近,学习率太大相当于一步迈出了峡谷,跌进了一个更差的位置。

常用的做法是:初始学习率设为1e-4,如果觉得收敛太慢就调到3e-4,再大就要小心;配合ReduceLROnPlateau调度器,当验证集Loss连续若干轮不下降时,把学习率降低一半,让模型在损失曲面中做更精细的搜索。

5.4 类别不均衡问题

六类行为图片数量不会完全一致,比如“行走”拍起来难度高,可能比“站立”少了两三百张。类别不均衡会造成模型在数量多的类别上表现更好,而少样本类别的召回率偏低。

第一眼可以做的事情是过采样:对数量较少的类别,在训练时重复采样,比如故意让每个epoch的采样比例一致。更简单的方式是在数据增强时对少样本类别施加更高强度的随机变换,相当于用同样的真实图片生成更多“看起来不太一样”的变体。

评估时要特别关注哪些类别混淆最多。最常见的是“坐”和“趴”互相混淆,其次是“行走”和“站立”。如果混淆太严重,回到数据层面,检查标注是否准确,或者考虑合并这些容易混淆的类别。

6. 评估与推理部署:从“跑通”到“能演示”

6.1 别只盯准确率:用混淆矩阵找短板

很多毕设论文只写一句“测试集准确率达到92%”,然后就没了。这其实是很大的浪费。准确率只能告诉你整体水平,但不能告诉你哪些类别容易搞混。用混淆矩阵能直观看到模型的薄弱环节。

可以用sklearn里的confusion_matrix生成矩阵,然后配合matplotlib画出热度图。示例代码:

from sklearn.metrics import confusion_matrix, classification_report import numpy as np all_labels = [] all_preds = [] with torch.no_grad(): for images, labels in test_loader: images, labels = images.to(device), labels.to(device) outputs = model(images) preds = outputs.argmax(dim=1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.cpu().numpy()) cm = confusion_matrix(all_labels, all_preds) print(classification_report(all_labels, all_preds, target_names=train_dataset.classes))

classification_report会输出每一类的精确率(Precision)、召回率(Recall)和F1分数。答辩时,用这一组指标比单一准确率有说服力得多。尤其当某几类准确率低时,你还能给出合理解释和进一步优化方向,评委立刻知道你是真做了实验的人。

6.2 摄像头实时推理:把模型搬到现场

毕设演示环节最怕的就是摄像头现场出问题。代码实现本身不难,难的是稳定复现。基于OpenCV的摄像头推理框架如下:

import cv2 import torch model.load_state_dict(torch.load("best_model.pth", map_location="cpu")) model.eval() model.to("cpu") cap = cv2.VideoCapture(0) if not cap.isOpened(): raise RuntimeError("摄像头打开失败") class_names = train_dataset.classes while True: ret, frame = cap.read() if not ret: break rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img = val_transforms(rgb).unsqueeze(0) with torch.no_grad(): logits = model(img) prob = torch.softmax(logits, dim=1) score, pred = torch.max(prob, dim=1) label = class_names[pred.item()] text = f"{label} ({score.item():.2f})" cv2.putText(frame, text, (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow("Pet Behavior Recognition", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

这里有几处细节要提醒。第一,摄像头读图默认是BGR通道顺序,送入模型前必须转换成RGB,否则颜色通道错乱,模型表现会明显下降。第二,val_transforms必须是验证集的预处理流程,不能包含随机翻转等增强操作。第三,推理时务必写model.eval(),它的作用是把Dropout和BatchNorm切到推理模式,不然同一个输入,不同次预测,结果可能不一样。

6.3 小步优化:从CPU推理到ONNX导出

演示时如果电脑没有NVIDIA显卡,PyTorch里的CPU推理速度会比较慢,一帧可能要几百毫秒,看起来一卡一卡的,体验很差。一个很有效的优化方式是把模型导出为ONNX格式,配合ONNX Runtime推理,CPU加速效果明显。

import torch.onnx dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, "pet_behavior.onnx", opset_version=13, input_names=["input"], output_names=["output"] )

之后在推理端用ONNX Runtime加载模型,代码改动量并不大。另外,在PyTorch推理时也可以设torch.set_grad_enabled(False)来减少内存开销,整体速度会有一定提升。

如果在树莓派、Jetson Nano这些低算力设备上部署,可以用MobileNetV3替换ResNet18,再结合INT8量化,把模型体积压缩到几MB,这样即便在没有GPU的迷你设备上也能达到接近实时的效果。这些可以作为论文的“部署优化”章节素材。

7. 给做这个毕设的同学的几条实在建议

7.1 时间规划:别把数据集和训练都挤到最后

结合前文的工作量,我整理一个六类行为数据的毕设时间表,按周分配:

时间段主要工作产出物
第1周阅读文献,明确行为类别,写开题报告类别定义表、需求分析
第2-3周拍摄视频、抽帧、清洗、标注训练集/验证集/测试集
第4周搭建环境,做数据增强,跑通基线模型初始模型
第5-6周训练调参、加入早停、评估最优模型、混淆矩阵
第7-8周摄像头实时推理,UI界面,导出ONNX可演示系统
第9-10周补充对比实验,写论文初稿论文初版
第11-12周反复测试、撰写答辩PPT答辩材料

这份表格只是一个参考,如果你的动作类别更少,或者数据收集更快,可以节省出两周用于扩展功能。实际上大多数同学最大的时间风险在数据标注上,几百上千张图片逐张标注是很枯燥的,稍不留神一周就消磨掉了。

7.2 论文写作和答辩演示的避坑点

论文里“系统设计”章节至少要包含三层内容:数据制备、CNN模型设计、实时识别流程。绘制系统架构图时,一定要体现摄像头输入到预处理到模型推理到结果展示的完整链路,而不是只画一张网络结构图。网络结构图的细节也要写清楚:输入尺寸224×224、归一化方式、主干网络层数、全连接替换策略、损失函数、优化器。

答辩现场有一个非常实际的建议:提前录制一段系统识别演示视频作为备用。现场摄像头光线、距离、宠物配合度都不可控,万一宠物不配合或者摄像头驱动出问题,可以直接播放视频,保证演示环节不中断。PPT里就把混淆矩阵、各类别精确率和召回率放上去,然后用你自己训练中遇到的问题当“项目特色”讲出来,比如“坐与趴的混淆问题”和“跨视频数据泄漏问题”,这在评委眼里远比背诵网络结构有价值。

7.3 可以继续扩展的方向

做完基础版的六类识别后,如果想给项目增加含金量,可以考虑三个方向。第一个是把单帧分类升级为视频行为判定,用多帧结果投票或者加入一个简单的时序模型,这样能做到行为状态的连续性判断,比如“从站立到趴下”的转换。第二个是先用目标检测网络找出宠物所在的区域,再对裁剪区域做行为分类,这样宠物不在画面中央时也能识别。第三个是用关键点检测技术替代纯分类,检测宠物四肢关节位置,用关节角度判断行为,这个方向对数据标注要求高,但也是宠物姿态分析的前沿方向之一。

这些扩展不一定都要做完,选一个方向做一两组实验,写进论文的“改进与展望”部分,就足够让项目从“课程作业水平”提升到“有研究味道”了。

我自己测试这种项目最深的一点体会是:模型结构只是冰山一角,真正决定成败的是前面的数据整理、类别定义、训练策略和部署细节。把这几块摸透了,即使你只用ResNet18,也不比那些堆叠复杂模型的人逊色。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进