ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

安全帽反光衣识别数据集解压到YOLOv8训练全流程拆解

安全帽反光衣识别数据集解压到YOLOv8训练全流程拆解 简介面向建筑工地、工厂等安全生产场景这份基于YOLO的数据集聚焦安全帽、反光衣和工作服的自动识别与检测可直接用于人员着装合规性分析。资源包含2000个文件压缩包约952.16MB其中1999个txt文件提供YOLO格式的标注框信息1个yaml文件定义类别名称和训练路径解压后即可接入YOLOv5/YOLOv8等常见训练框架无需额外转换格式。对算法开发者和安全管理人员而言该数据集能支撑实时监控画面中的违规行为检测、报警与留证有效降低施工和作业现场的安全风险。目前已有1599人学习下载适用于智慧工地、工厂安全巡检等场景的模型开发与算法验证相比自建数据集可大幅节省标注成本和时间。1. 安全帽、反光衣、工作服三合一识别数据集从解压到开训的落地拆解先说结论。工地入口摄像头对着逆光、夜间补光不足的场地拍安全帽和反光衣识别要落地最花时间的不是 YOLO 调参而是数据准备这一关。这套基于 YOLO 的安全帽/反光衣/工作服自动识别数据集解压之后就是 YOLO 训练直接能吃的格式类别恰好对应施工安全最常踩线的三样穿戴标注是归一化坐标的 TXT 文件train/val 划分也已经替你做好了。对于要在工地场景快速出一版安全帽检测模型的一线工程师、实验室里想完整跑一遍 YOLOv8 训练流程的学生以及要做“识别能不能上现场”验证的集成商这套数据至少能省掉两到三周标注时间。我按实际使用顺序把目录结构、标签统计、训练配置和踩坑要点一个一个拆开讲。2. 数据集结构拆解目录树、标签坐标与类别分布拿到压缩包我习惯先解压、再去看目录树而不是直接双击训练脚本。原因很简单标注数据的目录结构决定了后面data.yaml里路径怎么写类别编号决定了训练出来的框到底落在哪一类上。这几分钟不花后面训练跑一半发现路径错了重跑的时间成本翻倍。常见做法是统一的 images/labels 两根分支train/val 各自对应。假设解压后的根目录叫safety_helmet_vest_dataset结构一般是unzip -q safety_helmet_vest_dataset.zip tree -L 2 safety_helmet_vest_datasetsafety_helmet_vest_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamlYOLO 系列工具的约定是图像和标签分居images和labels两个目录train/val 各自保持同名映射。images/train/00001.jpg对应的标注在labels/train/00001.txt文件名严格同步不能出现图片有标注、标注没图片这类半截文件。下载这类数据集时如果压缩包里带了 README 或数据说明文档我会先读一遍。标注规范会写明“安全帽只框帽体部分”“反光衣框到上衣区域”这种语义边界别让后面的训练去猜。2.1 一行标注 5 个数的含义先抽一个标签文件看格式head -n 3 safety_helmet_vest_dataset/labels/train/00001.txt0 0.5123 0.4831 0.1321 0.2448 1 0.3340 0.6917 0.1775 0.2892 2 0.4217 0.7119 0.1133 0.2142每行表示一个目标框共 5 个数类别索引、中心点 x 坐标、中心点 y 坐标、框宽、框高。这里我把类别约定为 0 安全帽、1 反光衣、2 工作服和data.yaml里的 names 一一对应。四个浮点数都是相对图像宽度和高度的归一化值范围 0 到 1不能直接当像素坐标用。比如0.5123 0.4831这个中心点要放到一张 1920×1080 的原图上实际像素位置是x 0.5123 * 1920y 0.4831 * 1080。我见过不少人把归一化坐标误当成像素值直接画框结果所有框全挤在图像左上角先画了框再回头检查标注就是白费功夫。要把标注拉回原图肉眼检查我一般会写一段小脚本import os import cv2 def draw_yolo_boxes(image_path, label_path, class_names): img cv2.imread(image_path) h, w, _ img.shape with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls int(parts[0]) x_c, y_c, bw, bh map(float, parts[1:]) x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) color (0, 255, 0) if cls 0 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return img if __name__ __main__: classes [helmet, reflective_vest, workwear] img draw_yolo_boxes( safety_helmet_vest_dataset/images/train/00001.jpg, safety_helmet_vest_dataset/labels/train/00001.txt, classes ) cv2.imwrite(check_00001.jpg, img)脚本逻辑不难map(float, parts[1:])把字符串转成浮点数四个坐标值做从归一化到像素的换算。max(0, y1 - 5)是防止框贴着图像上边缘时文字跑到画面外变成负数坐标。跑完抽查若干张图如果发现框和内容对不上就要回头查标注文件是错标还是类别顺序写反了。2.2 类别分布和负样本的统计拆数据集时我会顺手统计每类框的数量因为类别不平衡会直接影响训练时的采样权重。安全帽框数量明显多于反光衣模型对安全帽这类特征收敛得更快反光衣因为样本少、特征容易被淹没。具体差距有多大用一个 Counter 就能看import os from collections import Counter label_dir safety_helmet_vest_dataset/labels/train class_names {0: helmet, 1: reflective_vest, 2: workwear} counter Counter() total_boxes 0 for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn), r) as f: lines f.readlines() if not lines: continue for line in lines: cls int(line.strip().split()[0]) counter[cls] 1 total_boxes 1 for cls_id in range(3): print(f{class_names[cls_id]}: {counter[cls_id]}) print(ftotal boxes: {total_boxes})我一般在拿到任何数据集后都会跑一遍这个统计注意那些完全空的 txt 文件。空标注文件属于负样本表示“这张图里没有检测目标”它让模型学会什么情况下不应该输出框。但负样本要放在训练集里如果大量负样本被分进 val 集验证结果里 False Positive 会看着特别高实际现场表现可能完全两码事。还需要看的是目标尺度分布。把每个框的宽度、高度收集起来看中位数能尽早判断目标是大是小import os import numpy as np sizes [] label_dir safety_helmet_vest_dataset/labels/train for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue _, _, _, bw, bh map(float, parts) sizes.append((bw, bh)) sizes np.array(sizes) print(框宽中位数:, np.median(sizes[:, 0])) print(框高中位数:, np.median(sizes[:, 1]))如果框宽中位数在 0.05 以下说明数据集里有大量小目标。这种情况后面imgsz还按 640 跑小目标特征会被压得很小现场漏检的坑在训练前就埋下了。2.3 读懂 data.yaml 里的字段大多数工程化的标注数据集会自带一个data.yaml供 ultralytics 直接引用。内容大致是path: /absolute/path/to/safety_helmet_vest_dataset train: images/train val: images/val names: 0: helmet 1: reflective_vest 2: workwearpath是数据集根目录的绝对路径train和val是相对 path 的子目录。names 的索引顺序必须和标签文件里的类别编号完全一致这一点我在后面训练章节会再强调一遍。如果压缩包里没带 yaml自己补一个命名保持简单不要用带空格或中文的目录名后续命令行参数解析容易出幺蛾子。3. 用 YOLOv8 跑通训练流程环境配置、损失函数与关键参数目录看明白了训练就不是黑匣子。我用 YOLOv8 做这一轮拆解因为它在检测精度、训练速度和工程生态之间平衡得比较稳。做安全帽、反光衣这类穿戴识别没必要第一次就用最复杂的模型先把一个中体量模型跑通再根据漏检率做针对性调整。3.1 环境准备与预训练权重训练前先建独立环境避免把项目依赖和系统 Python 混在一起conda create -n safety-yolo python3.10 -y conda activate safety-yolo pip install ultralytics torch torchvision python -c import torch; print(torch.cuda.is_available())torch.cuda.is_available()返回 True 说明 GPU 可用返回 False 大概率是装成了 CPU 版 torch或者服务器 CUDA 版本不匹配。yolo 预训练模型下载这一步ultralytics 在首次训练时输入modelyolov8m.pt会自动拉取权重文件。离线服务器上常见做法是提前把.pt放到当前工作目录避免训练中途卡在下载环节。模型体量选型上n、s 太小工地场景小目标多收不动x 太大显存需求和训练时长都往上跳。我一般从 m 起步精度居中、部署可控后面要提速再剪枝或换 s。3.2 数据配置与类别映射训练前把data.yaml准备好路径用绝对路径省掉一堆相对路径的解析问题path: /data/datasets/safety_helmet_vest_dataset train: images/train val: images/val names: 0: helmet 1: reflective_vest 2: workwear这段 yaml 最核心的是 names 顺序。数据集里标签文件的类别编号 0、1、2 已经写死了names 顺序不能随意调整。想把工作服放第一位等于把所有标签重新做一遍映射。很多人复现时遇到“安全帽被识别成反光衣”先查是不是 names 顺序和标签索引不对齐。3.3 训练命令与损失函数的关系cd /data/datasets yolo train \ datasafety_helmet_vest_dataset/data.yaml \ modelyolov8m.pt \ epochs120 \ imgsz640 \ batch32 \ device0 \ patience15 \ projectruns/safety_detepochs120是训练上限patience15表示连续 15 个 epoch 验证集指标没有提升就提前停避免无效空转。imgsz640是官方常用的输入分辨率如果第 2 章统计时发现大量小目标可以把imgsz960再跑。batch32在 24G 显存上跑 m 体量比较稳显存不足就降到 16不要硬扛。训练日志里的 loss 是一个组合值YOLOv8 把它拆成三块box_loss、cls_loss、dfl_loss。box_loss 管预测框和真实框的位置误差cls_loss 管类别判断误差dfl_loss 是分布焦点损失负责优化边框的边界细节三者加权求和就是总 loss。我一般会盯着一件事如果某个 loss 长时间不降说明对应的问题出在数据层面而不是训练参数。比如 cls_loss 降不下去先回数据集看标签是不是有错标、漏标而不是急着改损失函数权重。3.4 训练结束看什么指标训练完跑一次验证yolo val modelruns/safety_det/weights/best.pt datasafety_helmet_vest_dataset/data.yaml输出里会有一个指标表核心几列指标含义现场价值Precision预测框中真正目标的比例误报多不多Recall真实目标中被找出来的比例漏检多不多mAP50IoU 阈值 0.5 时的平均精度常规验收参考mAP50-95更严格的全程平均精度小目标敏感做工地安全帽识别我更看重 Recall。漏检一个人比误报一个人严重太多所以到调推理阈值时宁可牺牲一点 Precision也要保住 Recall。训练阶段不要只看 mAP50 一个数字要把三个类别各自的 Precision 和 Recall 拆开看反光衣的短板往往被安全帽的高分掩盖掉。4. 穿戴识别训练避坑清单五个翻车现场与对应的排错方案这部分是我复盘时最想留下的内容。每一条都是实际训练里真实见过的翻车现场按“现象 → 原因 → 解决”的顺序写。4.1 反光衣高光一过就漏检现象模型在正常光照下识别反光衣挺稳现场一遇到强光、车灯直射反光衣过曝变成一片白框直接消失。原因训练数据里反光衣大多数是顺光和均匀光照的画面极端曝光样本太少。反光衣材质本身反光率极高过曝后纹理被抹平模型没见过这种输入分布自然认不出来。解决我习惯在训练前给图像加随机曝光扰动模拟现场强光和逆光环境import random import numpy as np def exposure_shift(img, factor_range(0.55, 1.65)): factor random.uniform(*factor_range) img img.astype(np.float32) * factor return np.clip(img, 0, 255).astype(np.uint8)img * factor就是整图亮度增益factor 大于 1 往过曝方向走小于 1 往偏暗方向走。工地出入口的摄像头经常逆光取 0.55 到 1.65 这个范围能在不过度破坏特征的前提下覆盖大部分光照变化。注意 factor 上限设得太高会生成大片纯白区域反而把反光衣的反光条特征洗没了。4.2 远处小安全帽置信度压在 0.25 以下现象同一张画面里近处工人的安全帽识别得很好远处走过来的工人头上的安全帽只有二三十像素置信度始终拉不上来现场表现为时远时近、时灵时不灵。原因小目标在特征图下采样过程中信息严重丢失。imgsz640 时一个 30×30 像素的目标映射到 80×80 特征图只剩不到 4×4特征基本被压没了。解决把训练和推理的 imgsz 提到 960让目标在输入端保留更多像素yolo train ... imgsz960 batch16分辨率提高后显存占用涨得很快batch 要同步降下来。我在 24G 显存上用 m 体量跑 960 分辨率batch16 是相对稳妥的组合再低就观察显存占用动态调。4.3 工作服和普通工装互相错认现象验证集里 workwear 类别的误报集中出现在普通深色工装、连体工作服上反光衣反而很少被误认成工作服。原因这个问题的根源往往不在模型而在标注语义边界。工作服和普通工装在外观上本来就连续如果标注时有的框把带有反光条的工装算工作服有的不算模型会学到一套前后不一致的标准。解决先把标注规范定死。我约定工作服必须是有明显反光条、统一制服特征的画面普通便装不上这个类。对已标注数据做一轮复核把边界样本统一归到 hard example 集合不要直接混在训练集里。标签不一致带来的模型困惑靠调损失权重是救不回来的。4.4 训练集 mAP 高、现场连续漏检现象训练和验证阶段的 mAP50 到了 0.9看起来能交付结果拿现场摄像头录的 10 分钟视频一跑漏检连着出现。原因这是典型的数据划分泄漏。很多自采数据集把同一个摄像头拍摄的连续帧随机切进 train 和 val两边的画面高度雷同验证集等于泄了题。模型记住的是具体场景纹理而不是泛化出来的安全帽和反光衣特征。解决重新按采集来源划分数据。我一般会把文件名前缀或拍摄时间当成分组依据保证同一路视频的帧只出现在 train 或只出现在 val。这个操作做完验证指标会下降一点但现场表现的真实度上升一大截。4.5 训练到一半 loss 突然变成 NaN现象前几十个 epoch 曲线正常某个 epoch 开始 total loss 突然变成 nan后面所有指标跟着崩。原因常见诱因有两个。一个是学习率在后期仍然偏大梯度更新步子迈过头另一个是 batch 里混进了损坏图片比如下载途中被截断的 jpg解码出来是一张残缺的纯色图梯度异常放大。解决先把lr0从默认的 0.01 降到 0.005同时固定seed让训练可复现yolo train ... lr00.005 seed42再跑一轮前先做图片完整性检查from PIL import Image import os for root, _, files in os.walk(safety_helmet_vest_dataset/images): for fn in files: p os.path.join(root, fn) try: im Image.open(p) im.load() except Exception as e: print(坏图:, p, e)im.load()会强制触发解码截断文件到这一步就会抛异常。跑一遍把坏图挑出来顺便把对应的 txt 标注一起移走再重新训练loss 曲线的翻车概率会低很多。5. 验证与部署混淆矩阵、阈值选择与 ONNX 导出训练结束不意味着能直接上现场。从本地验证到工地工控机部署中间还有三道工序。5.1 先从混淆矩阵里读短板yolo val跑完会生成confusion_matrix.png横纵坐标是真实类别和预测类别对角线越亮越好。我一般先找两类误报反光衣被认成工作服以及安全帽被认成背景。前者表示两个类别在视觉上过于接近基本要回到标注规范处理后者说明小目标漏检优先考虑提高 imgsz而不是硬调阈值。背景误报严重时要回到负样本比例去补数据。5.2 推理阈值怎么选conf 和 iou推理阶段最重要的两个参数是置信度阈值conf和 NMS 的ioufrom ultralytics import YOLO model YOLO(runs/safety_det/weights/best.pt) results model.predict( source现场抓拍.jpg, conf0.35, iou0.5, imgsz960, classes[0, 1, 2], saveTrue, )conf0.35的取舍逻辑是先拿 100 帧现场画面在默认参数下跑一遍记录所有预测框的置信度分布。反光衣特征特殊置信度普遍在 0.4 上下安全帽可以到 0.7统一拿 0.35 作为门限两边都能保住。classes[0, 1, 2]很容易忘但不写的话模型会把没见过的背景也硬输出成检测框。5.3 导出 ONNX 部署到工控机现场工控机一般不会有完整的 torch 环境导出 ONNX 是标准做法yolo export modelruns/safety_det/weights/best.pt formatonnx opset12 imgsz960注意导出时的 imgsz 要和推理时一致ONNX 模型的输入尺寸是固定的。后端用 onnxruntime 跑最小依赖就能推理import onnxruntime as ort import cv2 import numpy as np sess ort.InferenceSession( best.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider] ) input_name sess.get_inputs()[0].name img cv2.imread(现场抓拍.jpg) img_resized cv2.resize(img, (960, 960)) input_img img_resized[:, :, ::-1].transpose(2, 0, 1) input_img input_img[None].astype(np.float32) / 255.0 outputs sess.run(None, {input_name: input_img})img[:, :, ::-1]是把 BGR 换成 RGBtranspose(2, 0, 1)把 HWC 变成 CHW最后归一化到 0-1。这三步的格式必须和训练时完全对齐否则同一个模型在不同机器上跑出两套结果我踩过这个坑。需要 N 卡 GPU 加速时再从 ONNX 转 TensorRT能再压一截延迟。5.4 交付前先拿没见过的视频过一遍验证集指标再漂亮也不代表现场能直接用。我的习惯是把模型拿到目标现场录一段没参与训练的 15 分钟视频逐帧跑一遍人工数出漏检和误报数量。不同品牌摄像头色彩倾向差异大现场偏色和验证集不完全一致这一步能提前发现颜色偏移带来的性能下降。真遇到色差严重的现场先做输入端的颜色归一化再考虑重新标数据。6. 影子数据回灌法用现场画面把模型再往上拽一截标注数据集训练出来的模型到现场往往差最后一口气我的做法叫影子数据回灌把模型放到真实现场跑一段时间把漏检、误检画面自动捞出来重点补标注之后混回原数据再做一轮微调。捞难例的脚本很简单from ultralytics import YOLO model YOLO(runs/safety_det/weights/best.pt) for i, res in enumerate(model.predict( source现场录屏.mp4, streamTrue, conf0.2 )): for box in res.boxes: if box.conf[0] 0.3: res.save(fhard_examples/frame_{i:05d}.jpg)conf0.2故意放低是为了把模型犹豫不决的框也捞出来。筛选原则有两个一是模型给了三类之一的低置信度框这种图片补上正确标签直接回灌二是画面里明明有人模型一个框都没出这种场景多半出在反光衣过曝或极端逆光段需要重新采集对应时段的画面而不是靠重复回灌硬顶。回灌节奏我一般控制在每次 300 到 500 帧新图重新训 40 到 60 个 epoch。量太少模型记不住新特征一口气灌 2000 帧模型容易过拟合到某一段光线下白天的识别反而变差。从那以后我每次做工地穿戴识别都会先让模型在目标现场跑一整天把统计漏检和低置信度帧变成固定流程再做一轮影子数据回灌最后才拿现场验收。这套做法比反复调阈值要稳定得多也成了我交付前的习惯。希望帮到你。本文还有配套的精品资源点击获取
返回列表