☰
深度学习实战:YOLO+Django构建考试作弊行为检测系统
2026/10/21 5:39:02 网站建设 项目流程

在深度学习落地项目里,目标检测一直是应用最广泛、最容易出成果的方向之一。但很多开发者一提到做检测系统,要么纠结模型选型,要么卡在算法到产品的衔接上,最终沦为一堆跑通就删的脚本。今天我想聊的是一个能直接变成完整产品的项目——基于深度学习的考试作弊检测系统,用YOLO系列模型做行为识别,配合Django搭建Web平台,从模型训练、数据预处理到最终在线推理,走通全流程。这个项目适合两类人:一类是对YOLO系模型了解但想把它们真正做成系统的人,另一类是刚学完深度学习、想用完整项目来整合知识点的学生。它能替你解决的核心问题就一句话:把摄像头采集到的考场画面,实时分析出异常动作并可视化报警,而不是事后翻录像查人。

先说结论,这个系统的检测目标不是简单的"作弊"标签,而是几个具体且可量化的行为——低头看桌下、转头偷瞄、侧身翻找、手部异常动作、长时间遮挡面部、两个人在不合适的时候出现高度接近等。每个行为拆成目标检测框加上行为类别,本质上就是一套训练好的权重在前端实时跑推理。

需要说明的是,我下面描述的是一些通用技术方案和教学演示项目的实现思路,不涉及针对任何现实中保密制度的讨论,所有示例均基于模拟的考试场景和通用技术原理。

1. 为什么要把YOLO和Django绑在一起

很多人有个误区,觉得训练好一个YOLO模型就算大功告成。但实际上模型本身只解决了"图像里有什么"的问题,一个完整的产品还必须回答"怎么接入摄像头""结果怎么展示""告警怎么推送""历史记录怎么回溯"。这就需要一个Web平台来兜底,Django恰好是干这个事的合适选择。

1.1 模型生命周期式落地

YOLO系列到今天已经是v12时代,从v5到v12,每个版本的核心进步基本都围绕三点:更强的特征提取主干(Backbone)、更高效的特征融合结构(Neck)、以及更优的标签分配和损失函数。拿YOLOv8和YOLOv5比,v8的C2f结构替换了v5的C3结构,梯度流通更丰富,对遮挡目标和小目标的召回率明显提升。而YOLOv10是真正在推理层面去掉了NMS的后处理,省掉了大量排队时间。YOLOv11和v12进一步改进了主干网络结构与注意力机制的融合方式,在小目标、密集人群这类复杂场景下,精度和速度都往上走了一截。

但真正到考试考场这种场景,模型选型不能只看榜单数据。考场的监控视角通常是固定机位,单通道覆盖的人数和距离都是已知的,此时用最重的模型反而未必合理。我们实际测下来,YOLOv5s极轻量版本在GPU上的单帧推理时间比YOLOv8m快近一倍,精度差距在考场这种相对静态的背景下并不明显。所以在这个项目里,模型的定位是"按需选择":如果边缘设备算力弱,用v5或者v8n;如果中心服务器统一推理,用v11或v12m。

注意:不要陷入"用最新版本就一定最好"的思维定式。最终选定哪个模型,取决于数据集的体量、目标尺寸范围、可用推理硬件的显存上限,这三个要素哪个权重最大,版本选择就往那边倾斜。

1.2 Django负责什么

Django在这个系统里承担的角色相当重。它以MVT模式组织整个服务端逻辑,我用它干了几件事:用户与权限管理(区分管理员和监考员)、考场与摄像头配置、视频流的接入与转发、推理结果的接收与持久化、告警事件在页面上的实时刷新。

整个数据流是这样走的:RTSP或HTTP摄像头流 → Django后台的流处理任务(拉帧、缩放、格式化)→ 送入YOLO模型推理 → 结果加上时间戳和考场ID写入数据库 → 前端通过WebSocket或HTTP轮询拿到结果并渲染。

很多新手容易在这里犯一个错:把推理逻辑直接写在Django的View函数里,前端请求一次就跑一次推理。这会让系统卡到没法用,因为模型推理是CPU/GPU密集型操作,串行处理会直接堵塞主线程。实际做法是把推理放到后台任务队列里执行,Django只负责接收任务和返回任务状态。我在项目里用的是Django-channel加简单的内存队列做消费者模式,视频帧流入一个任务队列,模型推理进程不断消费队列,然后把结果通过WebSocket推送出去。这样哪怕同时接入多个摄像头,HTTP层也不会被拖垮。

2. 数据集:决定检测效果的上限

很多人训练检测模型时把大量精力花在调参上,但真正决定效果上限的是数据本身。考试作弊检测数据集比通用目标检测数据集要难搞得多,因为作弊动作的样本并不像行人、汽车那样公开且海量。目前的处理方式是"通用数据集结合自建场景数据"的组合拳。

2.1 行为类别定义与标注策略

我把检测类别定义为五个具体的可观察行为,而不是泛泛的"作弊":

  • 低头:头部明显低于镜头水平基线,且持续时间超过阈值。
  • 转头:头部大幅度向两侧偏移。
  • 遮脸:手部或物品遮挡面部区域。
  • 传递:手部在桌面以上跨越中线,或者两个目标框水平距离突然接近。
  • 起身:人体检测框高度变化超限。

标注时遵循两个原则:一是用矩形框框住行为发生的主体部位,而不是整个人或整张桌子。以"低头"为例,框的锚点是头部及肩部上沿,这样做能让模型学到"头部下沉+肩部相对位置变化"这种组合特征,而不是单纯记住某个位置有个人。第二个原则是每张图里宁可漏标也不可错标,一个错误标注带来的损失远大于十个正确标注带来的增益。

这些数据里,如果条件允许,应尽量把监控视角、教室不同位置的光照条件、远近距离、随机人数都覆盖到,否则模型在真实考场上会漏检。建议训练集和其他集的比例按8:1:1划分,且确保同一考场不同时间段的画面不能同时出现在训练集和验证集里,否则你评估的分数会虚高。

2.2 数据增强的针对性设计

通用目标检测常用的旋转、翻转、缩放等增强手段在这里要谨慎使用。考场场景对空间方向的语义要求很高,"低头"如果把图像旋转180度就变成了"仰头",标签语义就变了。所以在我的训练脚本中,对行为类目标只做轻度平移、亮度对比度扰动、以及小角度的倾斜(±15度以内),完全禁掉旋转增强和翻转增强。

另一个针对性的增强是"马赛克增强",这是YOLO系训练里非常关键的技巧。把四张图拼成一张训练样本,可以让模型在小尺寸目标上的表现稳定不少。但考场画面里人员通常相对居中,直接用马赛克可能导致目标的边界被切掉。我处理的方案是把马赛克拼接的偏移量严格限制在画面边缘的20%以内,这样做既保留了多尺度优势,又不会把关键目标切出画面。

如果你不打算一开始就自己采集数据,可以先用公开的头部姿态、手部交互、遮挡检测相关的数据集做一些预训练迁移。先跑出第一版权重,再用这个权重去测试考场画面,把那些误报、漏报的帧截取下来,人工修正后再回填训练集。这种"半自动难例挖掘"的方法,对整个系统精度提升的贡献通常远大于调参,很多实际项目跑完都发现,难例回填后的第二轮训练往往比第一轮提升好几个点。

3. 模型训练的关键环节:从配置到收敛

数据集和标注准备好后,剩下的事情就是训练。训练本身不神秘,但几个关键的细节决定了收敛速度和最终精度的上限。

3.1 训练文件的组织与模型选型

按照YOLO系通用格式组织数据:

data.yaml # 类别与路径配置 train/ # 训练集图像 val/ # 验证集图像 annotations/ # 标签文件,每行:class cx cy w h

data.yaml的内容大致如下:

path: ./dataset train: train val: val names: 0: bow_head 1: turn_head 2: face_occlusion 3: pass_behavior 4: stand_up

训练入口命令(以YOLOv8为例,其他版本几乎同构):

yolo detect train \ --model yolov8m.pt \ --data data.yaml \ --epochs 150 \ --imgsz 640 \ --batch 16 \ --patience 20 \ --device 0

这里有个值得展开的点:--patience参数设成20意味着验证集指标连续20轮不涨就提前终止。训练这类小目标行为检测时,验证集mAP经常到第80到100轮才出现明显上升,千万不要因为前期涨得慢就断言"模型学不会",提前停掉训练是新手最容易犯的错误。

YOLOv8m是精度和速度比较均衡的选择,显存占用约4GB左右,进可攻退可守。如果只有CPU算力,建议用YOLOv5s,虽然精度稍低,但推理速度在CPU上能跑到每秒8到12帧,用于演示系统完全够用。

3.2 损失下降与评估观察点

训练过程中,除了看总损失和mAP曲线,我还习惯单独盯验证集的分类损失和边框损失的变化趋势。如果总损失一直在降但验证集mAP一直不动,大概率是过拟合了;如果分类损失快速下降但边框损失居高不下,问题通常出在锚框尺寸和标注框尺寸不匹配上——YOLO系模型基于数据自动计算锚框,需要在训练前确认锚框尺寸是否与你的目标大小基本一致。如果考场画面上的人员普遍很小,而默认锚框偏大,目标就难收敛。可以在训练前用以下命令查看数据集的统计信息:

yolo detect checks \ --data data.yaml

这个命令会输出标注框的分布情况,比如框的平均宽高比、常见尺寸区间。如果发现标注框普遍小于200×200,建议把模型输入的imgsz从默认的640调成800或1024,等于是让模型在更高分辨率下看到更多小目标细节。代价是显存和推理时间略微上升,但对小目标检测的提升非常显著。我在实践中的体会是,这一步的收益往往比换一个更大的模型还明显。

训练完成后,从日志目录里选取best.pt和last.pt分别存放。这两个权重各有用途:best.pt用于最终集成到系统中的推理请求,last.pt用于继续训练或做验证集上的超参搜索。

4. Django系统的架构设计与核心实现

模型训练只是万里长征的一半,我在这套系统里花了最多时间的其实是Django端的设计。核心原则是:Web层和推理层完全解耦,推理永远是后台进程,Web层只负责流接入、状态管理和结果展示。

4.1 系统模块划分

整个Django项目按功能拆成以下几个模块:

  • auth模块:用户登录、会话管理、权限区分。
  • camera模块:摄像头设备的增删改查,每个摄像头绑定考场ID、IP地址、RTSP端口。
  • detection模块:实时推理任务管理与历史记录查询。
  • alert模块:告警事件的生成、推送与处理状态追踪。
  • report模块:按考试场次生成作弊行为报告。

项目后台使用SQLite或PostgreSQL都可以,演示阶段用SQLite就够了,但实时接入多个摄像头时建议尽快切到PostgreSQL,否则并发写入容易出现锁等待。

4.2 视频流接入与预处理

摄像头流的接入是第一个坑。直接用OpenCV的VideoCapture去接RTSP流,默认参数情况下一旦网络出现抖动,重连会非常慢,或者长期卡在最后一帧上。我用的处理方案是给VideoCapture加缓冲区和超时重连机制:

import cv2 import time class StreamReader: def __init__(self, rtsp_url, queue, reconnect_interval=3): self.url = rtsp_url self.queue = queue self.cap = cv2.VideoCapture(self.url) self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) def read_forever(self): while True: ok, frame = self.cap.read() if not ok: self.cap.release() time.sleep(self.reconnect_interval) self.cap = cv2.VideoCapture(self.url) continue # 缩放至推理所需尺寸,减少带宽与计算量 resized = cv2.resize(frame, (640, 640)) self.queue.put(resized)

注意这里的CAP_PROP_BUFFERSIZE要设置为1,只保留最新帧,否则模型消费速度跟不上摄像头帧率时,队列会堆积旧帧,造成检测结果延迟越来越大。

帧进入推理队列之后,由后台进程负责推理,并把结果写回数据库。Django这部分我用的是一个简单的消费者线程池,每个摄像头分配一个线程,轮流消费自己队列里的帧,避免多个摄像头互相抢占。

4.3 Web实时推送与报警逻辑

结果的前端展示用的是WebSocket推送。Django安装channel库,将ASGI配置加入项目,然后写一个consumer负责接收前端画面编号,再把检测结果实时推送到该画面的浏览器页面。前端拿到结果后,在视频画面上叠加矩形框、类别标签和置信度。

报警逻辑要避免"一帧报警、下帧不报"这种抖动情况。实际做法是引入一个短暂的时间窗口统计机制:某个行为类别在连续3秒内被检测到的帧数达到总帧数的60%,才算一次有效告警事件入库。这样虽然损失了部分灵敏度,但能极大降低报警的误报率。考场系统里,误报的代价远高于漏报,因为监考老师一旦对系统失去信任,系统就形同虚设。

历史告警事件的查询可以直接在Django后台管理页面完成。我额外加了两个筛选条件:按考场名和按时间段。这样监考员考后可以快速回看某场考试的全部异常记录,点击事件时间戳,自动跳转到对应录像回放时间点。

5. 训练到部署的完整流程与参数参考

这里给出一份可以直接照做的操作清单,从数据集准备到最终Web演示系统跑通。

5.1 完整实操步骤

  1. 准备环境:Python 3.10以上,安装PyTorch(带CUDA版本)、ultralytics库、Django 4.x、openCV、channels。
  2. 采集考场画面:建议至少覆盖10间不同教室、不同角度、不同人数的视频片段,每个片段截取关键帧,总共2000到3000张图。
  3. 使用labelimg(或anylabeling)标注五类行为,输出YOLO格式txt。
  4. 按8:1:1划分训练集、验证集、测试集。
  5. 执行数据集统计检查,根据标注框尺寸调整输入分辨率。
  6. 执行训练命令,等待收敛,选用best.pt。
  7. 在Django工程里创建detection模块,封装YOLO模型推理接口。
  8. 启动后台消费进程,接入RTSP流。
  9. 配置ASGI与WebSocket路由,启动Django服务。
  10. 前端页面实时接收并渲染检测框与告警信息。

5.2 关键参数速查表

参数常见取值说明
输入分辨率640~1024目标小则调大,显存有限则保持640
Epochs100~200提前终止设为20,避免浪费迭代
Batch size8~32偏低更稳,显存不足则优先缩小
初始学习率0.01YOLO默认,一般无需改动
置信度阈值0.35~0.5演示项目建议0.45,现场运行可调低至0.35
NMS IoU阈值0.45防止同一目标出现多个重叠框

5.3 部署微调经验

部署时最容易忽略的是帧采样率。RTSP流默认25到30帧,如果每一帧都送进模型推理,GPU占用会非常高,但实际比赛中一个人低头或者转头是持续数秒的动作,完全没必要每帧都推理。我在项目里是每3帧取1帧送入模型,推理负载直接降到原来的1/3,检测效果几乎不受影响。这是一个性价比很高的优化。

另一个值得做的优化是检测区域裁剪。考场画面里,往往讲台、黑板、窗户占据大量区域,真正需要检测的是考生座位区域。可以在Django后台为每个摄像头配置一个检测区域,推理前把区域外的画面遮黑,模型只关注有效区域。这个操作简单粗暴,但能明显降低误报率,尤其是把走道中偶然经过的人排除掉。

如果部署的机器没有GPU,需要注意模型精度和速度的平衡。在纯CPU上YOLOv5s推理640×640输入,实测大约每帧80到120ms,也就是每秒能跑8到12帧。配合间隔采样和区域裁剪,勉强能支撑两路摄像头实时处理。如果是4路以上,就会比较吃力,需要降低输入分辨率或者换更小的模型。

6. 常见问题与排查实录

训练和部署过程中遇到问题很常见,我把踩过且查过根因的问题整理在这里,方便对照排查。

6.1 训练发散或损失不降

这类问题优先检查几件事:数据标注格式是否正常(类别索引是否从0开始、坐标是否归一化)、数据增强是否太过激进、学习率是否过高等。还有一个初学者容易忽略的点是数据集里是否存在大量空标签图片,即一张图没有任何目标。如果这类图比例太高,模型会倾向于学"什么都没有"的默认输出,导致有目标时反而漏检。训练集里空标签图比例建议控制在10%以内。

6.2 推理结果有大量重叠框

模型输出多个边框套在同一个目标上时,优先排查NMS阈值是否偏低,将其从0.45调高到0.6试试。另一个可能原因是训练数据标注不够一致,比如同一类行为有的框大有的框小,模型对不同尺度的预测置信度都很高。尽量让所有标注框的比例风格统一,减少这种问题。

6.3 接入RTSP流画面卡顿

第一时间检查OpenCV的缓冲区设置和推理进程的消费速度是否匹配。默认情况下VideoCapture会缓存多帧,网络抖动时画面延迟越积越多。按前面写的方案,设置CAP_PROP_BUFFERSIZE为1,同时把帧采样间隔设为3,问题基本能得到解决。

6.4 Django后台页面推送延迟

如果用的是HTTP轮询而不是WebSocket,建议改成WebSocket方案。HTTP轮询方案下,一次请求带着完整视频帧的Base64,并发量稍微上来就堵住了。WebSocket方案的优势是长连接推送,帧数据只走一次握手路径,延迟下降非常明显。前端接收到推理结果后,交给Canvas直接绘制检测框,不要用DOM元素叠加,这个细节对流畅度影响很大。

6.5 Web界面显示结果与摄像头画面对不上

一般是音视频时间戳与系统时间戳没有对齐导致的。在保存检测结果时,把帧的抓取时间和推理完成时间都记录到数据库,前端展示时以抓取时间为准。同时要保证摄像头时间同步到服务器时间,否则会出现"动作已经发生好几秒,页面才显示报警"的错觉。

7. 关于项目扩展的几点真实建议

这个检测系统做完之后,后续扩展方向其实很多。我自己试过最有意思的方向是把行为识别从单帧检测升级到时序行为识别:用YOLO检测出目标后再送入一个轻量级的LSTM或时序卷积网络,对连续几帧的目标状态做行为分类。这样能把"低头瞬间"和"持续低头达数秒"区分开,让系统更贴近真实的监考逻辑,而不是单纯对每一帧打标签。

更轻量一点的方案是直接在后处理里加规则判断,比如某个人一旦被连续检测到低头状态超过5秒,就认为可疑,这种方案在演示阶段效果已经很可观,而且几乎不增加任何计算成本。

还有一点经验想分享:这类项目最容易失败的地方不在技术,而在数据。很多人在训练阶段发现mAP不错,一到现场就傻眼——光照不一样、摄像头角度不一样、人离镜头距离不一样,所有在训练集里没见过的变量都会变成误报或漏报的来源。所以从一开始就要有意识地用多个教室、多个时段、不同设备拍摄的数据来做训练,宁可数据维度杂一点,也不要在单一场景下追求虚高的指标。

如果你最终要交付给模拟客户或导师,不要只交一个训练好的权重文件。把Django工程、权重、数据集说明、部署文档、演示脚本打包成一个完整交付物,能让人几分钟内跑起来,这是整个项目最关键的加分项。我自己经历过几次类似项目的验收,印象最深刻的反而不是模型多准,而是"拿到手能不能立刻跑起来"。系统能用、文档清楚、演示流程自然清晰,这些远比单点精度重要。

这个项目做到这里,本质上是用一个真实场景把深度学习工程化流程的所有环节都串了一遍——从数据清洗到模型训练,再从模型封装到Web系统,最后一公里铺到了用户面前。如果你正在找项目练手,试着把它完整跑通,你收获的不只是几个模型文件和能展示的网页。

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

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

立即咨询