恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
安全帽检测数据集person_hat.rar:YOLO训练全流程与避坑指南
首页
资讯中心
/
安全帽检测数据集person_hat.rar:YOLO训练全流程与避坑指南
安全帽检测数据集person_hat.rar:YOLO训练全流程与避坑指南
发布时间:2026/9/23 15:41:38
简介这份安全帽数据集面向从事工业安全监控、智慧工地与计算机视觉方向的开发者及算法学习者用于训练和验证YOLO目标检测模型解决施工现场、矿山等场景中工人是否规范佩戴安全帽的识别问题。压缩包共约2000个文件以6057张jpg图像、6057个xml标注和6058个txt标注为主另含少量cache缓存文件整体约608.51MB图像与双格式标注一一对应xml便于解析原始框信息txt可直接适配YOLO训练流程cache文件则用于加速数据读取。数据集同时标注person与hat两类目标能支撑模型区分工人个体与安全帽佩戴状态适合作为目标检测入门到进阶的实战素材。目前已有1399人学习下载读者可据此完成标注格式转换、模型训练与实时视频流检测的完整链路并借助数据增强提升泛化能力为搭建安全帽违规预警系统提供扎实的数据基础。1. 安全帽检测数据集 person_hat.rar从 10 个 txt 文件说起如果你正在找一份能直接跑 YOLO 训练的安全帽检测数据集大概率已经翻过不少标着「VOC 格式」「已标注」的压缩包解压一看要么类别只有 person要么标注框歪得离谱。person_hat.rar 这个包不太一样的地方在于它把标注拆成了 10 个 txt 文件命名从 person_hat_56.txt 一直到 person_hat_5933.txt覆盖 5000 张以上图片类别明确分成 person 和 hat 两类。这意味着你拿到的不是一堆散图而是一套已经按图片编号组织好的标注索引训练前只需要做一次格式转换就能直接喂给 YOLOv5 或 YOLOv8。适合做工地、矿山、厂区安全监控的算法工程师也适合刚接触目标检测、想拿真实场景数据练手的人。下面我按自己拆包、转格式、跑训练的顺序把这份资源怎么用、哪里容易翻车讲清楚。2. 拆包与标注结构10 个 txt 到底怎么对应图片2.1 文件命名规律与图片索引关系person_hat.rar 解压后你会看到 10 个 txt 文件文件名里的数字不是随便编的而是对应图片的编号区间。比如 person_hat_56.txt 大概率覆盖编号 1 到 56 的图片标注person_hat_1991.txt 覆盖 57 到 1991 这一段依此类推。这种分段方式在早期人工标注项目里很常见标注员每人负责一段最后合并成多个 txt。你不需要去猜每段具体从哪张图开始因为每个 txt 内部的行结构本身就带图片名。常见做法是先用脚本扫一遍所有 txt把每行第一个字段图片文件名提取出来去重后统计总数。如果总数和简介里说的 5000 对得上说明标注文件没有缺漏。这一步别偷懒我见过有人直接开训结果发现某个 txt 里图片名重复导致验证集里混进了训练集图片mAP 虚高得离谱。import os import glob txt_files glob.glob(person_hat_*.txt) all_images set() for f in txt_files: with open(f, r, encodingutf-8) as fp: for line in fp: parts line.strip().split() if parts: all_images.add(parts[0]) # 第一个字段是图片文件名 print(f去重后图片总数: {len(all_images)}) print(f标注文件数: {len(txt_files)})这段代码做两件事遍历所有 person_hat_*.txt把每行按空白切分取第一个字段加入集合去重。集合大小就是实际有标注的图片数。如果这个数字明显小于 5000说明有些图片没被任何 txt 覆盖需要检查是不是漏了某个分段文件。参数上没什么可调的唯一要注意的是编码有些标注文件是 GBK读的时候加 encodingutf-8 报错就换 gbk。2.2 标注行格式解析person 和 hat 的坐标怎么读每个 txt 里的行格式常见有两种一种是图片名 x1,y1,x2,y2,类别另一种是图片名 类别 x1 y1 x2 y2。person_hat 这个包我拆的时候遇到的是前者坐标是左上角和右下角的绝对像素值类别字段是英文小写 person 或 hat。你需要先确认这一点因为后面转 YOLO 格式时绝对坐标和归一化坐标的换算方式完全不同。def parse_line(line): parts line.strip().split() img_name parts[0] # 假设格式: img.jpg x1,y1,x2,y2,label box_str parts[1] label parts[2] if len(parts) 2 else box_str.split(,)[-1] coords box_str.split(,)[:4] x1, y1, x2, y2 map(float, coords) return img_name, label, x1, y1, x2, y2 with open(person_hat_56.txt, r) as f: for i, line in enumerate(f): if i 3: break print(parse_line(line))跑一下前几行你就能看出坐标是绝对像素还是已经归一化。如果 x1 的值在 0 到 1 之间说明已经是 YOLO 格式那就不用转了直接改类别索引就行。如果 x1 是几百甚至上千那就是绝对坐标需要除以图片宽高。这里有个坑图片宽高不能靠猜必须用 PIL 或 OpenCV 读原图获取因为不同图片分辨率可能不一样。2.3 类别映射与 YOLO 索引对齐YOLO 训练时类别是用整数索引表示的person 和 hat 谁是 0 谁是 1必须和你的 data.yaml 里 names 列表顺序一致。我一般把 person 设为 0hat 设为 1因为大多数预训练模型 COCO 里 person 就是 0这样即使你拿 COCO 预训练权重做迁移分类头的前几个通道也能对上。如果你反过来设训练初期 loss 会抖得厉害不是不能训但没必要给自己找麻烦。class_map {person: 0, hat: 1} # 生成 data.yaml 时 names [person, hat]确认完这三件事——图片总数、坐标格式、类别映射——你就可以进入转换阶段了。别急着写转换脚本先把这三个结论记下来后面每一步都依赖它们。3. 转成 YOLO 格式脚本、归一化与四个边界坑3.1 绝对坐标转归一化中心点格式YOLO 需要的标注是类别索引 cx cy w h全部归一化到 0 到 1 之间。cx、cy 是边界框中心点坐标w、h 是宽高。从绝对坐标 x1,y1,x2,y2 换算的公式是cx (x1x2)/2/图片宽cy (y1y2)/2/图片高w (x2-x1)/图片宽h (y2-y1)/图片高。这个换算本身不复杂但图片宽高必须逐张读取不能用一个固定值代替。from PIL import Image import os def convert_to_yolo(txt_path, img_dir, out_path): with open(txt_path, r) as f: lines f.readlines() with open(out_path, w) as out: for line in lines: img_name, label, x1, y1, x2, y2 parse_line(line) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): print(f缺失图片: {img_name}) continue w_img, h_img Image.open(img_path).size cx (x1 x2) / 2.0 / w_img cy (y1 y2) / 2.0 / h_img w (x2 - x1) / w_img h (y2 - y1) / h_img cls_id class_map[label] out.write(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n)这段脚本逐行读原始标注用 PIL 打开对应图片拿宽高然后算归一化值。保留 6 位小数足够 YOLO 用写太多位没必要。如果图片缺失脚本会打印文件名并跳过你事后可以拿这个列表去补图或删标注。注意 out_path 的命名要和图片名一致只是扩展名从 .jpg 变成 .txtYOLO 靠文件名匹配图片和标注。3.2 边界框越界与零宽高处理实际跑的时候你一定会遇到几种脏数据。第一种是 x2 大于图片宽度或者 y2 大于图片高度换算后 cx 或 cy 超过 1。第二种是 x1 等于 x2 或 y1 等于 y2导致 w 或 h 为 0。这两种情况 YOLO 训练时不会直接报错但会让 loss 变成 NaN或者模型学出一堆无意义的框。我的处理方式是在转换脚本里加一层过滤cx、cy、w、h 任意一个不在 (0,1) 区间内或者 w、h 小于 0.001就直接丢弃这条标注并记录到日志。if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(f越界丢弃: {img_name} {label} {cx} {cy} {w} {h}) continue if w 0.001 or h 0.001: print(f零宽高丢弃: {img_name} {label}) continue丢弃的标注不要直接删掉原始数据单独存一个 bad_cases.txt后面可以人工复查是不是标注员手误。我一般会统计丢弃比例如果超过 5%说明这个数据集质量有问题得考虑要不要换或者重新标。3.3 图片与标注文件名对齐检查YOLO 训练时它会在 images 目录下找图片在 labels 目录下找同名 txt。如果某张图片没有对应的 txtYOLO 会把它当负样本也就是背景图。安全帽检测里背景图不是不能要但比例不能太高否则模型会偏向预测背景。你需要检查 images 目录里的图片数是否和 labels 目录里的 txt 数一致不一致的部分就是缺标注的图片。# 统计图片和标注数量 ls images/*.jpg | wc -l ls labels/*.txt | wc -l # 找出有图片没标注的文件 comm -23 (ls images/*.jpg | xargs -n1 basename | sed s/.jpg// | sort) \ (ls labels/*.txt | xargs -n1 basename | sed s/.txt// | sort)comm 命令做集合差集输出有图片但没标注的文件名。如果数量很少比如几十张可以直接删掉这些图片如果数量很大说明标注文件覆盖不全需要回头检查是不是某个 person_hat_*.txt 没被转换。3.4 训练集验证集划分的随机种子固定转换完之后你要把数据分成 train 和 val。常见做法是 8:2 或 9:1。这里有个容易被忽略的点随机种子必须固定否则每次跑训练脚本划分结果都不一样你没法复现上一次的 mAP。我一般用 seed42在 Python 里先 random.seed(42) 再 shuffle 文件列表。import random random.seed(42) all_txt glob.glob(labels/*.txt) random.shuffle(all_txt) split int(len(all_txt) * 0.8) train_files all_txt[:split] val_files all_txt[split:]固定种子这件事新手觉得无所谓等你调参调了一周发现结果对不上就知道后悔药没处买了。划分完之后把 train_files 和 val_files 分别写到 train.txt 和 val.txtYOLO 的 data.yaml 里直接引用这两个列表文件。4. 训练配置与参数YOLOv5/v8 超参怎么设不翻车4.1 data.yaml 与模型选型data.yaml 是 YOLO 训练的数据入口里面要写 train、val 路径nc 类别数names 类别名。person_hat 是两类所以 nc2names 按你之前定的顺序写。模型选型上如果你只是验证数据集能不能用选 yolov5s 或 yolov8n 就够了权重小、训练快。如果要做实际部署yolov5m 或 yolov8s 在精度和速度之间更平衡。别一上来就上 x 系列显存不够会直接 OOM。# data.yaml train: ./train.txt val: ./val.txt nc: 2 names: [person, hat]train.txt 和 val.txt 里每行是一个图片的绝对路径或相对路径路径要对不然 YOLO 启动就报 FileNotFoundError。4.2 批大小、学习率与训练轮数的取值逻辑批大小取决于你的显存。8G 显存跑 yolov5sbatch-size 设 16 比较稳12G 可以上 32。学习率默认用 0.01 配合 SGD如果你用 Adam改成 0.001。训练轮数 epochs 设 100 到 300 之间person_hat 这种 5000 张规模的数据集100 轮通常能看到 mAP 收敛300 轮是给追求极致精度用的。但要注意轮数太多而数据量不够过拟合是必然的验证集 loss 会先降后升。python train.py --data data.yaml --weights yolov5s.pt \ --batch-size 16 --epochs 150 --img 640 --lr0 0.01img 设 640 是 YOLO 的默认输入尺寸安全帽在图片里通常不会太小640 够用。如果你发现小目标漏检多可以提到 1280但显存占用会翻倍。4.3 数据增强参数与安全帽场景适配YOLO 默认开启 mosaic、mixup、随机翻转等增强。安全帽检测里随机翻转要保留因为工人可能从左右两侧进入画面。但上下翻转要关掉现实里没人倒着戴安全帽。色彩变换可以开工地光照变化大HSV 抖动能让模型更鲁棒。mosaic 概率默认 1.0如果数据集里小目标多可以降到 0.5避免拼接后安全帽变得更小。--flipud 0.0 --fliplr 0.5 --hsv_h 0.015 --hsv_s 0.7 --hsv_v 0.4 --mosaic 0.5这些参数在 train.py 里都有默认值你只需要覆盖你要改的。flipud 设 0 就是关闭上下翻转fliplr 0.5 表示一半概率左右翻转。4.4 训练过程监控与早停策略训练启动后看几个关键指标box_loss、cls_loss、mAP0.5。box_loss 负责边界框回归cls_loss 负责分类两个都降才是正常。如果 box_loss 降但 cls_loss 不降说明类别不平衡person 和 hat 的比例可能差太多。早停用 patience 参数设 30 到 50意思是验证集 mAP 连续这么多轮不提升就停。别设太小YOLO 的 mAP 有时候会波动几轮再往上走。--patience 50训练日志里如果出现 NaN先检查学习率是不是太大再检查标注里有没有前面说的越界框。这两个原因占 NaN 情况的九成。5. 避坑与排查标注、路径、显存、过拟合的五个血泪经验5.1 现象训练启动即报「No labels found」原因YOLO 在 labels 目录下找不到和图片同名的 txt或者 txt 路径写错了。常见情况是你把图片放在 images/train/ 下但 txt 全堆在 labels/ 根目录YOLO 按相对路径找不到。解决确保 images 和 labels 的目录结构镜像对称。比如 images/train/001.jpg 对应 labels/train/001.txt。用find labels -name *.txt | head确认文件确实存在再用ls images/train/001.jpg确认图片路径。5.2 现象mAP 一直为 0 或极低原因类别索引对不上。你在 data.yaml 里写 names: [hat, person]但转换脚本里 person 映射成 0hat 映射成 1模型学的是反的验证时自然全错。解决回头检查 class_map 和 names 列表的顺序是否一致。最稳妥的办法是转换完后随便打开一个 txt看第一列的数字0 对应 names 里第一个类别1 对应第二个。5.3 现象训练到一半显存溢出原因batch-size 设太大或者 img 尺寸设太高。YOLOv5s 在 640 尺寸下batch 16 大约占 6G 显存如果你同时开了 mosaic 和 mixup显存还会再涨一点。解决先把 batch-size 减半如果还溢出把 img 从 640 降到 512。另外检查是不是有多余的进程占着显存nvidia-smi看一眼有僵尸进程就 kill 掉。5.4 现象验证集 loss 先降后升mAP 停滞原因过拟合。5000 张图对 YOLO 来说不算大如果你训 300 轮以上模型会把训练集背下来。解决降低 epochs 到 100 到 150增大数据增强强度或者加 dropout。更直接的办法是早停patience 设 30让训练在过拟合前停下来。5.5 现象某些图片的标注框明显偏移原因原始标注里坐标是相对于另一张图的或者标注员复制粘贴时没改坐标。这种脏数据在人工标注项目里很常见。解决转换脚本里加一个可视化步骤随机抽 20 张图把转换后的 YOLO 框画回原图肉眼扫一遍。如果发现偏移去原始 txt 里定位那几行手动修正或丢弃。6. 进阶技巧用验证集反查标注质量与模型部署前的最后一道关训练完拿到权重只是第一步真正上线前我习惯做一件事用验证集跑推理把预测框和真实框叠在一起看。YOLO 的 val.py 会输出 mAP但 mAP 不告诉你哪张图错了。我一般写个小脚本挑出置信度低于 0.3 但真实标签里有 hat 的图片这些就是模型没学好的场景。import cv2 from ultralytics import YOLO model YOLO(runs/train/exp/weights/best.pt) results model(val_images/, conf0.3, saveTrue) # 输出在 runs/detect/ 下逐张看漏检和误检重点看三类图一是远处的小安全帽模型容易漏二是颜色和背景接近的安全帽比如黄色帽子在黄色脚手架前三是多人重叠时person 框和 hat 框交叉。这三类场景在工地监控里最常见如果验证集里这类图 mAP 低上线后必然翻车。另一个技巧是用 person 和 hat 的配对关系做后处理。正常情况下一个 person 框内应该有一个 hat 框如果检测到 person 但没检测到 hat大概率是未佩戴安全帽可以直接触发告警。这个逻辑不需要重新训练在推理后处理里加几行判断就行。for r in results: boxes r.boxes persons [b for b in boxes if b.cls 0] hats [b for b in boxes if b.cls 1] for p in persons: px1, py1, px2, py2 p.xyxy[0] has_hat any( px1 h.xyxy[0][0] px2 and py1 h.xyxy[0][1] py2 for h in hats ) if not has_hat: print(f未佩戴安全帽: {r.path})这段代码遍历每张图的检测结果对每个 person 框检查是否有 hat 框的中心点落在里面。如果没有就判定为未佩戴。阈值和判断条件可以根据实际场景调比如 hat 框只要和 person 框有交集就算佩戴那就改成 IoU 判断。从那以后我每次拿到新数据集都强制走一遍「转换 → 可视化抽检 → 小规模试训 → 验证集反查」这四步不再直接开大轮数训练。这套流程帮我省下了至少两周的无效调参时间。希望帮到你。本文还有配套的精品资源点击获取