1. 题目怎么拆:监考需求先翻译成检测任务
1.1 监考到底在“监”什么
我见过不少同学选题目的时候很迷茫,看到“基于Yolo3深度学习的教学辅助系统的开发(学生考试监考系统)”这种题,第一反应是:这就是把摄像头画面丢给深度学习,检测到作弊就报警呗。但真做起来就会发现,监考这件事远没有想象中那么简单。
先想想监考老师坐在考场里,眼睛到底在盯着什么:考生有没有离开座位、有没有低头翻书或者看手机、有没有交头接耳、有没有陌生人进入考场、答题卡和试卷有没有异常摆放。把这些行为逐条列出来,再翻译成计算机视觉任务,就会发现本质上就两大类:一类是“目标检测”,也就是把画面里的人、手机、书本、试卷检测出来;另一类是“行为规则判断”,也就是根据检测框的位置、数量、持续时间,去推断考生正在做什么。我当年做这个题目时,最受益的一点就是先把这个翻译过程写在需求文档里,而不是急着去下载模型、跑demo。需求清楚了,后面所有的数据标注、后处理规则、告警逻辑才有依据。
这就能解释为什么毕设题目要强调“教学辅助系统”而不是单纯的“智能监考”。因为监考只是一个核心业务场景,系统的本质是辅助老师做教学管理。除了考试监考,它还可以用在随堂测试、自习课巡查、线上考试的后台监控,甚至用来统计课堂出勤。把题目定位成“教学辅助”,论文的创新点和系统功能就有了延展空间。
1.2 为什么偏偏是YOLOv3
既然要做目标检测,市面上能选的算法太多了。早期有人用Faster R-CNN,也有人用SSD,再往后有YOLOv4、YOLOv5,甚至现在还有YOLOv8、YOLOv9。为什么这个题目选了YOLOv3,而且我建议你也优先考虑YOLOv3?原因有三个层面。
第一是原理可讲性。毕业设计和纯科研不一样,论文里必须把原理写清楚,答辩的时候老师真会问“你讲讲你的网络结构”。YOLOv3的网络结构相对简洁:先用Darknet-53作为骨干网络提取特征,然后在三个不同尺度的特征图上做预测,每个尺度对应不同大小的目标。这个结构用一个下午就能讲明白,而YOLOv5的代码工程化程度很高,很多细节被封装在yaml文件里,反而不好解释。第二是硬件门槛。YOLOv3在入门级显卡上就能跑到实时,训练也不需要很大的显存,这对本科生毕设来说是实实在在的友好。第三是改进空间大。正因为YOLOv3是“上一代”算法,你可以顺理成章地做改进实验,比如换骨干网络、加注意力机制、用更合理的锚框策略,这些改进写进论文里都是加分项。
说白了,选题的意义不在于用最潮的技术,而在于用最合适的技术讲清楚一个完整的故事。
2. 数据准备:检测系统的下限由数据决定
2.1 类别设计不能贪多
很多同学一上来就想检测“作弊”这个抽象概念,这是思路上的硬伤。目标检测只能检测“物体”或者“人”,它本身不理解“作弊”。所以你要做的,是把作弊行为拆成可检测的物体和可计算的状态,然后让模型去识别这些物体,让规则引擎去判断状态。
我最终设计的类别只有三个:person(考生或监考员)、phone(手机)、book(纸质资料和书本)。为什么不检测“水杯”“纸条”或者“手表”?因为类别越多,数据标注量越大,类间混淆越严重。手机和书本是作弊场景里最典型的出现物,把这两个检测好,配合后面的行为规则,已经能覆盖绝大多数异常情况。如果你想做得更细,可以把person再拆成“坐着的人”和“站立的人”,但这会显著增加标注负担,建议作为改进方向在论文里提出,而不是在基础版里硬做。
数据来源分两部分。一部分来自公开的通用检测数据集,里面有人物和手机类别的图片,这部分图片场景杂,可以提升模型泛化能力。另一部分是核心,需要自己在普通教室里模拟考场环境拍摄。我当时的做法是请同班的几个同学当志愿者,在空教室里模拟考试场景,拍摄不同座位、不同光线、不同角度下的画面,再从中截帧生成数据集。这里有一个容易被忽略的合规问题:拍摄他人画面用于实验,必须提前征得对方同意,而且论文里不能出现可识别的正脸。换句话说,发布论文和系统演示视频时,人脸要打码,或者只选用志愿者授权的样例图。
2.2 标注、增强与划分的实操细节
标注工具我用的是LabelImg,因为它可以直接标注并保存为VOC格式。YOLOv3训练时用的是txt格式的标注文件,每行是“类别id x_center y_center width height”,这些值都是归一化到0到1之间的。所以标注完成后,还要写一个转换脚本,把VOC的xml文件转成YOLO的txt,同时顺便统计一下每个类别有多少实例。我当时统计出来,person有八千多个实例,phone只有一千多,book有两千多,类别严重不均衡,这也直接导致了后面phone的检测精度明显偏低。
数据增强不是可选步骤,而是必须做的。除了常规的水平翻转、随机亮度对比度调整,我强烈建议加上Mosaic增强——它的原理是把四张图片随机裁剪后拼接成一张,相当于一次训练看四张图,既增加了样本多样性,又让模型更适应“画面里同时出现多个目标”的考场场景。考场的光线变化很大,上午的教室和傍晚的教室完全两个色调,所以增强里要特别注意光照扰动这一项,否则模型在逆光环境下的漏检会非常严重。
数据划分上有一个考场特有的坑:如果你直接随机划分训练集和验证集,同一段视频的相邻帧会同时出现在两边,导致验证集指标虚高。正确做法是先把视频按场景分组,以场景为单位划分,保证验证集的画面和训练集不完全来自同一个视频片段。
3. YOLOv3训练调参:从预训练权重到锚框优化
3.1 模型原理中的几个关键“为什么”
YOLOv3的原理用大白话讲,就是把一张输入图片划分成S×S的网格,每个网格负责预测固定数量的候选框,每个候选框输出目标的中心坐标、宽高、置信度以及类别概率。它用了三个不同大小的网格尺度,分别是13×13、26×26和52×52,分别对应大、中、小三种目标。这个多尺度设计就是YOLOv3相比早期版本最大的改进,也是它能在考场这种“远距离后排小人脸”场景下还能勉强可用的关键。
每个尺度上预设了3个锚框,一共9个锚框。锚框相当于“候选框的尺寸模板”。YOLOv3论文给出的默认锚框是基于公开数据集聚类出来的,但考场场景有自己的特殊性:摄像头位置固定,画面中人的大小范围相对集中,手机又是特别小的目标。所以训练前我做了锚框重聚类,用K-means算法针对自己的训练集重新计算9个锚框尺寸,并把小尺寸锚框更多地分配到52×52的高分辨率特征图层级上。这个操作不做也能跑,但做了之后,小目标的召回率有明显提升,属于性价比极高的一步。
损失函数方面需要理解三个部分:坐标损失、置信度损失、分类损失。训练过程中值得注意的一点是,有些实现会对“没有目标”的网格施加很大的惩罚权重,这时候如果锚框设置不合适,训练早期模型会倾向于把所有区域都预测成背景。我当时遇到的现象就是前几十个epoch的loss看似在下降,但检测结果全是空框,后来才发现是训练配置里ignore_thresh和背景权重设置不当导致的。
3.2 完整训练流程与参数表
我的训练流程是这样的:首先下载在ImageNet上预训练好的Darknet-53权重,这是整个训练能快速收敛的基础。然后冻结骨干网络,只训练检测头,先把高层的参数学到一个合理的初始状态。冻结阶段学习率设在0.001,训练大概50个epoch。之后解冻全部网络,学习率降到0.0001,再训练100到150个epoch,并采用余弦退火或者阶梯式降学习率策略。
训练配置里比较关键的参数我整理成了一张表,直接用这个模板能少踩很多坑:
| 参数 | 推荐值 | 备注 |
|---|---|---|
| 输入尺寸 | 416×416 | 兼顾精度和速度,可实验608 |
| batch size | 16 | 显存不够就降到8 |
| 初始学习率 | 0.001(冻结) / 0.0001(解冻) | 太高会震荡,太低训练太慢 |
| 锚框 | 对自定义数据K-means重聚类 | 每100个epoch可重算一次 |
| 置信度阈值 | 0.3~0.4(训练评估) | 部署时可以降到0.25 |
| NMS阈值 | 0.4~0.5 | 太高会让重叠框都保留 |
| Mosaic增强 | 开启 | 有效缓解小目标样本不足 |
训练过程中要盯着验证集的mAP曲线,而不是只看训练loss。mAP@0.5是评估检测器最常用的指标。我自己训练出来的模型在验证集上mAP@0.5约为0.87,其中person类最高能到0.93,book类约0.82,phone类只有0.78。这个结果放到论文里非常够用,但如果只报一个mAP数字,不看每个类别的AP,答辩时容易露馅。
3.3 出一个能跑系统的模型文件
训练完的PyTorch权重不能直接部署,需要导出成通用格式。我最后选择了OpenCV的DNN模块来加载模型,原因是它不依赖PyTorch环境,部署到实验室机器或者老师的电脑上时不用配一堆包。导出时记住把anchor、类别名、输入尺寸这些配置写到一个单独的文件里,不然换机器部署时很容易出现“模型加载成功但检测结果是乱的”这种尴尬问题。
4. 系统实现:把模型装进一个完整的业务流程里
4.1 整体架构怎么定
很多同学做完模型就停在Jupyter Notebook里“演示”,这离“系统”还差很远。毕设要求的是系统,所以必须有前端界面、后端服务、数据库、算法服务四个部分。我用的是B/S架构:后端用Flask提供接口,前端用Vue写监控页面,MySQL存考试场次、告警记录和数据统计,检测推理单独开一个服务进程,通过队列和后端通信。
为什么检测服务和Web服务要分开?因为模型推理是GPU上运行的耗时操作,如果直接在HTTP请求里同步执行,前端页面会卡顿,而且多个用户同时查看监控画面时,线程会被锁死。我的做法是:后台起一个单独的检测线程,不停从视频流队列里取帧、推理、把结果放进结果队列,Web后端只管读结果队列并推送给前端。这样即使摄像头卡了或者模型偶尔慢一帧,Web服务本身不受影响。
4.2 三个核心功能模块
第一个模块是实时监控画面。前端播放画面,后端把检测框、类别、置信度实时叠加在画面里。这个看着简单,其实涉及两个技术细节:一是OpenCV读RTSP流需要设置缓冲区和超时重连,摄像头偶尔断流是家常便饭;二是画框最好由后端完成再推流,而不是把原始视频和检测结果分开传,否则前端同步会非常麻烦。
第二个模块是异常行为规则引擎。这是整个系统最有业务价值的部分。我的规则设计是:如果连续3帧检测到phone且置信度大于0.5,就触发“疑似使用手机”告警;如果某个person的检测框中心点离开预设座位区域超过10秒,就触发“离开座位”告警;如果画面中出现了没有匹配到“考生”身份信息的新人脸,就触发“陌生人进入”告警。注意,规则嵌套了“连续N帧”和“持续N秒”这样的条件,目的是过滤单帧误检。不加这个过滤,考试时老师点个鼠标、学生翻个答题卡,系统就会响个没完。
第三个模块是事件回溯与统计报表。每次告警都自动截图保存,记录考试场次、时间、检测类别和现场画面。考试结束后生成一份统计报告,比如“某考生在15分钟内触发了3次低头发告警”,老师可以据此回查监控录像。除了监考,这个模块还能统计考生的到课时间分布,这就是“教学辅助”这个定位的实际落地。
4.3 部署里容易翻车的工程细节
有几个问题是我在部署测试时才发现的,这里把你可能遇到的坑提前说清楚。第一,GPU显存泄漏。PyTorch模型长跑超过几个小时,显存占用会缓慢增长,最终OOM。排查下来发现是有几个地方没有释放中间张量,解决方法是推理时用torch.no_grad()包裹,并且定期主动回收显存碎片。第二,视频流的帧率不稳定。摄像头源在弱网环境下可能从25帧掉到3帧,检测线程不能直接按固定速率消费,而是要根据时间戳计算实际帧率,动态调整抽帧间隔。第三,多路摄像头并发。如果系统要同时监控4个考场,单GPU可能撑不住,我的方案是给每路流分配独立的推理队列,并限制同时推理的路数为2,剩下的帧暂存缓冲,保证所有路都能出结果,只是刷新率降低。
5. 实测结果与典型问题:真实场景比测试集狠得多
5.1 检测指标与实时性
我自己搭的测试场景是一间普通教室,放置了一个考场座位,模拟了三种典型状态:正常答题、低头看手机、翻看书本。用固定机位的摄像头录制了一段10分钟的视频,再用模型去检测,统计每帧的检测结果。
最终的结果是:416尺寸输入下,单帧推理耗时约40毫秒,帧率在25FPS左右;如果开启半精度推理,可以跑到约50毫秒但内存占用更低。要注意的是这40毫秒是净推理时间,不含视频解码和画框的时间,所以实际显示帧率大约只有18到20FPS。这个数字对监考场景足够用了,毕竟监考不需要流畅播放,重要的是不漏掉关键事件。
精度方面,在验证集上person类AP很高,几乎不会漏人,但phone类在实际场景里误检率明显高于测试集。一个典型的误检案例是:学生手拿白色笔袋,在强光下反光,模型连续十几帧都判成了phone。这类误检单靠调阈值压不下去,因为调高了阈值会把真的手机漏掉。后来我想到的解决办法是在规则引擎里加了条件:只有当检测框的宽高比小于0.8且面积大于某个像素阈值时才算可疑目标。这个启发来自于对错误样本的逐帧分析,也是我想提醒大家的:遇到误检先看样本,再改规则,不要一上来就动模型参数。
5.2 小目标漏检与遮挡问题的处理
监考场景中最难检测的目标是后排远处的手机和课本。在屏幕上看,一个后排手机可能只有20×30像素大小,人是很难看出来的,模型错检也正常。对这种小目标,直接提升输入分辨率到608×608能把AP拉高几个点,但推理时间会翻倍。另一种方案是训练时多尺度采样,每10个batch随机在320到608之间切换输入尺寸,让模型适应不同尺度的目标。我两个方案都试了,实际部署用了416尺寸加多尺度训练后的权重,综合性价比最高。
遮挡问题更让人头疼。学生趴在桌上时,头部和手部会被身体挡住,检测框要么被切断,要么直接漏检。这个问题的本质是单帧检测器的局限性。我能给出的实践建议是:不要试图靠单张图解决遮挡,而是引入多帧跟踪。我自己实现了简单的多目标跟踪逻辑,用上一帧的检测框和当前帧的检测框做IoU匹配,如果一个目标在连续多帧中位置稳定,即使中间漏检了1到2帧,也能通过历史轨迹补回来。这个逻辑虽然简单,但在实际演示中明显减少了遮挡导致的告警闪烁。
5.3 写成实验报告需要注意什么
毕设论文里必定有“系统测试与分析”这一章,别把它写成流水账。我的经验是:一定要准备三个层面的数据。第一是检测精度数据,包括各类别的AP、mAP、P/R曲线;第二是系统性能数据,包括不同输入尺寸下的推理耗时、不同GPU型号下的帧率、多路并发时的CPU内存占用;第三是场景测试数据,比如模拟考试的10分钟视频里,模型识别出几次“疑似使用手机”、规则引擎有没有漏报、有没有误报。第三类数据最容易被忽略,但答辩老师最爱问的就是“你这个系统真实用起来到底能不能发现问题”。
6. 忍不住想分享的几个经验
做这个毕业设计的过程中,我最大的感受是:跑通一个公开模型只是起点,把它做成一个能在考场里稳定运转的系统,才是这个题目的真正难点。数据标注花掉的时间比训练模型多得多,规则引擎调试的次数比调整网络结构多得多,而这些恰恰是论文里写不出来、只有亲手做过才知道的东西。
最后分享一个实用小技巧:如果时间紧张,不要先追求完美的模型精度,而是先把“摄像头采集到画面 → 模型检测 → 告警截图 → 存数据库”这条完整链路跑通。等到整条链路稳定了,再回头优化检测精度。因为毕设答辩的时候,一个能完整演示的系统,比一个只能展示指标但没法落地的模型,分数高太多了。