简介:目标检测是计算机视觉的核心任务之一,旨在从图像或视频中定位并识别特定对象。YOLOv5作为单阶段检测算法的代表,通过回归方式直接输出目标位置与类别,在保持高精度的同时实现了极快的推理速度,尤其适用于需要实时响应的安全监管场景。PyTorch则凭借动态计算图和成熟的CUDA支持,为模型训练与调试提供了极大便利。基于这两项技术构建的头盔检测系统,可自动识别监控画面中人员是否佩戴安全帽,一旦发现违规立即告警,广泛应用于工地、园区、工厂等安全生产领域。本文从环境配置、数据标注、模型训练到推理部署,完整呈现了构建此类系统的全过程,并分享了解决漏检、误检及显存不足等实际问题的经验,为开发者提供了一套可复现的工程实践方案。 去年接了一个工地的安全监管需求:靠人盯监控,看工人有没有戴安全帽,眼睛根本看不过来。后来就把这个需求落地成了基于YOLOv5和PyTorch的深度学习头盔检测系统,能做到对图片、视频、实时监控画面中的头盔佩戴情况进行自动识别,检测到未佩戴立刻框出来并告警。整套流程从环境搭建、数据标注、模型训练到最终部署推理,我完整跑了一遍,这篇文章就把里面的关键细节和踩过的坑一次性讲清楚。适合想用深度学习做目标检测但还停留在"看过教程、没跑过完整项目"阶段的人,也适合准备在工地、园区、工厂等场景落地安全帽检测的开发者参考。
1. 项目整体设计与技术选型思路
1.1 检测算法选型:为什么锁定YOLOv5
头盔检测本质上是一个目标检测任务,核心要解决两个问题:第一,把画面里的人找出来;第二,判断这个人头部区域有没有佩戴头盔。市面上可选的目标检测算法不少,两阶段的Faster R-CNN、单阶段的SSD和YOLO系列,我最终选了YOLOv5,原因很直接。
YOLO系列发展到v5这代,工程化程度已经非常高。相比Faster R-CNN那种"先提候选区域、再逐个分类回归"的两阶段方案,YOLOv5把检测当成一个回归问题,一次前向推理直接输出所有目标的位置和类别,速度优势非常明显。对于头盔检测这种需要实时响应的场景,推理速度是硬指标,一秒钟处理几十帧画面才有实用价值。
另外,YOLOv5的代码结构在同期开源项目里属于最清晰的那一档。模型定义、训练流程、数据加载、推理脚本全部模块化,改配置就能换模型尺寸和数据路径,这对我们这种要把项目真正落地、后期还要根据不同摄像头场景反复调优的团队来说,太重要了。你想换YOLOv5s还是YOLOv5m,改一行参数就行,不用动代码。
还有一个关键因素:预训练权重。YOLOv5官方提供了在COCO数据集上预训练好的权重,虽然COCO里没有"头盔"这个类别,但模型已经学到了通用的特征提取能力——边缘、纹理、形状这些底层特征,我们只需要在它的基础上用自己的头盔数据集做微调,训练时间和数据量需求都能大幅降低。这跟让一个见过几万种物体的人去学认一个新物种,比让婴儿从零开始学要快得多,是一个道理。
1.2 深度学习框架选型:PyTorch为什么顺手
框架选了PyTorch,基本没纠结。一个项目里框架的选择往往决定了开发效率的上限,PyTorch最大的优势是动态计算图,模型结构可以在运行时改变,调试的时候你能随时打印每一层的输出形状和梯度信息,遇到问题可以非常直观地定位。
举个例子,训练过程中如果某一层的输出维度对不上,PyTorch会直接在报错信息里告诉你张量的形状和预期值,配合print或者pdb断点,几乎不需要猜。相比之下,静态图框架是"先构图、后执行",调试时候的反馈链路会绕很多。对深度学习的初学者和中等水平开发者来说,PyTorch的学习曲线也友好得多,社区里能找到的海量教程和开源代码大部分都是PyTorch版本,遇到问题一搜基本都有答案。
还有一点是硬件生态。我们当时训练用的就是NVIDIA的GPU,PyTorch对CUDA的支持非常成熟,从显卡驱动到CUDA Toolkit再到PyTorch版本,官方文档给出了完整的对应关系。训练时一个model.cuda()就能把网络搬到GPU上,nvidia-smi可以实时查看显存占用,非常顺手。
"为什么用PyTorch而不用TensorFlow"这个问题,我自己的答案很朴素:YOLOv5官方源码本身就是基于PyTorch实现的,选框架的第一个原则是跟随项目生态,能直接跑通、能快速迭代比什么都重要。框架本身只是个工具,好用、社区活跃、坑少,就是最大的竞争力。
1.3 系统整体架构与工作流程
整个头盔检测系统可以拆成离线训练和在线推理两条链路。离线训练链路是:数据采集和标注、数据集划分、模型训练、效果评估,产出一个训练好的权重文件。在线推理链路是:读取图片或视频流、送入模型做前向推理、后处理(非极大值抑制)、在画面上绘制检测框和置信度、根据检测结果触发告警逻辑。
两条链路都围绕同一套模型文件运转,这也是我比较推荐的落地方式。训练和推理解耦以后,你可以在训练阶段随意折腾模型结构和超参数,一旦权重固定下来,推理端就可以做成一个轻量服务,比如用Flask对模型做一层封装,再对接监控系统的告警模块。
我们当时的告警逻辑做了个简单的业务规则:检测到"人"但没检测到"头盔",且人的检测框与头盔检测框没有重叠,就判定为"未佩戴",触发记录和提醒。这里有个细节,直接用"人"和"头盔"两个类别的检测结果做逻辑判断,比单独训练一个"佩戴/未佩戴"分类模型要灵活得多,因为画面里人很多,逐个分类会增加漏检风险,而目标检测可以同时输出所有人的位置和属性,规则也好调整。
2. 环境搭建:从零配置PyTorch与YOLOv5
2.1 深度学习环境的三层结构
环境搭建往往是最劝退新手的一步,很多人在这一步就被各种版本冲突搞得心态崩溃。我把深度学习环境拆成三个层级来理解,会清晰很多。
最底层是驱动和CUDA。显卡驱动是操作系统和GPU硬件之间的桥梁,CUDA Toolkit是NVIDIA提供的并行计算平台,PyTorch在GPU上跑,底层就是调用CUDA来操作GPU算力。往上走是Python环境,我强烈建议用Anaconda来管理,每个项目创建独立的虚拟环境,互不干扰。最上层才是PyTorch框架本身。
一个常见的误解是"CUDA装得越新越好"。其实不是,关键是驱动版本要支持你选的CUDA版本,而PyTorch版本又要对应匹配某个CUDA版本。这块我踩过一次坑:有台机器装了很新的驱动,我直接装了最新版CUDA,结果那个版本的PyTorch还不支持,又得重来一遍。后来我学乖了,一律按PyTorch官方安装命令里指定的CUDA版本来驱动驱动的安装。
用nvidia-smi查看显卡驱动版本,右上角的CUDA Version表示该驱动最高支持的CUDA版本,只要你的CUDA Toolkit版本不高于这个数字,基本就没问题。然后去PyTorch官网选对应组合,官网会给出安装命令,直接复制执行即可。
2.2 PyTorch GPU版本的安装与验证
我们当时的安装路线是在Ubuntu 22.04上配置的,命令大概是这样的:
conda create -n helmet python=3.9 conda activate helmet pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这里有两个点要特别说明。第一,创建环境时Python版本选了3.9,不是最新的3.11或者3.12,原因是YOLOv5的依赖库(尤其是numpy、opencv-python这些)在3.9下兼容性最稳。深度学习项目的依赖往往很敏感,Python小版本升级可能导致某个库没有对应的预编译包,所以建议用版本适中、生态成熟的Python环境。
第二,PyTorch的安装命令里cu118表示CUDA 11.8版本。为什么选11.8而不是更高的12.x?因为当时YOLOv5的官方测试环境就是11.8,社区反馈这个组合下各种算子都稳定,没必要追求最新。
安装完成之后一定要验证GPU能不能正常调用,我一般会跑这三行:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果输出True和显卡型号,说明PyTorch已经能正常调用GPU。这一步验证不能省,很多人装完就急着训练,结果模型一直在CPU上跑,速度慢得离谱,还以为是代码问题。
2.3 获取YOLOv5源码与安装依赖
PyTorch环境就绪后,接着获取YOLOv5源码。这里我建议直接用Git拉官方仓库,后续要更新代码或者切换版本也方便:
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txtrequirements.txt里包含了YOLOv5运行所需的全部依赖,包括numpy、opencv-python、matplotlib、seaborn这些。有些教程会让你一条条手动安装,没必要,一条命令搞定。
装依赖的时候有个小坑:默认PyPI源在国内拉取很慢,有些大包(比如opencv-python)动辄几十兆,容易超时。我的做法是直接用国内镜像源,把pip下载地址切到清华源或者阿里源,速度能快一个数量级:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple环境装好后,可以直接跑一下官方自带的推理脚本,用预训练权重yolov5s.pt去检测自带的测试图片,确认整个链路是通的,再开始准备自己的数据。这一步相当于"验收环境",确保问题不堆积到后续环节。
3. 头盔数据集准备与标注
3.1 数据集来源与规模规划
深度学习是"数据喂出来的",模型效果好不好的下限由数据质量决定。头盔检测的数据集有两个主要来源:一是公开数据集,二是自己采集标注。我们在项目初期先用公开数据集把整套训练流程跑通,后期再针对实际场景补充自采数据。
公开数据集里有几个可以重点关注:SCUT-HEAD,这是华南理工大学开源的头部检测数据集,包含超过4000张标注好的头部图像;还有其他研究者发布的建筑工地安全帽佩戴检测数据集,标注了"head"和"helmet"两个类别。我当时的做法是把几个公开数据集合到一起,做了一个大约8000张图的初始数据集,类别就两类:helmet和head。
这里有个很关键的经验:不要贪多求全。头盔检测的类别设计越简单越好,我见过有人把类别分得很细,什么"红色安全帽""白色安全帽""蓝色安全帽",结果每个类别的样本量都不够,模型训练出来mAP惨不忍睹。头盔就是头盔,检测目标就是"有没有戴",类别分多了只会增加模型的负担。
数据集规模上,头盔检测这类任务,两张类别每类3000到5000个标注实例,已经能训练出一个可用的模型。如果完全从零开始训练没有预训练权重,可能需要更多数据,但我们用了YOLOv5的COCO预训练权重做迁移学习,数据需求量就小很多。
3.2 数据标注:labelImg标注头盔类别
数据标注是项目里最枯燥但最重要的一环,直接决定模型精度的上限。我们用的标注工具是labelImg,一个基于Python的图形化标注工具,体积小、操作简单、跨平台,标注结果直接保存为YOLO格式的txt文件。
安装labelImg很简单:
conda install pyqt=5 pip install labelImg labelImg打开工具后,用Open Dir打开图片文件夹,然后通过Change Save Dir设置标注文件保存路径,再在左侧选择类别,最常用的快捷键:W是创建标注框,A和D切换上一张/下一张图,Ctrl+S保存。
标注的核心原则是"框得准、不要漏"。画框的时候框体要紧贴目标边缘,稍微留一点点边距可以,但不要框得太大把背景包进去,也别框得太小截断了目标的一部分。一个容易忽略的细节是,画面里非常小、非常模糊的目标也要尽量标注,因为现实场景中摄像头距离远的时候,目标就是很小的一块,如果训练数据里全是清晰的大目标,模型在真实场景下的召回率会很低。
3.3 数据集目录组织与data.yaml配置
标注完成后,数据集要按YOLOv5要求的目录结构组织起来。YOLOv5的数据加载逻辑是:images目录放图片,labels目录放标注文件,图片和标注文件通过文件名一一对应,后缀不同(.jpg和.txt),名称相同。
目录结构如下:
helmet_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 └── labels/ ├── train/ # train图片对应的标注txt └── val/ # val图片对应的标注txt训练集和验证集的划分我习惯按9:1或者8:2来,但划分之前必须做一步"去重"。因为很多公开数据集的图片往往是从视频里逐帧截取的,连续帧之间相似度极高,如果不做去重,模型会在验证集上表现很好,到了真实场景就"打回原形",这就是过拟合的典型表现。我的做法是用脚本计算图片的感知哈希,把相似度高的图片归到同一个集合里,确保训练集和验证集的内容足够独立。
标注文件的内容是YOLO格式的纯文本,每一行代表一个目标对象,格式为类别id x_center y_center width height,四个坐标值都是归一化到0到1之间的比例值。比如0 0.5125 0.3452 0.2341 0.1876表示一个类别为0的目标,中心点在图片的横向51.25%、纵向34.52%的位置,宽高分别占整图的23.41%和18.76%。
最后配置一个data.yaml文件,内容很简单:
train: /path/to/helmet_dataset/images/train val: /path/to/helmet_dataset/images/val nc: 2 names: ['helmet', 'head']nc是类别数,names是类别名称列表,顺序必须跟标注文件里的类别id一致。这个data.yaml在训练时用--data参数指定,YOLOv5会自己读取。
4. 模型训练:参数理解与效果调优
4.1 从卷积到检测:YOLOv5核心结构速览
训练之前,哪怕不打算改模型结构,也建议把YOLOv5的网络结构过一遍,否则调参的时候会一头雾水。YOLOv5整体可以分成三块:Backbone、Neck和Head。
Backbone负责提取图像特征,使用的是CSPDarknet结构。它的特点是引入了Cross Stage Partial结构,把特征图分成两部分,一部分走卷积层做特征提取,另一部分直接连接到后面,既减少了计算量,又解决了深层网络梯度消失的问题。你可以把它理解为一条"高速公路",信息既能走铺满细节的小路,也能走无障碍的直达通道。
Neck部分用的是PANet结构,它的作用是融合不同尺度的特征。头盔有大有小——近处的头盔占半个屏幕,远处的头盔可能就十几个像素。PANet通过自顶向下和自底向上的双向路径,把浅层的细节信息和深层的语义信息融合在一起,让模型对小目标更敏感。
Head部分输出三个不同尺寸的检测结果,分别对应大、中、小目标。这也是YOLO系列的经典设计,每个网格单元预测若干个锚框,每个锚框输出位置偏移量、宽高、置信度和类别概率。
至于"深度学习的池化"这个知识点,在YOLOv5中也扮演了角色。SPP(Spatial Pyramid Pooling)模块在Backbone的最后一层使用不同池化核尺寸对特征图做池化,再把结果拼接起来,目的是增强模型对不同尺度目标的感受野。简单说,池化就是"压缩信息、扩大视野",让网络从更大范围去理解一个目标是什么。
4.2 训练超参数:每一行配置背后的逻辑
训练头盔检测模型,最常用的是YOLOv5s这个尺寸的模型。yolov5s是官方提供的五种尺寸中最小的,速度最快,精度也不错。对头盔检测这种目标特征相对简单的任务,用yolov5l甚至yolov5x属于杀鸡用牛刀,训练时间长、推理速度慢,精度提升却很有限。
启动训练的命令:
cd yolov5 python train.py --data helmet.yaml --cfg yolov5s.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640几个关键超参数我逐个说一下。
--epochs是训练轮数。100轮是一个比较合理的起点,YOLOv5训练过程中会自动保存效果最好的权重,所以轮数多一点不用担心过拟合,配合早停机制就行。我见过有人只训练30轮就停下来用,效果好不到哪去,因为模型还没充分收敛。
--batch-size是批次大小。这个参数受限于显存,显存不够就调小一点。但注意batch-size大小会直接影响训练效果,太大的batch会让模型收敛不稳定,太小的batch又会导致梯度估计噪声大。
--img是输入图片的尺寸。默认640x640,这个尺寸对大多数场景够用。头盔检测如果主要看监控画面,目标通常比较小,可以考虑用更大尺寸比如960或者1280来训练,对小目标检测有提升,但对应的显存消耗和推理耗时也会增加。
还有一组藏在data/hyps/hyp.scratch-low.yaml里的超参数,它们是深度学习训练中容易被忽略但很关键的细节。比如lr0是初始学习率,momentum是动量,weight_decay是权重衰减系数。这些参数YOLOv5官方已经调得比较合理,新手不建议动,等你跑完一轮训练,对损失下降曲线有感觉了,再来调学习率也不迟。
4.3 启动训练与过程监控
训练跑起来后,不是干等着,要会看训练日志。YOLOv5每个epoch结束会输出一组指标,包括box_loss、obj_loss、cls_loss和mAP。
box_loss代表预测框位置和真实框的误差,这个值应该在训练中逐渐下降;obj_loss代表目标置信度误差,也需要下降;cls_loss是分类误差,对头盔检测来说前期会降得比较快,因为两类区分度很高。mAP@0.5是最直观的指标,表示IoU阈值0.5下的平均精度均值,头盔检测任务一般能训练到0.85以上,就算非常可用的模型了。
训练过程中有一个现象第一次遇到可能会慌:损失值在初始几个epoch会跳来跳去,甚至短暂上升,这是正常的。因为迁移学习初期,模型在快速调整底层特征,损失波动不代表训练失败。我的经验是前20轮不用频繁关注指标,到30轮以后再看趋势才有意义。
显存不足的情况在训练中很常见,报错通常是CUDA out of memory。最简单的处理是调小--batch-size,从16降到8或者4,如果还是不足就换小尺寸的模型。另外可以在训练命令中加上--cache参数,把数据预加载到内存中,能减少GPU和CPU之间的数据搬运等待,但并不降低显存占用。
4.4 训练结果评估:mAP、PR曲线怎么读
训练结束后,YOLOv5会在runs/train/目录下生成实验记录,里面有results.png、confusion_matrix.png、PR_curve.png等图表,这些图对一个深度学习项目来说就是"体检报告"。
PR_curve.png是精度-召回率曲线。精度(Precision)衡量的是"模型预测为头盔的框里,有多少真正的头盔",召回率(Recall)衡量的是"真实头盔中有多少被模型找出来了"。这两个指标天然矛盾,鱼和熊掌不可兼得。PR曲线与坐标轴围成的面积就是AP值,面积越大表示模型效果越好。头盔检测这种安全相关的场景,我个人更看重召回率——漏检一个没戴头盔的人,比多误报几次更严重。所以后期调参时可以适当降低置信度阈值,提高召回率。
confusion_matrix.png是混淆矩阵,能直观看出模型在哪两类之间混淆。对头盔检测来说,最常见的混淆就是"头盔"和"头部"互相误判,比如把戴浅色帽子的人的头误判为头盔。如果混淆矩阵里这两类的交叉区域比较大,说明训练数据里"头盔和头部"的外观特征区分度不够,需要补充更多难例数据。
还有一个容易被忽视的评估指标是各类别的AP单独值。YOLOv5训练日志里会按类别分别输出AP,如果helmet类AP很高但head类AP很低,说明数据集中head类的样本不够或者标注质量有问题,需要针对性补充。
5. 实时检测:从模型到应用
5.1 使用训练好的权重进行图片和视频推理
训练完成后,最佳权重保存在runs/train/exp/weights/best.pt。使用这个权重做推理很简单:
python detect.py --weights runs/train/exp/weights/best.pt --source data/images --conf-thres 0.5--source既可以是图片文件夹,也可以是一个视频文件路径,甚至是摄像头设备的索引号,比如--source 0表示读取本机第一个摄像头。
推理时的一个关键调整参数是--conf-thres,即置信度阈值。这个值的设置直接影响系统的误报和漏报:阈值设高(比如0.7),模型只输出有把握的结果,误报少但容易漏检;阈值设低(比如0.3),漏检少但误报多。我建议头盔检测场景设0.4到0.5之间,兼顾两种情况。
detect.py跑完后,结果图片和视频会保存在runs/detect/目录下,检测框会标注类别名称和置信度。这里有个小技巧:可以给不同类别设置不同颜色和线宽,让"未佩戴头盔"的人脸框显示为红色醒目提示,而"已佩戴"的框显示为绿色,这对接实际业务体验很重要。
5.2 实时视频流检测与部署
detect.py默认方式是对视频逐帧检测然后保存结果,但真正的实时检测系统需要把模型集成到自己的代码流程里。核心思路是先加载模型,然后循环读取视频帧,每帧做一次前向推理。
一个简化的检测循环框架:
import torch import cv2 model = torch.hub.load('ultralytics/yolov5', 'custom', path='runs/train/exp/weights/best.pt', force_reload=True) model.conf = 0.5 model.iou = 0.45 cap = cv2.VideoCapture(0) # 读摄像头或视频流 while True: ret, frame = cap.read() if not ret: break results = model(frame) # results.pandas().xyxy[0] 包含所有检测框的坐标、类别和置信度 # 在这里编写告警逻辑:有人但无头盔 -> 发送告警 rendered = results.render()[0] cv2.imshow('Helmet Detection', rendered) if cv2.waitKey(1) & 0xFF == ord('q'): breaktorch.hub.load是PyTorch提供的一个非常方便的功能,可以从本地加载自定义训练好的模型,不需要手动构造网络结构。模型加载后,通过设置model.conf和model.iou控制置信度阈值和NMS的IoU阈值,这也是在线部署时最常用到的两个后处理参数。
实时检测的实测性能需要心里有数。YOLOv5s在NVIDIA GTX 1660 Ti显卡上,640x640输入,推理速度大概在3到5毫秒一帧,也就是每秒可以处理200到300帧,实时性完全不是瓶颈。真正的瓶颈往往在视频解码和显示环节,cv2.VideoCapture读RTSP流时延迟可能达到几百毫秒,这是摄像头侧和网络侧的问题,跟模型无关。
5.3 模型导出与边缘设备部署
模型训练好之后,如果只在自己电脑上跑演示没问题,但实际项目大多要部署到边缘设备或者服务器上。YOLOv5官方支持将模型导出为多种格式:ONNX、TensorRT、CoreML、OpenVINO。
导出ONNX格式的命令:
python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640导出ONNX的意义在于跨平台部署。ONNX是一种开放的模型表示格式,相当于深度学习的"通用语言",很多推理引擎和边缘设备芯片(比如RK3568、Jetson系列)都支持加载ONNX模型。我们在项目中用了瑞芯微的RK3568平台做边缘部署,流程是:PyTorch训练 -> 导出ONNX -> 转换成RKNN格式 -> 在板子上跑推理。
导出时有个细节需要留意:--img 640要和训练时的输入尺寸一致,否则导出后模型可能因为输入尺寸不匹配而报错或者精度下降。另外,如果想在TensorRT上部署,建议先导出ONNX再转TensorRT引擎,而不是直接导出,因为兼容性更好。
Jetson设备上部署PyTorch模型是另一个常见需求。注意Jetson平台使用的JetPack版本决定了支持的PyTorch版本,比如JetPack 6.x一般对应PyTorch 2.x,安装时不要用桌面版通用的pip命令,而是参考NVIDIA官方针对Jetson提供的预编译wheel包,这样能避免很多兼容性问题。
6. 常见问题与排查技巧实录
6.1 训练不收敛的排查思路
训练过程中最让人崩溃的就是loss不降,或者降着降着突然变成NaN(非数值),然后训练崩了。这个问题我遇到过不止一次,原因是多方面的,排查顺序建议按照"数据 -> 超参数 -> 环境"来。
先说数据层面。检查标注文件里有没有异常的框,比如坐标值超过1、宽高为0这种脏数据。YOLO格式要求归一化坐标在0到1之间,如果标注工具或脚本出了问题,某个坐标写成了5.7,模型训练时梯度就会爆炸。我有一个脚本专门扫描数据集里的非法标注,跑一遍就能发现并剔除问题样本。
其次是学习率。如果loss前几轮就出现NaN,大概率是学习率太大。YOLOv5的默认初始学习率是0.01,如果你的数据集很小,可以把hyp.scratch-low.yaml里的lr0调到0.005再试。学习率调小了一般能解决,代价是收敛速度变慢,多跑几轮就行。
环境层面的问题比较隐蔽。有一种情况是GPU驱动和CUDA版本不匹配导致某些算子计算异常,表面上看loss一切正常,但训练到中途就开始跳NaN。检查方法是先用官方模型跑一个小的训练任务,比如只训练5轮、只用100张图,如果官方模型也崩,那问题就不在你自己的数据和代码,而是环境层面,回退CUDA或者PyTorch版本重试。
6.2 显存不足的解决路径
CUDA out of memory是新手最常遇到的报错,看到一堆红色报错信息容易慌,其实解决路径很清晰。
第一步把batch-size调小。训练命令中--batch-size 16改成8,如果还爆就改成4。batch-size是显存占用的主要因素,调小它效果立竿见影。第二步是换小模型,YOLOv5s已经是小模型了,如果用的yolov5m或者更大,换回yolov5s。第三步是减小输入图片尺寸,--img 640改成--img 416,显存占用会下降不少,但检测精度也会相应降低。
这里说一个实用技巧:训练前不要开启太多无关程序。我在实际项目中发现,开着浏览器训练,显存占用会多出几百兆,对于显存比较小的显卡来说可能就是压垮骆驼的最后一根稻草。用nvidia-smi查看显存占用,确保训练前GPU是干净状态。
6.3 检测漏检和误检的处理
模型训练完,放到真实场景一测,通常会发现两个问题:漏检(该检测出来的没检测出来)和误检(把不是头盔的当成头盔)。
漏检的处理有几个方向。第一,降低置信度阈值,模型输出更多候选框,漏检自然少。第二,检查是不是目标太小,如果是远距离的目标检测不到,考虑用更大尺寸训练或者提升图像的采集分辨率。第三,补充更多遮挡、模糊、暗光环境下的训练数据,这是提升模型泛化能力最根本的手段。
误检的处理思路不同。误检通常是模型把和头盔外观相似的物体(比如圆的铁桶、白色管道、安全帽形状的装饰物)识别成了头盔。处理方法也是三个方向:提高置信度阈值、补充"难负样本"(把容易误检的图片加入训练集但标注为空,告诉模型"这些不是头盔")、在业务逻辑上增加过滤条件。另外,混淆矩阵能帮你定位到具体是哪两类在互相干扰,针对性地加数据往往比盲目调参更有效。
6.4 实测踩坑汇总
整理几个实操中容易踩的坑,每一个都是我付出过时间成本换来的。
第一个坑:验证集效果很好,一到真实场景就"翻车"。原因基本是训练数据和真实场景分布不一致。比如训练数据全是白天、晴天、正面拍摄,真实场景却是傍晚逆光、远距离俯拍。解决办法是数据采集时尽可能贴近真实部署环境,模拟各种光照和角度。
第二个坑:用OpenCV读取RTSP视频流时经常断流或者延迟很大。这个不完全是模型的问题,我后来把RTSP的传输协议从rtsp改成rtspt(TCP传输),稳定性有明显提升。还有就是在读取视频帧的循环里加一个缓存队列,避免因为处理速度跟不上导致画面延迟累积。
第三个坑:多个摄像头同时检测时,单卡GPU算力不够。我们的对接方案是GPU分时复用或者把推理任务拆到多块GPU上,每块GPU负责一路摄像头的检测。如果设备端算力有限,还可以降低每秒处理的帧数,比如每两帧抽一帧检测,对头盔检测这种安全场景来说,每秒5帧左右的检测频率已经足够及时发现违规行为。
第四个坑是数据泄露。这个比较隐蔽,我在一次复盘中发现某个类别的验证集AP异常高,排查后意识到训练集和验证集里出现了同一段视频的连续帧,模型相当于"记住了"答案而不是学会了识别。后来我就坚持做去重,绝不让验证集内容混入训练集。这个原则看起来简单,实际操作中非常容易疏忽。
最后再分享一个小技巧:训练完不要急着删掉所有实验记录,runs/train/下的每个exp目录都保留。后面做模型迭代和效果对比时,有历史实验做参照,你能很清楚地知道哪个改动带来了收益、哪个改动的效果是负面的。我做头盔检测这个项目,前后产生了二十多个实验记录,最后能快速锁定最佳模型,靠的就是这套"留痕"习惯。
头盔检测这个方向的技术难度不算高,但真正落地时要考虑的东西不少,从数据分布到推理性能,从告警逻辑到设备适配,每个环节都值得花时间打磨。这套基于YOLOv5和PyTorch的方案已经在我们几个项目里稳定跑了很久,如果你也在做类似的安全检测场景,可以参考这套流程直接复现,然后在数据层面多下功夫,效果应该不会让你失望。
本文还有配套的精品资源,点击获取