简介:面向目标检测初学者与工地安全帽检测项目开发者,资源以PDF说明文档的形式,完整介绍了一套1000张真实场景图片的安全帽检测数据集,其中图片覆盖工地行人、高空作业、遮挡及严重遮挡等多种复杂情况,均以labelimg标注,划分为佩戴安全帽(helmet)与未佩戴安全帽(head)两类。文档详细说明了VOC(xml)、COCO(json)、YOLO(txt)三种格式标签的对应关系,并附有标注截图与数据集缩略图,方便读者快速评估数据质量。压缩包仅含1个PDF文件,大小6.11MB,内附数据集网盘获取方式,目前已有886人学习下载。此外,配套说明还介绍了作者提供的YOLO11一键训练脚本,支持GPU、CPU及Mac(M芯片)多平台运行,并附训练结果日志,便于对照复现;对需要正规标注数据并快速跑通检测流程的开发者来说,是一份实用的入门指引。
1. 安全帽检测不是缺模型,而是缺一份能直接开训的数据集
目标检测落地到工地、电厂、化工厂时,安全帽检测数据集是被问得最多的需求之一。1000张安全帽图片不算大,但配合VOC、COCO、YOLO三种格式标签,再加上一份能在GPU、CPU、Mac三平台直接跑YOLO11的一键训练脚本,就能把「数据准备、格式转换、环境安装、训练调参」这些重复劳动一次带走。这个组合适合做安监算法验证、毕业设计、边缘盒子原型,也适合第一次接触YOLO11训练的人。真正要解决的三个问题很简单:标签格式怎么选、三平台环境怎么配、训练脚本里哪些参数不能乱动。
2. 三种标签格式选型:VOC、COCO、YOLO 的差异与转换逻辑
拿到安全帽图片后的第一个决策,不是选模型,而是选标签格式。VOC、COCO、YOLO 三套格式都能描述「哪里有帽子、哪里是没戴帽子的头」,但存储方式和坐标单位完全不同。习惯用 LabelImg 手工标注的人,主格式适合选 VOC;要接公开评测或做实例分割扩展,COCO 更通用;准备直接训练 YOLO11,txt 格式最省事。数据集同时给三种格式,不是要求你标三遍,而是同一份标注导出成三种投影。
2.1 VOC 与 COCO 的存储结构:XML、JSON、TXT 的字段对应
VOC 格式每张图对应一个 XML 文件,文件名就是图片名,所有目标框都挂在<object>节点下。安全帽检测里最简单的 VOC 标签长这样:
<annotation> <filename>IMG_0001.jpg</filename> <size> <width>1280</width> <height>720</height> </size> <object> <name>helmet</name> <bndbox> <xmin>214</xmin> <ymin>188</ymin> <xmax>363</xmax> <ymax>317</ymax> </bndbox> </object> </annotation>COCO 格式则把所有图片和标注写进一个 JSON 文件,框用bbox表示,顺序是[x, y, w, h]。同一张图的 COCO 片段对应如下:
{ "images": [{"id": 1, "file_name": "IMG_0001.jpg", "width": 1280, "height": 720}], "annotations": [{"id": 1, "image_id": 1, "category_id": 1, "bbox": [214, 188, 149, 129]}], "categories": [{"id": 1, "name": "helmet"}, {"id": 2, "name": "head"}] }YOLO 标签最直接,每张图对应一个 txt,一行一个目标,格式是class_id cx cy w h,坐标全部归一化到 0~1。比如上面那个框,在 YOLO 里写为:
0 0.2254 0.3507 0.1164 0.1792有一个起点坑会绊住很多人:VOC 和 COCO 的类别编号从 1 开始,YOLO 从 0 开始。COCO 里的category_id=1对应 helmet,转成 YOLO 时必须减 1 变成 0。如果漏掉这一步,模型会把「戴帽子的头」和「没戴帽子的头」整个学反,训练完 val 指标可能正常,推理时却全出乱框。
2.2 坐标转换逻辑:xmin/ymin 如何变成 cx/cy/w/h
无论从 VOC 还是 COCO 转 YOLO,核心都是坐标单位换算。VOC 给的是xmin, ymin, xmax, ymax,YOLO 需要的是框中心点和宽高。转换公式不复杂:
cx = (xmin + xmax) / 2 / img_w cy = (ymin + ymax) / 2 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h我一般会写一个几十行的 Python 脚本做这件事,顺手把类别名映射也做掉:
import xml.etree.ElementTree as ET class_map = {'helmet': 0, 'head': 1} def voc2yolo(xml_path, img_w, img_h): root = ET.parse(xml_path).getroot() lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_map: continue 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) cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") return '\n'.join(lines)这段代码里真正的注意点是class_map。如果标注人员把标签写成了hard_hat而不是helmet,脚本里没有映射就会直接跳过这个目标,一张图可能有几千个框被静默丢掉。建议转完之后按类别统计一次数量,再抽查几张图把框画回去,确认没有大面积漏标。
COCO 转 YOLO 同理:读取annotations里的bbox,用x, y, w, h推cx = x + w/2,cy = y + h/2,再除以图像宽高。唯一多的一步是把category_id减 1。
2.3 1000 张图的划分与三格式同步问题
1000 张图建议按 8:2 拆成 train 和 val,也就是 800 张训练、200 张验证。很多人会把验证集再从训练集里随机抽,这个做法在安全帽场景里不太推荐。工地视频连续帧高度相似,如果训练集和验证集来自同一段视频的相邻帧,val 的 mAP 会虚高,换一个工地马上掉点。我自己更倾向按拍摄场景或视频片段划分,保证验证集是「没见过的机位和光照」。
三种格式同步的关键是让 VOC 当主格式。LabelImg 标注产物就是 XML,后续加标签、改错误框都只动 XML,然后重新导出 COCO JSON 和 YOLO txt。不要手动去改 JSON 或 txt,否则三份标注很容易不一致,后期排查标签错位会非常消耗时间。
提示:COCO JSON 和 YOLO txt 都是导出产物。数据集的原始标签以 VOC XML 为准,二次修改必须走标注工具,再跑一次导出脚本。
3. 动手配置 YOLO11 训练环境:GPU、CPU、Mac 三平台怎么选
YOLO11 依赖 PyTorch 和 ultralytics 库。所谓三平台支持,本质上不是三套代码,而是 PyTorch 在不同设备上选不同后端:Linux/Windows 的 NVIDIA 显卡走 CUDA,Apple Silicon 走 MPS,什么都没有就走 CPU。环境配置的差异也集中在这一个选择上。
3.1 GPU/CPU 环境配置:创建虚拟环境并安装 ultralytics
GPU 机器上最省事的做法是用 conda 建一个干净环境,再装 ultralytics。装完先验证 PyTorch 能不能看到显卡:
conda create -n yolo11 python=3.10 -y conda activate yolo11 pip install ultralytics python -c "import torch; print('CUDA available:', torch.cuda.is_available())"如果输出True,说明 PyTorch 已经正确识别 GPU。如果输出False,先执行nvidia-smi看驱动是否正常,再看 PyTorch 版本和 CUDA 版本是否匹配。大多数情况下,直接pip install ultralytics会带上对应平台的 PyTorch 预编译包,不需要额外指定 CUDA 版本。只有当你本机驱动较老、或用的显卡太旧时,才需要去 PyTorch 官网按 CUDA 版本重装。纯 CPU 环境的安装命令完全一样,只是最后一步会输出False,后面训练时把设备指定为cpu就行。
3.2 Mac 用 MPS 训练:安装命令与内存限制
Mac 上的 MPS 是 PyTorch 对 Apple GPU 的支持方案,不需要单独安装 CUDA。环境创建方式与 Linux 基本一致:
conda create -n yolo11 python=3.10 -y conda activate yolo11 pip install ultralytics python -c "import torch; print('MPS available:', torch.backends.mps.is_available())"MPS available为True时,训练时把device参数写成mps即可。要注意的是 MPS 会占用 Mac 的统一内存,安全帽数据集虽然只有 1000 张,但开启缓存和默认数据加载器后,内存占用很容易超过 16GB。MPS 训练时建议把cache=False、workers=0、batch减半,这三项能显著减少内存压力。如果训练中遇到某个算子不支持,可以设置环境变量PYTORCH_ENABLE_MPS_FALLBACK=1,让不支持的算子自动落到 CPU。这不是最优解,但至少能保证训练不中断。
3.3 设备自动选择:一个函数解决三个平台
写训练脚本时,我会用一个pick_device函数把设备选择逻辑封装起来:
import torch def pick_device(): if torch.cuda.is_available(): return 0 if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): return 'mps' return 'cpu'判断顺序是 CUDA 优先,因为大多数训练场景还是 NVIDIA GPU;其次查 MPS,特别注意要先判断torch.backends里有没有mps属性,否则在纯 CPU 机器上直接访问torch.backends.mps会报 AttributeError;最后回落 CPU。这个函数放进训练脚本后,同一份代码在 GPU、Mac、纯 CPU 三台机器上都能直接跑,这就是标题里说的「三平台一键训练」的最小实现。
4. 把 1000 张安全帽图跑进 YOLO11:一键训练脚本与参数说明
环境就绪后,下一步是把数据集目录整理好,再把训练脚本跑通。整个训练过程由 ultralytics 封装,核心工作集中在 data.yaml、脚本参数和输出检查三件事上。
4.1 准备数据集目录与 data.yaml:路径和类别命名是头号变量
对于 YOLO 格式,目录结构建议这样组织:
datasets/safety_helmet/ ├── safety_helmet.yaml ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/data.yaml 是训练脚本的地图,内容很直接:
path: ../datasets/safety_helmet train: images/train val: images/val nc: 2 names: 0: helmet 1: headpath是数据集根目录,train和val是相对于根目录的子路径。我这里用../datasets/safety_helmet,是因为训练脚本放在项目根目录下的tools/里;如果你把 yaml 和数据集放在同一层,path直接写.或绝对路径更保险。有一个常见的翻车点:names的顺序必须跟 txt 标签里的class_id完全一致,第 0 类写 helmet,第 1 类写 head。如果数据里第 0 类其实是 head,模型会把类别名学反,loss 照样降,验证集混淆矩阵会非常难看。
4.2 train_yolo11.py 一键训练脚本:先跑通再调参
训练脚本的主体不需要很复杂,核心是把设备选择、权重选择、超参数三件事固化下来:
import argparse from ultralytics import YOLO import torch def pick_device(): if torch.cuda.is_available(): return 0 if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): return 'mps' return 'cpu' def main(): parser = argparse.ArgumentParser() parser.add_argument('--data', default='datasets/safety_helmet/safety_helmet.yaml') parser.add_argument('--model', default='yolo11n.pt') parser.add_argument('--epochs', type=int, default=100) parser.add_argument('--batch', type=int, default=16) parser.add_argument('--imgsz', type=int, default=640) args = parser.parse_args() device = pick_device() print(f"use device: {device}") model = YOLO(args.model) model.train( data=args.data, epochs=args.epochs, batch=args.batch, imgsz=args.imgsz, device=device, cache=True, plots=True, patience=50, project='runs/detect', name='helmet_train', exist_ok=True, ) if __name__ == '__main__': main()运行方式也很简单:
python train_yolo11.py --data datasets/safety_helmet/safety_helmet.yaml --model yolo11n.pt --batch 8这段脚本里几个参数需要说明。model默认用yolo11n.pt,这是 YOLO11 的 nano 版本权重,首次运行会下载预训练模型到本地缓存,再基于它微调。1000 张数据量不算多,从预训练权重开始收敛速度会远好于随机初始化。cache=True会把图像缓存进内存,加速训练;如果数据量超过内存,改成cache='ram'或关掉。plots=True会生成训练曲线、混淆矩阵和验证样例图,跑完一轮就能可视化判断训练是否健康。exist_ok=True表示重复运行时不新建目录,方便多次对比实验结果。
4.3 必调参数:batch、imgsz、epochs、patience 和增强开关
下面是针对 1000 张安全帽图最常用的一组参数起点,照着改即可:
| 参数 | 作用 | 建议值 |
|---|---|---|
| batch | 每次进 GPU 的图片数 | 8 或 16,显存小就减半 |
| imgsz | 训练输入分辨率 | 640 |
| epochs | 完整训练轮数 | 100~200 |
| patience | 验证指标连续多少轮不涨就早停 | 50 |
| cache | 是否把图像缓存进内存 | True |
| optimizer | 优化器 | auto 或 SGD |
batch是最先要考虑的参数。16 张 640x640 的图在 8GB 显存上经常放不下,跑之前看显存占用,显存不足就降到 8。imgsz对安全帽小目标影响很大,帽子在画面里经常只占几十像素,imgsz 太小容易漏检,建议直接 640;如果远距离样本多,可试 960,但 batch 要同步降低。epochs建议先跑 100,观察results.png里的验证曲线是否还在上升,再决定要不要追加。patience=50意味着 50 轮验证指标没提升就自动停,省时间也避免过拟合。
5. 安全帽检测训练避坑指南:五条实际踩坑记录与解决办法
这个项目看起来步骤不多,实际跑的时候有几个问题反复出现。下面按现象、原因、解决的思路写出来,每一条都是我或身边同事真实遇到过的情况。
5.1 训练损失下降但预测全空:类别编号从 1 带进 YOLO
现象:训练时 loss 正常下降,验证集 mAP 也显示有结果,但拿一张图片去 predict,输出完全没有框。
原因:转换脚本里没有处理类别起步编号。VOC 里helmet=1、head=2,转 YOLO txt 时直接写了 1 和 2,但 YOLO 要求类别从 0 开始,0 和 1 才是合法编号。模型把背景当成了第 0 类,真正要检测的目标变成了第 1、2 类,推理时类别的置信度全乱。
解决:写一个检查脚本,遍历所有 txt 标签,统计每条记录的class_id范围是否在[0, nc-1]内。同时按类别输出目标数量,例如 helmet 有 4000 个框,head 有 5000 个框,数量分布一目了然,能快速发现类别映射是否颠倒。
5.2 小目标安全帽漏检:imgsz 太小和模型规模不够
现象:近处工人头顶的帽子能识别,画面远处一个戴帽子的人走过,完全漏掉;把置信度降到 0.1 也检不出。
原因:安全帽在远距离画面里可能只有二三十像素,YOLO11 的 nano 模型参数少,对小目标的特征表达弱;再加上训练分辨率只有 416,输入放大后小目标特征进一步丢失。
解决:第一优先把imgsz提到 640 或 960,远距离场景尤其明显。第二把yolo11n.pt换成yolo11s.pt,模型变大后小目标召回率会好一些,代价是训练时间更长。如果换了还是漏,把远距离样本单独挑出来,做一次简单复制粘贴增强,让训练集里小尺寸目标数量增加。
5.3 Mac 训练到一半报 MPS 相关错误
现象:M1/M2 芯片的 Mac 上训练,前十几个 epoch 正常,然后突然报NotImplementedError或out of memory,进程直接退出。
原因:MPS 后端仍有少量算子没有实现,某些网络层在特定输入尺寸下会触发兜底失败;另一种情况是统一内存被训练缓存和数据加载占满,Apple 的统一内存上限毕竟和独立显存不同。
解决:先设置PYTORCH_ENABLE_MPS_FALLBACK=1,让不支持的算子自动落到 CPU。再在训练参数里把cache=False、workers=0、batch=8降下来。如果仍然崩,就把device改成cpu,这样能跑通但速度会明显变慢。不要急着加数据并行,MPS 的并行支持还没有 CUDA 成熟。
5.4 验证 mAP 虚高但不能用:划分和路径错位
现象:验证集的 mAP 到了 0.9,自己拍一段视频测试,同一个工人不同角度频繁漏检,和 val 结果严重不符。
原因:验证集和训练集来自同一段视频的连续帧,或者 data.yaml 里val路径写错,验证时加载的又是训练集图片。两种情况下模型都见过验证图,mAP 自然虚高。
解决:用脚本把 train 和 val 的图片路径取出来求交集,交集为 0 是底线。更好的做法是按视频片段划分,一段视频的前 200 帧进训练集,后 50 帧进验证集,这样验证集里的行人姿态、光照条件才是真正没见过的。
5.5 帽子颜色和光照变化大:数据增强与样本补充
现象:训练数据里大多是红色和黄色安全帽,到了现场出现大量白色和蓝色安全帽,模型开始漏检;逆光时帽子反光,边框紧贴帽檐的目标被误判为 head。
原因:1000 张图的多样性有限,颜色和光照没覆盖到;默认增强虽然开了,但没有针对性强化色彩扰动。
解决:训练时开启并加大颜色增强,比如hsv_h=0.015、hsv_s=0.7、hsv_v=0.4,让模型不过度依赖颜色。针对白帽蓝帽,补 50~100 张带标签的样本,比增加 200 张同色样本更能提升泛化。逆光场景可以在训练集里加入少量天黑前的帧,或者把这类样本单独留到验证集里持续观察召回变化。
6. 训练完成的验证与推理:从 best.pt 到视频流上手
训练结束后,runs/detect/helmet_train/下会有best.pt和last.pt。后续所有推理都用best.pt,它是验证集指标最好的权重。先跑单张图片,再跑视频流,最后检查混淆矩阵,这条路最稳妥。
6.1 用 best.pt 做图片和视频推理
最简单的推理代码只有几行:
from ultralytics import YOLO model = YOLO('runs/detect/helmet_train/weights/best.pt') results = model.predict('demo.jpg', conf=0.35, iou=0.5, save=True)conf=0.35是置信度阈值。安全帽检测场景里,漏检的代价通常比误检大,所以阈值不要设太高;如果后续接的是抓拍报警,0.35~0.45 比较合理。视频推理把demo.jpg换成demo.mp4即可,也可以在参数里加vid_stride=2,每两帧取一帧,本地视频和监控视频回放都能加速。
想观察模型到底看的是帽子形状还是反光边缘,可以借助特征热力图。用 ultralytics 推理时开启save_txt=True保存坐标,再配合 GradCAM 脚本在特定层上生成热力图。安全帽误检的头号来源是「白色反光区域被当成 head」,热力图能直观看出模型的注意力是不是落在帽檐反光上。如果注意力过于分散,优先检查训练集里有没有大量低质量反光样本。
6.2 用混淆矩阵判断误检方向
不要只看 mAP。训练过程中plots=True会自动生成confusion_matrix.png,放在runs/detect/helmet_train/下。这个图比单个数字有用得多。
model.val(data='datasets/safety_helmet/safety_helmet.yaml', split='val')跑完查看混淆矩阵里的 helmet 和 head 两列,重点看两个数字:背景被误判成 helmet 的比例,以及 helmet 被误判成 head 的比例。帽子被框出来了但显示为 head,说明两类样本的数量失衡,head 类别过多导致模型偏向输出 head。背景误检多,则需要提高conf阈值。混淆矩阵是最直接的调参依据,只看 mAP 很容易漏掉这些细节。
6.3 导出 ONNX 给边缘设备
验证满意之后,把权重导出成 ONNX 格式,方便后续接边缘盒子的推理框架:
model.export(format='onnx', imgsz=640)导出后会在权重目录生成同名.onnx文件。ONNX 不依赖 PyTorch 环境,很多 GPU/CPU 盒子都能直接加载。我的习惯是:先跑一遍 val 看混淆矩阵,再用一段包含多人、逆光、不同帽色的视频做冒烟测试,确认没有大面积漏检,最后才导出 ONNX 做部署。这个顺序能帮你把问题留在训练阶段,而不是部署之后到处救火。希望这些内容能帮你把安全帽检测从数据准备一路顺利带到实拍验证。
本文还有配套的精品资源,点击获取