ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

嗜睡检测数据集与YOLOv8训练全流程:从标注格式到避坑指南

嗜睡检测数据集与YOLOv8训练全流程:从标注格式到避坑指南 简介面向YOLO系列目标检测任务构建的嗜睡状态识别数据集覆盖头部下垂、唤醒、昏昏欲睡、分心、吸烟、打哈欠、打电话等典型驾驶与作业场景适合疲劳驾驶预警、驾驶员注意力监测等方向的模型训练和算法验证。压缩包约172.36MB共2000个文件适配8979张图像数据主要内容为VOC格式xml标注并配套存放YOLO格式txt标签YOLO标签按class、x_center、y_center、width、height的归一化方式组织类别索引从0开始可直接用于主流检测框架数据已划分训练集、验证集和测试集附带data.yaml配置适配YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等版本。已有91人学习下载免去自行采集图像、标注清洗和分配数据集的繁琐流程。对需要快速开展目标检测实验或落地疲劳状态识别的开发者、学生和算法工程师来说是一份结构清晰、拿来即用的训练数据基础。1. 嗜睡检测数据集8979 张图、双标签格式直接喂给 YOLO 训练做驾驶员监控DMS或者疲劳检测的同行应该都有体会找数据集比调模型还痛苦。要么是公开数据集类别对不上要么是标注格式要花一晚上写脚本转。这份嗜睡检测数据集是目前我见过最省事的——8979 张图像七类目标头部下垂、唤醒、昏昏欲睡、分心、吸烟、打哈欠、打电话全部带标签而且同时给了 YOLO 的 txt 和 VOC 的 xml 两种格式train/val/test 已经划分好连 data.yaml 都配好了。意味着你拿到手解压、改一下路径yolov8 直接就能开始训练不用做任何标注转换或划分工作。适合正在做疲劳驾驶检测、课堂专注度分析、工位离岗监测这类项目的从业者也适合刚入门 YOLO 想拿真实数据集练手的人。2. 先看懂资源结构txt 和 xml 双格式到底怎么对应2.1 解压后的目录与标签文件对应关系拿到压缩包第一件事不是急着训练而是先理解文件组织。这个数据集的标注文件是分两个文件夹存放的一个放 yolo 格式的 txt一个放 voc 格式的 xml。图像本体一般和标注在同一层级解压后你会看到类似这样的结构drowsy_dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── img_0792_5229.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels_yolo/ │ ├── train/ │ │ ├── img_0792_5229.txt │ │ └── ... │ ├── val/ │ └── test/ └── labels_voc/ ├── train/ │ ├── img_0792_5229.xml │ └── ... ├── val/ └── test/两份标注文件与图像同名只是扩展名不同。txt 是给 YOLO 系列直接吃的xml 是给 VOC 格式训练或做二次标注用的。我一般会先用一条命令校验数量是否对齐find . -name *.jpg | wc -l find . -name *.txt | wc -l find . -name *.xml | wc -l正常应该是三个数字一致部分 xml 可能因标注中途调整而略少或略多这里是已经对齐过的数字是 8979。如果哪个数字对不上说明数据有缺漏后面训练时会报 label 缺失警告需要先把缺的补出来。2.2 YOLO 坐标格式与归一化规则txt 文件的每一行代表一个目标框格式固定是五个数class x_center y_center width height。class 从 0 开始对应 data.yaml 里的 names 列表顺序四个坐标值全部是归一化后的比例范围 0 到 1不是像素坐标。看一个实际标注内容cat labels_yolo/train/img_0792_5229.txt # 输出示例 2 0.482031 0.491667 0.236667 0.325000 5 0.541667 0.529167 0.210000 0.383333第一行的 class 是 2代表该框类别索引为 2x_center 0.482 表示目标框中心点在图像水平方向的 48.2% 处y_center 0.492 是垂直方向的 49.2%width 0.237 是框宽占图像宽度的 23.7%height 0.325 同理。这种归一化设计的价值在于不管输入图片是 640×480 还是 1920×1080标注文件不需要改YOLO 在缩放图像时坐标自动跟随。尤其适合监控摄像头这种固定分辨率但不同场景会裁剪的情况。而 xml 文件里存的是绝对像素坐标例如xmin312/xminxmax458/xmax。两种格式可以互转但注意以 txt 为训练基准xml 只做校验和二次标注用。因为 YOLO 训练时读的是 txt 的坐标如果两个文件不一致模型学的是 txt 里的内容。2.3 类别与 data.yaml 的映射data.yaml 是 YOLO 训练的唯一入口配置内容一般长这样train: ./images/train val: ./images/val test: ./images/test nc: 7 names: [head_drop, awake, drowsy, distracted, smoking, yawning, phone]七类分别是头部下垂、清醒、嗜睡、分心、吸烟、打哈欠、玩手机。nc 必须与 names 长度一致否则训练直接报错。一个容易忽略的点yolo 训练时train路径指向的是 images 目录不是 labels 目录。模型会自动把路径中的images替换成labels_yolo去找对应的 txt。所以如果你把 images 目录改名或移动位置记住 labels_yolo 里要保留同名文件或手动在 data.yaml 里加一行names对应的标签路径映射。3. 环境准备与一键训练配置yolov8 和 yolo11 都能跑3.1 安装依赖与版本选择这个数据集适配 yolov5、v7、v8、v9、v10、yolo11本质是因为标签格式统一YOLO 系列都吃同一个 txt 格式。我的建议是直接上 yolov8 或 yolo11v8 的生态最成熟遇到问题网上解决方案最多yolo11 是最新版本精度和速度都有提升。这里用 yolov8 做演示因为它的 API 最简洁。# 创建虚拟环境Python 3.8~3.11 都支持 conda create -n drowsy python3.10 -y conda activate drowsy # 安装 ultralytics自带 yolov8 权重 pip install ultralytics # 验证安装 yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg如果刚装了 CUDA 环境建议同时确认一下 GPU 是否被识别python -c import torch; print(torch.cuda.is_available())输出 True 说明 GPU 可用False 的话 CPU 也能训练但 8979 张图 7 类CPU 训练一轮估计要 40 分钟GPU 只要几分钟差距很大。3.2 修改 data.yaml 并启动训练拿到的 data.yaml 里 train 和 val 路径是相对路径但它相对于执行命令的目录解析所以最稳妥的做法是改成绝对路径或者把终端 cd 到数据集根目录再执行。我的习惯是改成绝对路径一劳永逸train: /data/drowsy_dataset/images/train val: /data/drowsy_dataset/images/val test: /data/drowsy_dataset/images/test nc: 7 names: [head_drop, awake, drowsy, distracted, smoking, yawning, phone]保存后直接训练yolo detect train data/data/drowsy_dataset/data.yaml modelyolov8n.pt epochs50 imgsz640 batch16 device0几个关键参数epochs50对 8979 张图来说够用了再多容易过拟合imgsz640是 YOLO 默认尺寸这套数据集标注是按原图归一化的所以不用改batch16取决于显存12G 显存跑 16 没问题8G 就降到 8device0指定 GPU。训练过程看 loss 曲线正常情况下 box_loss 和 cls_loss 都在下降50 轮跑到后期 loss 曲线趋平就是收敛了。3.3 训练过程中的输出怎么看每轮训练结束终端会打印一张表格box_loss、cls_loss、dfl_loss、precision、recall、mAP50、mAP50-95。重点盯 mAP50它表示 IoU 阈值 0.5 时的平均精度数值在 0.9 以上说明模型已经很好了。如果你发现 mAP50 卡在 0.6 上不去大概率是数据里有类别不平衡比如 awake清醒样本占了一半而 smoking吸烟只有几百张这时先别调模型去看数据分布。最后模型会存在runs/detect/train/weights/best.pt这是验证集上 mAP 最高的权重后面推理都用它不要用last.pt——那是最后一轮的结果可能已经过拟合了。4. 数据分布检查与类别不平衡处理4.1 统计每个类别的样本量训练之前最好先看一眼每个类别的标注数量做到心里有数。写个脚本遍历 labels_yolo 目录import os label_dir labels_yolo/train class_count {i: 0 for i in range(7)} class_names [head_drop, awake, drowsy, distracted, smoking, yawning, phone] for file in os.listdir(label_dir): if not file.endswith(.txt): continue with open(os.path.join(label_dir, file), r) as f: for line in f.readlines(): cls int(line.split()[0]) class_count[cls] 1 for cls, count in class_count.items(): print(f{class_names[cls]}: {count})逻辑很简单读每个 txt 文件的每行取行首类别索引累加计数。跑完你会看到分布如果某个类别只有几百个框而另一个类别有上万训练时模型会倾向于学数量多的类。处理方式有几种最简单的做法是训练时给数据少的类别提高 loss 权重ultralytics 里可以传入class_weights参数或者做离线增强把数量少的类别对应图像做左右翻转、亮度变化、轻微旋转扩充到和多数类同量级。注意yolo 训练默认会做 mosaic 和随机仿射增强所以轻度不平衡其实问题不大但如果最少的类别只有 1/10 的量一定要处理。4.2 小目标问题头部区域占比小这批数据里有一个隐蔽问题头部下垂、打哈欠、打电话这类目标在画面中占的比例并不大。如果图像是 1920×1080 的驾驶室全景头部区域可能只有 200×200 像素归一化后 width 和 height 大概在 0.1 到 0.2 之间。YOLO 默认锚框对中大型目标友好小目标容易漏检。一个常见的补救做法是用数据集的原始分辨率训练而不是统一压缩到 640。把 imgsz 调到 960 或 1280小目标的特征保留更完整代价是显存占用和训练时间增加。如果你的部署设备是嵌入式比如 Jetson训练时用 640推理时也保持 640别训练用 1280 推理用 640精度会掉一截。另外可以看一下标注框的面积分布确认小目标占比import os label_dir labels_yolo/train small_count 0 total_count 0 for file in os.listdir(label_dir): if not file.endswith(.txt): continue with open(os.path.join(label_dir, file), r) as f: for line in f.readlines(): _, x, y, w, h line.split() area float(w) * float(h) if area 0.01: # 小于图像面积 1% 视为小目标 small_count 1 total_count 1 print(fsmall targets: {small_count}/{total_count}, {small_count/total_count*100:.1f}%)如果小目标占比超过 30%训练策略要变开启mosaic0.5降低马赛克增强概率因为 mosaic 会把目标缩得更小、把imgsz960、增大close_mosaic10的最后 10 轮关闭 mosaic 让模型微调。4.3 验证集划分是否合理数据集已经划分好了 train/val/test但还是要检查划分的合理性。最容易犯的错误是相似场景的图像同时出现在训练集和验证集。比如同一个人的连续帧前一帧在 train后一帧在 val模型训练时已经见过几乎相同的画面验证 mAP 会虚高。这是时间序列数据的经典陷阱。检查方法是直接对比文件名train_files set(os.listdir(images/train)) val_files set(os.listdir(images/val)) # 找出相似的帧——文件名通常是连续编号 overlap_candidates [f for f in val_files if f.replace(.jpg, )[:-4] * in [t.replace(.jpg, )[:-4] * for t in train_files]] print(f可能重叠的帧数: {len(overlap_candidates)})如果发现 val 里有大量与 train 高度相似的画面最直接的办法是手动把 val 里那些帧移到 test 目录让验证集对模型完全陌生。数据集作者做的是随机划分大概率没有考虑帧间连续性这块需要你自己把关。5. 常见问题避坑训练时报错、精度异常的排错记录5.1 训练一开始就报“Label shape error”或找不到标签现象执行yolo detect train后几秒内报错提示 label 文件缺失或 shape 不匹配。原因最常见的是 data.yaml 里的 train 路径指向了 images 目录但 images 目录旁边没有 labels_yolo 目录或者有些 txt 文件是空的该图像没有目标YOLO 读到了 0 行的空文件会跳过但不会报错真正报错往往是 txt 里出现了负坐标或大于 1 的坐标值。解决先跑一个统计脚本检查 txt 文件里是否有越界值grep -rE [0-9]\.[0-9] labels_yolo/train/*.txt | awk { if ($2 0 || $2 1 || $3 0 || $3 1 || $4 0 || $4 1 || $5 0 || $5 1) print } | head -20如果输出为空说明坐标合法有输出说明个别标注文件里坐标越界通常是标注工具导出时出错导致把这些文件单独拎出来删除或修复对应行。5.2 mAP50 很高但实际检测漏检严重现象训练完验证集 mAP50 有 0.95但拿到真实场景测试经常漏掉打哈欠和打电话的目标。原因验证集和训练集同分布模型记住了场景特征而不是目标特征。另外打哈欠这个动作本身有强时序特征单帧图片上嘴部张开程度小模型学不到「哈欠」和「张嘴说话」的区别。解决不要只看 mAP要看每一类的 precision/recall。训练结束后用验证脚本单独评估yolo detect val modelruns/detect/train/weights/best.pt datadata.yaml输出里每个类别的 mAP 都有哪类低就针对性补哪类数据。我遇到的情况通常是 smoking 类别最低因为吸烟动作在画面中面积小且被手遮挡多。解决方式是复制 smoking 类别的图像做两次水平翻转和随机亮度调整扩充数量后再补训 20 轮。5.3 batch 设置过大导致 CUDA OOM现象训练几轮后报CUDA out of memory。原因imgsz640时单张图的显存占用大约 1.5Gbatch16 需要 24G 显存如果用的是 3060 12G 或者 4060 8G必然 OOM。解决把 batch 降到与显存匹配一个经验值是显存 8G 用 batch812G 用 batch1624G 用 batch32。同时加cacheTrue参数把图像加载到内存能提速也能减少显存碎片。如果还 OOM就把workers0Windows 上多进程加载有时会造成内存峰值。5.4 类别索引与 data.yaml 错位导致学出来的模型张冠李戴现象训练正常但推理时把头部下垂标成打电话把打哈欠标成吸烟错位很规律。原因txt 文件里的 class 索引与 data.yaml 里的 names 顺序不一致。数据集的 txt 是按一个固定顺序生成的但如果你修改过 names 列表的顺序比如把 phone 放到了第 0 位而 txt 里 class0 本应是 head_drop那么整个模型学的就是错位映射。解决训练前先抽查一个已知的 txt 文件画框确认 class 对应的目标类别。用 OpenCV 检查import cv2 img cv2.imread(images/train/img_0792_5229.jpg) h, w img.shape[:2] with open(labels_yolo/train/img_0792_5229.txt) as f: line f.readline().split() cls, x, y, bw, bh int(line[0]), float(line[1]), float(line[2]), float(line[3]), float(line[4]) x1, y1 int((x - bw/2) * w), int((y - bh/2) * h) x2, y2 int((x bw/2) * w), int((y bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls), (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imwrite(check.jpg, img)然后打开 check.jpg 看框的位置与类别号是否吻合。这一步看似笨拙但我每接一个新数据集都会做能避免后期发现模型行为诡异时浪费半天查 bug。6. 进阶用法把单帧检测结果变成时序状态判断训练出模型只是第一步实际项目里最头疼的是单帧检测不稳定同一段视频里前一帧是 drowsy后一帧变成 awake直接做报警会疯狂误报。我的做法是加一个状态机利用时间序列上的连续性过滤抖动。核心逻辑很简单连续 N 帧检测结果一致才切换当前状态状态持续超过阈值才触发报警。用缓存队列实现from collections import deque class DrowsinessStateMachine: def __init__(self, window_size15, trigger_frames45): self.window deque(maxlenwindow_size) self.current_state awake self.state_count 0 def update(self, frame_result): # frame_result 是当前帧模型输出的最高分类别 self.window.append(frame_result) # 统计窗口内出现最多的类别 count {} for cls in self.window: count[cls] count.get(cls, 0) 1 majority_cls max(count, keycount.get) # 多数类别与当前状态不同且占比超过 80%尝试切换 if (majority_cls ! self.current_state and count[majority_cls] / len(self.window) 0.8): self.current_state majority_cls self.state_count 0 else: self.state_count 1 # 当前状态持续超过 trigger_frames 帧才真正确认 if self.state_count trigger_frames: return self.current_state return uncertain参数说明window_size15是滑动窗口大小15 帧大约 0.5 秒30fpstrigger_frames45表示状态稳定 1.5 秒后才输出确认结果。这样设计的价值在于单帧的误检被窗口平均掉了真正进入嗜睡状态需要连续 1.5 秒的持续证据误报率大幅下降。这个状态机放在模型推理输出的下游完全不影响数据集本身的训练效果任何 YOLO 版本都能用。如果你做的不是实时视频而是图片分类项目那直接跳过状态机用yolo predict批量推理即可yolo detect predict modelbest.pt source./test_images/ save_txtTrue save_confTruesave_confTrue会在 txt 里输出置信度方便后期做阈值筛选。从那以后我每拿到一个检测数据集都会先做一次类别抽查、一次数量统计、一次小目标占比计算三分钟的事却帮我避开了不少后面要花一小时的坑。这个嗜睡数据集本身质量不错双格式和划分都齐直接开训就行。希望帮到你。本文还有配套的精品资源点击获取
返回列表