简介:本资源为驾驶员佩戴安全带检测的YOLO格式数据集,面向从事目标检测学习与车辆安全场景开发的学生、算法工程师及竞赛参与者,可直接用于YOLOv5等框架的训练与验证。数据按YOLOv5标准目录组织,标注采用classes、x_centre、y_centre、w、h的相对坐标格式,仅含seatbelt一个类别,其中训练集1201张图片及对应1201个标签文件,验证集354张图片及354个标签文件,划分清晰、开箱即用。压缩包共2000个文件,以1556个txt标签、443张jpg图像和1个py脚本为主,整体约63.76MB,体积轻便便于传输与本地部署。资源附带可视化脚本,随机传入一张图片即可绘制边界框并保存到当前目录,无需修改即可运行,方便快速核验标注质量。目前已有139人学习,适合需要现成安全带检测数据、快速验证模型效果或搭建车辆安全监测原型的读者参考使用。
1. 驾驶员安全带检测数据集:1201 张训练图 + 354 张验证图,开箱即用的 YOLO 落地包
拿到一份标注好的数据集,最怕的不是模型训不动,而是格式对不上、类别文件缺失、可视化脚本跑不起来。这套驾驶员佩戴安全带检测数据集,直接按 YOLO 目录结构组织,训练集 1201 张图配 1201 个 txt 标签,验证集 354 张图配 354 个 txt 标签,类别只有一类 seatbelt,标注采用 YOLO 相对坐标格式(class、x_centre、y_centre、w、h)。它解决的是从零标注到能开训之间那段最耗时的脏活,适合做车载安全监测、DMS 驾驶员状态分析、以及想快速验证 YOLOv5/v8 训练流程的从业者。可视化脚本随机抽一张图就能画框存盘,不用改代码,先看数据质量再决定要不要投入训练。
2. 目录结构与标注格式:先对齐 YOLO 的读取逻辑
2.1 为什么这套目录能直接喂给 YOLOv5
YOLOv5 的数据加载器对目录结构有硬性约定:图片和标签分开放,标签路径由图片路径推导而来。这套数据集按datasets/images/train、datasets/images/val、datasets/labels/train、datasets/labels/val组织,正好命中 YOLOv5 默认的路径推导规则。图片文件名里带.rf.后缀是 Roboflow 导出时的哈希标记,不影响读取,但要注意图片和标签的主干名必须严格一致——SEatbelt-212-_jpg.rf.2afa941d...jpg对应的标签必须是SEatbelt-212-_jpg.rf.2afa941d...txt,差一个字符就会在训练时被当成无标签样本跳过。
常见做法是先用一条命令核对图片和标签的配对情况,避免训练到一半才发现有孤儿文件。下面这段脚本遍历 train 和 val 两个目录,输出缺失标签的图片和缺失图片的标签:
import os for split in ['train', 'val']: img_dir = f'datasets/images/{split}' lbl_dir = f'datasets/labels/{split}' imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbls = {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} # 图片有但标签没有 missing_lbl = imgs - lbls # 标签有但图片没有 missing_img = lbls - imgs print(f'[{split}] 图片 {len(imgs)} 张, 标签 {len(lbls)} 个') print(f' 缺标签: {len(missing_lbl)}, 缺图片: {len(missing_img)}') if missing_lbl: print(' 示例:', list(missing_lbl)[:3])逻辑说明:用集合差集找出不配对的文件。参数上,split对应 train 和 val 两个子集,路径按数据集实际存放位置调整。如果输出缺标签数量不为零,优先检查是不是文件名里的.rf.哈希段被改动过,或者标签文件被误删。这一步花两分钟,能省掉后面排查 loss 不下降的几个小时。
2.2 相对坐标标注的数值边界
YOLO 格式的标注是归一化后的相对坐标,每行五个值:类别索引、中心点 x、中心点 y、宽、高,全部在 0 到 1 之间。这套数据集的类别只有 seatbelt 一类,所以类别索引恒为 0。实际检查时,重点看有没有超出 [0,1] 的值,以及宽高是否出现 0 或负数——这两种情况在训练时不会报错,但会让边界框回归变成玄学,loss 震荡却找不到原因。
import os lbl_dir = 'datasets/labels/train' bad = [] for f in os.listdir(lbl_dir): with open(os.path.join(lbl_dir, f)) as fp: for i, line in enumerate(fp): parts = line.strip().split() if len(parts) != 5: bad.append((f, i, '字段数不对')) continue cls, x, y, w, h = map(float, parts) # 检查坐标范围和宽高有效性 if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): bad.append((f, i, f'越界: {x},{y},{w},{h}')) print(f'异常标注行数: {len(bad)}') for b in bad[:5]: print(b)逻辑说明:逐行解析标签,校验字段数和数值范围。参数上,0 < w <= 1排除了宽高为零的退化框。如果异常行数较多,说明标注导出环节有问题,需要回到标注工具重新导出;如果只有零星几行,可以手动修正或直接删除对应图片。这套数据集标注质量整体可控,但批量数据难免有个别漏网之鱼,跑一遍心里有底。
3. 可视化脚本与数据质量核查:先看再训
3.1 随机抽图绘制边界框的脚本拆解
数据集自带可视化 py 文件,随机传入一张图片即可绘制边界框并保存在当前目录。这个脚本的价值在于:训练前用肉眼确认标注框是否贴合目标,尤其是安全带这种细长目标,标注偏移几个像素在数值上不明显,但画出来一眼就能看出问题。脚本无需更改即可运行,核心逻辑是读图、读对应标签、把相对坐标还原成像素坐标、画框、存图。
import cv2 import os import random img_dir = 'datasets/images/train' lbl_dir = 'datasets/labels/train' # 随机抽一张图 img_name = random.choice(os.listdir(img_dir)) img_path = os.path.join(img_dir, img_name) lbl_path = os.path.join(lbl_dir, os.path.splitext(img_name)[0] + '.txt') img = cv2.imread(img_path) h, w = img.shape[:2] with open(lbl_path) as f: for line in f: cls, x, y, bw, bh = map(float, line.split()) # 相对坐标转像素坐标 cx, cy = int(x * w), int(y * h) pw, ph = int(bw * w), int(bh * h) x1, y1 = cx - pw // 2, cy - ph // 2 x2, y2 = cx + pw // 2, cy + ph // 2 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, 'seatbelt', (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite('vis_result.jpg', img) print(f'已保存 vis_result.jpg, 来源: {img_name}')逻辑说明:random.choice随机选图,保证每次运行看到的样本不同。参数上,x * w和y * h是相对坐标转像素坐标的关键,pw // 2和ph // 2从中心点反推左上角和右下角。画框颜色用绿色,线宽 2,类别文字标在框上方。如果运行后图片里没有框,先确认标签文件是否存在、路径是否拼对;如果框的位置明显偏移,说明标注时的图像尺寸和当前读取的尺寸不一致,需要核对原图是否被缩放或裁剪过。
3.2 用统计分布判断数据集是否值得训
可视化看单张图是定性检查,批量统计标注框的宽高分布和位置分布是定量检查。安全带检测有个特点:目标通常出现在画面中下部,宽高比偏扁长。如果统计出来大量框集中在画面顶部或宽高比接近正方形,大概率是标注串行或类别混淆。下面这段脚本统计训练集所有框的中心点 y 坐标和宽高比:
import os import numpy as np lbl_dir = 'datasets/labels/train' ys, ratios = [], [] for f in os.listdir(lbl_dir): with open(os.path.join(lbl_dir, f)) as fp: for line in fp: _, x, y, w, h = map(float, line.split()) ys.append(y) ratios.append(w / h if h > 0 else 0) ys = np.array(ys) ratios = np.array(ratios) print(f'中心点 y 均值: {ys.mean():.3f}, 中位数: {np.median(ys):.3f}') print(f'宽高比均值: {ratios.mean():.2f}, 中位数: {np.median(ratios):.2f}') print(f'y < 0.3 的框占比: {(ys < 0.3).mean():.2%}')逻辑说明:ys收集所有框的中心点纵坐标,ratios收集宽高比。参数上,y < 0.3统计画面顶部区域的框占比,安全带检测这个比例应该很低。如果均值和中位数偏离预期,比如 y 均值接近 0.5 且宽高比接近 1,就要怀疑标注是否把整张图或错误区域框了进去。这一步不直接改数据,但决定了要不要在训练前做一轮清洗。
4. 训练配置与类别文件:从 data.yaml 到训练启动
4.1 class 文件与 data.yaml 的对应关系
数据集里的类别文本文件只有一行seatbelt,对应类别索引 0。YOLOv5 训练时需要一份 data.yaml 告诉它去哪里找数据、有几个类别、类别名是什么。这份 yaml 不用自己从零写,按数据集实际路径填就行:
# data.yaml train: datasets/images/train val: datasets/images/val nc: 1 names: ['seatbelt']逻辑说明:train和val指向图片目录,YOLOv5 会自动推导同级的 labels 目录。nc是类别数,这里为 1。names是类别名列表,顺序必须和标签里的类别索引一致——如果标签里 seatbelt 是 0,names 里 seatbelt 就必须在第一个。常见翻车点是 names 写成了中文或带了空格,导致训练时类别名解析失败。改完 yaml 后,用 YOLOv5 自带的检查命令跑一遍,确认数据路径和类别数被正确识别。
4.2 训练参数怎么设才不浪费这 1201 张图
1201 张训练图在 YOLO 任务里属于小到中等规模,直接上大模型容易过拟合,用 nano 或 small 版本更稳。常见做法是从预训练权重起步,冻结 backbone 先跑几轮,再解冻全量微调。下面是一条可复现的训练命令:
python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --freeze 10 \ --cache逻辑说明:--weights yolov5s.pt加载预训练权重,小数据集上比从零训收敛快得多。--img 640是输入尺寸,安全带目标不算特别小,640 够用;如果显存吃紧可以降到 416。--batch 16在单卡 8G 显存上比较稳,显存更大可以往上加。--freeze 10冻结前 10 层,先让检测头适应新类别,避免一开始就把预训练特征带偏。--cache把图片缓存到内存,1201 张图占用不大,能明显加快每个 epoch 的读取速度。训练启动后重点看三个指标:box_loss 是否稳定下降、mAP@0.5 是否在 30 轮后开始爬升、验证集 loss 是否和训练集同步下降。如果训练集 loss 降但验证集 loss 涨,就是过拟合信号,需要减 epoch 或加数据增强。
4.3 置信度门限与推理验证
训练完拿验证集图片跑推理,默认置信度门限 0.25 有时会漏掉遮挡严重的安全带。安全带被方向盘或手臂挡住时,模型输出的置信度会偏低,这时候把门限降到 0.1 到 0.15 之间能召回更多目标,但误检也会增加。实际操作是先用默认门限跑一批图,看漏检和误检的比例,再决定往哪个方向调。推理命令里--conf控制门限,--iou控制 NMS 的 IoU 阈值:
python detect.py \ --weights runs/train/exp/weights/best.pt \ --source datasets/images/val \ --conf 0.15 \ --iou 0.45 \ --save-txt逻辑说明:--source指向验证集目录,批量跑完能直观看到模型在未见过的图上的表现。--conf 0.15降低门限召回遮挡目标,--iou 0.45是 NMS 的默认值,如果同一目标出现多个重叠框,可以适当调低。--save-txt把预测结果存成 txt,方便和真实标签做对比统计。跑完后重点看两类图:一类是明明系了安全带但没框出来的,一类是没系却框出安全带的,这两类错误直接决定这个模型能不能上实际场景。
5. 避坑与排查:标注、路径、显存里的那些坑
5.1 训练时提示“no labels found”
现象:训练启动后日志里反复出现No labels found,或者Labels统计为 0。原因:YOLOv5 根据图片路径推导标签路径,如果 labels 目录和 images 目录不在同级,或者标签文件名和图片文件名主干不一致,就会找不到。解决:用第 2 章里的配对脚本先核对一遍,确认datasets/labels/train和datasets/images/train同级,且每个 jpg 都有同名 txt。如果目录结构没问题,检查 data.yaml 里的 train 路径是不是写成了绝对路径但实际是相对路径。
5.2 可视化脚本画出来的框位置偏移
现象:运行可视化脚本后,框的位置和实际目标对不上,整体偏移或大小不对。原因:标注时的图像尺寸和当前读取的图像尺寸不一致,常见于图片被缩放或裁剪后标签没有同步更新。解决:打印img.shape确认当前图像宽高,再抽查标签文件里的坐标是否基于同一尺寸。如果数据集是从其他格式转换来的,转换脚本里可能漏了尺寸归一化这一步。这套数据集标注和图片是配套导出的,正常不会出现,但如果你自己替换了部分图片,就要重新核对。
5.3 显存不足导致训练中断
现象:训练跑了几轮后报CUDA out of memory。原因:--batch设得太大,或者--img尺寸过高,1201 张图在 640 尺寸下 batch 16 通常没问题,但如果同时开了--cache且内存也吃紧,会间接影响。解决:先把 batch 降到 8 或 4,再考虑降 img 到 416。如果降了还报,检查是不是有其他进程占着显存。另一个容易忽略的点是--workers设得过高,数据加载进程太多也会吃内存,一般设 4 到 8 就够。
5.4 验证集 mAP 始终为 0
现象:训练 loss 在降,但验证集 mAP 一直是 0。原因:验证集的标签路径或类别索引和训练集不一致,比如训练集类别索引是 0,验证集标签里写成了 1。解决:分别抽查 train 和 val 的标签文件,确认类别索引都是 0。另一个可能是 data.yaml 里的 val 路径指向了空目录或错误目录,导致验证时没有加载到任何标签。用ls datasets/labels/val | wc -l确认标签数量是 354,和图片数量对得上。
5.5 推理时框出一堆无关区域
现象:detect.py 跑出来的图里,背景区域被框成 seatbelt。原因:置信度门限设得太低,或者模型过拟合到了背景纹理。解决:先把--conf调回 0.25 看是否消失,如果消失说明是门限问题;如果还在,检查训练时是否用了过多的背景图或增强过猛。安全带检测的背景主要是车内饰,如果训练集里某些内饰纹理反复出现且标注框附近有相似纹理,模型容易混淆。这时候需要补充负样本或调整数据增强的裁剪比例。
6. 进阶技巧:用验证集反推标注质量与模型上限
训练完一轮后,别急着调参,先把验证集的预测结果和真实标签做一次逐图对比。具体做法是用--save-txt跑完推理,然后写个小脚本统计每张图的预测框数量和真实框数量的差值。差值大的图分两类:预测多真实少,说明误检;预测少真实多,说明漏检。把这两类图各抽十张出来看,能快速定位是标注问题还是模型问题。
import os pred_dir = 'runs/detect/exp/labels' gt_dir = 'datasets/labels/val' for f in os.listdir(gt_dir): gt_n = sum(1 for _ in open(os.path.join(gt_dir, f))) pred_path = os.path.join(pred_dir, f) pred_n = sum(1 for _ in open(pred_path)) if os.path.exists(pred_path) else 0 if abs(gt_n - pred_n) >= 2: print(f'{f}: 真实 {gt_n}, 预测 {pred_n}')逻辑说明:逐图对比真实框和预测框数量,差值大于等于 2 的挑出来人工复核。参数上,pred_dir是推理输出目录,gt_dir是验证集标签目录。如果某张图真实框有 3 个但预测只有 1 个,先看这张图的标注是不是把多个安全带实例标成了一个,或者标注框严重偏离目标。如果真实框和预测框数量一致但位置对不上,那就是回归精度问题,需要检查训练时的 box_loss 曲线。
另一个实用技巧是用验证集 mAP 反推数据量是否够用。1201 张训练图在单类别检测任务里,如果 mAP@0.5 在 50 轮后稳定在 0.85 以上,说明数据量和标注质量都过关;如果卡在 0.6 到 0.7 上不去,优先怀疑标注一致性,而不是模型容量。我一般会从验证集里挑 20 张 mAP 最低的图,逐张看标注框是否贴合安全带边缘——很多时候不是模型学不会,是标注本身就框歪了。
从那以后我每次拿到新数据集,都强制先跑一遍配对检查和分布统计,再抽图可视化,最后才动训练命令。这三步走完,后面踩坑的概率能降一大半。希望帮到你。
本文还有配套的精品资源,点击获取