尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

安全帽目标检测实战:YOLO数据集处理与训练全攻略

安全帽目标检测实战:YOLO数据集处理与训练全攻略 简介面向YOLO安全帽佩戴检测任务的数据集资源包包含5000张真实场景、场景丰富的高质量图片由LabelImg人工标注框体质量可靠并同时提供VOC、COCO、YOLO三种格式标签分别存放在独立文件夹中可直接用于YOLO系列模型的训练与评估。配套资料涵盖Linux/Windows双系统的YOLO环境搭建教程、基于案例修改训练数据的实战讲解以及自动划分训练集、验证集、测试集的Python脚本帮助快速完成从原始数据到模型训练的全流程。压缩包共2000个文件其中1986个xml标签文件构成数据主体另有html格式的图文教程、py格式的划分脚本和txt索引文件整体约167MB。目前已有761人学习/下载适合目标检测学习者、算法工程师及相关课程项目使用如需更多数量或定制数据集可通过作者博客进一步联系。1. 安全帽目标检测5000张图加三套标签直接省掉格式转换的弯路做工地安监或工厂巡检的项目安全帽佩戴检测几乎是每个目标检测团队的入门必修课。这份资源解压之后是一套完整可训练的YOLO安全帽目标检测数据集——5000张已经标注过的现场图片每张图同时给到VOC、COCO、YOLO三种格式的标签另外附带一个划分训练集/验证集的Python脚本和一份训练教程。这意味着从拿到压缩包到跑出第一批权重中间不需要再自己写XML转txt、JSON拆分成框的代码。适合刚接触目标检测、想用现成数据集把YOLO全流程走通的学生也适合急着做安全帽检测预研的工程师直接拿它当底料。2. 压缩包开箱三种标签格式的差异与数据完整性预检2.1 目录结构images和labels怎么对齐解压.rar之后先别急着去翻训练教程先把目录关系捋清楚。这类数据集的标准布局一般是根目录下放两个一级文件夹images里按train、val、test分好图片子目录labels里按同样的子目录名放对应标注文件。但有一个常见坑标注文件的命名并不总跟图片一致有时是单独的编号命名有时带额外前缀。拿到手第一件事应该是确认图片和标签的文件名基一一对应我一般会先跑两条命令统计数量find images -type f \( -name *.jpg -o -name *.png \) | wc -l find labels -type f -name *.txt | wc -l如果两个数字不相等说明有漏标的图或者多出来的空标签文件需要在划分之前就处理掉否则后面训练时ultralytics会在加载阶段报AssertionError: Label not found之类的错误。还需要看一个细节图片目录里是否混有.xml或.json文件因为这份资源同时提供了VOC和COCO格式它们可能和YOLO的txt标签放在同一条路径下训练时容易误读。确认数量之后再抽查三五张图的尺寸。安全帽现场图片常见的是1920x1080或者1280x720统一尺寸最好如果发现图片尺寸特别不齐比如有640x480和4K图混在一起后面训练时imgsz640的缩放比例会有明显差异小目标反而更容易丢。可以用Python快速检查from PIL import Image import os img_dir images/train sizes {} for f in os.listdir(img_dir)[:100]: if f.endswith((.jpg, .png)): w, h Image.open(os.path.join(img_dir, f)).size sizes[(w, h)] sizes.get((w, h), 0) 1 print(sizes)这段代码只取前100张做抽样统计输出每种尺寸出现的次数。如果sizes字典里只有一个键值对说明图片尺寸统一如果出现三四种尺寸分布就需要在data.yaml里考虑是否做letterbox之外的处理。实测中尺寸不齐的数据集默认的rectTrue模式会按批次内最短边填充虽然能跑但会浪费一部分计算量。2.2 VOC、COCO、YOLO三套标签到底差在哪这份资源最有价值的地方就是同一批标注同时给了三种格式省去了转换工具链的配置。三种格式不只是文件后缀不同坐标表达方式和存储粒度完全不同用一个表说清格式文件形态坐标表达类别信息适用工具链VOC每张图一个XMLxmin, ymin, xmax, ymax绝对像素XML标签内嵌name字段老牌检测框架、Pascal VOC评测COCO整个数据集一个JSON[x, y, width, height]绝对像素先定义categories列表再引用idDetectron2、MMDetection等YOLO每张图一个TXTcx, cy, width, height归一化到[0,1]每行第一个数字是类别索引Ultralytics YOLO系列VOC的XML是嵌套结构每个object节点包含name和bndbox解析要自己写循环或者用xmltodict。COCO的JSON是先有categories段定义类别名和id然后annotations段里每个标注通过category_id引用类别——这是最容易出错的地方如果JSON里的category_id和images的id对应不上加载时会导致标注串图。YOLO的txt是最简洁的一行一个目标空格分隔格式固定为class_id center_x center_y width height但正因为简洁坐标必须提前归一化到[0,1]一旦转换时忘了除以图片宽高训练出来的模型直接没法用。拿VOC转YOLO举例常见做法是使用ultralytics自带的数据转换脚本或者用下面这段简洁代码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 if name not in class_map: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) cx ((xmin xmax) / 2) / img_w cy ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return linesclass_map是一个字典比如{helmet: 0, head: 1}它决定了最终训练时类别索引的顺序。这段代码里最容易出错的地方是括号里的坐标顺序xmin xmax除以二得到中心点x先算(xmin xmax)再除以2 * img_w和先除以2再除以img_w在数学上等价但代码里一旦写成(xmin xmax) / 2 / img_w之外的形式比如漏了外层的除2中心点就偏到一边去了。我见过不少翻车案例就是转换后框全部偏在图片左上角根因在这儿。2.3 开箱预检标注框的合法性检查图片和标签对得上还不够还要检查标注框本身是否合法。YOLO txt里所有坐标值都应该在[0,1]区间内width和height必须大于0。有时候源数据集里存在极端的标注比如框的xmax超出了图片宽度转换时又不取边界裁剪就会产生大于1的归一化坐标。训练时YOLO会强制把超出边界的部分裁掉但损失函数在计算时会把原始未裁剪的框也算进去导致置信度震荡。用一段脚本做合法性检查是明智的做法import os bad_files [] for split in [train, val, test]: label_dir flabels/{split} if not os.path.exists(label_dir): continue for f in os.listdir(label_dir): path os.path.join(label_dir, f) with open(path) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_files.append((path, 字段数不为5)) continue cls, cx, cy, w, h parts cx, cy, w, h float(cx), float(cy), float(w), float(h) if w 0 or h 0 or w 1 or h 1: bad_files.append((path, f框尺寸异常: {line.strip()})) for item in bad_files[:20]: print(item)这段脚本遍历train、val、test下所有YOLO标签文件核查每行是否恰好5个字段、框宽高是否在合法范围。输出前20条异常记录。实务里这笔检查值得做因为一旦混进去几条垃圾标注训练到五六百轮时验证集mAP会突然掉2-3个点排查起来比改标注还费时间。3. 划分脚本实战比例、随机种子与划分后分布验证3.1 脚本核心逻辑从全量图片到三个子集压缩包里带了划分脚本但我还是建议先读懂它的逻辑再跑不要拿到就双击。这类划分脚本的典型流程是读取全部图片文件名按比例随机打乱分成train、val、test三个列表然后把图片和对应标签复制进各自目录同时生成一个train.txt或val.txt记录文件路径。如果脚本是直接移动文件而不是复制建议先备份原始目录因为一旦划分结果不满意重新切分还得靠原文件。典型的划分代码核心部分长这样import os import random import shutil random.seed(42) images_all [f for f in os.listdir(images/all) if f.endswith(.jpg)] random.shuffle(images_all) train_ratio, val_ratio 0.8, 0.15 train_cnt int(len(images_all) * train_ratio) val_cnt int(len(images_all) * val_ratio) splits { train: images_all[:train_cnt], val: images_all[train_cnt:train_cnt val_cnt], test: images_all[train_cnt val_cnt:], } for split, files in splits.items(): os.makedirs(fimages/{split}, exist_okTrue) os.makedirs(flabels/{split}, exist_okTrue) for img in files: shutil.copy(fimages/all/{img}, fimages/{split}/{img}) label img.replace(.jpg, .txt) src_label flabels/all/{label} if os.path.exists(src_label): shutil.copy(src_label, flabels/{split}/{label})参数上三个值最关键random.seed(42)的种子数、train_ratio和val_ratio。种子数决定每次运行时洗牌结果是否一致固定成42是为了可复现——你跑出来的划分结果和别人跑出来的一样后面训练时对比指标才有意义。train_ratio0.8意味着5000张里4000张进训练集val_ratio0.15对应750张验证集剩下250张做测试。如果你的训练资源充足可以把验证集比例压到10%但不要低于10%否则验证集统计波动太大一个批次的好坏就能让mAP上下抖动。测试集比例不应该为0哪怕只有一两百张也要留出完全没参与训练调整的样本做最终评估。3.2 类别分布不均衡时的划分修正随机划分有一个隐性风险如果原数据集中“佩戴安全帽”的正样本有4200张“未佩戴安全帽”的负样本只有800张纯随机切分后测试集里负样本数量可能少到只有40张评估结果毫无统计意义。更麻烦的是训练集里负样本更少模型会严重偏向预测成正类。这时候需要在划分前统计每个类别的分布再按比例做分层抽样保证每个子集里类比大致与全集一致。分层抽样在代码层面不复杂from collections import defaultdict labels_by_cls defaultdict(list) for f in os.listdir(labels/all): if not f.endswith(.txt): continue with open(flabels/all/{f}) as fp: first_cls int(fp.readline().strip().split()[0]) labels_by_cls[first_cls].append(f) train_files, val_files [], [] for cls, files in labels_by_cls.items(): random.shuffle(files) split_idx int(len(files) * 0.8) train_files.extend(files[:split_idx]) val_files.extend(files[split_idx:])核心是按类别而非按全部样本先分组再各自切分每个类别内部都用同样的80/20比例。要注意的是这段代码只取了每个txt的第一行类别作为分组依据如果一个txt里有多个类别的目标高估了单类样本占比最好改成统计每个txt所有出现过的类别并归类到每一组。实际数据集中一张图既包含戴安全帽的人头也包含不戴安全帽的人头很常见所以上述示例只适合数据集类别比较单纯的情况复杂标注时使用更稳健的做法把每张图的类别分布向量加入random.shuffle后直接忽略直接用精确分层抽样会更繁琐但收益也更明显。3.3 划分后的二次检查脚本跑完之后不要马上开训练先看三个子集里图片数量和标签数量是否吻合再检查有没有空的标签文件。空标签文件是训练时的隐藏杀手某个txt文件只有0字节YOLO加载时会跳过它但图片对应的标注信息缺失模型在该图上就永远不产生损失梯度等于白白浪费训练样本。我在检查时习惯打印每个子集的样本数和类别统计确保类别id没有漂移。for split in [train, val, test]: n_images len([f for f in os.listdir(fimages/{split}) if f.endswith(.jpg)]) n_labels len([f for f in os.listdir(flabels/{split}) if f.endswith(.txt)]) n_empty 0 cls_counter defaultdict(int) for f in os.listdir(flabels/{split}): if not f.endswith(.txt): continue filepath flabels/{split}/{f} if os.path.getsize(filepath) 0: n_empty 1 continue with open(filepath) as fp: for line in fp: cls_counter[int(line.strip().split()[0])] 1 print(split, 图片:, n_images, 标签:, n_labels, 空标签:, n_empty) print(dict(cls_counter))这段输出会直接暴露问题如果train里类别0出现了4000次、类别1只出现150次说明负样本严重不足如果val里某个类别压根没出现验证集的mAP会偏低且带有欺骗性。碰到这种情况的常规解法是调整划分比例或者补充负样本而不是直接开训。4. 用这份数据集跑通YOLO训练data.yaml与关键训练参数4.1 写data.yaml五处不能错的配置项数据准备完毕之后进入训练准备环节。用ultralytics训练时第一个文件是data.yaml它告诉YOLO去哪里读图、有几个类别、类别叫什么。以安全帽检测常见两分类为例path: /home/user/datasets/safety_helmet train: images/train val: images/val test: images/test nc: 2 names: 0: helmet 1: head五处不能错的地方第一是path必须是绝对路径或者确保你在数据集根目录下执行命令使相对路径有效第二是train和val的值是相对path的目录路径不要写上images/train/末尾的斜杠多余字符第三是nc必须和实际标签最大类别索引1保持一致如果标签里出现类别id为2而nc: 2训练直接崩第四是names的索引从0开始且顺序必须和YOLO txt每行第一个数字对应第五是path指向的目录层级不要写成datasets/safety_helmet/images/train而要拆成path和二级路径否则图片读取路径重复拼接。这里需要强调names与txt类别索引的一致性很多安全帽数据集的类别顺序不是helmet和head而是head在前、helmet在后。拿到压缩包后先随便打开一个txt看每行第一个数字再对照类别定义文件确认顺序不要凭经验猜。我见过有人在data.yaml里写0: head, 1: helmet但txt里0其实是helmet整个训练过程既不报错也不提示直到验证时才发现预测结果和实际类别完全对调。4.2 启动训练从yolov8n到yolov8s的选型逻辑ultralytics包的训练命令非常简练命令行和Python接口二选一即可。命令行方式适合快速开始yolo detect train datasafety_helmet.yaml modelyolov8n.pt epochs120 imgsz640 batch16 device0modelyolov8n.pt会从官方的模型库下载预训练权重然后用自己的数据集继续训练这叫微调迁移学习。第一次跑的时候如果没有预先下载权重命令会先自动下载大约7M左右。epochs120是训练轮数imgsz640把输入图片统一缩放成640x640batch16是每批图片数量device0指用第一块GPU。如果你的显卡显存只有6G建议把batch降到8imgsz保持640不要动——imgsz降太多会直接影响小目标的检测精度安全帽在画面里通常属于中小目标512x512以下就开始明显掉点了。想在训练过程中实时看到更多指标可以用Python接口拿到训练返回的结果对象from ultralytics import YOLO model YOLO(yolov8n.pt) results model.train( datasafety_helmet.yaml, epochs120, imgsz640, batch16, patience20, seed42, save_period10, projectruns/safety_helmet, nameexp1, )patience20是早停的容忍轮数意思是如果验证集mAP连续20轮没有提升就自动停止训练。这个参数对时间紧张的项目很友好可以避免无效的长时间训练但如果设置太小比如5模型还没收敛就被打断损失函数还在明显下降时训练就停了反而拿不到好权重。seed42固定随机种子保证同参数多次训练结果一致。save_period10每10轮保存一次中间权重在runs/safety_helmet/exp1/weights/下会看到last.pt和best.pt两个文件best.pt是按照验证集mAP选出的最优权重日常推理和部署都用它。4.3 训练过程中的四个观察点训练一旦跑起来不要扔在那里就等结果。YOLO训练在终端里持续输出的指标四类box_loss、cls_loss、dfl_loss和mAP50、mAP50-95。前三个是训练损失正常情况应该一路下降后趋于平稳如果box_loss降到0.7左右就开始来回震荡说明学习率偏高或者数据里有噪声标注。mAP50是IoU阈值0.5时候的平均精度安全帽检测这种单类目标检测任务一般训练结束时mAP50应该能到0.85以上mAP50-95是更严格的综合指标会偏低一些大致在0.55-0.7之间都算正常。第二个观察点是训练曲线是否在后期出现断层。如果loss在第60轮突然跳高再回落大概率是学习率策略触发了一个阶段性调整这本身是正常现象。但如果跳高之后再也没降回来就要怀疑数据划分时验证集混入了和训练集高度相似的图片比如同一场景连续帧。安全帽数据集如果来自视频抽帧这种现象极其常见解决办法只有重新清洗数据集。第三个观察点是早停是否被频繁触发。如果你设置patience15结果每15轮就停一次说明验证集指标停滞模型过拟合风险在增加需要考虑加正则或者减小模型规模。第四个观察点是GPU util是否达到80%以上如果一直只有30%左右说明数据加载瓶颈在磁盘IO而不是算力应该把cacheTrue参数加上让ultralytics把图片缓存进内存速度能提升好几倍。5. 常见问题与避坑记录格式转换、路径与标注不一致的排查5.1 现象训练刚启动就报标签文件解析失败现象ultralytics执行训练后几秒钟内直接抛出RuntimeError: CUDA error: invalid argument或者更常见的AssertionError: Label file ... does not exist。原因YOLO标签txt里出现了多余的列比如某些行是0 0.5 0.5 0.3 0.3 extra或者类别id超出了nc定义范围。还有一种可能是标签文件的后缀不对YOLO要求.txt但从VOC转过来时可能生成了.txt.txt这类双层后缀。解决先跑一段脚本扫描所有标签文件按空格切分检查字段数是否恰好为5且第一个字段是否是0到nc-1之间的整数。字段数异常的整行删除或者重新回到原始VOC标注检查是不是转换脚本里多了个print字符混入。教训是写转换脚本时顺手加个assert len(parts) 5成本低收益高。5.2 现象Windows上跑训练能读图换Linux服务器读不到数据集现象压缩包在Windows上解压、划分、写yaml全部正常。传到Linux服务器上训练报FileNotFoundError但路径明明存在。原因数据集路径中含中文字符或者文件夹名里带空格。Windows文件系统默认支持中文目录名但Linux上如果locale没设置对Python的pathlib读路径时会因为编码差异失败。另一个隐蔽原因是压缩包在Windows上用记事本编辑过data.yaml保存成了带\r\n换行的UTF-8编码Linux下yaml.safe_load解析时把\r当成路径一部分于是路径末尾多了个不可见字符。解决补一个习惯所有路径和yaml文件一律用ASCII字符命名文件夹叫safety_helmet而不是安全帽数据集编辑过文件后统一跑一遍dos2unix data.yaml或者用VSCode把文件编码改成UTF-8、行尾改成LF。这属于一次配置、长期受益的规范从那以后我每次交接数据集都会先执行一条file data.yaml检查编码。5.3 现象训练不报错但推理时框全部偏移且尺寸异常现象训练loss正常收敛mAP也不低但一到实际图片上推理预测框全部挤在图片左上角或者框的宽高比明显不对。原因把VOC或COCO转成YOLO时归一化坐标使用了错误的图片原始尺寸。常见场景是训练照片是1920x1080但转换脚本里硬编码了416x416作为img_w和img_h或者读取了缩略图的高宽。YOLO对坐标非常敏感中心点偏移一点点都会导致检测框对不准目标如果比例关系错误框就会系统性偏到一侧。解决打开一个转换后的txt文件取出一个框手动计算一下cx * img_w看结果是否落在标注目标的中心区域。批量验证时把txt里的框画回原图保存出来肉眼扫一遍即可。这一步是格式转换后的必做检查不要相信转换脚本本身。5.4 现象验证集mAP很高但现场测试效果差现象训练日志里mAP50到0.92验证集loss也低但拿到手机随手拍的现场图上一测漏检率极高。原因数据集划分没做地域隔离。如果全部图片来自同一个工地、同一个摄像头视角模型学到的是背景和光照的强相关特征而不是安全帽本身的形状特征。验证集里的图片可能和训练集来自同一批视频的连续帧指标虚高一换场景就完蛋。解决做数据清洗时按采集点分组确保同一个场景的视频帧全部落在同一个子集里不要让同一场景的图片同时出现在训练集和验证集。最稳妥的做法是按拍摄时间段或者摄像头编号做分组划分而不是纯随机切分。这是安全帽数据集最容易踩的坑也直接决定了项目能不能落地。5.5 现象训练中cls_loss不降反升mAP震荡现象cls_loss在前30轮正常下降之后突然反弹伴随mAP剧烈震荡整体不收敛。原因类别数量极不均衡比如helmet有上万框head只有几百框。分类损失被大类别主导小类别学习不足导致验证集上对大类别过拟合、对小类别欠拟合的交替波动。解决先用划分脚本里的类别统计确认比例关系。如果偏差超过10倍考虑改动损失权重。ultralytics里可以在model.train()时传cls1.5适当提高分类损失的权重更彻底的思路是给head类做数据增强或者在可接受范围内少量复制小类别样本。这个方法治标根本方案还是要补充负样本数据量。6. 验证模型与进阶玩法用混淆矩阵盯漏检再扩展自己的数据集训练结束后best.pt拿在手里不要立刻部署先跑一遍官方的验证命令拿到详细指标yolo detect val modelruns/safety_helmet/exp1/weights/best.pt datasafety_helmet.yaml batch1验证完成后runs/detect/val目录下会生成confusion_matrix.png、results.png和F1_curve.png。安全帽检测场景里我最关注的是混淆矩阵因为它能直接指出哪两类最容易互相认错。正常两分类的混淆矩阵应该是对角线占绝对主导helmet被正确识别为helmet的比例在95%以上如果head这一行的漏检率超过15%说明模型把大量未戴安全帽的人头当成背景忽略掉了这时候回到训练阶段去补head类样本比调阈值更有效。进阶玩法方面5000张数据集做完基础训练之后可以走一遍伪标注循环用best.pt对你自己收集的现场视频帧做预测把置信度高于0.8的框自动转成标注人工只核查低置信度部分。这相当于让模型帮自己标注新数据能快速把类别覆盖不足的地方补上。具体做法是用model.predict()输出结果里的boxes.xyxy和boxes.cls直接写成YOLO格式的txtfrom ultralytics import YOLO import os model YOLO(best.pt) results model.predict(new_scene_images, saveFalse, conf0.8, imgsz640) for r in results: img_path r.path txt_name os.path.splitext(os.path.basename(img_path))[0] .txt with open(os.path.join(labels/new, txt_name), w) as fp: for box, cls in zip(r.boxes.xyxy, r.boxes.cls): x1, y1, x2, y2 box.tolist() w, h x2 - x1, y2 - y1 cx, cy (x1 x2) / 2, (y1 y2) / 2 img_w, img_h r.orig_shape[1], r.orig_shape[0] fp.write(f{int(cls)} {cx/img_w:.6f} {cy/img_h:.6f} {w/img_w:.6f} {h/img_h:.6f}\n)conf0.8是置信度阈值定得太低会引入大量噪声标注定太高能生成的标注太少一般先在0.7-0.85之间试一轮看生成的标注文件里框的数量与质量再微调。imgsz640和训练时保持一致避免尺度差异影响框的坐标准确性。这段代码跑完后新场景图片和自动生成的标签就组合成一条补充数据混入原数据集重新训练即可。从那以后我每次做安全帽检测项目都强制自己走一遍完整闭环先校验标签再分层划分训练结束后必定翻混淆矩阵最后拿best.pt去标一批新数据回来补训。5000张数据集只是起点真正让模型在工地实战中立住的反而是循环迭代这个动作希望帮到你。本文还有配套的精品资源点击获取
返回列表