ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

三类别电动车头盔检测数据集:YOLOv5训练与调参实战指南

三类别电动车头盔检测数据集:YOLOv5训练与调参实战指南 简介面向目标检测初学者与头盔佩戴自动识别需求提供一套按YOLOV5目录结构整理的道路电动车头盔检测数据集。共3个类别戴头盔、没戴头盔以及行人整体标注图片为1920×1080分辨率的RGB道路场景适合直接接入YOLOV5训练流程免去格式转换与手工整理。资源共2000个文件主体为1999个txt标注文件配合1个show.py可视化脚本压缩后502.44MB数据划分清晰训练集包含3646张图片与对应标签验证集包含911张图片与对应标签另有txt类别说明文件。其中show.py无需修改即可运行随机读取一张图片就能绘制出边界框并保存到当前目录方便快速核验标注效果。目前已有955人学习下载适合用于毕业设计、课程项目或算法练手省去采集与标注的重复工作。1. 为什么“3类别”的电动车头盔数据集反而比二分类更难用很多人在找“道路上电动车是否佩戴头盔目标检测数据集”时默认以为拿到一个分好类的压缩包、跑通YOLOv5训练就完事了。但真正做过这个方向的人会告诉你头盔检测的难点从来不是“有没有戴”而是“戴的是什么”和“没戴到什么程度”。这个数据集标成3类——通常是helmet正确佩戴、head_without_helmet完全没戴、head_with_helmet_bad戴了但没系扣或挂在脖子上——意味着你训练出来的模型不仅要识别“有无”还得判断“佩戴是否规范”。这对标注一致性、正负样本比例、以及YOLOv5的anchor设置都提出了比二分类更苛刻的要求。这篇文章面向的是真正要拿这个数据集去跑训练、调参、甚至部署到边缘设备上的从业者。我会从数据集目录结构讲起直接给你一份能跑的YOLOv5训练配置再把3类别最典型的几个坑——类别不平衡、头盔小目标漏检、误检高发场景——逐一拆开。你跟着走一遍至少能少翻三次车。2. 拿到数据集后先别急着训目录结构、标注格式与类别核对2.1 数据集的常见目录组织方式这类“道路电动车头盔检测”数据集一般会按YOLO格式或VOC格式交付。以最常见的YOLO格式为例helmet_dataset/ ├── images/ │ ├── train/ │ │ ├── img_0001.jpg │ │ ├── img_0002.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ │ ├── img_0001.txt │ │ ├── img_0002.txt │ │ └── ... │ ├── val/ │ └── test/ ├── classes.txt └── dataset.yamlclasses.txt里通常就三行helmet、head_without_helmet、head_with_helmet_bad。注意有些数据集会把类别顺序写成head_without_helmet在前、helmet在后如果你直接用别人的dataset.yaml而不核对训练出来的模型类别顺序就是乱的推理时张冠李戴。标签文件里每行对应一个目标框class_id center_x center_y width height。这里的center_x、width全部是相对于图像宽高的归一化值。我刚拿到这类数据集时习惯先用Python快速扫一遍所有标签看类别分布和边界框尺寸分布——这一步能省掉后面一大半调参的力气。2.2 用脚本核对样本数与类别平衡3类别里最典型的分布是helmet样本最多head_without_helmet次之head_with_helmet_bad最少。有的数据集里第三类可能只占5%不到。这种分布会让YOLOv5在训练时严重偏向多数类。import os from collections import Counter label_dir helmet_dataset/labels/train counter Counter() for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: cls_id int(line.split()[0]) counter[cls_id] 1 print(counter) # 输出示例: Counter({0: 5210, 1: 2870, 2: 430}) # 如果类别0是helmet类别2只有400出头就要考虑采样策略这段代码做的事很简单遍历训练集所有标签文件统计每个类别的目标框总数。逻辑说明我统计的是“目标框数量”而不是“图片张数”因为一张图里可能有5个不戴头盔的人导致类别比例看上去均衡实际目标框却失衡。参数说明如果发现最少的类别占比不足10%建议在训练时开启--image-weights参数YOLOv5会根据每个类别的样本权重动态调整采样比例。2.3 边界框尺寸分布决定anchor策略YOLOv5默认的anchor是针对COCO数据集设计的COCO数据集80类、物体普遍大而居中。但电动车的头盔在道路监控画面里往往只占很小一块尤其是远景电瓶车头盔像素可能只有20x20。这时候要重新聚类anchor。import numpy as np from pathlib import Path def load_bboxes(label_paths): all_wh [] for lp in label_paths: with open(lp, r) as f: for line in f: parts line.split() w float(parts[3]) h float(parts[4]) all_wh.append([w * 640, h * 640]) # 假设图像resize到640 return np.array(all_wh) label_files list(Path(helmet_dataset/labels/train).glob(*.txt)) wh load_bboxes(label_files) print(f边界框数量: {len(wh)}, 平均尺寸: {wh.mean(axis0)})参数说明640是YOLOv5默认的imgsz训练尺寸如果你后续部署用imgsz416这里改成416。跑完这段脚本你大概会看到平均框宽高在[35, 30]左右——明显小于COCO的平均水平。这类数据集我一般直接换用k-means聚类出的anchor而不是吃YOLOv5默认值。3. 用YOLOv5训练3类别头盔检测从环境配置到启动训练3.1 环境配置与依赖安装YOLOv5训练环境本身不算复杂但版本兼容问题足够让人折腾一天。我当前的常用搭配是Python 3.9-3.10、PyTorch 1.13、CUDA 11.7。如果你用最新版PyTorch 2.x某些旧版YOLOv5代码会报torch.insert的坑建议直接拉最新的官方仓库。# 创建conda环境并安装依赖 conda create -n yolov5 python3.10 -y conda activate yolov5 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt这段命令里最值得留意的是--index-url。逻辑说明默认PyPI源装的PyTorch是CPU版本训练时根本用不上GPU跑起来慢得让人怀疑人生。参数说明CUDA版本以你自己的显卡驱动为准如果是RTX 4090这类新卡需要安装适配CUDA 11.8以上或12.x的PyTorch否则会报no kernel image is available。装完依赖后用官方自带的detect.py跑一次推理验证环境python detect.py --weights yolov5s.pt --source data/images/bus.jpg如果能看到输出图里正确框出人、车、bus说明环境基本通了。这一步没跑通就不要继续往下走否则后面训练出的问题你会分不清是环境问题还是模型问题。3.2 编写dataset.yaml并做数据集划分YOLOv5要求数据集配置一个YAML文件。路径建议都用绝对路径而不是相对路径——相对路径在这个项目里是我踩过最多次的坑# helmet.yaml path: /home/user/helmet_dataset # 数据集根目录 train: images/train # 训练集相对path的路径 val: images/val # 验证集 test: images/test # 测试集可省略 nc: 3 # 类别数 names: [helmet, head_without_helmet, head_with_helmet_bad]逻辑说明path字段是YOLOv5从5.0版本之后引入的写法用来统一拼接train、val、test路径。如果你把train写成/home/user/helmet_dataset/images/train这种全路径同时path又写了根目录YOLOv5会把两段拼起来直接报路径不存在。参数说明names的顺序必须和标签文件里的class_id一一对应这里最容易翻车的就是把这个顺序搞错。数据集划分如果原包没有给好用train_test_split脚本快速处理import os import random from shutil import copyfile image_files os.listdir(helmet_dataset/images) random.seed(42) random.shuffle(image_files) train_split int(len(image_files) * 0.8) val_split int(len(image_files) * 0.9) # 留10%做测试 for i, fname in enumerate(image_files): src_img fhelmet_dataset/images/{fname} src_label fhelmet_dataset/labels/{fname.replace(.jpg, .txt)} if i train_split: dst split/train elif i val_split: dst split/val else: dst split/test copyfile(src_img, f{dst}/images/{fname}) copyfile(src_label, f{dst}/labels/{fname.replace(.jpg, .txt)})说明这个切分逻辑是按整图划分不是按目标框划分。按图划分能避免同一张图的内容同时出现在训练集和验证集造成验证指标虚高。random.seed(42)保证每次运行得到相同划分结果方便复现。3.3 启动训练并盯住关键指标万事俱备启动训练python train.py \ --data helmet.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache \ --name helmet_v5s参数说明--weights yolov5s.pt用COCO预训练权重做迁移学习。头盔检测属于目标检测的下游任务COCO里虽然没头盔类别但特征的通用性足以让模型快得多收敛。如果从零训练--weights 100个epoch大概率还在过拟合边缘挣扎。--cache把图片一次性加载进显存能显著缩短训练时间。代价是显存占用暴涨我现在用的RTX 3090 24G才能配合batch16和imgsz640跑得动。显存小的机器建议去掉或用--cache ram。--name helmet_v5s指定本次训练的日志和权重输出目录默认为runs/train/exp多个实验跑下来会非常混乱。训练过程中每分钟刷一次的指标矩阵里我第一个看的不是mAP而是train/box_loss和val/box_loss。这两个值在训练初期如果完全不下降大概率是标签坐标归一化出了问题——通常是标注文件的坐标范围写成了像素值。第二步看metrics/precision和metrics/recall的差距如果precision高但recall低说明模型倾向于保守漏检多反过来则误检多。头盔检测场景里漏检比误检严重得多——漏检一个不戴头盔的骑手等于这个模型在实际部署中形同虚设。3.4 显存不足时的降级方案如果你用的显卡只有8G显存比如RTX 3060 Laptop上面这条命令大概率会直接爆发显存溢出。我一般从三个方向降python train.py --data helmet.yaml --weights yolov5s.pt --img 512 --batch 8 --epochs 100 --name helmet_v5s_512减小--img首当其冲从640降到512显存占用直接少一半。不过注意训练尺寸变了推理时最好也保持同样尺寸否则尺度不一致会影响精度。其次把--batch降到8最后可以考虑换yolov5s.yaml为yolov5n.yaml——这个配置文件控制的是网络宽度和深度n版参数只有s版的三分之一精度会掉一些但换来的是训练效率和边缘部署的友好性。4. 3类别检测的四个必调参数anchor、类别权重、IoU阈值与NMS设置4.1 用自动anchor让模型匹配头盔尺度YOLOv5的train.py每次训练都会在启动时重新计算anchor除非你显式传--noautoanchor禁用。这个机制会根据当前数据集的边界框尺寸用k-means聚类出更适合的anchor。不过它只在首个epoch做一次如果数据分布太偏它聚类出的anchor也可能不够理想。我通常先跑一遍自动anchor训练然后查看runs/train/exp/opt.yaml里记录的anchor值再手动改models/yolov5s.yaml里的anchor定义锁定后训练# anchors: 3 scales x 3 aspect ratios anchors: - [10, 13, 16, 30, 33, 23] # P3/8 小目标 - [30, 61, 62, 45, 59, 119] # P4/16 中目标 - [116, 90, 156, 198, 373, 326] # P5/32 大目标 (原COCO默认)头盔检测数据集中中大型anchorP5那组几乎用不上我把这组换成更小尺寸比如[20, 25, 30, 55, 55, 80]。逻辑说明YOLOv5在三个不同尺度的特征图上分别做预测P3负责小目标P5负责大目标。道路监控画面里的头盔通常落在P3和P4与其让P5那组大anchor占着权重不如把小anchor尺寸往小压。改完之后训练你能明显看到小目标类别的recall拉上来。4.2 类别不平衡给少数类加loss权重3类别数据集的常见问题是head_with_helmet_bad样本太少。YOLOv5官方没有直接提供类别权重参数但可以在train.py里改loss.py的部分代码——这种做法比较侵入另一种思路是直接在数据层面处理。我更推荐做离线数据增强把少数类对应的图像旋转、左右翻转、亮度抖动后复制进训练集。注意增强时标签坐标要跟着变换用albumentations库里的RandomBrightnessContrast配合BboxParams可以省掉自己写变换逻辑的麻烦。4.3 推理阶段的IoU阈值与NMS设置训练是一回事部署时调参数又是另一回事。detect.py里最值得改的是这两个参数python detect.py \ --weights runs/train/helmet_v5s/weights/best.pt \ --source test_images/ \ --conf-thres 0.25 \ --iou-thres 0.45 \ --img 640参数说明--conf-thres是置信度阈值低于这个值的目标直接被丢弃。头盔检测这种安全相关场景我推荐设0.15到0.25——宁可多一些误检也要保证不戴头盔的人被框出来。--iou-thres是NMS过程中判断两个候选框是否是同一目标的IoU阈值默认0.45如果画面上人挨着人、电动车并排骑行导致头盔框互相交叠把这个值调大到0.5以上可以减少重叠框被抑制后只保留一个的情况——但调太大也会让两个不同目标被合并成一个框。常见的调参失误有人为了提高precision把--conf-thres调到0.5结果原本就难检的head_with_helmet_bad几乎全军覆没。这类“戴了但没系好”的正样本特征上与“正确佩戴”非常接近置信度天然偏低。阈值设太高等于把这个类别删掉了。4.4 超参数让误检和漏检更可控YOLOv5的data/hyps/hyp.scratch-low.yaml里有一些全局超参数头盔检测里最值得动的是hsv_h、hsv_s、hsv_v——这是训练时对图像做HSV色彩增强的强度。道路监控画面受天气和光照影响极大阴天、逆光、夜间场景下头盔颜色和轮廓都会发生变化。我把默认的hsv_h: 0.015、hsv_s: 0.7、hsv_v: 0.4稍微调高到0.02、0.8、0.5让模型在训练中见到的颜色变化更多样减少对特定颜色的过拟合。另外fliplr: 0.5是水平翻转增强的比例保持默认就好——头盔左右对称翻转不会生成错误的监督信号。5. 3类别头盔检测的避坑合集从标注到部署的5个教训5.1 类别标注不一致同一顶头盔能被标成两类现象训练完验证集mAP不低一测试发现模型把“戴头盔的人”和“没戴头盔的人”反复横跳同一张图换个角度就分类翻转。原因数据集标注阶段对“戴了但没系扣”的边界定义不清晰。有的标注员把帽子、鸭舌帽也标成了helmet有的把挂在车把上的头盔标成了head_with_helmet_bad——这根本不是人头位置目标框和人体完全不匹配。解决训练前把所有标签按类别抽出来可视化一遍。用cv2.rectangle把目标框画在图片上人眼扫一遍把明显标错的图直接删掉或重新标注。这个过程得有心理准备1000张图可能得花两三个小时。但跳过这一步后续所有调参都是在烂数据上瞎折腾。5.2 验证集指标虚高实际场景全崩现象训练时val mAP到0.85部署到真实道路监控准确率掉到一半。原因验证集和测试集图片来自同一个视频流的不同帧高度相关。模型在验证集上看到的场景和实际部署场景差异极大——数据集里几乎全是白天光线充足的画面而你部署的位置可能有树荫、雨雾、夜晚路灯。解决用--source单独拉一个真实场景的视频文件做鲁棒性测试不要只看mAP。尤其是夜间场景如果训练集里没有晚上骑电动车的图片模型对暗光下的头盔基本睁眼瞎。我一般会从数据集外补充夜间和逆光图片用训练好的模型打伪标签再手工修正混入训练集做一次微调。5.3 后处理阶段漏检大量小目标现象大尺寸头盔全部检测正常距离远一点、像素小一点的头盔全部漏掉。原因小目标在YOLOv5的P3特征图上对应的特征较少。如果训练时--img用640推理时NMS的阈值又偏严小目标经过两次下采样后只剩下极小的特征激活区域置信度天然上不去。解决把推理尺寸从640提升到960或1280但显存和时间消耗也会同步涨。我是先做anchor聚类如果聚类结果中小框占比超过一半直接换yolov5s6.yaml这种6个输出尺度的模型结构如果数据和算力都有限就老老实实把训练和推理的--img都提到960。注意这会让训练时间几乎翻倍。5.4 骑手手臂与摩托车把手造成误检现象头盔检测模型把路人的手机、背包、甚至路边园丁的草帽识别成了头盔。原因3类别中的helmet是一个相对宽泛的语义——圆形的、深色的、在人头顶的物体。数据集中有些图的头盔外形差异大比如粉色头盔、半盔和全盔模型学到的特征是“圆形颜色深在人头上”导致手机的凸起、背包的圆角也被激活。解决检查数据集中头盔的形态多样性是否覆盖了目标场景。如果不够补充数据或者做更激进的颜色/几何增强。另外一条路是在模型后接一个基于位置约束的过滤逻辑——头盔必须出现在人的头部区域如果检测框中心点距离人体框顶部太远直接丢弃。这类后处理通常用pip install shapely算IoU来过滤。5.5 训练中断后继续训练导致loss暴涨现象训练到epoch 40时显存溢出或断电用--resume继续训练loss曲线直接飞到天上。原因训练中断时保存的last.pt是断点处的优化器状态和权重但如果你在中断后改了--batch或--img学习率调度器和优化器状态对不上了。解决--resume时保持和上次完全一致的batch和img参数。如果非要改建议从best.pt重新训而不是从last.pt续跑。另外给train.py传--workers 0可以避免某些Windows环境下dataloader的线程问题导致的中断。6. 把头盔检测模型部署到边缘设备模型降级与推理加速的实用做法这个项目最常见的落地场合是电动自行车出入口的监控摄像头现场往往只有一台Jetson Nano或树莓派。这些设备的算力跑不动YOLOv5s原始模型需要做量化裁剪。我当前的推荐做法是先用yolov5s.pt训练验证通过后导出为torchscript或engine格式再做INT8量化。YOLOv5官方仓库自带的export.py可以直接把PyTorch权重导出为TensorRT引擎python export.py \ --weights runs/train/helmet_v5s/weights/best.pt \ --include engine \ --imgsz 640 \ --half参数说明--half开启FP16精度显存占用减半推理速度几乎翻倍精度损失在头盔检测这种语义不那么精细的任务上基本可以忽略。如果你用TensorRT--int8需要额外的校准数据集做法是在data/helmet.yaml里指定calib: images/val然后导出时加--int8。部署时我用的是TensorRT的Python API加pycuda做异步推理。整条pipeline包括读帧、预处理resize到640再Normalize、推理、后处理解anchor、NMS。实际在Jetson Nano上FP16的YOLOv5s能跑到15-20 FPS如果卡到10 FPS以下把--imgsz降到480甚至416——头盔检测在小尺度下掉点还在可接受范围但流畅度直接影响抓拍成功率。一个人逃不过的经验头盔检测的能不能用七分在数据两分在调参一分在算力。我做过不止一次把大量时间花在调NMS阈值和anchor上最后发现数据集本身有几百张标注错乱的图。所以拿到这个数据集第一件事不是跑train.py而是花半天把标签可视化检查一遍。等模型在真实场景里稳定跑上一周你再回头看会发现最值得投入的永远是数据质量本身。希望帮到你。本文还有配套的精品资源点击获取
返回列表