基于YOLO的疲劳驾驶检测实战:8511张DMS数据集快速跑通指南
发布时间:2026/9/30 3:30:14来源:尧图网络
简介本资源为面向疲劳驾驶检测DMS场景的YOLO系列目标检测数据集适合从事车载监控、驾驶员状态识别方向的算法工程师与高校研究者使用可支撑安全带佩戴、疲劳唤醒、打哈欠、打电话等行为的识别模型训练与验证。压缩包共2000个文件以VOC格式的xml标注文件为主同时提供YOLO格式txt标签两种标注分别存放于独立文件夹并附带data.yaml配置文件可直接对接yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流框架。数据集已按训练与验证需求划分完毕图像总量达8511张覆盖无安全带、唤醒、昏昏欲睡、安全带、电话、打哈欠等多类驾驶行为标注坐标采用归一化中心点与宽高表示便于直接读取训练。资源包整体约240.12MB目录结构清晰已有393人学习下载适合快速搭建疲劳驾驶检测基线模型并开展对比实验。1. 8511 张带标签的 DMS 疲劳驾驶数据集为什么值得你花一个周末跑通如果你正在找一份能直接喂给 YOLO 的驾驶员监控DMS数据集又不想在清洗和标注上耗掉两周那这个 8511 张图像、带标签、覆盖「没系安全带 / 唤醒 / 昏昏欲睡 / 系安全带 / 打电话 / 打哈欠」六类行为的压缩包基本就是为「快速验证一个疲劳驾驶检测 demo」准备的。它解决的不是学术 benchmark 问题而是工程里最烦的那一步拿到一批已经框好的真实驾驶舱图像直接开训当天就能看到检测框落在人脸、手部、安全带和嘴部上。适合两类人一类是想把 YOLO 从「会跑官方 coco」推进到「能跑自己业务数据」的算法新手另一类是要给座舱产品做行为识别预研、需要先出一版可演示模型的工程师。数据集本身不复杂但把它跑通、跑对、跑出能看的指标中间有几个参数和标注坑值得提前说清楚。2. 先看清这 8511 张图里到底有什么六类行为的标注逻辑与选型理由2.1 六类标签的语义边界决定了你的类别设计这个数据集的核心价值在于标签是按「驾驶行为」而不是按「物体」来分的。常见的六类大致是安全带已系、没有安全带、打电话、打哈欠、昏昏欲睡、唤醒。注意这里有个容易翻车的点——「安全带」和「没有安全带」是互斥状态而「打电话」「打哈欠」「昏昏欲睡」是可以和安全带状态共存的。也就是说一张图里可能同时出现「系安全带 打电话」两个框。所以你在设计data.yaml的names时不要想当然地把它当成六个互斥类别去做单标签分类它本质是多标签目标检测。YOLO 的多框机制天然支持这种共存但你要保证标注文件里同一张图允许多个不同 class id 的框同时存在。我一般会先统计每个类别的框数量分布确认没有某一类被漏标成背景。import os from collections import Counter label_dir labels/train counter Counter() img_with_multi 0 for f in os.listdir(label_dir): if not f.endswith(.txt): continue classes set() with open(os.path.join(label_dir, f)) as fp: for line in fp: line line.strip() if not line: continue cid int(line.split()[0]) counter[cid] 1 classes.add(cid) if len(classes) 1: img_with_multi 1 print(每类框数量:, dict(sorted(counter.items()))) print(含多类共存的图像数:, img_with_multi)这段脚本做两件事统计每个 class id 的框总数以及统计有多少张图同时含多个类别。逻辑说明classes用集合去重避免同一张图同类多框被重复计数img_with_multi直接告诉你多标签共存的比例。参数说明label_dir换成你解压后的实际标签路径YOLO 格式的标签是class x_center y_center width height全部归一化到 0~1。如果发现某一类框数为 0基本就是标签目录选错了或者类别映射对不上。2.2 为什么选 YOLO 而不是分类网络来做这件事有人会问疲劳检测用 CNN 分类睁眼/闭眼、张嘴/闭嘴不就行了吗在这个数据集上不行原因是「打电话」和「打哈欠」是局部动作分类网络只能给整图一个标签无法定位是手在耳边还是嘴在张。而 DMS 产品最终要在画面上画框、给区域告警检测是更自然的形态。YOLO 系列在 8511 张这个量级上从 yolov8n 到 yolov8s 都能在单卡上几十分钟内跑完一轮性价比很高。选型上我的建议先用yolov8n或yolov5s这种轻量骨干把流程跑通确认标签没问题、mAP 能起来再换大模型冲指标。别一上来就上大模型否则训练慢、排错也慢标签错了你还以为是模型不行。2.3 数据划分8511 张别全塞进 train8511 张不算多划分比例直接按 8:1:1 走即 train 约 6800、val 约 850、test 约 850。但这里有个血泪经验如果同一段视频抽出来的连续帧被随机打散到 train 和 val验证集指标会虚高因为相邻帧几乎一样。正确做法是按「驾驶员/视频片段」维度划分而不是按帧随机划分。# 假设原始图像和标签分别在 images/ 和 labels/ # 先按文件名前缀代表不同驾驶员或片段分组再整组划分 python - PY import os, random, shutil random.seed(42) img_dir, lbl_dir images, labels groups {} for f in os.listdir(img_dir): if not f.lower().endswith((.jpg, .png, .jpeg)): continue key f.split(_)[0] # 按前缀分组按你实际命名调整 groups.setdefault(key, []).append(f) keys list(groups.keys()) random.shuffle(keys) n len(keys) train_k keys[:int(n*0.8)] val_k keys[int(n*0.8):int(n*0.9)] test_k keys[int(n*0.9):] def dump(keys, split): os.makedirs(fdataset/images/{split}, exist_okTrue) os.makedirs(fdataset/labels/{split}, exist_okTrue) for k in keys: for f in groups[k]: shutil.copy(os.path.join(img_dir, f), fdataset/images/{split}/{f}) lf os.path.splitext(f)[0] .txt src os.path.join(lbl_dir, lf) if os.path.exists(src): shutil.copy(src, fdataset/labels/{split}/{lf}) dump(train_k, train); dump(val_k, val); dump(test_k, test) print(划分完成) PY逻辑说明先按文件名前缀把属于同一驾驶员或同一片段的图聚成组再整组分配到 train/val/test避免相邻帧泄漏。参数说明f.split(_)[0]里的分隔符要按你实际命名规则改如果你的文件名没有可分组前缀就退而求其次按时间戳或文件夹分组。random.seed(42)保证划分可复现团队协作时别省这一步。3. 用 YOLOv8 在本地跑通第一版疲劳驾驶检测模型3.1 环境与目录结构先把地基铺平训练前先把目录摆正YOLO 对路径很敏感路径错了报的错往往和真实原因差十万八千里。推荐结构如下dms_yolo/ ├── dataset/ │ ├── images/{train,val,test}/ │ └── labels/{train,val,test}/ ├── data.yaml └── runs/data.yaml内容path: ./dataset train: images/train val: images/val test: images/test names: 0: seatbelt 1: no_seatbelt 2: phone 3: yawn 4: drowsy 5: wake参数说明path是数据集根目录train/val/test是相对path的子路径别写成绝对路径换机器就崩。names的顺序必须和标签文件里的 class id 严格对应错一位整个训练就废了。装环境用 pip 即可pip install ultralytics python -c import ultralytics; print(ultralytics.__version__)3.2 训练命令与关键参数怎么设第一版训练不要调花哨的参数先把 baseline 跑出来yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns \ namedms_baseline逻辑说明modelyolov8n.pt用官方预训练权重做迁移学习8511 张图从零训容易过拟合迁移能明显加速收敛。imgsz640是速度和精度的平衡点座舱图里人脸、手部目标不算特别小640 够用。batch16按你显存调8G 显存跑 640 一般能到 16爆显存就降到 8。patience20是早停连续 20 轮验证指标不涨就停省时间。lr00.01是初始学习率迁移学习场景下这个值比较稳别一上来就 0.1。训练过程中重点盯三个东西box_loss是否稳定下降、mAP50是否在涨、验证集的precision/recall是否严重失衡。如果 recall 很低多半是漏标或者类别不平衡如果 precision 低可能是背景被误检检查一下有没有把方向盘、后视镜误标成目标。3.3 推理验证把框画到图上肉眼确认指标是一回事框画得对不对是另一回事。训完立刻拿几张验证图跑推理yolo detect predict \ modelruns/dms_baseline/weights/best.pt \ sourcedataset/images/val \ conf0.25 \ saveTrue \ projectruns \ namedms_pred参数说明conf0.25是置信度阈值疲劳检测场景下我一般先设 0.25 看召回宁可多框也别漏框后续再按业务调。saveTrue会把带框的图存到runs/dms_pred直接翻图看。重点看三类错误安全带框到衣服上、打哈欠和昏昏欲睡混淆、打电话的手部框偏。这三类是这个数据集上最常见的翻车点。4. 训练不收敛、指标虚高、类别混淆DMS 数据集排查清单4.1 现象mAP 一直卡在很低的值不涨原因最常见的是data.yaml里names顺序和标签 class id 对不上或者标签路径写错导致读不到标签模型在学背景。其次是图像和标签文件名没对齐比如图是001.jpg标签是001.txt但放在错误子目录。解决先跑一遍第 2.1 节的统计脚本确认每类框数正常再用yolo detect train启动后看日志里打印的train: xxx images, xxx backgrounds如果 backgrounds 数量异常高说明大量图没匹配到标签回去查文件名和路径。4.2 现象验证集 mAP 高得离谱实际推理一塌糊涂原因相邻帧泄漏。同一段视频的连续帧被随机分到了 train 和 val验证集里全是训练集见过的画面指标自然虚高。解决按第 2.3 节的方式按驾驶员或片段整组划分而不是按帧随机。划分完可以抽查几张 val 图确认它们和 train 图不是同一时刻的连续帧。4.3 现象打哈欠和昏昏欲睡总是互相误检原因这两类在视觉上高度相关——昏昏欲睡的人往往也在打哈欠标注时边界模糊同一张脸可能被不同标注者打成不同类。解决先人工抽查一批混淆样本看是标注本身不一致还是模型能力不足。如果是标注问题考虑把两类合并成一个「疲劳」大类先跑通再决定要不要细分。如果坚持细分可以在训练时对这两类做更强的数据增强如随机遮挡嘴部逼模型学更细的特征。4.4 现象安全带相关类别 recall 特别低原因安全带是细长目标且颜色常和衣服接近640 分辨率下特征弱另外「没有安全带」这类负向状态容易被标成背景。解决把imgsz提到 960 或 1280 再训一版对比检查「没有安全带」是否真的有框很多数据集这类负向类标注稀疏需要确认标注规范。必要时对安全带区域做裁剪增强。4.5 现象训练到后期 loss 突然变成 nan原因学习率过高、batch 里有异常标注坐标超出 0~1 范围、或者某张图标签格式损坏。解决先降lr0到 0.001 重跑再写脚本校验所有标签坐标是否在 [0,1] 内把越界的行揪出来修掉。YOLO 对越界坐标不会报错但会悄悄把训练带偏这个坑很隐蔽。import os bad [] for f in os.listdir(dataset/labels/train): if not f.endswith(.txt): continue with open(os.path.join(dataset/labels/train, f)) as fp: for i, line in enumerate(fp): p line.split() if len(p) ! 5: bad.append((f, i, 字段数不对)); continue vals list(map(float, p[1:])) if any(v 0 or v 1 for v in vals): bad.append((f, i, 坐标越界)) print(异常标签条数:, len(bad)) for b in bad[:20]: print(b)逻辑说明逐行检查字段数和坐标范围把格式损坏和越界的标签列出来。参数说明路径按你的实际 split 改train/val 都要查一遍。这个脚本建议在每次训练前都跑一次当作数据体检。5. 把 demo 推到可用类别合并策略、阈值调优与一个提点技巧跑通 baseline 之后真正决定这个方案值不值得继续投入的是它能不能在业务阈值下稳定工作。这里分享几个我实际用下来有效的进阶做法。第一是类别合并的取舍。六类里「昏昏欲睡」和「打哈欠」在工程上经常合并成一个「疲劳征兆」类因为产品告警逻辑往往不区分你是张嘴还是点头只要判定疲劳就触发。合并后类别从 6 降到 5混淆减少mAP 通常能涨几个点。但如果你要做精细化交互比如打哈欠才提醒休息就得保留细分代价是要补更多边界样本。我的习惯是先合并跑一版看上限再决定要不要拆。第二是置信度和 NMS 的联合调优。疲劳检测对漏检容忍度低对误检容忍度相对高所以conf可以压到 0.2 甚至 0.15同时把iouNMS 阈值从默认 0.7 调到 0.5~0.6避免同一张脸被重复框。这两个参数要一起调只调一个往往顾此失彼。参数默认值疲劳检测建议值影响conf0.250.15~0.25降低提升召回误检增多iou0.70.5~0.6降低抑制重复框imgsz640640~960提升小目标与细长目标batch16按显存影响训练稳定性第三是一个提点技巧对安全带这类细长目标与其硬调模型不如在推理后处理里加一层「区域先验」。座舱图里安全带必然出现在驾驶员躯干的对角区域你可以根据检测框中心点位置做一次简单过滤把明显落在错误区域的框滤掉。这不是万能药但在 demo 阶段能快速把误检压下去给后续标注补数据争取时间。# 推理后处理按区域过滤安全带框示意 def filter_seatbelt(boxes, img_w, img_h): kept [] for b in boxes: cls, x1, y1, x2, y2, conf b cx, cy (x1 x2) / 2 / img_w, (y1 y2) / 2 / img_h if cls in (0, 1): # 安全带相关类 # 安全带中心一般落在画面中部偏左/右的躯干区 if 0.2 cx 0.8 and 0.3 cy 0.9: kept.append(b) else: kept.append(b) return kept逻辑说明对安全带类框做中心点区域约束落在合理躯干区域的保留其余丢弃。参数说明0.2 cx 0.8和0.3 cy 0.9是经验区间要按你实际摄像头安装位置标定不同车型视角差别很大别照搬。这个后处理只适合 demo 快速收敛长期还是靠补数据。最后说个我自己的习惯每训完一版我都会把 val 集里所有误检和漏检的图单独拷到一个badcase/目录按类别命名攒够一批就集中看。看 badcase 比看指标有用得多指标只告诉你「差」badcase 告诉你「差在哪」。这个数据集 8511 张的规模认真迭代两三轮出一个能演示的疲劳驾驶检测模型是完全现实的。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网