简介:面向建筑工地安全监控场景的目标检测数据集,共600张真实监控图片,覆盖8个关键类别:安全帽、安全背心、防护靴、机械、人员,以及无安全帽、无口罩、无安全背心三类违规行为。数据分为训练集426张、验证集117张、测试集57张,采用YOLO格式标注,包含边界框坐标与类别标签,可直接用于YOLOv5、YOLOv8等主流框架的训练与评估。
资源包内含1202个文件,以600个jpg图像、600个txt标注文件为主,另有1个yaml配置文件和1个docx说明文档,压缩包大小37.43MB,便于下载与快速上手。该数据集适用于工地安全监控系统开发、工业安全管理预警、计算机视觉安全违规识别研究及安全培训演示,帮助开发者训练模型自动检测安全装备佩戴情况并预警违规行为。
数据集来源于实际工地监控,包含多样作业环境和光照条件,边界框标注精细,泛化能力较强。目前已有264人学习下载,适合需要真实工业场景数据支撑的算法落地与实验验证。
1. 建筑工地安全目标检测数据集:600张贴标图、8个类别,能做什么
很多工地项目在写验收报告时,都想加一块“AI自动识别未戴安全帽”的功能。真正落地时你会发现,最花时间的不是写模型,而是找一套能直接用的建筑工地安全目标检测数据集。这份资源就是这个用途:600张真实工地监控画面,训练集426张、验证集117张、测试集57张,全部带YOLO格式的边界框标注。类别覆盖8个,既有安全帽、反光背心、防护靴这类合规装备,也有无安全帽、无口罩、无安全背心这类违规行为标注。拿它跑一遍YOLOv8,你就能得到一个具备PPE检测雏形的模型,适合算法工程师做工业demo、安全管理人员做监控预警方案验证,也适合刚接触目标检测的开发者快速打通整个流程。
2. 先看数据边界:文件结构、标注口径与质量核验脚本
目标检测模型的性能上限,七成由标签质量决定,另外三成才轮得到模型选型。所以拿到数据集先别急着训练,花十分钟把这套数据的边界看清楚,能帮你省下后面反复调参的半天时间。
2.1 文件命名和目录结构
解压后你会看到train、valid、test三个子目录,每个子目录下都有images和labels两个目录,文件名是一一对应的。像156_jpg.rf.4be21e8a95b997340b64abac4aa089fc.jpg这种带rf后缀的命名方式,是Roboflow导出数据集的典型特征,说明这套数据经过在线平台清洗,统一做过随机划分和格式转换。
600张的划分比例是426/117/57,大约是7:2:1,符合目标检测任务里常用的训练、验证、测试比例。每个jpg旁边对应一个同名txt,里面的内容就是YOLO格式的归一化标注,每行5个数字:类别id、中心点x、中心点y、宽度、高度,都是0到1之间的小数。
注意:如果你在别的数据集里看到类似的
_jpg.rf.xxxxx.jpg命名,基本可以判定它也是Roboflow导出的,目录结构大概率也是images/labels这种标准形态。
这套数据覆盖的是真实监控角度,不是网上爬的摆拍图。这点很重要,因为工地监控通常架在围挡或塔吊上,俯拍角度居多,目标尺度偏小。用网图训练出来的模型,一上监控画面就会明显掉点,这类“场景鸿沟”靠调参很难弥补。
2.2 八类标签的分类逻辑值得细看
训练之前,先把8个类别的设计逻辑看明白。这8个类别分成了两类:一类是合规装备,Boots(靴子)、Hardhat(安全帽)、Safetyvest(反光背心)、Person(人员),另一类是违规行为,No-Hardhat(无安全帽)、No-mask(无口罩)、No-safetyvest(无安全背心),另外还有一个Machinary类别,用于识别工地上的机械设备。
注意原始类别名就是Machinary,不是Machinery,拼写虽然不太标准,但训练时别把它改成Machine,否则所有标签文件的类别ID都会错位,这也是后面要讲的坑之一。保留原始类别名,模型只认ID不认字符串。
“No-”类才是这套数据里最有价值的部分。实际监管方更关心“没戴安全帽的人”,而不是“戴了安全帽的人”。如果只训练Hardhat,模型会把所有漏检的人当成背景,违规告警无从谈起。这里把正反样本同时标出来,训练出来的模型可以直接用规则做合规判断。
另一个值得注意的点是Person类和头部装备类的关系。同一张画面里,一个人既可能有Person框,也可能同时有Hardhat或No-Hardhat框,它们是不同粒度的标注。后面做部署报警时,可以用“头部框是否落在某个人框内”来做二次过滤,这种关联逻辑在PPE检测里非常实用。
2.3 用Python脚本核验标注质量
我每次拿到新数据集都会先跑一遍核验脚本,检查三件事:类别分布是否均衡、坐标是否越界、有没有格式错误的行。训练集和验证集里的标注文件,每行必须是5列。
import os from collections import Counter from pathlib import Path data_root = Path("./dataset") # 改成你的数据集根目录 class_names = [ "Boots", "Hardhat", "Machinary", "No-Hardhat", "No-mask", "No-safetyvest", "Person", "Safetyvest" ] split_dirs = ["train", "valid", "test"] label_counter = Counter() bad_files = [] for split in split_dirs: label_dir = data_root / split / "labels" if not label_dir.exists(): print(f"[跳过] 未找到 {label_dir}") continue for txt_path in label_dir.glob("*.txt"): for line in txt_path.read_text(encoding="utf-8").splitlines(): parts = line.strip().split() if len(parts) != 5: bad_files.append((str(txt_path), line.strip())) continue cls_id = int(parts[0]) coords = list(map(float, parts[1:])) if any(c < 0 or c > 1 for c in coords): bad_files.append((str(txt_path), line.strip())) continue if cls_id >= len(class_names): bad_files.append((str(txt_path), f"class_id={cls_id} 超出范围")) continue label_counter[class_names[cls_id]] += 1 print("各类别标注框数量:") for name, cnt in label_counter.most_common(): print(f" {name}: {cnt} 个框") print(f"\n格式错误或越界的标注:{len(bad_files)} 条") for f, line in bad_files[:5]: print(f" {f}: {line}")这个脚本做的事很简单:遍历train、valid、test三个目录下的所有标签txt,逐行拆分成5列,校验类别ID是否在0到7之间、坐标是否落在0到1区间,最后统计每个类别的框数。
跑完之后重点看两类问题。第一类是格式错误,如果bad_files数量不为0,说明标签在导出过程中有损坏,训练前必须先清理,否则YOLO会直接报错或把这个框的梯度变成噪声。第二类看类别分布,正常情况下Boots、Hardhat、Person这类“主要目标”框数明显偏多,No-mask这类违规行为的框数偏少是实际场景的正常体现。如果某个类别的框数接近0,那这个类基本训不出来,后面要么补数据,要么就别把它算进验收指标。
3. YOLOv8训练流程:数据配置、超参数与结果解读
数据核验没问题之后,就可以直接进训练环节。YOLO格式的标签兼容YOLOv5、YOLOv8以及ultralytics框架下的新版模型,不需要做格式转换,这是这份数据集最省心的地方。
3.1 为什么选YOLOv8起步
工地安全检测本质上是一个实时目标检测任务,监控流通常有多路,单卡推理至少要跑到15fps以上才能实用。YOLOv8s是速度和精度比较平衡的选择:在640分辨率下比YOLOv8l快不少,检测安全帽、反光背心这种中等尺度目标完全够用。如果你机器配置一般,可以先换yolov8n跑通流程,再逐级往上试。
用预训练权重而不是随机初始化,对小数据集尤其重要。600张图要想从零训出一个稳定模型很难,但在COCO预训练权重的基础上做微调,模型已经具备了通用的特征提取能力,只需要适配工地的颜色、光照和角度。
3.2 data.yaml配置与训练命令
在ultralytics框架下,数据集路径由一个yaml文件描述。新建一个data.yaml,内容如下:
path: ./dataset train: train/images val: valid/images test: test/images nc: 8 names: 0: Boots 1: Hardhat 2: Machinary 3: No-Hardhat 4: No-mask 5: No-safetyvest 6: Person 7: Safetyvestpath指向数据集根目录,train、val、test写的是相对于根目录的图片文件夹路径。nc是类别总数8,names必须和标签txt里的类别ID一一对应,顺序不能乱。这里再强调一次:Machinary是原文拼写,保持原样就好。
配置写好之后,训练命令就一行:
yolo detect train data=data.yaml model=yolov8s.pt epochs=80 imgsz=640 batch=16 device=0 patience=15model=yolov8s.pt会从ultralytics官方仓库下载预训练权重,第一次运行需要联网。epochs=80表示最大训练轮数,对600张的小数据集来说80轮足够看到收敛趋势。batch=16按显存调整,如果报CUDA out of memory就降到8或4。patience=15是早停耐心值,连续15轮验证集指标不提升就自动停,能有效防止过拟合。device=0指定第一块GPU,CPU训练可以改成device=cpu但速度会慢很多。
注意:yolo命令要求ultralytics包版本不低于8.0,安装用
pip install ultralytics即可。如果你的环境里有老版本YOLOv5的依赖冲突,建议用虚拟环境单独装。
3.3 训练结果怎么读
训练结束后,在runs/detect/train/目录下会生成一组结果文件。results.png包含每轮的box_loss、cls_loss以及验证集的mAP50曲线,confusion_matrix.png是混淆矩阵,val_batch_pred.jpg是验证集的可视化预测结果。
看结果不要只盯总体mAP。对安全检测场景来说,No-Hardhat、No-mask、No-safetyvest这三个违规类的召回率(recall)才是核心指标。因为漏报一个违规行为,比误报一次严重得多。如果混淆矩阵里No-Hardhat大量被预测成Hardhat,说明模型把没戴安全帽的头部误判成了安全帽,这类错误在安全告警场景下是不能接受的,需要在后期调整类别权重或者补样本。
一个我常用的判断依据:看val_batch_pred.jpg里小目标有没有被漏检。监控画面里的安全帽往往只占几十个像素,如果可视化结果里远处的目标大多没框出来,就要考虑把imgsz从640提到960,或者用多尺度训练。代价是训练和推理速度都会下降,需要自己权衡。
4. 训练阶段避坑:我在600张小数据上踩过的五个坑
小数据集训练的最大特点就是“问题藏不住”。600张图,模型容量稍微大一点就过拟合,标签稍微脏一点就疯狂震荡。下面五条是我跑这套数据时实际踩过的坑,每一条都按现象、原因、解决三个环节拆开写。
4.1 标签坐标越界导致Box_loss掉不下去
现象:训练到第10个epoch,box_loss还停在4附近,验证集mAP只有0.1,怎么看都不收敛。
原因:某个标签txt里出现了一行坐标大于1的数值,大概是导出时归一化出了纰漏。坐标越界会把这个边界框的损失放大到异常水平,让梯度方向被几个坏样本带偏,模型一直在收敛边缘挣扎。
解决:用第2章那个核验脚本把所有txt扫一遍,找到越界行。如果只有几行,直接删掉对应文件;如果坏文件多,就用脚本把坐标重新裁到0到1区间再写回去。从那以后,我每次训练前都强制先跑一遍这个脚本,不干净不进训练。
4.2 mAP看起来不错,No-Hardhat的recall却很低
现象:总体mAP50到了0.87,看起来是个不错的模型。但把每个类别拉出来看,No-Hardhat的recall只有0.42,一半以上的违规行为被漏掉了。
原因:类别不均衡。工地画面里戴安全帽的人是大多数,没戴的是少数,Hardhat的框数远多于No-Hardhat。模型天然偏向高频类别,低频类别就被“牺牲”了。总体mAP高是因为Hardhat、Person这些大类考得好,把均值拉上去了。
解决:不要只看总体mAP,按类看recall。对样本少的类做针对性增强,把No-Hardhat的图复制几份放进训练集,或者用马赛克增强提高它在每个batch里的出现频率。另外可以在损失函数上对低频类调高cls_loss权重,ultralytics的class_weights参数可以做这件事。
4.3 小数据集过拟合来得太快
现象:第20个epoch时验证集损失曲线就开始掉头向上,训练损失还在降,典型的过拟合特征。
原因:600张图对YOLOv8s来说容量偏大,尤其验证集只有117张,波动特别明显,模型很快记住训练集上的细节而不是学通用特征。
解决:换更小的模型,yolov8n起步;打开更强的数据增强,hsv、flipud、scale都保持默认;早停patience调到15;最后用best.pt而不是last.pt。记住一个原则:小数据集优先选小模型,别让模型的记忆能力超过数据的信息量。
4.4 好心改了个类别名,训练直接报错
现象:看着Machinary拼写难受,改成Machine,结果训练时直接报class index out of range。
原因:YOLO标签只保存类别ID,不保存类别名。改classes.txt里的名字会牵动ID顺序,而标签txt里记录的0到7是和原来顺序绑定的,一旦顺序错位,ID就指向了不存在的类。
解决:YOLO的标签体系是“ID绑定”的,不是“字符串绑定”的。类别名随便改成什么展示名都不影响训练,但顺序不能动。真要改,也得写个脚本重新映射ID并重写所有txt文件,不能手工改配置了事。
4.5 Person和No-Hardhat框重叠导致部署期误报
现象:模型部署到监控流之后,只要有人路过,屏幕上同时弹出Person和No-Hardhat两个框,报警响个不停。
原因:没戴安全帽的人的头,就是人的一部分。模型在Person大框内部同时检测出了No-Hardhat框,这不算检测错误,但从业务角度看就是无效报警——每个没戴帽子的人都可能被反复报警。
解决:在后处理里加一层关联判断。保留头部框中心落在某个Person框内部的No-Hardhat结果,并做连续帧去抖,只有同一违规连续出现3帧才触发报警。这样能把大部分误报压下去。
5. 部署到监控流:实时推理脚本与规则报警的最小闭环
训练产出best.pt之后,真正的验收才开始。能不能把模型接到监控流上、报警逻辑怎么写得既灵敏又不扰人,是工地安全检测项目能否落地的关键。
5.1 实时推理脚本
下面这段脚本读取一个视频流,逐帧推理并统计违规类连续出现的帧数。帧数达到阈值就触发一次报警。
from collections import defaultdict from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") # 换成你训练好的权重 ALERT_FRAMES = 3 # 连续多少帧出现才算有效违规 VIOLATION_CLASSES = {"No-Hardhat", "No-mask", "No-safetyvest"} violation_frames = defaultdict(int) def process_frame(frame, frame_id=0): results = model.predict(frame, conf=0.35, iou=0.45, imgsz=640, verbose=False) current_violations = set() for box in results[0].boxes: class_name = model.names[int(box.cls[0])] if class_name in VIOLATION_CLASSES: current_violations.add(class_name) for v in VIOLATION_CLASSES: if v in current_violations: violation_frames[v] += 1 else: violation_frames[v] = 0 # 中断就清零 if violation_frames[v] >= ALERT_FRAMES: # 这里接你的推送、录像或声光报警接口 print(f"ALERT frame={frame_id}: {v} 连续 {ALERT_FRAMES} 帧") violation_frames[v] = 0 # 触发后清零,防止重复上报逻辑说明:ALERT_FRAMES是去抖的核心参数,连续帧数不足时报警不触发,避免单帧误检造成骚扰。触发后的清零很重要,否则同一违规行为会反复报警。current_violations用集合去重,同一帧里出现多个同类目标只算一次。violation_frames用defaultdict计数,每个类别独立统计,互不干扰。
5.2 推理参数怎么调
部署阶段的参数和训练阶段逻辑相似,但目标不同,我把关键参数整理成了一张表:
| 参数 | 默认值 | 调整建议 |
|---|---|---|
| conf | 0.35 | 监控距离远、目标小时降到0.25,减少漏检;近景可调到0.45以上 |
| iou | 0.45 | 目标密集时降到0.35,减少重叠框对报警逻辑的干扰 |
| imgsz | 640 | 只检测中近景时保持640;需要看远处目标时提到960 |
| device | 0 | 多路视频流建议用GPU,CPU只能支撑1到2路低分辨率流 |
| max_det | 300 | 大工地场景目标多,默认值够用;场景空旷时可调低以提速 |
部署推理时最常见的问题是conf一刀切。同一个工地,白天和晚上、近景和远景的最优置信度完全不同。我一般会先录三段不同时段的监控视频离线跑一遍,画出置信度和召回率的关系曲线,再选一个平衡点,而不是拍脑袋定参数。
5.3 从检测到报警的最小闭环
脚本里的print位置,实际部署时替换成报警动作。工地现场的常见做法是:把报警截图和类别信息通过HTTP请求推到值班室webhook,同时把原始帧写入本地磁盘做证据留存。写盘时记得带上frame_id、类别、置信度和时间戳,方便事后复盘。
一个更稳妥的联动策略是分级报警:No-Hardhat连续3帧出现推到现场声光报警,No-mask连续5帧出现才推给管理人员。不同违规的容忍度不同,告警阈值也应该不同。这个逻辑用一个Frames字典就能实现,不必上复杂的规则引擎。
6. 进阶技巧:用自己的工地视频做增量训练,把模型压得更准
拿这份600张的数据集训出的模型已经能跑,但它不认识你这个工地的塔吊颜色、围挡样式和特殊光照。真正的工地项目,最终都要把自有数据融进去。
我习惯的做法是:先用这份数据集的best.pt当预训练权重,再在自己工地录1到2小时监控视频,用OpenCV每5秒抽一帧,挑出500张左右有明显目标且画面不重复的图,做一轮简单标注后跑增量训练。
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") # 上一阶段训练的权重 model.train( data="your_site.yaml", # 你自己的数据集配置 epochs=30, freeze=10, # 冻结前10层,保留原有权重提取特征 lr0=0.003, # 增量训练学习率比全新训练小一个量级 patience=10, )这段配置的关键是freeze=10和lr0=0.003。冻结前10层可以保留这套数据训练出来的底层特征,让模型不会一上来就被新数据带偏。学习率调小则避免在微调阶段把原有权重破坏掉。如果你的新数据里有新的类别,记得把data.yaml的nc改成新总数,并把新的标签文件对应好ID。
增量训练完成后,用三段不同时段的视频做验证:白天正光、傍晚逆光、夜间补光。我发现工地数据最容易掉点的是傍晚逆光场景,安全帽和背心的颜色在夕阳下会严重偏色。如果某个时段掉点明显,优先补那个时段的数据,而不是盲目加训练轮数。
从那以后我每次拿到新工地的数据,都会先跑一遍第2章那个核验脚本,再检查一遍类别分布,然后才把新旧数据合并进训练流程。这个习惯帮我避免了至少两次返工,一次是标签坐标越界,一次是类别ID没对齐,都是提前十分钟能发现的问题。工地安全检测不比学术刷榜,能稳定报警、少误报、扛得住复杂光线,才是真把模型用起来了,希望帮到你。
本文还有配套的精品资源,点击获取