ARTICLE DETAIL

资讯详情

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

YOLO算法DMS疲劳驾驶数据集实战:8511张图像从解析到训练全流程

YOLO算法DMS疲劳驾驶数据集实战:8511张图像从解析到训练全流程 简介本资源为面向疲劳驾驶检测DMS场景的YOLO系列目标检测数据集适合从事智能座舱、驾驶员状态监测的算法工程师与高校研究者使用可支撑安全带识别、疲劳唤醒、打哈欠、打电话等行为的检测模型训练与验证。数据集共8511张图像并全部带标注压缩包内约2000个文件以VOC格式的xml标注文件为主同时提供YOLO格式的txt标签两种格式分别存放于独立文件夹便于按需选用。包内已划分好训练与验证集并附带data.yaml配置文件可直接适配yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流算法无需额外转换即可开箱训练。标签覆盖无安全带、唤醒、昏昏欲睡、安全带、电话、打哈欠等类别坐标采用归一化中心点与宽高表示规范统一。资源包整体约240MB目前已有393人学习下载适合快速搭建疲劳驾驶检测基线并开展对比实验。1. 8511 张 DMS 疲劳驾驶数据集从拿到压缩包到跑通 YOLO 检测你拿到一个名为yolo算法-dms疲劳驾驶员数据集-8511张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip的压缩包第一反应大概率是类别到底怎么分、标签是什么格式、8511 张够不够训、YOLO 哪个版本能直接吃。DMSDriver Monitoring System驾驶员监控系统是这两年车载视觉里落地最猛的方向之一疲劳检测、分心检测、打电话、抽烟、没系安全带几乎每个主机厂和 Tier1 都在做。这个数据集把「昏昏欲睡、打哈欠、打电话、没系安全带、唤醒」这几类行为打包在一起本质是一个多分类行为识别数据集而不是单纯的人脸关键点数据集。它适合谁适合想快速验证 DMS 算法链路的算法工程师、做车载嵌入式部署的工程团队以及需要一份带标签数据来跑通 YOLO 训练全流程的学生和独立开发者。8511 张不算大但类别覆盖了 DMS 最核心的几个报警场景拿来跑通「数据解析→格式转换→训练→评估→部署」这条链路是够用的。真正要花心思的地方不在模型本身而在标签解析、类别不平衡和车载场景的域差异上。2. 拆开压缩包先看什么目录结构、标签格式与类别定义2.1 先摸清目录结构和标签存放方式拿到压缩包别急着解压完就丢进训练脚本先看清楚它怎么组织的。DMS 类数据集常见的组织方式有三种VOC 风格JPEGImages Annotations 两个文件夹、YOLO 风格images labels 两个文件夹标签是 txt、以及 COCO 风格一个 json 管所有。这个数据集标题里写了「带标签」但没说是哪种所以第一步是解压后列目录。# 解压到独立目录避免污染当前工作区 unzip yolo算法-dms疲劳驾驶员数据集-8511张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip -d dms_dataset # 看顶层结构判断是 VOC 还是 YOLO 风格 find dms_dataset -maxdepth 2 -type d | head -30 # 统计图像数量确认是不是 8511 张 find dms_dataset -type f \( -iname *.jpg -o -iname *.png -o -iname *.jpeg \) | wc -l # 看标签文件长什么样随便抽一个 find dms_dataset -name *.txt | head -3 find dms_dataset -name *.xml | head -3逻辑说明find -maxdepth 2只列两层目录避免输出爆炸统计图像数量是为了核对标题里的 8511 是否属实很多数据集标题写 8511实际可用图像可能因为损坏、重复少几百张。如果抽出来的是.txt大概率是 YOLO 格式每行class_id x_center y_center width height坐标是归一化到 0~1 的如果是.xml就是 VOC 格式里面是绝对像素坐标的xmin/ymin/xmax/ymax。这一步不做后面格式转换必翻车。参数说明-iname忽略大小写因为有些数据集图像后缀混着.JPG和.jpghead -30是防止目录太深刷屏。如果发现图像数量和标签数量对不上先别往下走缺标签的图会在训练时被当成背景直接污染模型。2.2 类别映射把「没有安全带、唤醒、昏昏欲睡、安全带、电话、打哈欠」对齐成 class_id标题里列了六个行为词但真实类别数不一定是六个。常见情况是「安全带」和「没有安全带」是一对二分类「昏昏欲睡」和「打哈欠」可能合并成疲劳也可能分开。你必须自己建一份类别映射表不能靠猜。标题行为词建议类别名class_id说明安全带seatbelt0已系安全带正常状态没有安全带no_seatbelt1未系安全带报警类电话phone2手持打电话分心类打哈欠yawn3疲劳早期信号昏昏欲睡drowsy4疲劳严重状态唤醒awake5正常清醒作为负样本平衡# 建立类别映射后续所有转换脚本都引用这一份 CLASS_MAP { seatbelt: 0, no_seatbelt: 1, phone: 2, yawn: 3, drowsy: 4, awake: 5, } ID_TO_NAME {v: k for k, v in CLASS_MAP.items()} # 如果原始标签里用的是中文或拼音先做一次归一化 RAW_TO_STD { 安全带: seatbelt, 没有安全带: no_seatbelt, 未系安全带: no_seatbelt, 电话: phone, 打哈欠: yawn, 昏昏欲睡: drowsy, 唤醒: awake, 清醒: awake, }逻辑说明类别映射必须落成代码里的常量而不是散落在各个脚本里。后面做格式转换、训练配置、评估可视化都要引用同一份改一处全生效。参数说明class_id从 0 开始连续YOLO 要求这样如果原始数据里类别是字符串先用RAW_TO_STD归一化再查CLASS_MAP。这一步的坑在于「唤醒」和「清醒」可能是同一类「昏昏欲睡」和「打哈欠」可能被标注员混用建议先抽样看 50 张图的标签确认标注一致性再定最终类别数。2.3 用脚本统计类别分布先判断能不能直接训8511 张图如果某一类只有 200 张直接训必然类别不平衡。先统计再决定要不要重采样或加权重。import os from collections import Counter def count_classes(label_dir, id_to_name): counter Counter() for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: for line in f: line line.strip() if not line: continue cls_id int(line.split()[0]) counter[cls_id] 1 for cid, cnt in sorted(counter.items()): print(f{id_to_name.get(cid, cid)}: {cnt}) count_classes(dms_dataset/labels, ID_TO_NAME)逻辑说明逐行读 YOLO 标签取第一个字段作为类别 id累计计数。参数说明label_dir换成你实际的标签目录如果标签是 VOC 的 xml这段要改成解析 xml 的object/name。统计完你会看到类似awake: 4000, drowsy: 800, phone: 600这种分布如果最大类是最小类的 10 倍以上训练时就要在data.yaml里考虑过采样少数类或者用 YOLO 的cls损失权重调整。这一步是决定「这个数据集值不值得直接投入训练」的关键别跳过。3. 把标签转成 YOLO 能吃的格式VOC 转 YOLO 与数据划分3.1 VOC 转 YOLO坐标归一化和边界裁剪如果第 2 章发现标签是 VOC 的 xml必须先转成 YOLO 的 txt。核心是绝对坐标转归一化中心点坐标并且要处理越界。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h, class_map): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_map: continue cls_id class_map[name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图像边界内防止标注越界 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines逻辑说明VOC 的bndbox是绝对像素坐标YOLO 要的是归一化后的中心点加宽高。先裁剪再计算避免标注框超出图像导致归一化后出现负数或大于 1 的值。参数说明img_w和img_h从对应图像读取不要硬编码class_map用第 2 章定义的映射:.6f保留六位小数YOLO 官方推荐精度。转换完要抽查几个 txt确认每行五个字段、坐标都在 0~1 之间。3.2 按 8:1:1 划分 train/val/test注意同一驾驶员不能跨集DMS 数据集有个特殊坑同一个驾驶员的多张连续帧如果被分到训练集和验证集验证指标会虚高。常见做法是按驾驶员 id 或视频片段划分而不是随机按图划分。import random import os import shutil def split_dataset(img_dir, label_dir, out_dir, ratios(0.8, 0.1, 0.1), seed42): random.seed(seed) imgs [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(imgs) n len(imgs) n_train int(n * ratios[0]) n_val int(n * ratios[1]) splits { train: imgs[:n_train], val: imgs[n_train:n_train n_val], test: imgs[n_train n_val:], } for split, files in splits.items(): os.makedirs(f{out_dir}/images/{split}, exist_okTrue) os.makedirs(f{out_dir}/labels/{split}, exist_okTrue) for f in files: shutil.copy(os.path.join(img_dir, f), f{out_dir}/images/{split}/{f}) label_name os.path.splitext(f)[0] .txt src_label os.path.join(label_dir, label_name) if os.path.exists(src_label): shutil.copy(src_label, f{out_dir}/labels/{split}/{label_name}) print({k: len(v) for k, v in splits.items()}) split_dataset(dms_dataset/images, dms_dataset/labels, dms_yolo)逻辑说明先打乱再按比例切seed42保证可复现。参数说明ratios是训练/验证/测试比例8511 张按 8:1:1 大约是 6808/851/852如果数据集有驾驶员 id 字段应该先按 id 分组再切这里给的是通用随机切法。划分完检查dms_yolo/images/train和dms_yolo/labels/train的文件名是否一一对应缺标签的图要么补要么删别留着。3.3 写 data.yaml把路径和类别名固定下来YOLO 训练靠data.yaml找数据这份文件写错一个路径就报No labels found。# dms_yolo/data.yaml path: /abs/path/to/dms_yolo train: images/train val: images/val test: images/test nc: 6 names: 0: seatbelt 1: no_seatbelt 2: phone 3: yawn 4: drowsy 5: awake逻辑说明path用绝对路径最稳train/val/test是相对path的子路径。参数说明nc必须等于names的条目数且和标签里的 class_id 范围一致如果第 2 章统计后发现某类样本极少可以在这里先保留训练时用cls权重或过采样处理。写完用python -c import yaml; print(yaml.safe_load(open(dms_yolo/data.yaml)))验证语法YAML 缩进错一格就解析失败。4. 用 YOLOv8 跑通训练参数、显存与类别不平衡处理4.1 选 YOLOv8n 还是 YOLOv8s8511 张的模型容量判断8511 张图属于中小规模直接上 YOLOv8l 大概率过拟合而且车载部署算力有限。常见做法是从 YOLOv8n 或 YOLOv8s 起步先跑通再考虑换大模型。# 安装 ultralytics注意版本兼容 pip install ultralytics8.2.0 # 用 YOLOv8n 预训练权重起步跑 100 epoch yolo detect train \ datadms_yolo/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectdms_runs \ nameyolov8n_baseline逻辑说明modelyolov8n.pt会自动下载预训练权重这是迁移学习的关键别从零训。参数说明imgsz640是 YOLO 默认输入尺寸DMS 场景里人脸和手部占比较大640 够用batch16在 8GB 显存上比较稳显存不够就降到 8lr00.01是初始学习率YOLOv8 默认用 SGD 时这个值合适patience20表示 20 轮验证指标不升就早停省时间。如果训练日志里cls_loss一直不降先回去看第 2 章的类别分布大概率是类别不平衡。4.2 类别不平衡过采样、加权与数据增强三选一DMS 数据集里awake通常最多drowsy和yawn偏少。三种处理方式按成本从低到高排数据增强、损失加权、过采样。# 方式一在 data.yaml 同级目录建一个过采样列表训练时重复少数类 import os import random def build_oversample_list(label_dir, target_per_class1500): class_files {} for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: classes {int(line.split()[0]) for line in f if line.strip()} for c in classes: class_files.setdefault(c, []).append(fname) oversampled [] for c, files in class_files.items(): if len(files) target_per_class: oversampled.extend(files * (target_per_class // len(files) 1)) else: oversampled.extend(files) random.shuffle(oversampled) return oversampled逻辑说明统计每个类别出现在哪些标签文件里对样本数不足target_per_class的类别重复采样。参数说明target_per_class1500是经验值让最少类也有一千多张的量级重复采样后要重新生成一份train_oversampled.txt列表YOLO 支持用 txt 指定训练图。注意过采样会让训练集变大epoch 时间变长但比直接加权更稳。如果不想改数据也可以在训练命令里加cls1.5提高分类损失权重但效果不如过采样直接。4.3 训练过程看什么mAP、混淆矩阵和验证集损失训练不是跑完看最后一行就完事中间指标决定你要不要调参。指标正常表现异常信号处理box_loss持续下降后平稳震荡不降降 lr0 或加 batchcls_loss持续下降不降或上升查类别不平衡mAP50逐步升到 0.7卡在 0.3 以下查标签格式mAP50-95逐步升到 0.5远低于 mAP50查框回归质量val box_loss随 train 下降上升过拟合早停或加增强# 训练完在验证集上跑评估输出混淆矩阵 yolo detect val \ modeldms_runs/yolov8n_baseline/weights/best.pt \ datadms_yolo/data.yaml \ splitval \ plotsTrue逻辑说明val会输出 mAP、混淆矩阵和 PR 曲线plotsTrue把图存到 runs 目录。参数说明splitval指定用验证集别用 test 调参混淆矩阵里如果drowsy大量被预测成awake说明这两类视觉特征太接近要么合并要么加更多 drowsy 样本。这一步的坑在于只看 mAP 不看混淆矩阵mAP 高但关键报警类漏检上车就是事故。5. 避坑与排查DMS 数据集训练最容易翻车的 5 个点5.1 标签坐标越界导致训练报错或框乱飞现象训练启动后报Label class x is invalid或normalized coordinates out of range或者训练完预测框跑到图像外面。原因VOC 转 YOLO 时没做边界裁剪标注框的xmax超过图像宽度归一化后大于 1。解决回到 3.1 的转换脚本确认xmin/xmax被min(max(...))夹在[0, img_w]内转换后写个校验脚本扫一遍所有 txt发现坐标小于 0 或大于 1 的直接打印文件名。5.2 图像和标签文件名不匹配训练时静默丢样本现象训练日志里train: 0 images或实际训练图数远少于预期。原因图像是.jpg标签是.txt但文件名前缀不一致比如图像叫driver_001.jpg标签叫driver_001_bbox.txt。解决写脚本对比images/train和labels/train的文件名集合差集打印出来。YOLO 找标签的规则是同名同目录多一个后缀就找不到。5.3 同一驾驶员跨集导致验证指标虚高现象验证集 mAP 0.85测试集掉到 0.5。原因随机按图划分同一驾驶员的连续帧同时进了训练和验证模型记住了人脸而不是行为。解决如果数据集有驾驶员 id 或视频名按 id 分组划分没有的话用图像感知哈希或简单的时间戳排序把相邻帧分到同一集。这个坑在 DMS 里特别常见因为疲劳是连续状态相邻帧几乎一样。5.4 类别名和 class_id 对不上预测结果全错位现象训练正常但推理时phone被标成seatbelt。原因data.yaml里的names顺序和标签里的class_id不一致比如标签里 0 是no_seatbeltyaml 里 0 写成了seatbelt。解决以第 2 章的CLASS_MAP为唯一真相生成data.yaml时用脚本从CLASS_MAP导出别手写。推理后用一张已知标签的图验证看预测类别名是否和标注一致。5.5 车载红外/夜间图像导致模型泛化差现象白天数据训练 mAP 0.8夜间或红外图像上掉到 0.4。原因DMS 数据集如果以可见光为主夜间和红外是不同域模型没学过。解决训练时加hsv_h/hsv_s/hsv_v增强模拟光照变化或者用mosaic和mixup提升鲁棒性如果数据集里夜间样本极少考虑单独收集或合成。这个坑没有捷径只能靠数据覆盖。6. 进阶技巧用混淆矩阵反推标注质量再决定要不要重标训练完拿到混淆矩阵大多数人只看对角线其实非对角线才是宝藏。DMS 数据集里最常见的标注问题是「昏昏欲睡」和「打哈欠」混标、「唤醒」和「安全带」同时出现时只标了一个。你可以用混淆矩阵反推如果drowsy有 30% 被预测成yawn而人工看这些图确实像打哈欠那说明标注标准本身模糊不是模型问题。import numpy as np import matplotlib.pyplot as plt from ultralytics import YOLO model YOLO(dms_runs/yolov8n_baseline/weights/best.pt) results model.val(datadms_yolo/data.yaml, splitval, plotsFalse) # 拿混淆矩阵行是真实类列是预测类 cm results.confusion_matrix.matrix names [seatbelt, no_seatbelt, phone, yawn, drowsy, awake] # 找每行里非对角线的最大值定位最容易混的类对 for i, row in enumerate(cm): row_norm row / (row.sum() 1e-9) for j, v in enumerate(row_norm): if i ! j and v 0.15: print(f真实 {names[i]} 被预测成 {names[j]} 的比例: {v:.2f})逻辑说明confusion_matrix.matrix是原始计数矩阵归一化后看比例。参数说明阈值0.15是经验值超过 15% 的混淆就值得人工复查。跑完你会得到类似「真实 drowsy 被预测成 yawn 的比例 0.22」然后去抽这 22% 的图人工判断是标注错了还是模型弱。如果是标注错回去修标签重训如果是模型弱加这类样本或调增强。我自己的习惯是每训完一版先不看 mAP先看混淆矩阵里关键报警类no_seatbelt、phone、drowsy的召回。DMS 场景漏报比误报严重得多no_seatbelt召回低于 0.9 就不上车。8511 张的数据集跑通不难难的是把标注一致性做上去这一步没有工具能替你只能一张张看。希望帮到你。本文还有配套的精品资源点击获取
返回列表