资讯动态

基于深度学习的课堂专注度分析与作弊检测系统:从摄像头到告警的工程实践

发布时间:2026/10/2 18:40:45 来源:尧图企业网站定制
简介这份资源是面向计算机相关专业毕业设计学生与项目实战学习者的深度学习应用源码包聚焦课堂专注度分析与考试作弊检测两个典型场景提供可运行的Python实现与配套文档说明。项目经导师指导并通过评审源码均经本地编译调试难度适中适合作为毕设选题或算法落地练习。压缩包共626个文件约87.63MB以383个py源码文件为核心辅以41个md说明文档、32个yaml配置、25张jpg与21个gif效果图以及cfg、cu、cpp等模型与加速相关文件覆盖数据配置、网络定义、训练推理与结果展示等环节。目前已有163人学习下载。读者可据此获得完整的检测系统实现思路、模型配置与调试经验理解专注度评估与作弊行为识别的工程组织方式并借助文档快速复现运行为毕设答辩或二次开发提供可参考的代码基础。1. 课堂专注度分析与作弊检测从摄像头到告警的完整工程链路一个教室里坐着四十个学生摄像头挂在讲台正上方画面里每张脸只有巴掌大小。你要做的不是识别他们是谁而是判断谁在走神、谁在低头抄答案。这件事听起来像是一个纯算法问题但真正落地时你会发现模型只是整条链路里最容易被替换的一环。基于深度学习开发的课堂专注度分析和考试作弊检测系统本质上是一套「视频流接入 → 人脸检测与对齐 → 行为特征提取 → 时序分类 → 告警输出」的工程管线。它适合两类人一是想拿它做课程设计或毕业设计的学生二是想把类似能力接入自己教务系统的开发者。Python 源码和文档说明只是起点真正决定这套系统能不能跑起来的是你对输入分辨率、帧率、阈值和误报率的取舍。下面我按实际搭建顺序把每个环节拆开讲清楚。2. 系统架构与模型选型为什么不是「一个 CNN 走天下」2.1 从业务需求倒推技术模块课堂专注度分析和考试作弊检测表面上是两个任务实际上共享同一条前端管线。专注度分析关注的是学生头部姿态、眼部开合、面部朝向的持续状态作弊检测关注的是低头、侧身、手部异常移动、多人头部靠近等瞬时动作。如果分别建两套系统摄像头要接两次人脸要检两次算力直接翻倍。常见做法是前端统一做人脸检测和关键点提取后端分两个分支一个做时序专注度打分一个做作弊行为触发。这里有一个容易被忽略的点课堂场景的人脸尺寸普遍偏小。1080P 画面里后排学生的人脸可能只有 40×40 像素。如果你直接用 ResNet 这类为 224×224 设计的骨干网络特征图下采样五次之后人脸区域只剩一两个像素信息基本丢光了。所以选型的第一原则是骨干网络的下采样倍数不能太大或者必须配合人脸对齐把区域裁出来放大。我一般会这样分配模块模块常见方案输入尺寸输出人脸检测RetinaFace / YOLOv8-face640×640人脸框 5 关键点人脸对齐仿射变换112×112对齐人脸图专注度特征MobileNetV3 GRU112×112×T专注/分心概率作弊行为姿态估计 规则引擎关键点序列作弊告警告警输出滑动窗口投票—事件记录这个表不是让你照抄而是让你理解每一层的输入输出边界。很多源码包跑不起来就是因为检测模型输出的关键点顺序和对齐代码里的索引对不上。2.2 专注度模型时序建模比单帧分类更靠谱单帧判断「专注」几乎不可靠。一个学生低头看笔记单帧看是分心但如果他持续低头 30 秒且头部有轻微摆动可能是认真做题。专注度本质是一个时序问题需要看一段窗口内的状态变化。常见做法是用 CNN 提取每帧的空间特征再用 GRU 或 LSTM 做时序聚合。下面是一个最小可运行的专注度模型定义基于 PyTorchimport torch import torch.nn as nn from torchvision.models import mobilenet_v3_small class FocusNet(nn.Module): def __init__(self, hidden_dim128, num_classes2, seq_len16): super().__init__() # 骨干网络MobileNetV3 小模型适合边缘部署 backbone mobilenet_v3_small(weightsIMAGENET1K_V1) self.features backbone.features # 输出通道 576 self.pool nn.AdaptiveAvgPool2d(1) # 时序建模GRU 输入维度 576隐藏层 128 self.gru nn.GRU(576, hidden_dim, batch_firstTrue, bidirectionalTrue) # 分类头双向 GRU 输出 256 维 self.classifier nn.Sequential( nn.Linear(hidden_dim * 2, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, num_classes) ) self.seq_len seq_len def forward(self, x): # x 形状: (batch, seq_len, 3, 112, 112) b, t, c, h, w x.shape x x.view(b * t, c, h, w) feat self.features(x) # (b*t, 576, 4, 4) feat self.pool(feat) # (b*t, 576, 1, 1) feat feat.view(b, t, -1) # (b, t, 576) out, _ self.gru(feat) # (b, t, 256) out out[:, -1, :] # 取最后一帧输出 return self.classifier(out)这段代码的关键参数有三个。seq_len16表示每次输入 16 帧按 25fps 算大约是 0.64 秒太短会抖动太长会延迟告警。hidden_dim128是 GRU 隐藏层维度双向之后变成 256如果你的 GPU 显存紧张可以降到 64。Dropout(0.3)是为了防止小数据集过拟合课堂数据通常只有几十个学生不加重 dropout 很容易在验证集上翻车。训练时要注意专注和分心的样本极不平衡。大部分学生大部分时间都是专注的如果你直接拿原始数据训练模型会倾向于全部预测为专注。常见做法是对分心样本做重采样或者在损失函数里加类别权重。2.3 作弊检测规则引擎比端到端模型更可控作弊检测如果也用一个端到端模型你会遇到一个尴尬的问题作弊行为样本太少而且定义模糊。两个学生同时低头可能是都在捡笔也可能是传纸条。端到端模型很难区分这种上下文依赖的场景。我一般会用「姿态估计 规则引擎」的组合。先用轻量姿态模型提取每个人的头部和手部关键点然后定义几条硬规则头部持续偏向一侧超过 3 秒且手部进入邻座区域头部低垂角度超过 45 度且持续超过 5 秒两人头部距离小于 30 像素且同时低头这些规则用 Python 写出来并不复杂import numpy as np def check_cheating(head_poses, hand_poses, fps25): head_poses: list of (x, y, pitch, yaw) 每帧每人头部状态 hand_poses: list of (x, y) 手部位置 返回: 作弊事件列表 events [] window fps * 3 # 3 秒滑动窗口 for i in range(len(head_poses) - window): seg head_poses[i:iwindow] # 规则1头部持续侧偏 yaw_vals [p[3] for p in seg] if np.mean(np.abs(yaw_vals)) 30: events.append((head_turn, i)) # 规则2低头角度过大 pitch_vals [p[2] for p in seg] if np.mean(pitch_vals) 45: events.append((head_down, i)) return events这里的fps25必须和实际视频帧率一致否则窗口时长会算错。yaw和pitch的单位是角度阈值 30 和 45 是我在几个教室场景里调出来的经验值光线和摄像头角度变化时需要重新标定。规则引擎的好处是可解释告警时你能告诉老师「这个学生低头超过 5 秒」而不是只给一个黑匣子分数。3. 数据准备与训练流程从原始视频到可训练数据集3.1 视频抽帧与人脸对齐的四个参数拿到一段课堂录像第一步是抽帧。不是每一帧都需要25fps 的视频抽成 5fps 就够用了既能覆盖动作变化又能把数据量降到五分之一。抽帧命令用 OpenCV 就能完成import cv2 import os def extract_frames(video_path, out_dir, target_fps5): cap cv2.VideoCapture(video_path) src_fps cap.get(cv2.CAP_PROP_FPS) interval int(src_fps / target_fps) # 每隔多少帧取一帧 os.makedirs(out_dir, exist_okTrue) idx, saved 0, 0 while True: ret, frame cap.read() if not ret: break if idx % interval 0: cv2.imwrite(f{out_dir}/frame_{saved:06d}.jpg, frame) saved 1 idx 1 cap.release() print(f原始帧率 {src_fps}, 抽帧后 {saved} 张)target_fps5是经验值太高会增加标注和训练成本太低会漏掉快速低头动作。interval必须是整数如果原始帧率是 29.97取整后会有一点误差但影响不大。抽完帧之后做人脸对齐。对齐的目的是把不同角度的人脸统一到标准姿态减少模型需要学习的变量。常见做法是用检测到的 5 个关键点双眼、鼻尖、双嘴角做仿射变换输出 112×112 的图。这里有一个坑如果关键点检测不准对齐后的人脸会扭曲反而比不对齐更差。所以我在对齐前会加一个置信度过滤关键点置信度低于 0.8 的直接丢弃。3.2 标注策略专注度用弱标签作弊用强标签专注度标注不需要逐帧标。我一般会按时间段标每 10 秒一个片段标注这个片段内学生是专注还是分心。这样标注效率高而且和时序模型的输入窗口天然对齐。标注文件用 CSV 存student_id,start_frame,end_frame,label 01,0,250,focus 01,251,500,distract 02,0,250,focus作弊标注则需要精确到帧因为作弊是瞬时事件。标注时记录事件类型和起止帧号训练规则引擎时用来调阈值训练端到端模型时用来做正样本。注意标注一致性比标注数量更重要。同一个人标两遍如果分心判断不一致模型学到的就是噪声。建议先标 100 个片段隔一天再标一遍看一致率能不能到 90% 以上。3.3 训练参数与验证指标专注度模型训练时我常用的配置是Adam 优化器学习率 1e-4batch size 16训练 30 个 epoch。学习率太高会导致 GRU 梯度爆炸太低则收敛太慢。验证指标不要只看准确率因为类别不平衡时准确率会虚高。看 F1 分数和混淆矩阵特别是分心类的召回率。如果分心召回率低于 0.6说明模型对分心样本学得不够需要加权重或增加分心样本。作弊检测的规则引擎不需要训练但需要调阈值。我会用标注好的作弊事件做网格搜索找到误报和漏报的平衡点。通常把误报率控制在每小时不超过 2 次漏报率不超过 10%。4. 避坑与排查五个让我熬夜的翻车现场4.1 人脸检测在逆光下集体失效现象下午的教室窗户在学生背后摄像头拍出来全是剪影人脸检测框一个都出不来。原因RetinaFace 这类模型在正常光照下训练逆光时人脸纹理丢失置信度直接掉到阈值以下。解决在检测前加一个自适应直方图均衡化CLAHE提升暗部对比度。如果还是不行换用红外摄像头或者补光灯。我在一个项目里试过把检测阈值从 0.5 降到 0.3结果误检了大量桌椅后来还是靠补光解决。4.2 时序窗口错位导致告警延迟现象学生已经低头 10 秒了系统才报作弊。原因滑动窗口步长设得太大比如窗口 16 帧但步长也是 16 帧意味着每 16 帧才检测一次延迟自然高。解决把步长设为窗口的 1/4即 4 帧检测一次。代价是计算量增加四倍但告警延迟从 0.64 秒降到 0.16 秒。如果算力不够可以只对检测到人脸的区域做时序推理背景区域跳过。4.3 多人场景下 ID 跳变现象同一个学生上一帧是 ID 3下一帧变成 ID 7专注度曲线断成几截。原因人脸检测没有做跟踪每帧独立分配 ID。解决加一个简单的 IOU 跟踪器或者用 ByteTrack。跟踪器的作用是给每个人分配稳定 ID时序模型才能按人聚合。如果不想引入跟踪库可以用人脸位置做最近邻匹配但效果不如专用跟踪器。4.4 模型在 GPU 上跑得好部署到边缘设备就崩现象开发机上是 RTX 3090推理速度 30fps部署到 Jetson Nano 上只有 3fps。原因MobileNetV3 虽然轻量但 GRU 是串行计算在边缘设备上并行度低。而且很多源码包默认用 FP32 推理没有做量化。解决把模型转成 ONNX再用 TensorRT 做 FP16 或 INT8 量化。GRU 部分可以换成 Temporal Convolutional NetworkTCN并行度更高。如果还不行降低输入分辨率到 96×96精度会掉两三个点但速度能翻倍。4.5 文档说明和源码对不上现象文档里写「运行 train.py 即可开始训练」实际运行报错说找不到 config.yaml。原因源码包在打包时漏了配置文件或者文档是旧版本。解决先看 README 里的目录结构确认所有依赖文件都在。如果缺文件根据代码里的argparse或yaml.load反推需要哪些参数手动补一个最小配置。常见缺失的是config.yaml、label_map.json和预训练权重路径。补的时候注意路径分隔符Windows 和 Linux 不通用。5. 从能跑到好用三个让系统真正可用的技巧5.1 用置信度平滑代替硬阈值规则引擎的硬阈值在边界场景下会频繁抖动。比如低头角度阈值设 45 度学生低头 44 度就不报45 度就报导致告警断断续续。我一般会加一个置信度平滑连续 5 帧中有 3 帧超过阈值才触发告警触发后持续 2 秒才解除。这样告警更稳定老师不会收到一堆碎片化通知。from collections import deque class SmoothTrigger: def __init__(self, threshold, window5, min_hits3, hold_sec2, fps25): self.threshold threshold self.window deque(maxlenwindow) self.min_hits min_hits self.hold_frames hold_sec * fps self.hold_counter 0 self.active False def update(self, value): self.window.append(1 if value self.threshold else 0) if sum(self.window) self.min_hits: self.hold_counter self.hold_frames self.active True elif self.hold_counter 0: self.hold_counter - 1 else: self.active False return self.active这个类的参数需要根据实际场景调。window5对应 0.2 秒min_hits3表示 5 帧里至少 3 帧超标。hold_sec2是告警保持时间防止一闪而过的动作触发告警。5.2 用轻量级姿态模型替代完整人体姿态估计OpenPose 这类模型精度高但太重课堂场景不需要全身 18 个关键点只需要头部和手部。我一般用 MediaPipe 的 Face Mesh 加 Hands 组合CPU 上就能跑到 15fps。如果连 MediaPipe 都跑不动可以只检测头部手部用背景差分粗略估计。5.3 告警分级与人工确认不是所有异常都需要立刻通知老师。我会把告警分成三级一级是疑似专注度低于阈值但未触发作弊规则只记录不通知二级是警告触发一条作弊规则推送到教师端但不高亮三级是确认触发两条以上规则或持续超过 10 秒高亮显示并保存截图。这样既不会漏掉严重事件也不会让老师被误报淹没。级别触发条件处理方式一级专注度低于 0.4 持续 5 秒记录日志二级触发一条作弊规则推送通知三级触发两条规则或持续 10 秒高亮 截图这套分级策略是我在真实教室里跑了两个月后定下来的。一开始没有分级老师一天收到上百条通知后来直接把系统关了。分级之后每天三级告警平均只有 3 到 5 条老师愿意看了系统才真正用起来。5.4 验证方法用回放测试代替现场调试现场调试成本太高老师上课不能等你调参数。我一般会录几段典型场景的视频正常上课、小组讨论、随堂测验、期末考试。然后在本地用回放模式跑系统统计误报和漏报。回放时把推理速度调成和实际帧率一致否则时序窗口会对不上。每改一次参数就在这四段视频上跑一遍看指标有没有退化。这个习惯帮我省了很多次现场翻车。最后说一个我自己的教训不要追求 100% 准确率。课堂行为本身就有模糊性两个学生讨论问题你说他是专注还是分心系统能做到 85% 的准确率加上人工确认就已经比纯人工巡课效率高很多了。把精力花在降低误报和提升可解释性上比死磕模型精度更值得。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑