news 2026/9/28 5:04:54

基于Python的课堂行为图像识别:YOLO与CNN两阶段实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python的课堂行为图像识别:YOLO与CNN两阶段实战

简介:这份资源是基于Python与深度学习框架实现的课堂行为图像识别分类项目源码,面向计算机、人工智能相关专业的学生及自学者,可用于毕业设计、期末大作业或课程实践,帮助解决课堂场景下学生行为自动分类的建模与实现问题。压缩包共包含1206个文件,以1183张jpg图像样本为主体,辅以14个py源码文件、3个png图示、2个md说明文档及license等配置项,整体约100.29MB,数据与代码结构完整,便于直接训练与复现。项目已通过严格调试,评审分达到95分以上,可放心运行。目前已有571人学习下载,读者可从中获取完整的图像分类流程、数据集组织方式、模型训练脚本与推理代码,并参考目录结构快速理解课堂行为识别的实现思路,适合作为深度学习入门到实战的参考案例。

1. 课堂行为图像识别:从一段监控视频到可用的分类模型

教室里摄像头拍下的画面,和 ImageNet 里那些摆拍图完全是两回事。学生低头、侧身、被前排挡住、光线忽明忽暗,一个班里几十号人同时出现在一帧里,你要判断的是每个人此刻在听课、写字、举手还是趴桌子。基于 Python 的深度学习课堂行为图像识别分类项目,要解决的就是把这种非受控场景下的学生行为自动分成若干类别,输出每帧里每个人的行为标签。它适合两类人:一类是正在做课程设计或毕业设计、需要一套能跑通的完整流程的学生;另一类是已经会写 Python、想找一个真实场景练手检测加分类的工程师。热搜里 python 安装教程、vscode python 环境配置、深度学习入门这些词反复出现,说明大量人卡在环境而不是算法本身,所以这篇会从数据准备一路讲到推理部署,把每一步的参数和坑都摊开。

课堂行为识别和通用图像分类最大的区别在于:它不是给整张图打一个标签,而是先定位到人,再判断这个人在干什么。常见做法是两阶段——检测器负责框出每个学生,分类器负责判断框内的行为。也有单阶段方案直接输出行为框,但对小目标和遮挡的鲁棒性往往不如两阶段稳。我一般会先跑通两阶段,因为中间结果可查,哪一步出问题一目了然,调起来有后悔药。数据集通常来自公开课堂行为数据集或自己标注的教室监控截图,类别常见的有听讲、书写、举手、趴桌、站立、讨论这几类,具体类别数按你的标注来定,不要照搬别人的标签体系。

2. 数据准备与标注:课堂行为数据集怎么攒才不返工

2.1 课堂场景的数据特点与采集策略

课堂监控画面有几个绕不开的特点。第一是视角固定但俯角大,学生呈前后排堆叠,后排人脸小、遮挡重。第二是光照不均,靠窗一侧过曝、靠门一侧偏暗。第三是行为边界模糊,比如“低头写字”和“低头玩手机”在单帧上几乎一样,只能靠时序或手部区域区分。采集时我建议按教室、时段、课程类型分层抽样,至少覆盖上午、下午、靠窗、靠门、前排、后排这几个维度,否则模型很容易学到“靠窗=听讲”这种伪相关。

采集频率上,如果只做单帧分类,每秒抽 1 到 2 帧就够;如果后面想接时序模型,建议保留原始视频或按 5 到 10 fps 抽帧。分辨率不要一味求高,1080p 缩到 960 宽通常足够,再高只是徒增显存。每类行为至少准备 800 到 1500 个样本框,类别不均衡时优先补少样本类,而不是简单复制。

2.2 用 LabelImg 标注并转成 YOLO 格式

标注工具用 LabelImg 或 Labelme 都行,检测任务用矩形框即可。标注时统一规则:框住可见身体范围,严重遮挡超过一半的样本直接丢弃,不要硬标。标完导出 YOLO 格式,每张图对应一个 txt,每行是类别 中心x 中心y 宽 高,坐标都归一化到 0 到 1。

import os import xml.etree.ElementTree as ET # 把 LabelImg 的 VOC xml 批量转成 YOLO txt classes = ["listen", "write", "raise", "lie", "stand", "discuss"] xml_dir = "annotations" out_dir = "labels" os.makedirs(out_dir, exist_ok=True) def convert(xml_path, out_path): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in classes: continue cls_id = classes.index(name) bnd = obj.find("bndbox") x1 = float(bnd.find("xmin").text) y1 = float(bnd.find("ymin").text) x2 = float(bnd.find("xmax").text) y2 = float(bnd.find("ymax").text) # 归一化并转成中心点加宽高 cx = (x1 + x2) / 2.0 / w cy = (y1 + y2) / 2.0 / h bw = (x2 - x1) / w bh = (y2 - y1) / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines)) for f in os.listdir(xml_dir): if f.endswith(".xml"): convert(os.path.join(xml_dir, f), os.path.join(out_dir, f.replace(".xml", ".txt")))

这段脚本做三件事:解析 xml、按类别表映射 id、把绝对坐标归一化成 YOLO 需要的中心点格式。classes列表顺序必须和后面训练配置里的names完全一致,错一位整个训练就废了。归一化用图像真实宽高,不要用固定值,否则不同分辨率混在一起会错位。转换完抽查几张,用可视化脚本把框画回原图确认没偏。

2.3 划分训练集与类别不均衡处理

划分比例常用 7:2:1,但课堂数据要按“教室”划分而不是随机划分,否则同一教室的相似帧会同时进训练和验证,指标虚高。类别不均衡时,轻度过采样少样本类,重度不均衡就在损失里加类别权重。别用随机旋转 90 度这种增强,课堂场景里人是站立的,转 90 度会造出物理上不存在的样本。

3. 检测加分类两阶段模型:YOLO 框人,CNN 判行为

3.1 为什么选两阶段而不是端到端

端到端方案听起来省事,但课堂场景里小目标密集,单阶段直接回归行为框容易在遮挡处漏检。两阶段的好处是检测和分类解耦:检测器只关心“这里有没有人”,分类器只关心“这个框里的人在干嘛”。检测器可以用 YOLOv8n 或 YOLOv5s 这类轻量模型,分类器用 ResNet18 或 MobileNetV3,整体在单张消费级显卡上就能训。常见做法是先用检测器裁出人框,再把框缩放到 224×224 送进分类网络,两个阶段分别训练、分别调参,出问题能快速定位是框不准还是分类错。

3.2 训练检测器:YOLOv8 配置文件与关键参数

YOLOv8 的数据配置用一个 yaml 描述路径和类别。下面是最小配置。

# classroom.yaml path: ./dataset train: images/train val: images/val nc: 6 names: ["listen", "write", "raise", "lie", "stand", "discuss"]

path是数据集根目录,train和val是相对路径下的图片文件夹,标签文件夹默认与图片同级同名替换为 labels。nc是类别数,必须和 names 长度一致。训练命令:

yolo detect train model=yolov8n.pt data=classroom.yaml epochs=100 imgsz=960 batch=8 device=0

imgsz=960是因为课堂画面里人偏小,输入太小会丢细节;batch=8按显存调,8G 显存跑 960 大概就是这个量级。epochs=100配合早停,验证 mAP 连续 20 轮不升就停。训练时重点看验证集的 mAP50 和召回,召回低说明漏检多,优先加数据或调低置信度阈值,而不是盲目加轮数。

3.3 训练行为分类器:ResNet18 微调与数据增强

分类器输入是检测框裁出的人体图。数据增强用随机水平翻转、颜色抖动、随机擦除,不要用大角度旋转。下面是用 PyTorch 微调 ResNet18 的核心代码。

import torch import torch.nn as nn from torchvision import models, transforms # 分类类别与检测类别保持一致 num_classes = 6 model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT) model.fc = nn.Linear(model.fc.in_features, num_classes) # 课堂场景增强:翻转加颜色抖动,不做大角度旋转 train_tf = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(0.2, 0.2, 0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30)

weights=DEFAULT加载预训练权重,课堂数据量不大时这是精度保障。lr=1e-4是微调常用值,太大容易把预训练特征冲掉。CosineAnnealingLR让学习率余弦下降,后期收敛更稳。训练时如果训练集准确率远高于验证集,先查是不是同一教室的帧泄漏到了两边,再考虑加增强。

3.4 两阶段串联推理:从视频帧到行为标签

推理时先检测再分类,把结果画回原图。核心逻辑如下。

from ultralytics import YOLO import cv2 import torch detector = YOLO("runs/detect/train/weights/best.pt") clf = model.eval().cuda() names = ["listen", "write", "raise", "lie", "stand", "discuss"] cap = cv2.VideoCapture("classroom.mp4") while True: ok, frame = cap.read() if not ok: break results = detector(frame, conf=0.4, iou=0.5)[0] for box in results.boxes: x1, y1, x2, y2 = map(int, box.xyxy[0].tolist()) crop = frame[y1:y2, x1:x2] if crop.size == 0: continue inp = train_tf(crop).unsqueeze(0).cuda() with torch.no_grad(): pred = clf(inp).argmax(1).item() cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, names[pred], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow("result", frame) if cv2.waitKey(1) == 27: break

conf=0.4是检测置信度阈值,课堂遮挡多时可以降到 0.3 换召回。iou=0.5控制重叠框合并。分类输入必须用和训练一致的train_tf,归一化参数不一致会让精度断崖式下跌。裁剪框要判空,边界框贴边时可能裁出空数组。

4. 训练调参与评估:课堂行为分类的指标怎么看

4.1 关键参数与调整方向

参数常用值调整方向
检测 imgsz960人小就加大,显存不够降 batch
检测 conf0.3~0.5漏检多降,误检多升
分类 lr1e-4不收敛降到 5e-5
分类 batch32~64按显存调
增强强度中过拟合时加强,欠拟合时减弱

4.2 评估指标与混淆矩阵排查

检测看 mAP50 和召回,分类看每类准确率和混淆矩阵。课堂行为里最容易混的是“听讲”和“书写”,因为两者都是低头坐姿,区别在手部位置。混淆矩阵里如果这两类互相错得多,说明单帧信息不够,要么加手部关键点特征,要么引入短时序。另一个常见混淆是“举手”和“站立”,前者手臂举起但身体坐姿,后者整体离座,框的高度比例能区分,可以在分类前加一个宽高比过滤。

评估时按教室分组统计,别只看总体准确率。某个教室准确率明显低,多半是光照或视角差异,针对性补该教室数据比全局调参有效。

5. 避坑与排查:课堂行为识别项目里最容易翻车的五件事

现象:训练 loss 正常下降,但验证 mAP 一直很低。原因:训练集和验证集按随机帧划分,同一教室相邻帧高度相似,造成数据泄漏,验证集其实在“背答案”。 解决:按教室或按视频片段划分,确保验证集来自训练时没见过的教室。

现象:检测框位置对,但分类结果几乎全是一类。原因:分类训练时类别极度不均衡,或者归一化参数和预训练模型不匹配。 解决:先统计各类样本数,加类别权重;核对 Normalize 的均值和方差是否为 ImageNet 标准值。

现象:推理时画面卡顿,帧率只有个位数。原因:检测和分类串行跑在同一张卡上,且每帧都重新加载模型。 解决:模型在循环外加载一次;分类可以攒 batch 再推理;必要时用半精度model.half()。

现象:换一个教室准确率暴跌。原因:模型学到了特定教室的背景、光照、座位布局等伪特征。 解决:训练时加入不同教室数据,增强里加随机亮度对比度,必要时对背景做随机遮挡。

现象:举手行为经常漏检。原因:举手时人体框变高变窄,和训练集中坐姿框的宽高比差异大,检测器没学好这种形态。 解决:补充举手样本,或在检测后加宽高比规则做二次筛选,别只靠网络。

6. 把模型跑得更稳:时序平滑与置信度融合的实用技巧

单帧分类最大的问题是抖动,同一学生相邻帧一会儿“听讲”一会儿“书写”,输出像心电图。我一般会在推理后加一层时序平滑:对每个检测框用跟踪算法分配 id,然后对同一 id 最近 5 到 10 帧的分类概率做滑动平均,取平均后最大的类别作为输出。这样既压住了抖动,又不会引入太大延迟。跟踪可以用简单的 IOU 匹配,也可以用 ByteTrack,课堂场景里 IOU 匹配基本够用。

另一个技巧是置信度融合。检测置信度和分类置信度相乘作为最终分数,低于阈值的框直接丢弃,能过滤掉一部分误检。如果某帧某个框的分类概率分布很平(最大概率不到 0.5),说明模型也不确定,这时可以回退到上一帧的平滑结果,而不是硬输出一个类别。

from collections import deque # 每个跟踪 id 维护一个概率队列 history = {} def smooth(track_id, probs, window=7): if track_id not in history: history[track_id] = deque(maxlen=window) history[track_id].append(probs) avg = sum(history[track_id]) / len(history[track_id]) return avg.argmax(), avg.max()

window=7是我在 25fps 视频上试出来比较平衡的值,太小压不住抖动,太大行为切换会滞后。avg.max()低于 0.5 时建议保持上一帧标签。这套平滑逻辑不改变模型本身,纯后处理,加在任何两阶段方案上都能用。

最后说个我自己的习惯:每次改完参数,先在一个固定的小验证集上跑一遍,把检测 mAP、分类准确率、推理帧率三个数记下来,再决定这次改动是留还是回滚。课堂行为识别没有一劳永逸的配置,只有不断对着真实教室画面调出来的稳定。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 5:04:45

2026最新wordpress菜单管理系统实战指南

2026最新wordpress菜单管理系统实战指南 网站做好了没人访问,这大概是每个站长最头疼的噩梦。很多人花重金请人建站,代码写得飞起,UI做得精美,结果上线三个月,后台日志里除了爬虫全是0。其实,问题往往出在那些不起眼的细节上,比如你的导航菜单。在2026最新的搜索引擎算法视角下,用户体验权重占…

作者头像 李华
网站建设 2026/9/28 5:03:59

深圳极速网站建设定制:网站被黑别慌,3招用免费工具自查

深圳极速网站建设定制:网站被黑别慌,3招用免费工具自查 你的网站是不是突然打不开了?或者打开后弹出一堆奇怪的广告,甚至被浏览器标红警告“不安全”?别急,先深呼吸。这种网站被黑挂马的情况,在深圳乃至全国的中小企业里太常见了。很多老板第一反应是删文件重装,结果越删越乱,最后只能找外包公司花大价钱“救火”…

作者头像 李华
网站建设 2026/9/28 5:03:33

豆瓣Top250数据可视化实战:从爬取清洗到交互图表

简介:本资源是一份面向本科毕业设计、课程设计与期末大作业的Python数据可视化实战项目,聚焦豆瓣电影Top250数据集的采集、清洗、分析与多维可视化全流程。项目代码完整、注释详尽,涵盖Scrapy爬虫、Pandas数据处理、Matplotlib/Seaborn/Pyech…

作者头像 李华
网站建设 2026/9/28 5:03:31

做网站毕业设计存在的问题完整流程

2026最新:做网站毕业设计存在的8个坑,避开备案流程一头雾水 别以为毕业设计只是写代码,真正让你头大的是上线后的那些事。特别是 备案流程一头雾水 ,很多学生明明代码跑通了,却卡在服务器和证书上,导致最后答辩时网站打不开,或者页面全是警告。2026年的互联网环境更严了,合规性是底线。今天我从一个做了…

作者头像 李华
网站建设 2026/9/28 5:03:28

RTP.NET实战笔记:从RTP/RTCP协议到音视频收发的完整拆解

简介:RTP.NET是一套基于.NET框架实现的RTP(实时传输协议)封装库,面向需要构建VoIP、视频会议、流媒体服务等实时通信应用的.NET开发者。库中提供RTPSession会话管理、RTPParticipant参与者元数据、数据包化、负载类型识别、事件驱…

作者头像 李华
网站建设 2026/9/28 5:03:22

企业网站模板下载哪里好:5大渠道实测与避坑注意事项

企业网站模板下载哪里好:5大渠道实测与避坑注意事项 你是不是也遇到过这种情况:花大价钱买的企业网站模板,下载下来一看,配色土气、排版僵硬,往公司Logo一放,简直像个“电子废品”。更惨的是,刚把素材换完,手机打开全是乱码,客户点进去转圈半天直接关掉。这时候你才慌了:这模板到底哪里下才靠谱?…

作者头像 李华