尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv9学生上课状态识别实战:从环境搭建到模型部署

YOLOv9学生上课状态识别实战:从环境搭建到模型部署 简介这是一套基于YOLOv9的课堂学生上课状态识别检测系统源码包主要面向毕业设计、智慧课堂项目以及深度学习目标检测方向的学习者。项目采用YOLOv9-s模型平均准确率达到百分之九十九点六压缩包内附训练过程截图与评估指标曲线可用于学生抬头、低头、趴桌等课堂学习状态的识别检测。资源共一百七十六个文件包括八十三个Python脚本、三十个YAML配置文件、三个PyTorch模型权重文件以及大量的jpg、png检测结果示例图整体大小约为六十兆字节目录结构清晰并配套运行教程与环境配置说明支持自定义数据集完成训练和检测。目前已有二百零九人学习下载。通过该包可以快速跑通YOLOv9的训练与推理流程获得训练好的模型权重、可视化预测结果、评估曲线以及完整可复现的工程文件适合需要快速落地课堂状态识别项目的开发者参考使用。1. 学生上课状态检测为什么选 YOLOv9 而不是 YOLOv8线下课堂的学生状态识别常见做法是先对教室中的每个学生做目标检测再根据人体位置和行为类别判断“抬头、趴桌、玩手机”。这套基于 YOLOv9 的学生上课状态识别源码在 Python 环境里直接跑训练到推理自带训练好的 YOLOv9-s 模型和评估指标曲线平均准确率按 mAP0.5 口径到了 99.6%。选 YOLOv9 而不继续用 YOLOv8核心原因是 PGIProgrammable Gradient Information和 GELAN 这两个结构改动它们对课堂这种多人、小目标、相互遮挡的监控画面更友好。适合拿去做智慧课堂项目或毕业设计也适合想把手头检测代码升级到 YOLOv9 的人。2. YOLOv9 训练环境配置与课堂数据集的 YOLO 格式整理2.1 Anaconda 里把 YOLOv9 的 Python 环境一次弄干净这个资源最大的坑在环境而不是模型。源码里带了 requirements.txt但 YOLOv9 的依赖和 PyTorch 版本绑得很紧如果你直接pip install -r requirements.txt很可能装到 CPU 版 torch 或旧版 torchvision训练速度差很多。我一般先在 Anaconda 里建独立环境再单独装 PyTorch最后装其余依赖。conda create -n yolo9 python3.9 -y conda activate yolo9 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple第一行创建的是 Python 3.9 虚拟环境版本号可以根据你的显卡驱动调整第二行把 PyTorch 固定在 CUDA 12.1 这一套--index-url是直接从 PyTorch 官方源取包避免默认 PyPI 上的 wheel 跟 CUDA 不匹配第三行用清华源把剩余依赖装完速度会快很多。装完之后用python -c import torch, cv2; print(torch.__version__, torch.cuda.is_available())验证torch.cuda.is_available()返回 True 才能用--device 0训练。提示requirements.txt 里如果包含 torch 和 torchvision建议先执行上面的单独安装否则 pip 会按 requirements 里的版本号重新解析可能把你刚装好的 CUDA 版 torch 无声无息换掉。关于 PyTorch 和显卡的对应关系不用记太多版本只要看你的 nvidia-smi 最高支持 CUDA 多少PyTorch 安装源CUDA 版本适合显卡cu12112.1RTX 20/30/40 系cu11811.8GTX 10 系老卡cpu-无 N 卡纯调试用如果机器上没有独显可以直接把训练和推理都改成--device cpu但课堂数据如果有一两百个学生CPU 跑一个 epoch 会非常慢建议先用小数据量跑通流程再上全量。2.2 课堂状态数据集的 YOLO 格式与目录结构这个资源原本的示例数据是水果成熟度banana_ripe.yaml换成课堂场景后数据格式完全一样每张图片对应一个同名 txt文件放在 labels 目录里。txt 每行是五个数值class_id x_center y_center width height其中中心点和宽高都是相对图片宽高的归一化小数。课堂行为类别可以按自己的标注习惯定义比如path: dataset train: images/train val: images/val names: 0: attentive # 抬头听讲或看黑板 1: sleeping # 趴桌/低头过久 2: phone # 低头看手机 3: handup # 举手发言yaml 文件保存成 data/classroom.yaml。注意path建议改成绝对路径因为 PyCharm 里的工作目录经常跟终端不一致相对路径会让你在 IDE 运行时报dataset/images/train: No such file or directory。names的类别顺序必须和标注时保持一致训练前先检查 labels 文件夹里的 txt 有没有空文件或类别 id 超范围的情况。数据目录通常组织成 images 和 labels 两个平行目录训练集和验证集分开。标注工具产生的 label 文件要和图片同名否则训练脚本加载不到对应标注。dataset/ ├── images/ │ ├── train/ │ │ ├── img_001.jpg │ │ └── img_002.jpg │ └── val/ └── labels/ ├── train/ │ ├── img_001.txt │ └── img_002.txt └── val/训练集和验证集的划分资源里没有单独给脚本常见做法是先用脚本随机切分再检查。我会这样切import os import random import shutil from glob import glob random.seed(42) images glob(raw_images/*.jpg) val_ids set(random.sample(range(len(images)), int(len(images) * 0.2))) for i, img_path in enumerate(images): img_name os.path.basename(img_path) txt_path img_path.replace(.jpg, .txt) if i in val_ids: dst_img, dst_txt dataset/images/val, dataset/labels/val else: dst_img, dst_txt dataset/images/train, dataset/labels/train shutil.copy(img_path, os.path.join(dst_img, img_name)) if os.path.exists(txt_path): shutil.copy(txt_path, os.path.join(dst_txt, os.path.basename(txt_path)))这段脚本的核心是先用random.seed固定随机顺序再按 8:2 切分图片并同步移动同名 txt。没有对应 txt 的图片要删掉否则 train 阶段会报 “No labels found in”这个报错通常不是因为你没有标签文件夹而是 yaml 里的路径和这个目录结构对不上。2.3 训练前快速体检数据集训练前我会再跑一遍 label 边界检查避免某个标注框越界导致 loss 出现 NaN。检查脚本本身不复杂关键是能在一堆 txt 里快速找到格式错误和越界框from glob import glob for f in glob(dataset/labels/train/*.txt) glob(dataset/labels/val/*.txt): with open(f) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: print(格式错误:, f, line) continue _, cx, cy, w, h [float(p) for p in parts] if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(越界:, f, line)这段代码遍历所有 txt检查每行是否恰好 5 个值以及归一化后的中心点和宽高是否在 (0,1) 范围内。越界框虽然也能训练但在最后重参数化时容易产生无效 anchor导致导出 ONNX 后输出坐标全是负值。课堂图片的分辨率通常比较高标注时稍微拖出边界就会出现越界这个体检步骤能帮你省掉后面排错的时间。3. train_dual.py 训练配置与评估指标曲线解读3.1 训练入口参数从默认示例到课堂场景训练脚本是 train_dual.py对应 YOLOv9 的双分支训练体系。官方示例里--cfg models/detect/yolov9-c.yaml用的是 c 版本而资源给你的预训练权重是 yolov9-s.pt直接照抄容易在加载权重时报 shape mismatch。我复跑时会把--cfg改成和权重同尺寸python train_dual.py \ --weights yolov9-s.pt \ --cfg models/detect/yolov9-s.yaml \ --data data/classroom.yaml \ --hyp hyp.scratch-high.yaml \ --epochs 120 \ --batch-size 16 \ --imgsz 640 \ --device 0 \ --close-mosaic 15这里--weights决定初始化权重--cfg决定网络结构两者必须来自同一个模型尺寸--data指向刚才建好的 classroom.yaml--hyp是超参数文件YOLOv9 自带的 hyp.scratch-high.yaml 比 low 版本的数据增强更强课堂数据量一般不超过几千张用 high 版本效果更好。--epochs不必一上来就 200先跑 100 左右看结果曲线--batch-size根据显存定16G 显存可以跑到 168G 建议减半。--close-mosaic 15表示最后 15 个 epoch 关闭 Mosaic 增强让模型从“大图拼贴”的虚拟分布切回真实课堂分布这是收敛稳定很关键的设置。3.2 训练参数速查表参数含义课堂场景建议--weights预训练权重yolov9-s.pt--cfg网络结构配置与 weights 同尺寸的 yaml--data数据集 yamldata/classroom.yaml--epochs训练轮数80~150 轮看曲线是否饱和--batch-size每批图片数16/8显存不足时报 OOM 就减小--deviceGPU 编号或 cpu单卡填 0无卡填 cpu--close-mosaic最后 N 轮关闭 Mosaic15如果启动时显存溢出先不要盲目加高性能显卡很多情况是 workspace 参数太高。可以检查 train_dual.py 里的torch.cuda.empty_cache()调用或者把--cache-images打开让数据预加载到内存以降低 GPU 峰值。注意--batch-size并不总是越大越好课堂图片里学生数量多模型在单张图上的正样本数量本来就大batch 过大反而让正负样本比例失衡。3.3 训练中怎么判断模型真的在收敛训练完会在 runs/train/exp 下生成 results.csv 和一套评估指标曲线图。我一般不看终端里每轮打印的 loss而是直接看 results.csv 最后几行import pandas as pd df pd.read_csv(runs/train/exp/results.csv) cols [c for c in df.columns if mAP in c or box_loss in c] print(df[cols].tail(10))你如果发现 box_loss 一直在降但 mAP 停在某个值不动大概率是类别不均衡。比如“举手”这类动作在教室监控里非常少模型会为了降低总体 loss 把所有框都预测成 attentive造成 mAP 高但实际 useless。这时要么补数据要么给丢失比例高的类别调整--cls损失权重。评估指标曲线里面对应关系如下曲线文件看什么PR_curve.png精确率和召回率的权衡曲线越靠近右上角越好confusion_matrix.png类别混淆情况斜对角线之外的块要重点关注results.pngloss、mAP0.5、mAP0.5:0.95 随 epoch 的变化val_batch*_pred.jpg验证集预测可视化检查遮挡场景有没有重复框资源里给的 YOLOv9-s 模型平均准确率 99.6%关注的是 mAP0.5。这个值高是因为课堂状态类别之间差异通常很大但不能只盯着它更要用 val_batch_pred.jpg 看实际遮挡。教室后排学生互相遮挡时如果 val_batch 图里同一个学生出现两个框就要调推理阶段的--conf-thres和--iou-thres。4. detect_dual.py 推理测试与阈值参数调优4.1 用 best.pt 检测图片和视频训练完会同时保存 last.pt 和 best.ptbest.pt 是验证集 mAP 最高时的权重。推理入口是 detect_dual.py它支持图片目录、单张图、视频文件和摄像头。典型调用python detect_dual.py \ --weights runs/train/exp/weights/best.pt \ --source test_imgs \ --conf-thres 0.25 \ --iou-thres 0.45 \ --imgsz 640 \ --device 0--source传目录时脚本会遍历目录下所有图片传test.mp4则检测视频传0则打开默认摄像头。检测结果输出到 runs/detect/exp 下并把原图、标注框、类别名和置信度画在一起。输出文件的命名和源文件保持一致这个设计在批量验证时很有用可以直接和 val_batch_labels.jpg 对照找问题。资源里已经有 val_batch2_pred.jpg 这类示例图可以先用它们跑一遍检测确认权重能正常加载再换成自己的课堂视频。4.2 置信度阈值和 IoU 阈值怎么配--conf-thres是置信度阈值低于这个值的框会被过滤--iou-thres是 NMS 的 IoU 阈值。两个参数作用完全不同很多人只调 conf 不调 iou。对于课堂场景后排学生密集、目标框重叠面积大如果 iou 设置太高超过 0.6NMS 会把两个重叠的学生框当作同一个目标合并设置太低低于 0.3同一个学生会被框出两个框。我的经验是先固定 conf0.3把 iou 从 0.45 往下调同时看 val_batch_pred.jpg 里重复框的数量。场景conf-thresiou-thres教室前排目标独立0.350.5教室后排多人重叠0.30.4远程摄像头目标很小0.250.35视频抽帧标注归档0.40.45上表的逻辑是目标越小越容易被错误过滤conf 要放宽目标越密集NMS 合并力度要大iou 要调低。如果你发现很多框的置信度都在 0.2~0.3 之间不要只想着降阈值这说明训练数据里的背景负样本不够应该在课堂数据里加一些无人时的空教室图。4.3 测试集评估与常见报错处理要验证 best.pt 在测试集上的真实表现光看 detect_dual.py 的输出图不够我会再跑一遍带 label 的验证看测试集上每一类的 hard example。常见做法是写一个批量脚本把 val 图片过一遍 detect再和 ground truth 算 IoU 匹配。更省事的办法是直接用 YOLOv9 的 val.py它会对给定权重在 val 集上算 precision、recall 和 mAP并生成混淆矩阵。如果你在运行 detect_dual.py 时遇到weights name mismatch先检查--cfg和--weights是否同尺寸遇到cv2.error: OpenCV(4.x) ... assertion failed通常是测试图片路径里包含中文字符或空格把 test_imgs 改成纯英文路径遇到IndexError: index 4 is out of bounds for axis 1 with size 4通常是 names 里只写了 3 类但模型输出是 4 类或者反过来检查 classroom.yaml 和训练时的配置是否完全一致。这些都是我在这个资源里实际踩过的坑。5. 重参数化转推理模型与课堂实时部署技巧5.1 reparameterization.ipynb 到底要做什么YOLOv9 训练时的网络带有一个辅助可逆分支这个分支在训练阶段帮助梯度传播但推理时是冗余计算。资源里给的 reparameterization.ipynb 就是干这件事把训练权重重新参数化合并相邻卷积层去掉辅助分支得到更小、更快的推理权重。转换后的权重参数数量会明显下降推理时间大约可以减少 20%~30%。注意重参数化后的权重只能用于推理不能再继续训练。5.2 把重参数化权重导出为 ONNX课堂项目要接到摄像头和上位机最佳路径是先导出 ONNX再用 onnxruntime 或 TensorRT 加载。用官方 export.py 导出即可python export.py --weights runs/train/exp/weights/best.pt --include onnx --opset 12 --simplify加上--simplify会调用 onnx-simplifier 清理多余节点。导出后在 Python 里加载import cv2 import onnxruntime as ort import numpy as np net ort.InferenceSession(best.onnx) outputs net.run(None, {net.get_inputs()[0].name: input_tensor})[0]outputs的形状一般是(1, 4num_classes, 8400)前 4 行是预测框的x_center y_center width height偏移量后面是每个类别的得分。后续要自己解码坐标先把归一化偏移恢复到 640×640 尺度再做 NMS。这一步很容易漏掉scale_ratio换算导致画框位置偏移。建议在上线前用一张 val_batch*_pred.jpg 原图比对 ONNX 输出确认框对齐后再接摄像头。5.3 摄像头处理节奏课堂视频流一般不逐帧检测而是检测帧穿插跟踪。我通常的做法是先每隔 5 帧做一次 YOLOv9 检测中间帧用 IoU 匹配或简单的位置差值补上能省掉一大半模型推理时间。状态识别不需要精确到每一帧学生从抬头到低头通常持续数秒5 帧间隔足够。部署时把摄像头分辨率降到 960×540模型输入保持 640检测精度几乎不变但延迟下降明显。重参数化之后的 best.pt 可以继续压成 FP16在 TensorRT 上跑单卡实际帧率通常会超过 40 FPS。本文还有配套的精品资源点击获取
返回列表