驾驶员安全带检测数据集:YOLO训练全流程与避坑指南
简介驾驶员佩戴安全带检测的YOLO格式数据集面向目标检测初学者、课题研究者以及需要快速验证效果的开发者。数据按YOLOv5文件夹结构保存标注采用yolo相对坐标类别、中心点x、y、宽高仅含seatbelt一个类别并附有类别txt文件已划分训练集1201张、验证集354张每张图片均配有同名txt标签可直接加载训练。资源包共2000个文件以1556个txt标注文件、443个jpg图像和1个py可视化脚本为主整体约63.76MB压缩包类型为7z解压后目录层级清晰便于迁移到不同YOLO环境中。附带可视化脚本无需修改参数随机传入一张图片即可绘制边界框并保存便于直观核对标注质量。目前已有140人学习下载适合需要标准划分数据集、免去整理流程的开发者无论是入门YOLO实战还是课程设计、科研验证都能直接套用。1. 为什么“划分好的安全带检测数据集”能省掉你至少三天时间做驾驶员安全带检测的人手里最不缺的就是监控截图缺的是能直接喂给 YOLO 的标签。我见过很多人第一步就栽在数据上从视频里抽了几千帧标了三天结果有人把中心坐标想当然写成像素值没归一化有人用全角逗号分隔还有人在没有划分的情况下直接开训等 loss 降到 0.02 才发现验证集和训练集混着同一辆车的连续帧指标虚高得没法看。这套“驾驶员佩戴安全带检测”数据集把最耗时也最容易出错的三道工序替你完成了标注统一成了 YOLO 的 txt 格式train/val/test 已经切好类别 class 文件和可视化脚本也都放在包里你拿到手第一件事不是标图而是花十分钟把数据“看一遍”。适合三种人想快速跑通 YOLO 全流程的新手做车辆安全监管类项目的开发者以及需要给算法选型做快速验证的技术负责人。2. 安全带检测数据集的结构与类别约定先花 10 分钟对齐 class 文件拿到数据别急着解压完就跑去训练。先花十分钟把类别设计和目录结构看清后面所有调参都建立在“我知道这个包在说什么”的基础上。这一步省不掉因为 YOLO 训练器只认编号不认类名类名和编号的对应关系全写在 class 文件里对不齐的话你连 loss 曲线和混淆矩阵都读不懂。2.1 两种主流标注思路检测“安全带区域”还是检测“佩戴状态”安全带检测在工程上通常有两条路线你先要判断手里这份数据集走的是哪一条。第一种是单类别目标检测只把安全带当作前景目标来框选比如肩带斜跨过胸口的那一段。这种设计的优点是任务干净、类内差异小类别不均衡的问题不严重缺点是它不直接回答“驾驶员有没有系”后续还要加规则有安全带框就认为已佩戴没有就认为未佩戴。遇到安全带被外套遮挡、只有半截入镜的情况规则就容易误判。第二种是状态二分类把目标定义成“佩戴”和“未佩戴”两类class 文件里通常会有两个类别名比如 wearing_seatbelt 和 no_seatbelt有时还会额外带一个 person 类别做辅助。这种设计直接输出业务结论部署时少一层规则转换是我在安全监管类项目里的首选。拿到包先看一眼 classes.txt文件里有几行就说明这个数据集把任务定义成了几个类别。用下面的命令直接读cat classes.txt逻辑说明classes.txt 每行一个类名行号从 0 开始对应标注 txt 里每行开头的数字编号。比如 0 对应 wearing_seatbelt1 对应 no_seatbelt那么一张图里标注文本写 0 的就是系了安全带写 1 的就是没系。参数说明类名本身只影响可读性不影响训练过程但会影响你后续对验证集输出的理解建议先把编号和类名的对应关系抄在项目 README 里。2.2 目录结构与 YOLO txt 格式一张图上标了什么YOLO 的数据格式不复杂每张图片对应一个同名 txttxt 的每一行是一个目标框格式是 class_id x_center y_center width height四个坐标值全部是相对图片宽高的归一化比例范围在 0 到 1 之间。这套数据集既然说“划分好了”目录结构通常是下面这样的data/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── classes.txt └── data.yaml这里要注意的是“同名同路径”图片叫 000001.jpg标注就必须叫 000001.txt且两个文件要放在对应的 train 或 val 目录下。images/train 和 labels/train 必须一一对应多一张少一张都不行。data.yaml 是训练器读取数据的入口常见内容长这样path: ./data train: images/train val: images/val test: images/test nc: 2 names: 0: wearing_seatbelt 1: no_seatbelt逻辑说明path 是数据集根目录相对当前执行位置的路径train、val、test 分别填图片目录的相对路径训练器会自动把 images 换成 labels 去寻找标注文件nc 是类别总数必须和 classes.txt 的行数一致names 是类名列表顺序也必须和 classes.txt 一致。参数说明实际类名以你手上的 classes.txt 为准我这里只是为了举例。如果 nc 和 names 对不上训练器会直接报错或者把类别错位这是新手最容易翻车的地方。光看目录结构还不够我建议在训练前跑一个最小检查脚本扫描所有 txt 里的坐标值是否都在合法范围内。标注工具导出时偶尔会产生 0 到 1 之外的异常值这种脏数据不会让训练崩掉但会让 loss 曲线出现莫名的尖峰。from pathlib import Path label_dirs [Path(data/labels/train), Path(data/labels/val)] for ld in label_dirs: for txt in ld.glob(*.txt): for line in txt.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: print(f格式异常: {txt.name}: {line}) continue vals [float(x) for x in parts[1:]] if not all(0.0 v 1.0 for v in vals): print(f坐标越界: {txt.name}: {line})逻辑说明脚本遍历 train 和 val 两个标注目录逐行检查每个 txt 是否恰好有 5 个字段并对后四个坐标值做 0 到 1 的范围校验。凡是打印出来的文件都要人工确认。参数说明如果你拿到的是分成 3 类的数据集nc 和 names 相应改成 3 行即可检查脚本不需要变它只关心字段数量和坐标范围不关心类别数。2.3 划分合理性检查train 和 val 里有没有同源帧“划分好的数据集”不等于“划分合理的数据集”。很多从视频切帧得到的数据集划分时只是按文件名排序后切一刀结果同一个视频片段里相邻的几帧被拆到 train 和 val 两侧验证集里全是训练集里见过的场景评估结果虚高一截。最直接的检查是找出完全重复的图片md5sum data/images/train/*.jpg | sort /tmp/train.md5 md5sum data/images/val/*.jpg | sort /tmp/val.md5 comm -12 /tmp/train.md5 /tmp/val.md5 | wc -l逻辑说明第一条命令计算训练集所有图片的 MD5 并排序第二条对验证集做同样处理第三条用 comm 命令取出两侧相同的行也就是重复图片最后 wc -l 统计数量。如果输出是 0说明没有完全相同的图片。参数说明注意图片扩展名数据集里如果混着 .png 或 .JPG需要把通配符改成对应扩展名再跑一次。这种完全重复的情况比较少见更隐蔽的是“内容相似但文件名不同”的近似帧这种要靠抽帧间隔感知或感知哈希来查更详细的做法放在第 5 章排查里展开。3. 数据可视化脚本训练前把标注质量破绽找出来的最快路径这类数据包自带的可视化脚本常见功能不外乎三类把标注框画回原图、统计类别分布、输出样本量汇总。你拿到手先别急着只跑一遍看个热闹我一般会把这三类输出当作质量审计来用每一类都对应一种数据缺陷。3.1 图框叠加先看框贴不贴近目标再看类别贴不贴语义图框叠加是把每个 txt 里的归一化坐标还原成像素坐标画到原图上。这是最直观的质检手段能发现三类问题框把整片胸口都框进去而不是只框安全带区域安全带被方向盘或手臂遮挡时框还在但语义已经不对wearing 和 no_wearing 标签贴反。如果你拿到包的脚本只输出图片那我建议你额外准备一个最小可用的绘图函数十几行就能搞定import cv2 from pathlib import Path def draw_boxes(img_path: str, label_path: str, class_names: list, out_dir: str): img cv2.imread(img_path) h, w img.shape[:2] for line in Path(label_path).read_text().strip().splitlines(): cid, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 255, 0) if int(cid) 0 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[int(cid)], (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) out Path(out_dir) out.mkdir(exist_okTrue) cv2.imwrite(str(out / Path(img_path).name), img) for img_path in list(Path(data/images/train).glob(*.jpg))[:200]: label_path Path(data/labels/train) / img_path.with_suffix(.txt).name if label_path.exists(): draw_boxes(str(img_path), str(label_path), [wearing_seatbelt, no_seatbelt], vis/train)逻辑说明先把归一化的中心坐标和宽高换算成像素坐标然后按类别画不同颜色的框绿色是已佩戴红色是未佩戴框上方再写类名。我建议你渲染 train 和 val 各 200 张左右不要只盯着原始渲染图要带着“这个框能不能帮助模型学出安全带语义”的问题去看。参数说明颜色和类名的对应关系要和 classes.txt 一致如果数据换成了三分类class_names 列表要同步改color 的选取逻辑也要跟着编号走。看叠加图时重点看两点。第一是框的边界框太大会把胸口、方向盘都包进去模型学到的是“胸口附近有一个目标”而不是“斜跨的安全带”框太小会丢掉安全带的头尾模型学到的特征不完整。第二是类别边界如果同一条安全带上有的帧标为 wearing有的帧标为 no_wearing模型会直接学乱训练 loss 降不下去的锅经常在这里。3.2 类别分布与样本量统计一眼看穿不均衡安全带的类别不均衡问题比一般目标检测更棘手。现实监控里绝大多数驾驶员是系了安全带的no_seatbelt 的样本天然稀少而业务上恰恰最关心这种少数类。可视化脚本里的类别统计功能本质就是数一数每个类别在所有标注里出现了多少次from pathlib import Path from collections import Counter cnt Counter() for ld in [Path(data/labels/train), Path(data/labels/val)]: for txt in ld.glob(*.txt): for line in txt.read_text().strip().splitlines(): cnt[int(line.split()[0])] 1 for cid, num in sorted(cnt.items()): print(fclass {cid}: {num} 个实例)逻辑说明脚本遍历 train 和 val 两个标注目录对每个 txt 的每一行取行首的类别编号用 Counter 统计出现次数最后按类别编号打印。这个数字代表的是“实例数”而不是“图片数”一张图里有两个 no_seatbelt 框就会计两次对类别不均衡的判断要按实例数来。参数说明如果你只关心某一张图里有没有某个类别可以把累加逻辑改成 per-image 判断那个用于抽帧检查更合适。看完统计数字我的判断标准是少数类实例数如果不到多数类的 20%就需要在训练策略上做干预。干预手段不是删多数类样本而是做类别加权或者对少数类做过采样这一点会在第 4 章的进阶参数里具体给命令。可视化脚本里通常还会输出每张图的 box 数量分布这个指标用来判断一张画面里通常有几个目标如果 val 里大量图片是 0 目标就要警惕验证集里空帧太多影响 mAP 评估的参考价值。3.3 抽帧建目录把少数类样本单独挑出来人工复审有时候统计数字没有问题但你想确认少数类的样本质量这时候用目录抽帧比一张张看图更高效。我习惯把某个类别的样本单独复制到一个目录批量过一遍mkdir -p debug/no_belt python - EOF from pathlib import Path import shutil for txt in Path(data/labels/train).glob(*.txt): for line in txt.read_text().strip().splitlines(): if line.split()[0] 1: img Path(data/images/train) / txt.with_suffix(.jpg).name if img.exists(): shutil.copy(img, debug/no_belt) break EOF逻辑说明这段脚本在 train 标注目录里找所有包含类别 1 的 txt只要某张图里有至少一个 no_seatbelt 框就把对应图片复制到 debug/no_belt 目录然后 break 跳出这个小循环避免同一张图被复制多次。参数说明类别编号 1 对应 classes.txt 里的第二行如果你的类名顺序不同把这个数字换成实际的编号。复制出来之后用看图软件快速翻一遍重点确认两件事这些少数类样本里有没有大量重复的连续帧以及类别标注是否稳定一致。如果少数类样本大量来自同一个车牌同一段视频模型在真实场景里对“没系安全带”的泛化能力基本是空的。4. 用 YOLO 把安全带检测跑起来训练命令与参数取舍数据看完了接下来就是训练。这一章我只讲一件事怎么用最少的坑把 YOLO 训练跑通以及安全带场景下哪些参数值得你额外花时间去调。训练框架用 YOLO 官方仓库的 yolo 命令模型文件用预训练权重起步不要随机初始化从头训练。4.1 环境与数据路径先让 data.yaml 的 path 对齐到实际目录跑训练前先把环境装好核心是 PyTorch 和 YOLO 官方训练工具。GPU 不是必须的但没有 GPU 就别想用大分辨率训练CPU 跑一两个 epoch 验证流程可以完整训练不现实。装好之后第一件事不是直接开训而是确认 data.yaml 里的 path 能不能被正确解析。我见过太多人把数据集放在 /home/user/datasets 下data.yaml 里却写着相对路径训练器在错误的目录下找不到 labels一上来就报 no labels found。先跑一个最小验证用一个 epoch 在 CPU 上走一遍全流程python -c from pathlib import Path; print(Path(data/images/train).resolve()) yolo detect train datadata.yaml modelyolov8s.pt epochs1 imgsz640 batch4 devicecpu逻辑说明第一条命令打印训练图片目录的绝对路径确认你当前的工作目录离数据集足够近data.yaml 里的相对路径能正确解析。第二条命令用 1 个 epoch、CPU、batch 4 跑一个完整训练迭代目的是验证数据读取链路、标签匹配、类别数配置是否正确花不了几分钟。参数说明devicecpu 只是验证用正式训练换成 0 表示第一张显卡batch 在验证时可以调小CPU 内存不够就改成 2model 指定的是预训练权重文件yolov8s.pt 是中等体积的模型首次先不要选最大的模型跑通之后再换。这一步跑完你会看到训练日志里逐类打印目标数量和图片数量。重点看两个数字train 和 val 的图片数是否明显区分以及每个类别的实例数是否和你在第 3 章统计的一致。只要这里对得上后面训练再出问题就可以放心去调模型和参数而不是怀疑数据路径写错了。4.2 最小可复现训练命令先跑通再谈提点验证通过之后可以上正式训练命令。我用的是下面这条项目名 s 开头表示 small 模型起步跑通后再按需求换更大的模型yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns \ namesafety_belt逻辑说明data 指向 data.yamlmodel 指定预训练权重epochs 是训练轮数imgsz 是输入分辨率batch 是每批次图片数device 指定显卡project 和 name 决定输出日志和权重的保存目录。训练完成后会生成 runs/safety_belt 目录里面有权重文件、训练曲线和验证结果。参数说明batch 不是越大越好它受显存限制。以 16GB 显存为例imgsz640 时 batch 16 通常能跑但如果你把 imgsz 提到 1280batch 必须降到 4 到 8否则直接 OOM。epochs 100 是起步值安全带的类别差异不大一般 100 轮以内就能收敛重点看 val 曲线是否在后期还有上升空间。这条命令跑通后你会得到一个能用的基线模型。不要急着上线先看验证集上的 mAP50 和 mAP50-95 两个指标再结合第 6 章的坏例分析确认模型行为。如果 mAP50-95 明显偏低说明框的定位精度不行这会直接影响“安全带是否系好”的判断因为业务上关心的是安全带区域的精确位置。4.3 面向安全带场景的进阶参数分辨率、Mosaic 关闭时机、类别权重基线跑通之后安全带场景有两个明显的专项优化方向小目标和类别不均衡。小目标问题在远距离监控画面里特别突出驾驶员的安全带在 1280×720 的原图里可能只有几十个像素缩到 640 之后特征几乎消失。我的做法是把 imgsz 提到 1280同时把 batch 下调用训练时间换检测精度。另一个方向是类别不均衡如果第 3 章统计出少数类实例占比低于 20%我会在训练命令里显式做类别加权。yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs200 \ imgsz1280 \ batch8 \ mosaic1.0 \ close_mosaic10 \ patience20 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4逻辑说明imgsz 提高到 1280 后远处的小目标在特征图里能保留更多像素close_mosaic10 表示训练到最后 10 个 epoch 时关闭 Mosaic 增强避免模型在合成样本上过拟合最后阶段的评估更贴近真实分布patience20 表示验证集指标连续 20 轮不提升就提前停止hsv 三项是对色相、饱和度、明度的随机扰动安全带颜色与衣物、座椅接近时这类颜色增强能提升模型的抗干扰能力。参数说明imgsz 1280 会让显存占用翻倍batch 必须同步降下来close_mosaic 的值不要设太大10 到 15 比较合适太早关掉会损失 Mosaic 带来的鲁棒性收益。类别权重的设置在 YOLO 官方训练工具里通过 class_weights 参数传入需要你在训练前按少数类比例算好一组权重数组权重值我一般从 2 到 5 起步具体要看验证集上的召回率变化再调。加了这些参数之后训练时间会比基线长不少但安全带检测这个任务的业务价值恰恰体现在这些难例上近处的驾驶员大家都检得出来远处、侧光、遮挡状态下还能稳定输出才是这个模型真正能上线的原因。5. 安全带数据集训练的常见坑与排查现象、原因、解决这一章把我在安全带检测项目里实际撞过的坑集中说一说。每一条都按“现象 → 原因 → 解决”来写你可以直接对照自己的训练日志。5.1 训练一上来就报 no labels foundloss 一路为 0现象训练日志里出现 warnings提示某个图片目录下找不到标签文件loss 从第一个 epoch 开始就不变化等于模型在空数据上学了个寂寞。原因绝大多数是路径和文件名的匹配问题。data.yaml 里 val 写的是 images/val但实际目录叫 validation图片是 .JPG 大写扩展名而标注文件生成时用的是 .jpg或者 labels 目录下直接少了一批文件只有图片没有 txt。解决先检查 data.yaml 的目录名再检查图片和标注文件是否同名同扩展名。我习惯用一个命令统计两边文件数是否一致echo train images: $(ls data/images/train | wc -l) echo train labels: $(ls data/labels/train | wc -l)逻辑说明对比两个数字不一致就说明有图片没标注或者有标注没图片。参数说明如果扩展名不统一先统一扩展名再跑。这个坑在 CPU 验证那一步就能暴露所以无论如何不要跳过 4.1 里的最小训练验证。5.2 val 指标虚高划分泄露比你想的更隐蔽现象训练还没结束val 的 mAP50 就已经到 0.95 以上模型看起来强得离谱但放到真实视频流里测试连续报错一张没有人脸没有人身的空背景也能框出一个目标。原因数据集划分时把同一段视频的连续帧拆到了 train 和 val 两侧。模型记住的是场景和背景而不是安全带的语义特征。完全重复图片查重能查到但时间戳相邻的内容近似帧靠 md5 查不出来。解决先确认 val 的图片是否和 train 有内容重叠做法是随机抽 val 里 50 张图人眼比对有没有“同一辆车同一角度”的既视感。更规范的解法是回到源头把划分改成按视频片段切分同一个视频的所有帧只能进 train 或 val 中的一个集合不能两边都占。数据集的划分通常已经按这个思路做过但你最好自己验证一下。5.3 模型只会输出“未系安全带”类别不平衡的病征现象验证集上 no_seatbelt 的召回率很高wearing_seatbelt 的召回率很低每次预测几乎都在报“没系安全带”precision 和 recall 严重失衡。原因数据里 no_seatbelt 的实例数远少于 wearing_seatbelt模型在 loss 的驱动下倾向于把所有目标都判成多数类因为这样整体 loss 最小少数类的错误被大量多数类正确样本淹没了。可视化脚本统计出的类别分布数字在这一步就是排查依据。解决优先用类别加权或过采样不要盲目加训练轮数加轮数只会让模型更确信自己的偏见。在训练命令里给少数类更高的权重或者把少数类样本复制几份并配合随机增强让模型在少数类上多走几步。权重从 2 到 5 起步观察 val 上两个类别的 precision/recall 是否开始靠拢。5.4 远处驾驶员的安全带只有指甲盖大小小目标漏检现象近景检测正常一旦画面里有远景车辆、后排乘客或者摄像头安装角度偏低安全带就完全漏检。可视化叠加图里这类目标的安全带框只有十几个像素宽。原因640 的输入分辨率下小目标在 YOLO 的深层特征图里只剩几个像素语义信息被下采样抹掉了。数据分布里远景样本占比少模型没有机会学到这种尺度下的安全带形态。解决把 imgsz 提到 1280 是最直接的手段代价是训练和推理时间增加。如果项目对耗时敏感可以考虑在推理端对画面里的车辆区域先做一次检测再把车辆 ROI 放大后单独跑安全带模型这个两段式方案往往比单纯提分辨率性价比更高。另外用可视化脚本按框面积排一次序把面积小于图像总面积 2% 的样本单独统计比例如果数字超过三分之一就要认真对待小目标问题。5.5 安全带颜色和衣物、座椅、光影融为一体误检与漏检的情境化处理现象白色安全带配白色衬衫、深色安全带在逆光阴影里、安全带压在深色座椅上时模型时有时无一种颜色下检出来换个座椅颜色就丢掉。原因安全带是低纹理的目标可区分的特征本身就少靠的主要是颜色对比和几何走向。数据增强里如果没做颜色扰动模型会把某个特定颜色组合当成强特征。解决在训练命令里把 hsv_h、hsv_s、hsv_v 的扰动范围加大让模型见过各种色偏下的安全带。更重要的是检查标注语义框是否紧紧贴着安全带可见的斜段如果标注把大片衣物也框进去模型就会把衣物特征当成安全带特征遇到不同颜色的衣物立刻翻车。这一类问题没有一次性根治的招数只能在每次坏例分析后对持续出现的形态做定向补数据。6. 用坏例分析和 mAP 门控给安全带模型验收一个可复用的检查习惯6.1 先看数量差再看置信度最后看具体图训练结束不能只看一行 mAP 就收工。我给自己定的验收习惯是三步先看 val 上每个类别单独的 precision 和 recall再按“预测框数量与真实框数量的差值”把最离谱的样本挑出来最后一张张看挑出来的图。安全带业务的漏检和误检代价不一样漏掉一个未系安全带的司机比多报几次误警严重得多所以我的验收优先看少数类 recall。最简单有效的坏例抽取方式是做数量对比。预测结果和真实标签之间如果框数差很多大概率是漏检或者误检的集中爆发点from pathlib import Path pred_dir Path(runs/safety_belt/predict/labels) gt_dir Path(data/labels/val) for pred_txt in pred_dir.glob(*.txt): content pred_txt.read_text().strip() n_pred len(content.splitlines()) if content else 0 gt_txt gt_dir / pred_txt.name n_gt 0 if gt_txt.exists(): gt_content gt_txt.read_text().strip() n_gt len(gt_content.splitlines()) if gt_content else 0 if abs(n_pred - n_gt) 2: print(f{pred_txt.stem} pred{n_pred} gt{n_gt})逻辑说明脚本遍历验证集预测结果目录里的每个 txt统计预测框数量和真实框数量差值大于等于 2 的样本就是怀疑对象打印出文件名和两边数量。差值为正是疑似误检差值为负是疑似漏检。参数说明阈值设成 2 意味着至少差两个框才重点关注如果你的项目对漏检极敏感可以把阈值降到 1代价是人工要看的图多一些。跑完之后把打印出来的图片按文件名从 val 里找出来用可视化脚本渲染成叠加图逐张看模型到底犯了什么错。我最后会把这个坏例清单交给标注方再确认一遍边界情况。很多预测错误的背后不是模型笨而是标注本身在模糊边界上不一致这一轮确认下来既校准了数据也校准了模型的预期行为。以前我也为 val 指标好看就急着签验收吃过亏后来养成这个习惯每条新模型上线前都先跑一遍坏例分析再谈效果。希望帮到你。本文还有配套的精品资源点击获取