
简介面向智慧工地安全巡检场景一套YOLO目标检测数据集资源覆盖安全帽、反光衣、头盔、背心、靴子等安全装备识别任务可支撑人员安全着装检测、违规预警等应用开发。压缩包内共2000个xml标签文件采用Pascal VOC格式记录目标位置与类别信息总大小326.49MB对应工地现场不同角度、光照和天气条件下的图像标注内容可直接用于YOLO系列模型的训练、验证与评估xml文件命名规范便于按需划分训练集与测试集。适合具备深度学习与目标检测基础的研究人员、算法工程师及智慧工地项目开发者使用可用于模型微调、数据增强、检测精度调优等实验也可结合实际业务构建安全帽佩戴检测、反光衣识别等智能化监管工具。目前已有403人浏览学习数据标注结构清晰、类别明确是开展工地安全AI落地实践的有效支撑。1. 智慧工地数据集的价值7538张带标签图像到底能训出什么工地项目上监理每天蹲在监控墙前盯安全帽和反光衣眼睛根本看不过来。yolo算法加一份带标签的智慧工地数据集就是把这活儿交给模型。这个zip包里的7538张图像覆盖头盔、背心、靴子这几类穿戴目标是训练安全检测模型的地基。适合谁做工地安全AI的算法工程师、搞智慧工地平台的集成商还有实验室拿公开数据做毕业设计的学生。要解决的问题很具体如何从一堆带标签图像出发把它变成能识别工人是否合规穿戴的检测模型并且知道训练过程里哪些参数会翻车、哪些坑必须绕开。2. 拆解7538张图像标签体系与数据质量检查拿到zip先别急着训练先看清数据和标签长什么样。我见过太多人直接把zip包解压后丢进训练脚本结果训练到一半报错或者mAP一直上不去回头排查才发现是标签对不上、类别不统一这类基础问题。数据质量检查这一步花半小时后面能省一整天。2.1 类别字段与标注语义头盔、背心、靴子对应什么检测目标一份合规的智慧工地数据集通常包含person、helmet、reflective_clothing、boots这几类。标题里写的“靴子-头盔-背心”就是其中的三个关键穿戴物分别对应防护靴、安全帽、反光衣。标签不是随便画个框就完事标注规范一般要求helmet只标佩戴在头部的安全帽本体反光衣标穿在上身的背心区域靴子标工人脚上的防护鞋。如果数据集的标注者是按这个语义标的模型学到的“反光衣”才是现场能用的反光衣而不是把红色衣服都当成反光衣。数据质量检查的第一步是把每个标注文件对应到图像上看一遍。常见做法是写个脚本把标注框直接画回图里随机抽查几十张肉眼确认有没有错标、漏标、框得离谱的样本。这个步骤虽然费眼睛但对后续训练的影响比调参还大。我一般先看三样东西类别标签是否统一、标注框有没有明显越界、是否存在大量小目标比如远处工人头顶只有几十个像素的安全帽。这三样直接决定后面要不要做针对性处理。2.2 用Python统计标签分布找类别不平衡的隐患拿到解压后的数据先用一段小脚本看家底。以VOC格式的xml标注为例统计逻辑是这样的import os import xml.etree.ElementTree as ET annot_dir annotations counts {} image_sizes [] total_boxes 0 for fname in os.listdir(annot_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(annot_dir, fname)) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) image_sizes.append((img_w, img_h)) for obj in root.findall(object): cls obj.find(name).text.strip() counts[cls] counts.get(cls, 0) 1 total_boxes 1 print(类别统计:, counts) print(图像尺寸样本:, image_sizes[:5]) print(总标注框数:, total_boxes)这段代码做了三件事统计每个类别的样本数、记录图像尺寸分布、读取xml里的目标框。类别统计出来如果helmet有2万框而boots只有3千框这就是典型的长尾分布。训练时模型会优先拟合样本多的类boots的mAP大概率垫底。图像尺寸分布如果从640到4000像素都有后面训练imgsz和resize策略就要小心——直接统一缩放到640远处的安全帽会被缩没。统计完别急着走还要看标注框的面积分布。把每个框的宽高除以图像宽高得到归一化尺寸统计长边小于图像宽度5%的框占比。如果这类小目标占比高属于小目标密集场景后面训练要酌情开mosaic增强或者用更高的输入分辨率否则现场远距离检测基本是空的。2.3 标注格式识别从xml、json到txt最常见的坑是拿到手的zip里标注格式五花八门。有的数据集给VOC的xml有的给COCO的json有的直接给YOLO的txt。判断方法很简单打开一个标注文件看内容。以yolov8训练自己的数据集为例最终要的格式是每张图像对应一个同名txt每行是“类别id x_center y_center width height”坐标全部归一化到0到1。我一般先写一个函数把所有标注读成统一的结构体再按需转格式。这样无论原始是哪种格式后面预处理都走同一套代码避免转换过程中坐标系搞混。这里有个细节VOC的xml坐标可以带小数COCO的json里坐标是左上角x/y和宽高YOLO的txt是归一化中心点坐标。三者之间的换算错一步框就全歪了。归一化中心点坐标时x_center和y_center都要除以图像宽高宽和高也要分别除以图像宽高不是统一除以一个大边。不少新手在这里把宽高比搞反训练出来的模型框全是扁的。3. 把zip包变成可训练目录格式转换、数据划分与yaml配置标题里的zip云压缩包只是原材料yolo训练需要的是一个结构清晰的目录严格对齐的图像和标签、划分好的train/val、还有一份dataset.yaml。这一章把整套预处理流程走通每一步都给可直接抄的代码。3.1 解压、清洗与目录规划解压命令很直接unzip yolo算法-安全帽-反光衣智慧工地数据集-7538张图像带标签-靴子-头盔-背心.zip -d ./construction_safety cd ./construction_safety ls -la解压后先做三件事剔除损坏图片、剔除空标签、统一命名。损坏图片用opencv读一遍读不出来就挪到trash目录它会让训练中途报“Failed to load image”之类的错误。空标签指有图像但txt/xml里一个框都没有的文件这类样本在训练时会被当作纯背景少量没关系如果数量超过几十张建议单独放避免背景类比重失衡。命名统一也很重要图像名和标签主名必须完全一致比如IMG_001.jpg对应IMG_001.txt后缀不同没关系主名一致就行。我习惯把图像统一成.jpgpng转jpg能省不少磁盘但要注意png带透明通道的图转jpg后边缘会变黑这类图直接丢弃更省事。3.2 VOC/COCO转YOLO格式归一化与坐标系换算假设数据集是VOC格式的xml转YOLO txt的脚本写法如下import os import xml.etree.ElementTree as ET CLASSES [person, helmet, reflective_vest, boots] def convert_voc2yolo(annot_dir, label_dir): os.makedirs(label_dir, exist_okTrue) for fname in os.listdir(annot_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(annot_dir, fname)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): cls obj.find(name).text.strip() if cls not in CLASSES: continue cls_id CLASSES.index(cls) 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) # 越界裁剪到图像边界内 xmin max(0, min(xmin, w - 1)) ymin max(0, min(ymin, h - 1)) xmax max(0, min(xmax, w - 1)) ymax max(0, min(ymax, h - 1)) cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 过滤退化框 if bw 0 or bh 0: continue lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) if lines: out_path os.path.join(label_dir, os.path.splitext(fname)[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines)) convert_voc2yolo(annotations, labels)这段代码的关键点有三个。第一xmin这类坐标如果是浮点转YOLO时先裁剪到图像边界内再归一化防止训练时维度越界报错。第二过滤宽高为零的退化框这类框往往是标注软件误操作产生的。第三类别索引必须按CLASSES列表顺序dataset.yaml里的names要和这里一一对应否则模型把helmet预测成boots后面验证时的混淆矩阵全是脏数据。如果原始数据是COCO的json解析逻辑不同但归一化的数学公式完全一样。xml坐标是整数的话除以宽高后的精度损失足够用。如果数据集给的是float坐标且图像尺寸特别大比如5000像素宽保留6位小数也够。再往上提升精度对检测框的收益微乎其微没必要追求9位小数。3.3 划分train/val并生成dataset.yaml数据划分这一步看似简单但最影响模型可信度的是“同一场景不同帧的相似图被同时分进训练和验证”。工地监控视频抽帧出来的数据集相邻帧几乎一样如果随机划分验证集里全是训练集的“孪生兄弟”得到的mAP虚高。正确做法是按视频源或采集批次划分常见手段是把图像文件名里的场景或视频标识提取出来按这个维度切分保证同一场景的帧只进一边。import os import random import shutil random.seed(2024) images [f for f in os.listdir(images) if f.endswith((.jpg, .png))] random.shuffle(images) val_cnt int(len(images) * 0.15) val_set set(images[:val_cnt]) os.makedirs(images/train, exist_okTrue) os.makedirs(images/val, exist_okTrue) os.makedirs(labels/train, exist_okTrue) os.makedirs(labels/val, exist_okTrue) for img in images: stem os.path.splitext(img)[0] lbl stem .txt if img in val_set: shutil.move(os.path.join(images, img), images/val/) shutil.move(os.path.join(labels, lbl), labels/val/) else: shutil.move(os.path.join(images, img), images/train/) shutil.move(os.path.join(labels, lbl), labels/train/)shutil.move比copy省一半IO文件多时能明显提速。移动前把images和labels的目录都建好否则move会报FileNotFoundError。random.seed固定随机种子保证每次跑出来的划分一致方便复现实验。划分比例常见是85%训练、15%验证。如果数据有严重类别不平衡划分后要检查各类别比例优先保证验证集里每个类别至少有几十个样本太少的话mAP波动会很大。最后生成dataset.yamlpath: /home/user/construction_safety train: images/train val: images/val names: 0: person 1: helmet 2: reflective_vest 3: bootspath写绝对路径或相对路径都行但注意如果数据集目录有中文名部分dataloader对中文路径的兼容性有问题容易在读取时抛编码异常。建议装数据集时就把目录名改成纯英文。names列表的索引顺序和前面CLASSES必须完全一致这是yolo系列里最容易犯的低级错误。提示数据集目录和文件名务必改成纯英文yolo对中文路径的兼容性会直接影响dataloader稳定性。4. 用YOLOv8训练智慧工地模型命令参数与损失曲线速读目录就绪后进入训练环节。我用的是yolov8系列因为ultralytics仓库训练自己的数据集最省事一条命令就能跑起来。选yolov8m还是yolov5m取决于部署端如果只做实验验证推荐yolov8m精度和速度均衡如果后期要上jetson这类边缘设备yolov5s更成熟TensorRT导出的资料也多。4.1 训练命令与最小配置pip install ultralytics yolo detect train \ datadataset.yaml \ modelyolov8m.yaml \ pretrainedyolov8m.pt \ epochs150 \ imgsz640 \ batch16 \ device0 \ patience30 \ workers4 \ projectconstruction_safety \ namehelmet_vest_exp参数说明data指向上一章生成的yamlmodel用yolov8m.yaml表示从零搭结构pretrained是指定coco预训练权重。pretrained有两种写法一种是直接modelyolov8m.pt表示加载带权重的模型结构另一种是modelyolov8m.yaml配合pretrainedyolov8m.pt后者更灵活可以只加载backbone权重。imgsz640是输入尺寸工地监控场景如果标注框普遍偏小建议先试一下imgsz960小目标召回率能明显提升代价是训练时间变长。batch16在单卡16G显存下跑yolov8m刚好显存不够就降到8并把workers降到2。训练过程中会打印box_loss和cls_loss这两个数值在yolo损失函数里占主导。正常情况是loss整体下降但伴随波动。如果loss前20个epoch不降反升先检查学习率是不是太高。yolov8默认lr00.01batch和lr强耦合batch翻倍时lr也应上调反之亦然。4.2 训练中断续训与超参修正训练到一半断电或者显存溢出是常事不要从头重跑。yolov8的resume机制很简单yolo detect train datadataset.yaml modelconstruction_safety/helmet_vest_exp/weights/last.pt resumeTrueresumeTrue会读取last.pt里的epoch状态、优化器状态和当前学习率接着上次的进度继续跑。显存溢出CUDA out of memory不是改batch就能解决的还常伴随“worker exit unexpectedly”。遇到这类dataloader错误我一般先把workers降到0虽然速度慢一点但稳定优先。超参上智慧工地这类穿戴检测有一个特点正样本是“人身上的穿戴物”person这个类别是天然的上下文。如果希望模型在“人安全帽反光衣”上的联合检测更稳可以把cls_loss的权重从默认0.5调到1.0。另外数据集里全是白天样本的话最好把hsv增强的饱和度抖动打开这样工地常见的黄色帽子在弱光下不至于完全检测不到。4.3 训练结果验证weights目录、混淆矩阵与损失曲线训练结束后看results目录下的产物ls construction_safety/helmet_vest_exp/weights/ ls construction_safety/helmet_vest_exp/best.pt是验证集上mAP最高的一次权重last.pt是最后epoch的权重二者不要混用。我习惯用best.pt做后续所有推理实验因为last.pt往往过拟合了训练集。目录里的confusion_matrix.png和results.png是判断训练是否健康的最快途径。先说损失曲线results.png里包含train/box_loss和val/box_loss两条曲线训练健康的标志是两条线都下降且没有拉开过大差距如果train降val不降就是过拟合要加数据增强或提前止损。再看混淆矩阵如果helmet和reflective_vest互相错检说明两类外观太接近或者标注框有边界重叠。如果background被预测成某类的比例高说明负样本不足需要往训练集里补一些没有工人也没有穿戴物的纯工地背景图。跑完训练验证一下模型在真实视频上的表现。yolo自带的predict命令可以直接把best.pt推送到一段工地监控视频上yolo detect predict \ modelconstruction_safety/helmet_vest_exp/weights/best.pt \ sourcetest_site.mp4 \ conf0.35 \ iou0.5 \ saveTrueconf0.35是置信度阈值低于这个值的预测全部丢弃。智慧工地场景建议设0.3到0.45之间设太低误报多反光倒影会被当成背心设太高漏检多远处小目标安全帽被滤掉。iou0.5是NMS的阈值多个框重叠时去掉多余的一般不用动。保存的视频里如果发现模型对远处小目标的置信度一直在0.3附近抖动这就是imgsz偏小的典型症状下一章讲怎么处理。5. 智慧工地数据集训练五大坑现象、原因与解决这几条坑是我在做穿戴检测训练时踩过的每条按现象、原因、解决来写。这些血泪经验能帮你少走弯路。5.1 训练时报错index out of range类别ID越界现象用数据集训练跑到第一个epoch就报IndexError: index 5 is out of bounds for axis 0 with size 4。原因xml或json里的类别名转成txt时出现了不在CLASSES列表里的类别。这类数据集往往在person之外还会混入worker、safety_helmet等写法导致类别索引算出5而yaml里只有4类。解决先跑一遍第2.2节的统计脚本打印所有类别名对照CLASSES列表做别名映射比如把safety_helmet归一成helmet把reflective_clothing归一成reflective_vest。映射表写进转换脚本里一次处理干净比反复改yaml靠谱。5.2 验证集mAP高但现场实测漏检严重现象训练完成mAP0.5有0.9但现场视频里远处工人头顶的安全帽经常漏检。原因数据集里近距离大头照多小目标样本占比极少。mAP是在验证集上算的验证集也缺小目标所以指标虚高。现场远距离的样本对模型来说是分布外数据。解决拆开按目标尺寸看per-class AP。用小目标占比高的样本单独评估或者直接换用imgsz960重训。更直接的做法是去工地补采一批远景素材加进训练集。数据比调参值钱得多。5.3 训练出nan loss或者loss骤降后不动现象前几个epoch loss正常下降到第20个epoch突然变成nan或者loss降了一段后长时间不动。原因nan通常是梯度爆炸一般是学习率太大或者数据里有异常数值比如标注框坐标出现负数、图像分辨率超常规。loss不动则可能是模型容量不够yolov8m学不动复杂背景。解决先查数据过滤所有xml里坐标小于0或宽高为负的样本坏标注数据是梯度爆炸的常见源头。再调学习率训练脚本里加cos_lrTrue让学习率按余弦衰减很多loss卡住的问题都能缓解。还不行就把模型从yolov8m升到yolov8l或者换efficient head这类轻量检测头结构试试。5.4 反光衣和普通浅色衣服严重混淆现象混淆矩阵里reflective_vest和person的错检率高穿白色T恤的工人经常被识别为反光衣。原因反光衣的视觉特征是反光条加高亮背心但数据集的标注如果只框了上身区域浅色衣服和白色反光衣在RGB空间上高度相似。模型学到的是“浅色上身”而不是“反光材质”。解决增强数据多样性对反光衣样本做亮度、对比度随机扰动模拟不同光照或者采集逆光、暗光场景的反光衣样本补进训练集。如果现场能拿到红外相机换输入模态是更彻底的方案。5.5 zip解压后中文目录导致训练崩溃现象命令看起来没问题yolo detect train启动后dataloader一直报错提示找不到图片。原因数据集压缩包解压后目录名或文件名带中文中文路径解析异常导致图片加载失败。这类问题在windows环境尤其常见。解决解压后第一件事就是把整个目录重命名为纯英文文件名里的中文也统一改成英文或纯数字。yolo对路径中的非ASCII字符支持不够稳健直接改路径能省掉很多玄学报错。6. 从mAP到帧率验证模型能不能扛住工地现场训练完不等于能用最后一步是把模型放到“现场约束”里验证。我常用三步第一步看混淆矩阵和per-class AP不只盯mAP均值特别是helmet和boots这类样本少的类别单独看它们的AP50有没有接近0.8。第二步跑一段真实监控视频统计误报和漏报的具体帧看是光照变化还是遮挡导致的。第三步才是测帧率在目标设备上跑推理确认内存和算力足够。如果要对标T4这类显卡能接多少路视频流可以先用batch推理压测yolo模型。640分辨率下每路25fps采集单卡能并行多少路取决于推理延迟和显存占用。具体数字因模型版本和TensorRT优化程度差别很大我一般建议先做TensorRT FP16导出再逐路增加视频流测试直到延迟超过40ms为止这个阈值就是最大路数。不要迷信别人报的固定路数输入分辨率、batch策略、后处理逻辑都在影响最终结果只能拿自己的模型实测。最后是我的一点习惯每次训练完把dataset.yaml、训练命令、best.pt的mAP指标一起存档命名加上日期和版本号。这个习惯帮我省过很多次“这个模型是拿哪版数据训的”的翻车时刻。智慧工地检测这种场景现场环境一天一个样模型要不断迭代数据版本记不清就是给自己埋雷。希望这篇笔记帮你在yolo训练自己的数据集这条路上少踩几个坑把更多精力花在真正影响效果的样本和场景上。本文还有配套的精品资源点击获取