ARTICLE DETAIL

资讯详情

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

YOLO疲劳驾驶目标检测数据集实战:VOC/COCO/YOLO格式转换、划分脚本与训练调参避坑指南

YOLO疲劳驾驶目标检测数据集实战:VOC/COCO/YOLO格式转换、划分脚本与训练调参避坑指南 简介本资源为面向目标检测学习者的YOLO疲劳驾驶数据集适用于课程设计、毕业项目与算法验证等场景帮助解决疲劳驾驶检测中真实样本不足、标注格式不统一的问题。压缩包共2000个文件约389.57MB以1985个xml标注文件为主另含少量txt、html与py脚本分别对应标签数据、教程说明与数据集划分工具。数据集采用labelimg标注提供voc、coco和yolo三种格式标签分目录存放可直接接入YOLO系列训练流程。随包附赠环境搭建与训练案例教程覆盖Windows与Linux版本并配有训练集、验证集、测试集划分脚本可按需生成ImageSets索引文件。目前已有657人学习下载适合希望快速获得高质量标注数据、减少数据准备成本并跑通训练流程的读者参考使用。1. 从一堆散装图片到能训的 YOLO 数据集5000 张疲劳驾驶样本到底怎么用拿到一个标着「YOLO疲劳驾驶目标检测数据集(含5000张图片)对应voc、coco和yolo三种格式标签划分脚本训练教程.rar」的压缩包很多人的第一反应是解压、翻目录、找train.py然后直接开跑。跑完发现 mAP 上不去、验证集 loss 震荡、混淆矩阵里「闭眼」和「打哈欠」互相打架——问题往往不在模型而在数据这一层没吃透。疲劳驾驶检测本质是一个二分类偏多类的目标检测任务典型类别是open_eye、closed_eye、open_mouth、closed_mouth这几类有的数据集还会加phone、smoke、head_down。5000 张图不算大但三种标签格式并存意味着你可以自由切换训练框架也意味着格式转换和划分策略会直接决定最终精度。这篇笔记面向已经拿到或准备找这类数据集的从业者把「三种格式怎么选、划分脚本怎么写、训练参数怎么调、坑在哪」一次讲透新手能照着复现熟手能对照检查自己的流程。2. 三种标签格式的取舍VOC、COCO、YOLO 各自适合什么场景2.1 先看清三种格式的字段差异VOC 格式的核心是每张图对应一个 XML里面用object节点记录类别名和xmin/ymin/xmax/ymax绝对像素坐标。COCO 格式把所有标注压进一个annotations.json用images、annotations、categories三个顶层数组组织bbox 是[x, y, width, height]的绝对坐标。YOLO 格式最轻每张图一个.txt每行class_id x_center y_center width height全部归一化到 0~1。维度VOCCOCOYOLO存储形态每图一个 XML单个 JSON每图一个 txt坐标类型绝对像素绝对像素归一化类别表示字符串id 字符串纯 id主流用途老框架、评测通用评测、分割YOLO 系训练大文件友好度一般差单文件巨大好选型逻辑很直接如果你用 YOLOv5/v8/v11 训练最终一定要落到 YOLO 格式如果要做跨模型对比或投稿评测COCO 是通用货币VOC 更多是历史遗留和中间态。这个数据集三种都给省了你从零标注的功夫但别三种都拿来训——选一种作为主格式其余作为转换校验的参照。2.2 用脚本在三种格式间无损转换转换的核心风险是坐标取整和边界溢出。下面这段把 VOC 转 YOLO重点看归一化和越界裁剪import os import xml.etree.ElementTree as ET from PIL import Image # 类别顺序必须固定训练时的 class_id 依赖它 CLASSES [open_eye, closed_eye, open_mouth, closed_mouth] CLASS_MAP {name: i for i, name in enumerate(CLASSES)} def voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 用真实图片尺寸不要信 XML 里的 size标注工具经常写错 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) w, h Image.open(img_path).size lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in CLASS_MAP: continue # 未登记类别直接跳过避免 id 错位 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) # 裁剪到图像边界防止标注越界导致归一化后 1 xmin, ymin max(0, xmin), max(0, ymin) xmax, ymax min(w, xmax), min(h, ymax) if xmax xmin or ymax ymin: continue # 无效框丢弃 xc (xmin xmax) / 2.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{CLASS_MAP[cls]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) out_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))逻辑说明CLASSES的顺序一旦定下就不能改训练配置里的names必须和它逐位对应否则模型学到的类别会整体错位。用PIL读真实尺寸而不是 XML 里的size是因为不少标注工具在图片被裁剪后没更新 size 字段这是血泪经验。坐标裁剪那两行是防越界的关键越界框归一化后会大于 1YOLO 训练时会被静默丢弃或产生异常梯度。参数说明xc/yc是框中心归一化坐标bw/bh是宽高归一化值保留 6 位小数足够。如果你的类别里有phone这种长宽比极端的框建议额外检查bw、bh是否小于 0.01过小的框在 640 输入下只剩几个像素学不动。COCO 转 YOLO 时注意 COCO 的bbox是[x, y, w, h]左上角加宽高不是[xmin, ymin, xmax, ymax]直接套 VOC 的公式会整体偏移半个框这是最常见的翻车点。3. 划分脚本怎么写别让同一段视频的帧同时进训练和验证3.1 随机划分为什么在疲劳驾驶数据上会虚高疲劳驾驶数据集很多是从连续视频抽帧来的相邻帧之间几乎一模一样。如果你用random.shuffle按图随机划分同一段视频的第 100 帧进训练集、第 101 帧进验证集模型在验证集上看到的几乎是训练时见过的画面mAP 会虚高 5~15 个点上线后直接打回原形。正确做法是按视频源或按时间片段分组划分保证同一来源的帧只出现在一个集合里。3.2 一个可复用的分组划分脚本import os import random import shutil from collections import defaultdict def split_dataset(img_dir, label_dir, out_root, ratios(0.8, 0.1, 0.1), seed42): random.seed(seed) # 按文件名前缀分组假设命名规则为 视频ID_帧号.jpg groups defaultdict(list) for f in os.listdir(img_dir): if not f.lower().endswith((.jpg, .png, .jpeg)): continue video_id f.rsplit(_, 1)[0] # 取视频ID作为分组键 groups[video_id].append(f) group_keys list(groups.keys()) random.shuffle(group_keys) n len(group_keys) n_train int(n * ratios[0]) n_val int(n * ratios[1]) splits { train: group_keys[:n_train], val: group_keys[n_train:n_train n_val], test: group_keys[n_train n_val:] } for split, keys in splits.items(): img_out os.path.join(out_root, images, split) lbl_out os.path.join(out_root, labels, split) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for key in keys: for img_file in groups[key]: stem os.path.splitext(img_file)[0] shutil.copy(os.path.join(img_dir, img_file), os.path.join(img_out, img_file)) lbl_file stem .txt src_lbl os.path.join(label_dir, lbl_file) if os.path.exists(src_lbl): shutil.copy(src_lbl, os.path.join(lbl_out, lbl_file)) else: print(f[warn] 缺失标签: {lbl_file}) # 空标签也要建空文件逻辑说明分组键video_id从文件名前缀提取这是假设你的数据命名带视频来源。如果文件名是纯数字乱序就得靠感知哈希或帧间相似度聚类来分组成本高但必要。seed固定保证可复现团队协作时大家划分结果一致。缺失标签时打印警告而不是静默跳过因为空标签文件在 YOLO 里代表「这张图没有目标」和「标签丢了」是两回事前者要建空 txt后者要修数据。参数说明ratios默认 8:1:15000 张图按视频分组后大概能得到 4000/500/500 的规模。如果视频源很少比如只有 20 段建议改成 7:1.5:1.5 或做交叉验证否则验证集覆盖不了足够多的场景。seed换成别的值可以快速做多次划分取平均判断模型稳定性。提示划分完成后一定要统计三个集合的类别分布闭眼样本在疲劳数据里通常远多于张嘴如果验证集里某类少于 30 个实例指标波动会非常大考虑分层抽样。4. 训练参数怎么设从 5000 张图榨出可用精度4.1 输入尺寸、batch 与预训练权重的组合5000 张图属于中小规模从零训基本没戏必须用预训练权重。以 YOLOv8 为例常见做法是加载yolov8s.pt或yolov8m.pt前者速度快适合边缘部署后者精度高适合服务器。输入尺寸imgsz设 640 是默认但疲劳驾驶的关键特征眼睛开合、嘴巴张合在画面里占比很小可以尝试 960 或 1280代价是显存和速度。batch 根据显存定单卡 16G 用yolov8s跑 640 可以上 batch32跑 1280 只能 batch8。yolo detect train \ datafatigue.yaml \ modelyolov8s.pt \ imgsz960 \ epochs150 \ batch16 \ lr00.01 \ lrf0.01 \ warmup_epochs3 \ cos_lrTrue \ mosaic1.0 \ mixup0.1 \ degrees10 \ translate0.1 \ scale0.5 \ fliplr0.5 \ patience30 \ projectruns/fatigue \ nameexp_v1参数说明lr00.01是 SGD 的初始学习率用 AdamW 的话降到 0.001。lrf0.01是最终学习率系数配合cos_lr做余弦退火。warmup_epochs3让前 3 个 epoch 学习率从极小爬升防止预训练权重被大梯度冲垮。mosaic1.0是 YOLO 的招牌增强但疲劳驾驶场景里 mosaic 会把四张图的眼部区域拼在一起可能产生不真实的上下文如果发现闭眼误检多把它降到 0.5。degrees10做小角度旋转fliplr0.5水平翻转——注意左右眼在语义上对称翻转安全但如果你的类别区分左右眼就不能翻。patience30是早停验证指标 30 轮不涨就停省时间。4.2 疲劳驾驶特有的增强与类别不平衡处理疲劳数据集的类别不平衡很典型正常睁眼样本占大头闭眼和打哈欠是少数。除了在划分时分层训练时可以用cls损失权重或复制少数类。YOLOv8 没有直接的类别权重参数常见做法是在数据集层面过采样少数类或者调box、cls、dfl三个损失系数# fatigue.yaml path: /data/fatigue train: images/train val: images/val test: images/test names: 0: open_eye 1: closed_eye 2: open_mouth 3: closed_mouthnames的 id 必须和划分脚本、转换脚本里的CLASS_MAP完全一致这是最容易出错的地方。训练时如果看到cls_loss一直居高不下而box_loss正常下降多半是类别不平衡或标签错位先回去查names顺序。注意疲劳驾驶检测里「闭眼」和「正常眨眼」的边界很模糊单帧判断天然有歧义。如果你的应用允许引入时序信息连续多帧投票比单纯堆单帧精度更有效这是单帧检测模型的天花板。5. 避坑与排查训练疲劳驾驶数据集时最常翻的 5 个车现象一训练 loss 正常下降但验证 mAP 始终在 0.3 以下。原因划分时同一视频的帧泄漏到了验证集或者names顺序和标签里的 class_id 错位。 解决先跑一遍划分脚本的类别分布统计确认三个集合的类别比例接近再随机抽 10 张图用可视化脚本把框画出来肉眼核对类别名和框位置。现象二混淆矩阵里closed_eye大量被预测成open_eye。原因闭眼样本太少或者闭眼和睁眼的图像分辨率下差异只有几个像素模型分辨不出。 解决提高输入尺寸到 960 以上对闭眼类做过采样检查标注质量很多闭眼框画得比实际眼睛大一圈把眉毛也框进去了。现象三训练到一半 BN 层崩溃loss 变 NaN。原因batch 太小比如 4 以下导致 BN 统计量不稳或者学习率过大。 解决换yolov8n减小显存占用以增大 batch把lr0降到 0.001开启ampFalse排除混合精度问题。热词里提到的「yolo训练中bn崩溃」多半是这几个原因。现象四验证集指标很好实际视频推理时框乱跳。原因单帧检测没有时序约束相邻帧的检测结果独立加上验证集和真实场景的光照、角度分布不一致。 解决推理时加跟踪算法如 ByteTrack做帧间关联或对连续 5 帧的检测结果做投票同时补充真实场景的难例重新训练。现象五转换后的 YOLO 标签里有坐标大于 1 或小于 0。原因VOC 或 COCO 标注越界转换时没做裁剪。 解决在转换脚本里加边界裁剪见 2.2 节代码转换后跑一遍校验脚本统计所有标签的坐标范围发现越界就回溯原始标注。6. 把 5000 张图用出 5 万张的效果几个我反复验证过的技巧第一个技巧是难例挖掘闭环。先用初始模型在验证集和一段真实视频上推理把置信度在 0.3~0.6 之间的框对应的图挑出来人工复核后加进训练集。疲劳驾驶的难例高度集中在侧脸、戴眼镜、夜间逆光这几类补 200~300 张难例往往比再加 2000 张普通图管用。我一般每训完一轮就做一次这个动作三轮下来 mAP 能涨 4~6 个点。第二个技巧是标签平滑和 CIoU 的配合。YOLOv8 默认用 CIoU 做框回归对疲劳驾驶这种小目标可以把box损失系数从默认 7.5 适当降到 5.0让模型不要过度拟合框的精确位置转而关注分类特征。同时开label_smoothing0.1缓解闭眼/睁眼边界模糊带来的过拟合。第三个技巧是导出时的动态尺寸。训练用 960导出 ONNX 或 TensorRT 时如果部署端算力有限可以导出 640 的动态 batch 模型推理时按需缩放。但要注意训练和推理的输入尺寸差异过大会掉点最好在验证集上对比 640/960/1280 三档的 mAP 和延迟选性价比最高的那档。技巧预期收益代价难例挖掘mAP 4~6人工复核时间损失系数调整mAP 1~2需重新调参输入尺寸提升mAP 3~5显存和延迟翻倍时序投票误检率 -30%引入跟踪模块最后一个习惯每次改完数据或参数先跑 10 个 epoch 的小实验看 loss 曲线趋势别一上来就 150 轮。我吃过太多次「训了一晚上发现标签错了」的亏10 轮快跑能在 20 分钟内暴露 80% 的数据问题。这套流程从拿到压缩包到跑出可用模型顺利的话两天内能走完卡住的地方九成在数据层而不是模型层。希望帮到你。本文还有配套的精品资源点击获取
返回列表