news 2026/10/2 8:37:20

课堂行为检测数据集与YOLOv8训练实战:5622张图7类行为双格式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
课堂行为检测数据集与YOLOv8训练实战:5622张图7类行为双格式

简介:面向需要构建课堂行为识别模型的算法工程师、科研人员与教育信息化开发者,该数据集提供5622张课堂场景图片,同时给出Pascal VOC格式xml与YOLO格式txt标注,覆盖dk、dx、js、tt、xt、zl、zt七个类别,可直接用于目标检测模型训练、算法验证与课堂分析系统开发。压缩包共2000个文件,以xml标注文件为主,便于标注解析与跨框架使用,另有1个txt说明文件,整体大小约268.46MB,目录结构清晰,下载后无需复杂转换即可按需取用。目前已有1531人学习下载,数据组织规范,可支撑从标注格式理解、标签校验、模型迭代到课堂行为统计的完整实践流程。无论是毕设实验、科研对比还是落地教学督导工具,都能省去人工标注与格式对齐的重复工作,快速获得可用于训练与评估的数据基础。

1. 学生课堂行为检测数据集:5622 张图、7 类行为、VOC 与 YOLO 双格式直接开训

做课堂行为检测,卡住大家的第一道坎往往不是模型,而是数据。通用数据集场景不对口,自己标又费劲。这套学生课堂行为检测数据集一共 5622 张 jpg 图片,每张图配套一个 Pascal VOC 格式的 xml 和一个 YOLO 格式的 txt,三件套数量完全对齐,解压出来就能直接开训,不需要先写格式转换脚本。标注类别 7 个,覆盖课堂里最高频的行为:举手、站立、转头、抬头、低头、写字、打瞌睡。适合做智慧教室、教学督导、课堂考勤的从业者,也适合刚入门目标检测、想拿一份真实场景数据跑通「数据准备 → 训练 → 评估」全流程的开发者。如果你手里只有 COCO/VOC 这类通用数据,这份数据能直接补上场景缺口,省掉最耗时的标注环节。

2. 读懂标注体系:VOC xml 与 YOLO txt 的对应关系、7 个类别代码的语义

拿到数据集先别急着开训,花十分钟把标注体系看清楚。这套数据的核心是「一个样本三件套」:jpg、xml、txt 三个文件同名同数量,xml 是原始标注源头,txt 是从 xml 转好的 YOLO 格式。7 个类别代码都是拼音缩写,不提前搞清楚语义,训练完才发现类别指错了,等于白跑一轮。

2.1 一个样本三件套:jpg、xml、txt 怎么对上

每个样本由三个同名文件组成,比如firc_class_707.jpg、firc_class_707.xml、firc_class_707.txt。展开目录后,三种文件各 5622 个,一一对应,不存在某个样本缺标注的情况。这在下载数据集里算相当干净的,至少省去了「图片有但标签没有」的补对工作。

文件类型数量作用
jpg5622原始图像帧
xml5622Pascal VOC 标注,含类别名、目标框、图像尺寸
txt5622YOLO 格式标注,归一化坐标,直接供 YOLO 系框架读取

这里要注意一个点:txt 不包含分割路径信息,也不包含类别名,只存类别id x_center y_center width height五列数字。所以训练前必须靠我们自己组织目录结构,把图片和 txt 按 train/val/test 分好,这就是第 3 章要做的事。xml 里存的是人类可读的类别名(如<name>dk</name>),txt 里存的是数字 id,两者靠「类别列表顺序」对应起来,改列表顺序等于重排 id,这是后面最容易翻车的点之一。

2.2 七个类别代码:先搞清楚每个行为指的是什么

7 个类别代码是["dk", "dx", "js", "tt", "xt", "zl", "zt"],按这个顺序从 0 开始编号。按这类数据集最常见的约定,语义对应如下:

代码常见语义判定要点
dk打瞌睡趴在桌上或低头闭眼,头部姿态低且长时间静止
dx低头低头看桌面/手机,但还没到趴下的程度
js举手手臂抬起,通常伴随身体挺直
tt抬头面部朝前朝上,面向黑板或老师
xt写字低头且手部在桌面区域有书写动作
zl站立整体站起,目标框高度明显大于坐姿
zt转头头部转向侧面或后方,通常伴随身体转动

具体语义以包内「使用前必读.txt」为准,我一般会在开工前打开这个文件确认一遍,因为不同数据集对 dx 和 xt 的边界定义可能有差异。比如有的数据集把「低头玩手机」单独列一类,有的并进低头里。对这份数据,先按上面的默认约定跑基线,训练完看混淆矩阵再决定要不要合并易混类别,比一开始纠结定义更实际。

2.3 VOC 坐标与 YOLO 归一化坐标:换算公式与两种笔误

xml 里一个目标的长相是这样的:

<object> <name>dk</name> <bndbox> <xmin>280</xmin> <ymin>146</ymin> <xmax>512</xmax> <ymax>388</ymax> </bndbox> </object>

对应到 txt 里的那一行是0 0.330 0.247 0.242 0.224。VOC 存的是绝对像素左上右下坐标,YOLO 存的是归一化中心点加宽高。换算关系:x_center 取 xmin 和 xmax 的中点,再除以图像宽度;y_center 同理除以高度;宽高分别用 xmax-xmin、ymax-ymin 除以图像宽高。写成函数就是:

import xml.etree.ElementTree as ET CLASSES = ['dk', 'dx', 'js', 'tt', 'xt', 'zl', 'zt'] def voc_xml_to_yolo_line(xml_path): root = ET.parse(xml_path).getroot() w = float(root.find('size/width').text) h = float(root.find('size/height').text) lines = [] for obj in root.iter('object'): cid = CLASSES.index(obj.find('name').text) box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) x_c = round(((xmin + xmax) / 2) / w, 6) y_c = round(((ymin + ymax) / 2) / h, 6) bw = round((xmax - xmin) / w, 6) bh = round((ymax - ymin) / h, 6) lines.append(f'{cid} {x_c} {y_c} {bw} {bh}') return lines

这段代码的核心逻辑是:先读 xml 里<size>存的实际图像宽高,再遍历所有<object>,用CLASSES.index()把类别名转成 id,最后按公式算归一化坐标。两个常见笔误提醒一下:一是计算时没除以size里存的宽高,而是除以了自己记的尺寸,图片一旦被压缩过,整套框就整体偏移;二是CLASSES列表顺序和 txt 里的 id 约定不一致,导致类别张冠李戴。这两个错在训练时都不报错,只在结果上体现,特别坑。

3. 用 YOLOv8 训练这套课堂行为数据:目录划分、data.yaml 与参数选择

标注看懂了就开始动手。下面按 YOLOv8 的习惯组织目录,给出可直接复制的划分脚本、配置文件和训练命令,并把每个关键参数为什么这么设说清楚。整套流程走下来大约半小时能出第一个基线模型。

3.1 先划分数据:8:1:1 切分脚本与文件同步

这份数据没有预切分目录,需要自己划分。训练集、验证集、测试集按 8:1:1 切,约 4498 张训练、562 张验证、562 张测试。划分时有个细节:文件名是firc_class_707这类连续编号,如果这些图是从同一段监控视频抽帧得到的,相邻帧内容高度相似,随机洗牌后可能把相似帧同时分进 train 和 val,造成验证集虚高。我一般会固定随机种子,并把 val 比例保持在 10% 以上,尽量摊平这种相关性。

import os, random, shutil random.seed(2024) # 固定种子,保证划分结果可复现 src_img, src_txt = 'images', 'labels' dst = 'classroom_behavior' for split in ('train', 'val', 'test'): os.makedirs(f'{dst}/images/{split}', exist_ok=True) os.makedirs(f'{dst}/labels/{split}', exist_ok=True) imgs = [f for f in os.listdir(src_img) if f.lower().endswith('.jpg')] random.shuffle(imgs) n = len(imgs) # 5622 cut1, cut2 = int(n * 0.8), int(n * 0.9) for i, img in enumerate(imgs): stem = os.path.splitext(img)[0] split = 'train' if i < cut1 else 'val' if i < cut2 else 'test' shutil.copy(f'{src_img}/{img}', f'{dst}/images/{split}/{img}') shutil.copy(f'{src_txt}/{stem}.txt', f'{dst}/labels/{split}/{stem}.txt') print(f'train {cut1} val {cut2 - cut1} test {n - cut2}')

这段脚本先创建 train/val/test 的 images 和 labels 目录,再用固定种子洗牌后按比例切分,最后通过复制而不是移动文件来保留原始完整副本。stem是去掉扩展名的文件名,确保 jpg 和同名 txt 进入同一个 split。跑完建议抽 20 张验证集图片人工看一眼,确认标签和画面内容对得上——这一步能提前发现文件名对不齐的问题。

3.2 写 data.yaml:nc、names、路径三处易错点

YOLOv8 靠一个 yaml 文件描述数据集路径和类别定义。很多人在这里翻车,三个位置最容易错:path 写了相对路径导致找不到图片、names 顺序和 txt 里的 id 不一致、nc 数错了类别。按这份数据的类别列表,配置文件如下:

# classroom_behavior.yaml path: /home/user/classroom_behavior # 改成你解压后的绝对路径 train: images/train val: images/val test: images/test nc: 7 names: 0: dk 1: dx 2: js 3: tt 4: xt 5: zl 6: zt

path建议写绝对路径,因为 YOLOv8 对 train/val/test 的解析是相对于 path 的,相对路径在不同工作目录下行为不一致。names的键就是类别 id,必须和 txt 第一列的数字一一对应。这份数据本身对齐得干净,只要别手滑改动 names 顺序就行。

3.3 启动训练:模型、imgsz、batch 怎么选

配置文件就绪后,直接跑训练命令:

yolo detect train \ data=classroom_behavior.yaml \ model=yolov8n.pt \ imgsz=640 \ epochs=100 \ batch=16 \ project=runs/classroom \ name=yolov8n_baseline

第一次跑建议用yolov8n当基线,速度快、显存占用小,先把整个流程跑通。单卡显存 8G 以上可以换yolov8s或yolov8m,精度会有明显提升。参数选择我一般这样定:

参数我一般怎么设说明
modelyolov8n.pt 起步先拿可用基线,再换大模型对比
imgsz640 起步,验证用 960教室多人小目标,960 通常更稳
batch16 或 8以不爆显存为准,爆了就减半
epochs100五千张图 100 epoch 足够收敛
patience默认 50验证集 50 轮不涨会自动早停

训练结束后看runs/classroom/yolov8n_baseline/weights/best.pt,这就是验证集上最优的权重。首次训练到收敛大约需要一到两小时(视显卡而定),中途打开results.png看 loss 曲线,如果验证集 loss 在 60 轮后还明显下降,可以顺手加 50 epoch 继续跑。

4. 常见问题与避坑:五条实测踩坑记录

这套数据本身对齐得比较干净,但从拿到手到训出能用的模型,中间至少有五个坑我踩过或看过别人踩过。每条按「现象 → 原因 → 解决」写,都是能直接对着排查的。

4.1 训练时类别越界,提示 nc 不匹配

现象:yolo detect train跑起来后立刻报 index 越界,或者提示nc与names数量不匹配。

原因:某个 txt 里出现了 0~6 之外的 class id,最常见的是标注工具从 1 开始编号,或者有人改过类别顺序后没同步 txt。我见过有人把 names 按中文语义重新排序,直接把 id 打乱了。

解决:先扫描全部 txt 的 class id,把越界的找出来:

import os bad = [] for f in os.listdir('labels'): if not f.endswith('.txt'): continue for line in open(f'labels/{f}'): cid = int(line.split()[0]) if cid < 0 or cid > 6: bad.append((f, cid)) print('越界数量:', len(bad), bad[:10])

这段脚本遍历所有 txt,取每行第一列做范围检查,正常应该输出 0 条。如果报出大量越界,大概率是标注工具约定不同,用 2.3 的转换函数重新生成 txt 即可。

4.2 验证集 mAP 不低,后排小目标几乎全漏

现象:整体 mAP50 到了 0.85,但把置信度阈值一提高,后排学生全部消失,只剩前排大目标。

原因:教室是典型的多人小目标场景,后排人物在原始画面里可能只有 20×40 像素,缩到 640 输入后只剩几个像素,信息量不够模型判别。

解决:把imgsz提到 960 或 1280,batch 相应减半;还不够就在推理侧做滑窗,把原图按 2×2 切片分别过模型再合并结果。对于课堂考勤这类场景,imgsz=960 + yolov8s是个性价比很高的组合。

4.3 dx(低头)和 xt(写字)互相误检

现象:混淆矩阵里这两个类串得厉害,一个低头写字的样本,有时判成 dx 有时判成 xt。

原因:两个类姿态本身接近,xml 框的都是整个人,头部朝向和手部动作在低分辨率下根本分不开,标注边界天然模糊。

解决:两条路,一是先用合并类训(把 dx/xt 合并成「低头/书写」一类,7 类变 6 类),推理时再按规则细分;二是单独收集这两类的贴近特写数据做二次分类。对大多数课堂考勤场景,合并后整体准确率反而更高,这个取舍值得做。

4.4 视频实测时检测结果一帧有一帧无,抖得没法用

现象:单帧检测正确率不低,但同一个学生连续 30 帧里被检出来 15 次、消失 15 次,统计行为占比时数据完全不能用。

原因:单帧检测的置信度在姿态变化时会抖动,比如转头过程中目标框恰好处于半遮挡状态,置信度在阈值附近来回横跳。

解决:加一个滑窗投票机制,连续 N 帧里出现频次超过 60% 的行为才判定为有效。这个在 5.3 节会给一个最小实现,直接抄即可。

4.5 自己补标新数据后,旧类别 AP 反而下降

现象:把自己拍的课堂视频截图加入数据集重新训练,训练集变大了,但旧类别的 AP 掉了好几个点。

原因:常见做法是用在线标注工具或 labelImg 补标,导出的 txt 有的没按图像实际宽高归一化;或者图片经过聊天工具转发被压缩过尺寸,XML 里记录的尺寸和实际 jpg 不一致。

解决:每次合并新数据前,强制跑一遍 4.1 的脚本,再随机抽 20 张图人工比对框和画面。从固定摄像头录的原始视频直接抽帧补标,不要用转发压缩过的图片,这个习惯能省掉大量返工。

5. 进阶玩法:课堂场景的数据增强与视频流检测接入

基线跑通之后,想让模型在实际教室里更稳,主要看两件事:增强参数怎么围绕课堂场景调、以及怎么把单帧检测变成稳定的行为判定。这两件事都做好了,才谈得上落地。

5.1 数据增强:围绕教室场景调,别用通用默认值

YOLOv8 自带的增强参数针对的是通用场景,直接套到教室上不一定合适。课堂视频的特点是:相机固定安装(顶装或侧装)、人物始终处于直立姿态、光照基本恒定。基于这个先验,我一般这样调:

yolo detect train \ data=classroom_behavior.yaml \ model=yolov8s.pt \ imgsz=960 \ epochs=120 \ batch=8 \ hsv_h=0.01 hsv_s=0.6 hsv_v=0.5 \ degrees=0.0 scale=0.5 fliplr=0.5 \ mosaic=1.0 mixup=0.2

参数逻辑:degrees=0关掉旋转增强,因为教室不存在横着的人,开了只会让模型学出奇怪的特征;fliplr=0.5保留左右镜像,举手行为不分左手右手,镜像不会改变类别语义;hsv三项用接近默认的值,教室灯光固定,过度色偏增强会让模型去拟合颜色噪声而不是形体特征;scale=0.5配合mosaic模拟不同距离下学生尺度的变化,这对小目标很有帮助。

5.2 视频流推理:conf 和 iou 参数设多少

训练完用best.pt对视频做推理,命令如下:

yolo detect predict \ model=runs/classroom/yolov8n_baseline/weights/best.pt \ source=classroom.mp4 \ conf=0.35 \ iou=0.45 \ save=True \ show=True

source可以是视频文件路径、摄像头设备号或图片文件夹。conf=0.35是教室场景的合理起点:调太低会出一堆误检,调太高后排小目标会被过滤掉。iou=0.45是 NMS 用的阈值,多人密集场景可以降到 0.4,减少互相重叠的框被压掉的情况。

这里要强调一个认知:yolo detect predict是逐帧独立推理,帧与帧之间没有状态。实际课堂中「打瞌睡」是一个持续过程,单帧判定天然不稳定,所以需要下面的时序平滑。

5.3 行为时序平滑:15 帧滑窗投票的最小实现

我一般会在推理结果后面接一个滑窗投票器,让连续出现的检测结果互相投票,滤掉单帧抖动:

from collections import deque, Counter class BehaviorSmoother: def __init__(self, window=15, min_ratio=0.6): self.window_buffer = deque(maxlen=window) self.min_ratio = min_ratio def push(self, cls_ids, confs, threshold=0.35): hit = {cid for cid, conf in zip(cls_ids, confs) if conf >= threshold} self.window_buffer.append(hit) def stable_behaviors(self): votes = Counter() for frame in self.window_buffer: votes.update(frame) return [c for c, cnt in votes.items() if cnt >= len(self.window_buffer) * self.min_ratio]

用法是每帧检测完调用一次push,把置信度高于阈值的类别 id 集合送进去,stable_behaviors()返回窗口内稳定出现的行为。参数含义:window=15对应约 0.5 秒(30fps 视频),越大越稳定、对急性动作响应越慢;min_ratio=0.6表示 60% 以上的帧都出现才算数,做课堂考勤这个值合理,如果要做举手这类瞬时动作,降到 0.4 更合适。

6. 把检测结果落成行为统计:导出标签、算占比、定位问题片段

模型训好了,最后一步是让它产出业务上能用的东西。对课堂行为检测来说,最实用的输出不是画框视频,而是「一段时间内各类行为出现多少次、占比多少」。用 YOLOv8 自带的导出功能加一个小脚本就能实现。

先对测试帧批量推理,并导出每帧的检测结果:

yolo detect predict \ model=runs/classroom/yolov8n_baseline/weights/best.pt \ source=test_frames/ \ conf=0.35 \ save_txt=True \ save_conf=True \ project=runs/export \ name=behavior_stats

加上save_txt=True后,每一帧的检测结果会写成一个同名 txt,每行格式是类别id 置信度 x_center y_center w h。接下来统计所有帧里各类行为出现的总频次:

import glob from collections import Counter CLASSES = ['dk', 'dx', 'js', 'tt', 'xt', 'zl', 'zt'] counter = Counter() for txt in glob.glob('runs/export/behavior_stats/labels/*.txt'): with open(txt) as f: for line in f: cid = int(line.split()[0]) counter[CLASSES[cid]] += 1 total = sum(counter.values()) for name, cnt in counter.most_common(): print(f'{name}: {cnt} ({cnt / total:.1%})')

这段脚本把每个 txt 的第一列类别 id 拿出来计数,再除以总数得到占比。跑完你会得到类似「tt 抬头 38.2%、dx 低头 22.5%、dk 打瞌睡 11.3%」的结果,这就是教学督导最关心的课堂行为分布。配合时间维度,按分钟把视频切成片段分别统计,还能画出「后半节课低头、打瞌睡占比上升」的曲线。

最后验证统计可信度的一个技巧:把conf从 0.35 换成 0.5 重跑一遍,对比两份占比。如果某个类别的占比差超过 10%,说明模型在这个片段上不够稳,优先去翻置信度在 0.35~0.5 之间的检测结果,确认是误检还是漏检,再决定调阈值还是补数据。

从那以后我每拿到一份新数据集,都强制走一遍「先校验三件套、再固定种子划分、训完看混淆矩阵、导出标签做统计」这条流程,一步都不跳。很多看起来玄学的模型效果问题,最后都出在数据没对齐,而不是网络结构上。希望帮到你。

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

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

SDN网络流量监控与控制:Python源码解析与环境搭建实战

简介&#xff1a;一套基于SDN架构的网络流量监控与控制Python项目源码&#xff0c;面向网络工程、计算机等专业学生&#xff0c;适用于毕业设计、期末大作业和课程设计等场景&#xff0c;解决缺少可运行、可解释的高评分源码的痛点。代码注释覆盖关键逻辑&#xff0c;新手也能看…

作者头像 李华
网站建设 2026/10/2 8:37:12

药丸缺陷检测数据集VOC+YOLO格式使用指南

简介&#xff1a;这是一份面向计算机视觉与工业质检场景的药丸缺陷检测数据集&#xff0c;提供2759张药丸图像的目标检测标注数据&#xff0c;覆盖污染、裂纹与合格品三类目标&#xff0c;适用于训练YOLO、Faster R-CNN等主流检测模型。数据同时给出Pascal VOC格式的xml标注与Y…

作者头像 李华
网站建设 2026/10/2 8:36:58

VOC垃圾分类数据集详解:15000张真实场景图与YOLO训练实战

简介&#xff1a;VOC垃圾分类检测数据集面向需要训练目标检测模型的开发者、研究人员及学生&#xff0c;提供约一万五千张真实场景高质量标注图片&#xff0c;覆盖纸张、塑料、果皮、玻璃杯、易拉罐、厨余垃圾等常见类别&#xff0c;场景丰富、角度多样。全部图片以jpg格式保存…

作者头像 李华
网站建设 2026/10/2 8:36:57

Java个人博客系统毕业设计:从环境配置到部署答辩全流程指南

简介&#xff1a;基于Java的个人博客系统毕业设计资料包&#xff0c;面向高校计算机相关专业学生及Java Web入门开发者&#xff0c;适用于课程设计、毕业设计或项目实战。压缩包约178.52MB&#xff0c;包含项目报告、答辩PPT、源代码、数据库脚本及部署教学视频等主要文件类型&…

作者头像 李华
网站建设 2026/10/2 8:36:09

Claude Code实战:重构十年遗留系统的方法论

1. 这不是又一个“AI写代码”故事&#xff0c;而是给真实世界里那堆跑着十年的老系统续命的实操笔记我接手过三套平均年龄8岁的遗留系统&#xff1a;一套用VB6写的车间排产模块&#xff0c;数据库还是Access&#xff1b;一套Java Web项目&#xff0c;Spring版本停在2.5&#xf…

作者头像 李华
网站建设 2026/10/2 8:35:53

Java直播平台源码实战:从架构拆解到高并发部署全指南

简介&#xff1a;这是一套基于Java与Spring Boot构建的在线直播平台完整源码工程包&#xff0c;面向具备Java基础、希望学习企业级直播业务落地的开发者。项目采用前后端分离架构&#xff0c;涵盖腾讯云直播流接入、直播鉴黄、礼物打赏、支付宝充值提现、弹幕聊天室等核心模块&…

作者头像 李华