简介:面向目标检测与工地安全监控场景,这份YOLOV5目录格式的建筑工地安全隐患数据集,整合了10类常见目标(含安全帽、口罩、车辆等),图片为640×640 RGB,已做mosaic增强,可直接用于训练与验证,省去格式转换和标注整理环节。压缩包共2000个文件,其中1999个txt为标签与类别信息,1个py为可视化脚本;包体148.9MB,内含训练集2605张图及对应txt标签、验证集196张图及对应txt标签,目录按datasets/images/train与val划分,符合YOLOV5默认读取习惯。资源另附可直接运行的show.py,随机传入一张图像即可绘制边界框并保存结果,便于快速检查标注质量。目前已有611人学习下载,适合正在做安全帽佩戴、人员防护或工地车辆识别的算法工程师、学生及竞赛团队。
1. 建筑工地安全隐患检测:这份数据集解决的是训练集最后一公里的问题
给工地做安全巡检的视觉项目,最磨人的往往不是模型结构,而是数据。摄像头里的画面是现成的,但要把“未戴安全帽、没有反光衣、临边防护缺失”这些隐患变成模型能学的东西,你得先有一份目标检测数据集,而且最好直接就是YOLOV5目录格式,10个类别按业务分类命名好,拿到就能跑训练。这类数据集的定位很明确:它不是给你看效果的Demo图集,而是让你不用从零开始标几千张、还能在yolov5训练自己的数据集时少踩格式坑的稳定基础。适合谁?给工地做算法落地的工程师、准备毕业设计的学生、以及需要在现场快速迭代原型的小团队都能用,前提是你愿意花半天时间把目录整理干净,而不是解压后直接开训。
2. 看懂YOLOV5目录格式:从标注工具到训练脚本的最后一公里
2.1 images / labels 双目录:先理解YOLOV5约定
很多第一次接触YOLOV5目录格式的人拿到压缩包,看到images和labels两个目录就以为中间对应关系是全自动的。其实YOLOV5的训练逻辑是:读取data.yaml或指定目录,然后按图片路径找同名txt文件,目录结构靠的是固定约定,而不是数据库记录。一份完整的YOLOV5数据集目录大致是这样:
dataset/ ├── images/ │ ├── train/ │ │ ├── frame_0001.jpg │ │ ├── frame_0002.jpg │ ├── val/ │ │ ├── frame_0101.jpg ├── labels/ │ ├── train/ │ │ ├── frame_0001.txt │ │ ├── frame_0002.txt │ ├── val/ │ │ ├── frame_0101.txt ├── data.yaml这里有个很容易忽略的细节:labels里放的不是标注可视化图片,是每个框的纯文本描述,且文件名必须和对应图片完全同名(只差扩展名),否则训练时这张图会被直接跳过,又不报错,最后你只会觉得“明明标了500张,怎么训练时只剩300张”。所以收到数据集第一件事不是看图片有多清晰,而是跑一遍配对检查。
我一般会在conda里单独建一个yolov5环境,把yolov5源码clone下来,然后用pip安装requirements.txt里的依赖。这套环境不只要能训练,还要能跑检查脚本,因为数据集里的脏数据通常比模型参数更早让你翻车。
2.2 从LabelImg/CVAT导出到txt:归一化坐标为什么是四个小数
YOLO格式的txt每行对应一个目标框,格式是:class_id x_center y_center width height,五个数按顺序写。其中后四个都是归一化值,也就是相对于图片宽高的比例,取值在0到1之间。拿一个安全帽标注举例:
0 0.6312 0.4128 0.1531 0.1846 1 0.4352 0.5613 0.0968 0.2194第一列是类别编号,从0开始计数,不是从1。对应的data.yaml里,names列表的第一个就是编号0。目标检测常用标注工具像LabelImg、CVAT、X-AnyLabeling,导出时一般都能选YOLO格式,但不同工具的导出细节差异很大。CVAT导出的zip包解压后是images和labels两个文件夹,但图片会被重新命名,LabelImg则默认保持原名。这个差异在合并多批标注时最容易制造出“同名不同内容”的坑。
我建议拿到任何标注结果后,先统一文件名规则,比如用“项目名_时间戳_编号”重命名,避免多个标员标出来的frame_0001互相覆盖。这个步骤虽然琐碎,但能省掉后面大量排查时间。
2.3 data.yaml与类别顺序:模型只认识编号,不认识中文名
data.yaml是训练脚本和数据集之间的唯一桥接,它长这样:
# 数据集路径,训练时会用路径拼接 images/train 和 labels/train train: /home/user/dataset/images/train val: /home/user/dataset/images/val # 类别数量,必须和names列表长度一致 nc: 10 # 类别名按编号顺序排列,编号0对应第一行 names: 0: helmet 1: person 2: vest 3: no_helmet 4: guardrail 5: safety_net 6: safety_sign 7: cable 8: fire_extinguisher 9: equipmentnames的顺序不是给人看的备注,而是训练时类别编号到语义标签的唯一映射。训练脚本在读取labels时会根据当前类别数nc做一次校验,如果某个txt里出现数字9,但nc只写了9(也就是允许0到8),会出现“Label class 9 exceeds nc=9”的报错。这类报错我在现场被新同事问过很多次,绝大多数情况不是标注内容错,是yaml写错了。
我个人习惯在第一次训练前,先打印一遍所有标签文件里出现过的类别编号集合,和yaml里的names列表做人工核对。这个动作只需要几分钟,但能拦住90%的“训练崩了、loss是nan”类问题。关键认知是:模型从头到尾不认“安全帽”这三个汉字,它只认整数编号,yaml只是给人看的语义对照表。
3. 把10类隐患整理成能用数据:目录搭建、配对检查、样本划分
3.1 一条命令搭好YOLOV5标准目录骨架
拿到整理好的图片和txt后,先用下面这段脚本搭骨架。如果你收到的数据集本身就带标准目录,跳过这一节,直接跑3.2。
# 在数据集根目录下创建YOLOV5要求的目录结构 mkdir -p dataset/images/train dataset/images/val \ dataset/labels/train dataset/labels/val # 把未划分的图片和标注按名字从原目录移入train(后续用脚本划分) mv raw_images/*.jpg dataset/images/train/ mv raw_labels/*.txt dataset/labels/train/需要说明的是,上面这条mv只是临时搬运,最终必须按8:1:1或者9:1做训练集和验证集划分。YOLOV5的默认参数只认train和val两个集合,test集对训练流程没有影响,只在测试阶段手动指定。目录树搭好之后,data.yaml里的路径建议写成绝对路径,避免不同机器启动训练时因为相对路径找不到文件。
3.2 图片与标签配对检查脚本
这是我认为整个数据集上线前最重要的一步。下面脚本会遍历images目录,逐个检查四类问题:标签缺失、标签多余、图片打不开、标签文件为空。
import os from pathlib import Path from PIL import Image data_root = Path("dataset") missing_label, empty_label, broken_image = [], [], [] for split in ["train", "val"]: img_dir = data_root / "images" / split for img_path in img_dir.glob("*.jpg"): label_path = data_root / "labels" / split / (img_path.stem + ".txt") if not label_path.exists(): missing_label.append(str(img_path)) continue # 标签文件存在但为空,说明这张图没有被标过,参与训练会拉低指标 if label_path.stat().st_size == 0: empty_label.append(str(img_path)) # 用PIL打开一次图片,损坏文件在训练读取时会导致进程卡死 try: with Image.open(img_path) as im: im.load() except Exception: broken_image.append(str(img_path)) print(f"missing_label: {len(missing_label)}") print(f"empty_label: {len(empty_label)}") print(f"broken_image: {len(broken_image)}")这段脚本的价值在于把问题暴露在训练之前。missing_label数量不为0时,我会把这些图片直接剔除出数据集,而不是补标,因为补标的成本远比删图高。empty_label同理,除非这张图确实一个目标都没有,那它应该作为负样本单独处理,不能混在正样本训练集里。broken_image则必须删,训练时遇到损坏图片会导致加载线程卡住,整个训练看起来像黑匣子一样没有响应。
3.3 按比例切分训练集与验证集
划分样本的原则是随机且可复现。shutil.move会把文件从train移到val,所以要在3.2检查完毕之后再划分。代码如下:
import random import shutil from pathlib import Path random.seed(42) # 固定随机种子,保证每次划分结果一致 src_img_dir = Path("raw_images") src_label_dir = Path("raw_labels") val_img_dir = Path("dataset/images/val") val_label_dir = Path("dataset/labels/val") all_images = list(src_img_dir.glob("*.jpg")) random.shuffle(all_images) val_size = int(len(all_images) * 0.2) # 20%作为验证集 for img in all_images[:val_size]: shutil.move(str(img), str(val_img_dir / img.name)) label_file = src_label_dir / (img.stem + ".txt") if label_file.exists(): shutil.move(str(label_file), str(val_label_dir / label_file.name))这里的seed可以随意设置,但固定下来后,后续哪怕增加图片,已经划分过的文件集合也不会变,这是复现实验结果的前提。val_size取20%是比较通用的做法,数据量特别大时(超过两万张)可以降到10%。注意先把3.2的检查结果跑干净再做划分,否则把脏数据划进验证集,后面的mAP数字就没有参考意义。
数据划分完成后就可以开始yolov5训练自己的数据集了:
# 使用yolov5源码目录下的train.py启动训练 python train.py --data /path/to/dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100几个参数的含义要理解清楚:--weights用yolov5s.pt是速度和精度的平衡点,显存不够就换yolov5n.pt,工地现场需要更高精度则考虑yolov5m.pt;--img 640对应输入分辨率,安全帽这种小目标建议不要低于640;--batch 16在8G显存下刚好能跑s模型,batch再大就需要调低分辨率或换n模型。刚跑起来的前几十步特别容易暴露数据问题,如果loss直接是nan,优先回头查标签,而不是调学习率。
4. 训练前必做的三项检查:类别偏斜、坐标越界、超参数设置
4.1 统计每个类别的标注数量:别让模型变成“偏科生”
10个类别的隐患样本数量天然不均匀。工地现场安全帽和人员出现的频次远高于灭火器和警示标识,如果标注数据也沿袭这种分布,训练出来的模型对少数类几乎等于瞎猜。我先跑一段统计脚本,把每个类别的框数拉出来看:
from collections import Counter from pathlib import Path label_dir = Path("dataset/labels/train") counter = Counter() for label_file in label_dir.glob("*.txt"): for line in label_file.read_text().strip().splitlines(): if line: class_id = int(line.split()[0]) counter[class_id] += 1 for class_id, count in counter.most_common(): print(f"class {class_id}: {count} boxes")如果某个类别的框数不到其他类别的十分之一,就要考虑两类处理手段。第一类是数据层面:把少类别的图片做复制粘贴增强,或者去现场补采;第二类是训练层面:在hyp.yaml里调整那个类别的loss权重。yolov5超参数文件里默认没有给单个类别单独加权的写法,常见做法是把少类别的图片多复制几份放进训练集,配合随机翻转、裁剪增强,让比例拉到1比5以内。类别不均衡会让recall差距明显,只看总mAP看不出来,分类别看AP才会暴露哪个类拖了后腿。
4.2 检查标签坐标是否越界:归一化坐标的边界坑
YOLO格式要求坐标归一化到0到1之间,但标注工具偶尔会导出超出边界的值,尤其当标注框贴着图片边缘、或者图片在预处理时被resize过之后没有重新生成标注。越界坐标在训练时会被YOLOV5自动裁剪掉,而这个裁剪动作会降低框的中心点精度,严重时导致部分框变成空框。检查脚本如下:
from pathlib import Path label_dir = Path("dataset/labels/train") issues = [] for label_file in label_dir.glob("*.txt"): for idx, line in enumerate(label_file.read_text().strip().splitlines()): parts = line.split() class_id = int(parts[0]) x_center, y_center, width, height = map(float, parts[1:]) # 检查归一化坐标是否还在合理区间内 if not (0 <= x_center <= 1 and 0 <= y_center <= 1 and 0 <= width <= 1 and 0 <= height <= 1): issues.append(f"{label_file}: line {idx} class {class_id}") print(f"found {len(issues)} invalid boxes") for issue in issues[:20]: print(issue)这条检查在YOLOV5目录格式的数据集上特别关键,因为脚本解析txt时不报错,越界坐标会被静默处理。发现越界后,如果数量少,直接把坐标clip回0到1之间;如果数量多,说明标注工具导出有问题,得回到标注源修一遍。
# 把刚好越界的坐标裁剪回合法区间,而不是删掉这个框 line = "0 1.03 0.45 0.20 0.18" parts = line.split() x, y, w, h = map(float, parts[1:]) x, y, w, h = max(0, min(1, x)), max(0, min(1, y)), max(0, min(1, w)), max(0, min(1, h))这里有个权衡:框越界但中心点还在图内时,clip后这个框仍有效;框大部分在图外的,clip完几乎只剩一条边,参与训练只会教模型学一个畸形的目标,这类我建议直接剔除。判断标准很简单,clip后w或h小于0.05,删除。
4.3 训练参数里要改的几项:imgsz、batch、epochs和hyp
很多从yolov5基础笔记入门的同学,第一次训练直接沿用COCO的默认参数,结果数据集只有几千张时模型过拟合到训练集上。对建筑工地隐患检测,我一般会把imgsz设成640而非默认的416,因为安全帽、电缆接口这类目标在画面里的像素占比非常低;batch按显存尽量往上给,但要注意batch大了之后要同步调大epochs或者用cos LR,否则收敛不稳定。
epochs建议起步100,工地场景数据量少,100轮足够看趋势。训练时观察两个信号:train loss持续下降且val loss不再下降,就该停止;如果val loss一路走高,说明过拟合已经发生。yolov5超参数里的anchor、mosaic、mixup对工地场景效果差异很大,我习惯在hyp.scratch-low.yaml的基础上关掉mosaic的最后10个epoch,这样能避免小目标在最后一阶段还在被拼接图干扰。模型后处理里的NMS阈值也很值得调,工地画面目标密集,默认的0.45常会让安全帽与人员互相抑制,把iou-thres从0.45微调到0.5反而能多保留一层小目标。训练完成后用detect.py跑几张现场截图做冒烟测试,再谈部署。
注意:data.yaml里的路径在跨机器迁移时最容易失效,换机器训练前先确认images和labels目录真实存在,再启动train.py。
5. 常见问题避坑:5个最容易被问到的工地检测翻车记录
5.1 训练时报“AssertionError: Label class 9 exceeds nc=9”
现象:train.py刚启动数据加载阶段,进程直接退出,报错信息指出某个class编号超过了类别总数。 原因:labels里的类别编号写到了9,但data.yaml里nc只写了9。YOLO类别编号从0开始,所以合法编号是0到8,出现9说明要么标注工具类别顺序和yaml不一致,要么yaml漏写了一个类。 解决:打开报错提到的txt文件,确认它的类别编号,按这个编号补全data.yaml的nc和names。不要把names列表里的类删掉来迁就错误的标注,那样模型学习到的语义就全乱套了。检查全部标签里出现过的最大编号,再和nc比对一次,这个动作是训练前最便宜的安全网。
5.2 验证集mAP很高,现场摄像头一拍就漏检
现象:训练和验证阶段mAP能达到0.85以上,但把模型放到工地现场摄像头画面上,漏检率立刻上来。 原因:训练集和验证集来自同一批网络公开图片,视角统一、光线干净;现场摄像头是俯视角度,还有逆光、扬尘、夜间补光,数据分布不同,模型泛化自然失效。 解决:训练收敛后,先去现场录制一段10到20分钟视频,按帧抽成图片补进训练集。这也是一开始用YOLOV5目录格式组织的优势——补数据只需把新图片和同名txt丢进对应目录,重新跑一遍3.2的配对检查就能继续训练。别指望用公开数据集直接交付,工地项目的模型参数永远要在现场数据上再训一轮。
5.3 远处安全帽小目标完全检测不到
现象:人走近时安全帽能框住,人走到塔吊下或者画面远处,帽子就成了漏检目标。 原因:安全帽在640分辨率下图宽可能不到8个像素,小目标特征在下采样后几乎消失,加上锚框尺寸是为COCO常见目标设计的,对这类小尺寸目标不敏感。 解决:第一优先级是提高输入分辨率,imgsz从640调到960,代价是显存增加;第二是用SAHI这类切图推理,把大图切成带重叠的patch再检测;第三是重新聚类自己的标注数据生成新锚框,不要套用COCO预训练的那组anchor。如果现场算力支持,优先用yolov5m而不是s模型,小目标检测能力会明显好一截。
5.4 类别编号错位:反光衣被识别成安全帽
现象:模型输出里所有反光衣目标都带了helmet标签,但训练集里两个类别的图片看起来都标对了。 原因:标注过程中A标员把反光衣定成了编号2,B标员把安全帽定成了编号2,两批txt合并后编号2这个语义被污染了。模型学到的是“编号2=画面里高亮的穿戴物”,两个类别在特征空间重叠。 解决:先跑一遍4.1的类别统计,把每个编号的样本量分布和随机抽出的图片都看一遍,人工抽查编号是否有歧义。合并标注批次前,先统一命名规则和类别顺序,然后写一个检查脚本把所有txt里出现过的编号去重后打出来。编号错误是黑匣子问题,模型表现再差也不会直接告诉你“你的标号合并错了”。
5.5 loss正常下降但recall一直上不去
现象:训练loss曲线正常,但是PR曲线里的recall最高只能到0.5左右,很多真实目标没被召回。 原因:正负样本极不均衡。工地画面里背景占比极大,模型倾向于把候选框都判成背景;加上默认置信度阈值是0.25,对遮挡严重的实例这个阈值偏高。 解决:一是增大epochs并启用更强的数据增强,mixup、copy_paste这类增强会让模型对遮挡更鲁棒;二是在detect推理时把conf_thres降到0.1,recall通常会有明显提升,代价是误检多一点;三是检查labels里是否有大量被截断的目标没有标注完整,截断目标学不到完整特征,对整个类别的召回影响很大。这里没有玄学,recall不够就是正样本不够或标注质量不够,优先补正样本。
6. 验证你的模型并用ONNX把它部署到边缘设备
6.1 用detect.py和一段Python脚本做最终验证
模型训练完,先用YOLOV5自带的detect跑一遍标准和现场图。
python detect.py --weights runs/train/exp/weights/best.pt \ --source /path/to/test_images \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt需要关注的不只是框,还要看save-txt输出的txt里类别编号和实际语义能不能对上,这一步是前面讲的编号错位坑在后端推理阶段的重现。落地到Jetson或者树莓派5这类小内存设备前,我习惯把best.pt导出成ONNX还有TensorRT。
python export.py --weights runs/train/exp/weights/best.pt --include onnx engine导出后先用onnxruntime跑一张图验证数值和pytorch输出差距,再上设备。yolov5的swish激活函数和部分算子在老款设备上兼容性一般,如果engine导出失败,优先检查设备驱动和TensorRT版本。
6.2 现场部署时要保留的两个后处理习惯
第一,推理时输入分辨率不要盲目跟着训练走。现场设备算力有限,Jetson Nano上跑960分辨率可能只有几帧,我一般会导出两个引擎:一个640精度版本做回放分析,一个416实时版本做实时预览。第二,置信度阈值和NMS阈值在部署侧要单独可调,不要在代码里写死,现场调试时这两个参数是最常动的。这套YOLOV5目录格式后面迁移到yolov8几乎不用改数据组织,yaml和目录结构一样能直接复用,这也是当初坚持规范目录的原因。
现在回头看,YOLOV5目录格式带给我的最大收益不是某个模型指标很好看,而是一套“拿到数据就能跑、跑出问题能查、现场反馈能回灌”的闭环。数据集的格式整理、配对检查、类别统计这三板斧做得越扎实,后面训练和部署阶段省的时间就越多。希望帮到你。
本文还有配套的精品资源,点击获取