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

资讯详情

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

YOLO嗜睡数据集实战:8979张图像驱动的疲劳驾驶检测训练指南

YOLO嗜睡数据集实战:8979张图像驱动的疲劳驾驶检测训练指南 简介这是一套面向YOLO系列目标检测模型的嗜睡状态识别数据集覆盖头部下垂、唤醒、昏昏欲睡、分心、吸烟、打哈欠、打电话等多种驾驶疲劳相关场景适用于疲劳驾驶监控、安全辅助驾驶等视觉任务。数据集已完成训练/验证划分并内置data.yaml配置文件可直接接入yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流算法进行训练与测试。压缩包内共2000个文件以VOC格式xml标注文件为主同时配套YOLO格式txt标签文件标签中记录了类别索引、归一化中心坐标及宽高信息方便研究者按需选用。整个资源包约172.36MB结构清晰便于快速开展实验对比。目前已有91人学习下载适合正在学习YOLO目标检测或需要现成数据集做模型训练与调优的开发者。1. 嗜睡数据集 YOLO8979 张带标签图像把疲劳驾驶检测从实验台推向真实场景拿到「yolo算法-嗜睡数据集」这个压缩包的人大概率不是来凑热闹的——要么是车队安全管理系统要上线驾驶员状态识别要么是研究生在做基于视觉的疲劳检测课题还有一部分是刚入门学习 YOLO、想找一份带标签的真实数据集跑通全流程。这个数据集把 8979 张图像按「头部下垂、唤醒、昏昏欲睡、分心、吸烟、打哈欠、电话」7 类行为做了标注恰好覆盖了驾驶舱内最典型的疲劳与分心动作。比起自己从零标注几万张图或者拿 COCO 那种通用数据集硬套这份数据的价值在于它的标签和场景集中在「人脸 上半身行为」上和驾驶员监控DMS的实际部署环境高度匹配。但数据到手只是第一步真正决定模型能不能用的是解压后你怎么处理标注格式、分布偏差和验证方式。2. 解压先别急着训练目录结构、标签格式与坐标合法性才是第一关2.1 zip 解压后的常见目录结构先认清 images 和 labels 是怎么组织的拿到 zip 文件后最忌讳的是解压完直接丢给训练脚本跑。先花五分钟把目录结构看清楚后面能省下大量排错时间。常见的数据集打包方式是images/存图像、labels/存同名 txt 标签文件偶尔会附带一个classes.txt或data.yaml。用 unzip 解压时加-d指定输出目录避免把一堆文件散落在当前文件夹里。unzip yolo算法-嗜睡数据集-8979张图像带标签-*.zip -d drowsiness_dataset cd drowsiness_dataset find . -maxdepth 2 -type d | sort-d参数指定解压目标目录find只列出两层目录结构目的是快速判断是否存在嵌套目录。很多压缩包在根目录下又套了一层文件夹例如drowsiness_dataset/images和drowsiness_dataset/labels这种情况下训练脚本里的路径前缀就要加上内层目录名。另外有的数据包把训练集和验证集已经分开如train/、val/下各自有images/和labels/有的则全部混在一起由你自己划分。第二步先确认有没有classes.txt有的话cat看一下类别顺序这个顺序必须和标签文件里的数字索引严格一致。2.2 标签文件逐行解析YOLO 格式的 class cx cy w h 到底怎么存YOLO 系列v5/v8 等使用的标签格式是纯文本每行一个目标五个字段类别索引、归一化中心点 x、归一化中心点 y、归一化宽度、归一化高度。注意两点一是中心点坐标不是左上角坐标二是宽高都是相对图像宽高的比例取值在 0 到 1 之间。这个数据集的 7 个类别索引从 0 开始对应关系完全取决于data.yaml里names列表的顺序这一点非常关键——它不是 COCO 那种固定 80 类语义而是自定义的索引映射。head -5 labels/000001.txt典型的输出可能长这样0 0.6843 0.5127 0.1284 0.3561 1 0.7198 0.4266 0.1132 0.3146第一列0代表第一个类别至于是head_droop还是其他要看data.yaml的names顺序0.6843 0.5127是目标中心点的归一化坐标0.1284 0.3561是目标框的归一化宽高。如果某个文件里有5这个索引说明它对应的是第七个类别标签。数据集的标注人员是按自己的类别定义来写这个数字的训练脚本只认数字不认名称一旦names顺序写错整个训练结果就会张冠李戴。2.3 用 Python 快速核对 7 个类别的样本数与标签坐标合法性不要轻信压缩包里的文件数量统计。图像有 8979 张但对应的标签可能存在空文件、重复文件、坐标越界等问题。写一个脚本跑一遍把每个类别的框数量统计出来同时检查坐标是否越界、宽高是否为非正数。这一步相当于给数据集做一次体检花不了几分钟但能拦住后面几小时的白忙活。import os from collections import Counter labels_dir labels class_counter Counter() invalid_files [] for fname in os.listdir(labels_dir): if not fname.endswith(.txt): continue path os.path.join(labels_dir, fname) with open(path, r, encodingutf-8) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: invalid_files.append((fname, 字段数不是5)) continue cls, cx, cy, w, h parts try: cx, cy, w, h float(cx), float(cy), float(w), float(h) except ValueError: invalid_files.append((fname, 坐标非数值)) continue if not (0 cx 1 and 0 cy 1): invalid_files.append((fname, f中心点越界 {cx},{cy})) if w 0 or h 0: invalid_files.append((fname, f宽高为非正数 {w},{h})) class_counter[cls] 1 print(每个类别的标注框数量:, dict(class_counter)) print(异常文件数:, len(invalid_files)) for item in invalid_files[:10]: print(item)Counter统计的是标注框总数而非图像数因为一张图可以包含多个目标比如司机和副驾同时出现在画面里。越界判断用0 cx 1与0 cy 1YOLO 训练时遇到坐标超出图像范围的目标会强制裁剪或直接丢弃导致训练样本分布和标注初衷不一致。invalid_files只打印前 10 条避免刷屏。这一步跑完你就能回答「这 7 个类别的样本够不够训」这个核心问题。3. 数据体检类别不平衡、坏图和标注噪声直接决定 mAP 天花板3.1 先看类别分布唤醒样本多、头部下垂样本少是情理之中但要量化嗜睡检测数据集的类别分布几乎必然是不平衡的。正常驾驶状态下「唤醒」类图像最多「打哈欠」「头部下垂」这类疲劳表征相对难采集数量可能只有前者的十分之一。这不是数据集的缺陷而是场景的客观规律。关键是你得知道不平衡到什么程度以及它会对训练造成什么影响。YOLO 的损失函数是逐目标累计的多数类样本贡献的梯度占绝对主导模型会倾向于把一切模棱两可的目标判成多数类。最直观的后果是「头部下垂」这类少数类的召回率极低——测试时十个真正下垂的只检出两三个。我的处理经验是先画一张类别分布表。框数量低于 1000 的类别视为稀缺类训练时要单独给 loss 加权或做针对性增强500 以下的基本上需要考虑补数据否则光靠增强撑不起泛化能力。类别估计框数区间处理建议唤醒3000正常参与训练可适当随机下采样昏昏欲睡1500-3000常规参与训练分心800-1500重点关注配合增强打哈欠500-1000需要针对性增强或补样头部下垂300-800稀缺必须加权或补样吸烟/电话300-800稀缺可考虑合并或补样表格里的区间是估计值实际以你自己跑出来的统计为准。如果「唤醒」类是「头部下垂」类的 10 倍以上先别急着对数据下手直接训练一版 baseline 看看混淆矩阵往往稀缺类不是完全检不出而是被邻近的多数类吞掉了。3.2 清洗坏图与异常标注代码损坏图像、缺失标签、空标签文件分别处置8979 张图里存在少量损坏文件太正常了。常见的翻车有三类图像本身损坏cv2.imread返回None图像存在但对应标签文件缺失标签文件是 0 字节。YOLO 训练时缺标签的图像会被跳过并打 warning但如果你用的是自定义数据加载器空文件可能直接报错。写一个清洗脚本跑一遍把这些问题文件隔离到corrupted/目录而不是直接删除——删错了还能后悔删了就没了。import cv2 import os import shutil img_dir images labels_dir labels corrupt_dir corrupted os.makedirs(corrupt_dir, exist_okTrue) for img_name in os.listdir(img_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue base os.path.splitext(img_name)[0] img_path os.path.join(img_dir, img_name) label_path os.path.join(labels_dir, base .txt) img cv2.imread(img_path) if img is None: print(f损坏图像: {img_name}) shutil.move(img_path, os.path.join(corrupt_dir, img_name)) if os.path.exists(label_path): shutil.move(label_path, os.path.join(corrupt_dir, base .txt)) continue if not os.path.exists(label_path): print(f缺少标签: {img_name}) # 移动但保留记录后续可以人工补标 shutil.move(img_path, os.path.join(corrupt_dir, img_name)) empty_count 0 for fname in os.listdir(labels_dir): path os.path.join(labels_dir, fname) if os.path.getsize(path) 0: empty_count 1 print(f空标签文件数: {empty_count})cv2.imread对损坏的 JPEG 会返回None但也要注意有些图像能读出来却不完整如半张黑屏脚本无法察觉需要抽检。缺失标签的图像移入corrupted后训练集和标签集重新对齐。空标签文件数是单独统计的0 字节 txt 在 YOLO 训练中会被当成「背景图」处理——如果空标签文件太多说明很多图像被标为无目标这类图多了会让模型过度偏向背景出现严重漏检。一般空标签占比超过 5% 就要警惕。3.3 数据增强按场景补别把 mosaic 当万能药驾驶舱场景和通用目标检测不一样人脸、头部、手部的位置分布有强先验目标基本集中在画面中央偏上角度变化有限光照是最大的变量。这就决定了增强策略不能照搬 COCO 训练那套——随机 90 度旋转会生成「头朝下」的物理荒谬图像水平翻转倒是可以因为左右驾驶位都存在。合理的组合是色域扰动亮度 ±20%对比度 ±15%、高斯模糊模拟摄像头对焦不稳、小角度旋转±10°、随机裁剪缩放scale 0.8~1.2。mosaic 增强对小目标检测效果显著但在这个数据集上要慎用——几张图拼接后人脸的位置关系完全失真模型学到的东西和真实驾驶舱不符。import albumentations as A train_transform A.Compose([ A.HorizontalFlip(p0.5), A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.15, p0.6), A.GaussianBlur(blur_limit(3, 7), p0.2), A.Affine(rotate(-10, 10), scale(0.8, 1.2), p0.5), A.CLAHE(clip_limit2.0, tile_grid_size(8, 8), p0.1), ])brightness_limit0.2模拟白天阴影和逆光夜间场景后续单独用 Gamma 变换或仿真低照度。blur_limit按奇数三元组给出模拟轻微失焦Affine里的旋转和缩放模仿驾驶员头部微小位移。CLAHE用于局部对比度增强处理后眼睑和瞳孔区域更清晰对嗜睡检测——特别是眼睑下垂这个关键特征——有实际帮助。不建议在这里用RandomCrop大范围裁剪因为标注框本身就不大过度裁剪会丢失语义上下文。4. 划分数据集与训练配置从 data.yaml 到训练命令一次跑通 GPU 训练4.1 按类别分层划分训练集、验证集和测试集把 8979 张图按默认顺序直接切成 8:1:1 是新手最容易犯的错。由于图像是按采集时间排序的连续帧高度相似如果前 80% 都是白天样本后 20% 都是夜间样本验证集和测试集会失去代表性。正确做法是按「图像中包含的类别集合」做分层划分至少保证稀缺类在三个集合里的比例接近原始分布。sklearn 的train_test_split支持按标签分层但这里有个更简单的路径先把每张图的类别标签聚合到文件名上再按这个聚合结果做分层。import os from sklearn.model_selection import train_test_split import shutil imgs [f for f in os.listdir(images) if f.endswith((.jpg, .jpeg, .png))] imgs.sort() # 保证可复现 # 用文件名前缀作为 stratify 依据真实场景更严谨的做法是聚合每张图的类别组合 train, val train_test_split(imgs, test_size0.15, random_state42, stratify[f.split(_)[0] for f in imgs]) train, test train_test_split(train, test_size0.1, random_state42) for split, files in [(train, train), (val, val), (test, test)]: os.makedirs(fimages_{split}, exist_okTrue) os.makedirs(flabels_{split}, exist_okTrue) for fname in files: src_img os.path.join(images, fname) dst_img os.path.join(fimages_{split}, fname) shutil.move(src_img, dst_img) base os.path.splitext(fname)[0] src_lbl os.path.join(labels, base .txt) if os.path.exists(src_lbl): shutil.move(src_lbl, os.path.join(flabels_{split}, base .txt))这里stratify接受的是每个样本的类别分组标签我临时用了文件名前缀做演示。真实项目中正确做法是遍历每张图对应的标签文件收集该图所有类别索引然后把这些索引拼接成字符串作为分层依据比如0_3代表这张图同时含类别 0 和类别 3。这样才能保证「头部下垂」这种稀缺类不会整批落入测试集。random_state42固定随机种子确保两次运行划分结果一致。划分后移动文件到各自目录train、val、test互不重叠且同类目标占比接近原始数据。4.2 写对 data.yamlnc 数量、names 顺序、路径三者必须保持一致YOLOv8 训练时读的 data.yaml 是这个数据集能否被正确消费的核心。三个最容易出错的点nc必须是 7和实际类别数一致names列表的顺序必须和标签文件里的数字索引一一对应路径必须是绝对路径或相对当前工作目录的有效路径。先确认标签文件里出现的最大索引值——如果脚本统计发现最大索引是 6说明 7 类从 0 到 6 排布如果最大索引是 5说明数据集实际只有 6 类nc写 7 会让最后一个类别永远没有正样本。# data.yaml path: /home/yourname/drowsiness_dataset # 改成你的实际绝对路径 train: images_train val: images_val test: images_test nc: 7 names: 0: head_droop 1: awake 2: drowsy 3: distracted 4: smoking 5: yawning 6: phonepath字段告诉 YOLO 数据集根目录在哪里。如果path是相对的YOLO 会根据当前执行命令的工作目录拼接容易因为终端位置不同而报错所以我通常直接写绝对路径省心。names列表的顺序就是类别索引的语义映射。第一次拿到这个数据集时我先随机抽了几张图用脚本把每个标注框画出来看——框应该落在人的哪个部位——对照 names 逐项验证。这一步不能省纯靠文字猜类别对应关系极大概率翻车。画框脚本可以用cv2.rectangle坐标反归一化回像素值再画20 行代码就能完成。4.3 训练命令与关键超参先用小模型跑通再谈提升YOLO 训练可以先用yolov8n.pt这个最小模型做 baseline。n 模型参数最少训练速度快一轮跑下来大概十几分钟到半小时取决于 GPU能让你快速确认数据链路是否畅通。确认没有报错、loss 在下降、mAP 在上涨之后再换yolov8s.pt或更大模型。imgsz 用 640 起步因为驾驶员的脸在图像里占比通常较小640 分辨率下小目标已经比较吃力如果显存充足且标注框多数大于 32×32 像素可以试 960但训练时间会增加。cd drowsiness_dataset yolo detect train \ datadata.yaml \ modelyolov8n.pt \ imgsz640 \ epochs100 \ batch16 \ patience10 \ projectruns/drowsy \ namebaseline各参数作用modelyolov8n.pt加载 COCO 预训练权重做迁移学习不是从零随机初始化能显著缩短收敛时间imgsz640输入分辨率实际训练中会自动 resizeepochs100给足迭代次数配合patience10实现早停即验证集 mAP 连续 10 个 epoch 不提升就结束训练防止过拟合batch16按显存调整8GB 显存跑 n 模型可以更小16GB 跑 s 模型也可用这个值project和name指定输出目录避免覆盖之前的实验。第一次训练时建议把patience关掉或设大一点先看完整的 loss 下降曲线了解这个数据集能收敛到什么程度。5. 训练与推理的 4 个常见坑从标签索引到损失函数逐一排查5.1 标签索引张冠李戴loss 不降反升mAP 几乎为零现象训练正常启动box_loss 在前几个 epoch 下降后停滞cls_loss 跌不下去训练集的 mAP0.5 一直低于 0.1。原因data.yaml的names顺序和标签文件里的索引对不上。比如数据集的作者把「头部下垂」定义成索引 0你的names列表里索引 0 却写的awake——模型被反复纠错梯度互相矛盾训练自然不收敛。解决回到第 2.3 节用脚本抽取每个索引对应的图像块人工核对哪张图代表哪个类别。我自己的血泪经验是不仅要看框的位置还要看框里的内容——有些数据集的「头部下垂」框只包含头部有的把肩膀也包含进去这会直接影响模型学到的特征范围。5.2 验证集 mAP 很高真实驾驶视频里漏检严重现象baseline 在验证集上 mAP0.5 达到 0.87看起来不错但把训练好的模型接到行车记录仪视频上夜间场景「昏昏欲睡」一个都检不出来。原因验证集和训练集同源光照条件、摄像头角度、图像分辨率都高度一致模型在「同分布」数据上表现好不代表泛化到真实场景。数据集的 8979 张图大概率是实验室环境下用固定摄像头拍的而实际部署可能是红外摄像头、从下往上拍的角度、强逆光或夜间低照度。解决不要只看验证集指标。找一段现场真实视频切成帧挑 200~500 张做补充标注混入训练集重新训练。形态差距太大时先做数据增强的「现场模拟」降亮度、加噪声、模拟红外灰度跑一版看效果。5.3 相邻类别互相混淆哈欠被识别成昏昏欲睡分心和电话纠缠不清现象混淆矩阵显示 5yawning和 2drowsy交叉严重3distracted和 6phone也互相串。原因嗜睡数据集的语义边界本身模糊。打哈欠时眼睛半闭、嘴张开和昏昏欲睡的外貌特征高度重叠分心可能包括扭头、低头看手机、摆弄中控屏标注人员很难统一口径。纯视觉检测在一个 640 分辨率的图片上区分这些细微差异本身就接近上限。解决第一合并语义相近的类别——把「吸烟」和「电话」并入「分心」把「打哈欠」并入「昏昏欲睡」减少类别数后 mAP 通常肉眼可见地涨第二若业务必须细分则考虑在检测基础上加一个分类分支比如对检测框做二次分类或者换用更大分辨率输入让嘴部张合、手持物体等细节更清晰第三检查损失函数YOLOv8 默认的cls_loss用 BCEWithLogits类别不平衡时可以在训练配置里增大稀有类别的 loss 权重。5.4 显存不足或训练中途 OOM别每次从头跑学会续训和降配置现象batch16 训练到第 30 个 epoch 时显存溢出CUDA out of memory进程被杀重启后只能从头训练。原因模型在训练过程中会为梯度图和中间激活值占用显存如果推理阶段的显存估算和训练阶段差距大容易出现前几十轮没事后面炸掉。另一类原因是Rectangular training启用后batch 内图像尺寸不固定偶尔出现超大分辨率图导致显存峰值。解决把batch降到 8 或 4这是最直接的或者开启梯度累积梯度的方式——严格来说 YOLO 本身不直接提供gradient_accumulation参数需要改代码不如直接降 batch再把cache参数从ram改成disk或关掉避免缓存全部图像到内存导致 CPU 内存也不够。如果已经训练到 30 轮可以用resumeTrue从上次的 checkpoint 续训不必真正从零开始。6. 让 8979 张图发挥真正的价值迁移学习、夜间场景增强与端到端验证6.1 用预训练权重做迁移学习别硬从零初始化这个数据集有 8979 张图对于 YOLO 这种数据饥渴模型来说只够微调不够从零训练。我建议第一版就用yolov8n.pt或yolov8s.pt预训练权重起步它的 COCO 特征提取层对边缘、纹理、人体部件已经有不错的先验。如果你想把精度再压一档可以试efficient head yolo这类改检测头的结构但前提是 baseline 已经跑通、数据没有低级错误。否则改结构和换损失函数叠加一起出了问题你根本分不清是数据问题还是模型问题。6.2 夜间场景的针对性增强嗜睡检测真正的主战场是夜间和隧道但这个数据集中夜间样本大概率占比不高。一个有效的做法是离线生成夜间仿真图对原图做亮度衰减gamma 1、加高斯噪声、做局部阴影遮挡然后把这些仿真图混入训练集。另一种思路是用图像超分辨率重建或低照度增强算法把夜间图先提亮再送入检测模型但这会增加一重预处理开销嵌入式平台上不一定划算。我的习惯是先做在线增强在训练时对每张图以一定概率套用 CLAHE 和亮度扰动白天图也能模拟夜间对比度分布。6.3 端到端验证闭环用一段 5 分钟真实视频检验模型而不是只看 mAP训练完别急着部署。取一段带有人工标注的 5 分钟驾驶室视频切成帧跑推理统计每个类别的检出帧数、漏检帧数、误报帧数。如果你手里没有这种视频退而求其次从原始数据集的测试集里把表现最差的那一类的样本都抽出来逐张看检测结果。我第一次拿这类数据集训完直接跑了一段工地夜间值班室的视频模型把安全帽反光当成了「唤醒」把风扇转动产生的抖动全判成「分心」后来发现是数据集的「分心」标注过宽把转头、弯腰都算进去了。从那以后我做任何检测项目都先画混淆矩阵再谈精度指标——先找哪些类别在互相打架再决定是调损失权重还是合并类别。用了两份不同来源的嗜睡数据混合训练后模型才真正扛住现场环境。训练数据集的规模永远赶不上真实场景的多样性保留一部分最近采集的现场数据做持续微调才是让检测系统长期好用的常态。希望帮到你。本文还有配套的精品资源点击获取
返回列表