简介:目标检测是计算机视觉的核心任务之一,其原理是在图像中定位并分类多个物体。在施工安全监测场景中,安全帽检测与反光衣识别是典型的落地需求,要求算法在实时性与精度之间取得平衡。YOLO作为单阶段检测器的代表,凭借高效推理和成熟生态,成为此类工程项目的首选框架。然而,实际应用效果往往取决于数据质量与训练流程。以一份4314张图像的安全帽与反光衣数据集为例,从解压后的标签格式校验、类别语义梳理,到训练集划分避免数据泄漏,再到基于迁移学习的YOLOv8模型调参,每一步都影响最终部署性能。通过合理的清洗、增强与阈值调整,中小规模数据集也能训练出可靠的防护穿戴检测模型,为工地、工厂等场景的智能监管提供支撑。 搞工地的、搞工厂的、搞电力巡检的朋友,这几年基本都绕不开一个需求:用摄像头自动检查工人有没有戴安全帽、穿反光衣。我最近在处理一份YOLO算法的安全帽与反光衣数据集,4314张图像带标签,里面还包含靴子、头盔、背心这些类别。这东西看起来就是一个.zip压缩包,但真正用起来,从解压到训练、从评估到部署,每一步都有门道。这篇博文我就把自己完整走一遍的流程和踩过的坑写出来,给同样准备上手安全防护穿戴检测的朋友做个参考。
这数据集不大不小,4314张图放到目标检测里属于典型的“场景聚焦型中小数据集”,配合预训练权重做迁移学习,是够用的。不过它能不能用、好不好用,取决于你在训练前怎么对待它。下面我按实际操作的顺序,把这套流程一点点拆开讲。
1. 施工安全监测为什么是YOLO的黄金落地场景
1.1 安全帽、反光衣检测的业务逻辑
先想清楚一个问题:安全帽检测这个需求,最终要识别的到底是什么?
表面上是“识别画面里有没有安全帽”,但落到业务现场,监控系统真正想报的是“画面里那个人没戴安全帽”。这是一句听起来没什么差别、实际差别很大的话。前者是目标分类,后者是目标检测加上触发告警规则,比如“人体区域上方没有安全帽框”这种关系判断。很多刚从分类任务转过来的新手,容易忽略这层业务语义,导致模型训出来之后,报警逻辑不知道怎么接。
反光衣同理。施工现场、高速公路养护、港口码头、化工厂区,监管方要求进入作业区域的人员必须穿反光衣。这类需求在行业里已经非常成熟,网上随便一搜能找到大量公开数据和现成案例。而YOLO系列之所以成为这个场景的绝对主力,不是因为它精度世界第一,而是它在“精度够用”和“速度足够快”之间找到了最适合工程落地的平衡点。
1.2 为什么是YOLO而不是其他检测框架
YOLO是单阶段检测器,一次前向推理直接输出目标类别和边界框。相比之下,Faster R-CNN这类两阶段方法要先做候选区域提取,再做分类回归,精度上限通常更高,但速度差了一个数量级。施工现场是什么环境?摄像头数量多、机位固定、画面变化不算剧烈,但每一路画面都要实时分析。边缘计算盒子或者后端GPU服务器同时要扛住几十路视频流,这时候YOLO的低延迟优势就是决定性的。
另一个原因是社区生态。YOLOv5、YOLOv8的教程铺天盖地,命令行工具封装得很好,一个新手拿到带标签的YOLO格式数据集,按照文档敲两条命令就能开始训练。遇到报错,搜索一下基本都能找到解决方案。对于做工程落地的人来说,这种成熟度比某些论文里精度高0.5个点但代码没人维护的模型要可靠得多。
1.3 4314张在这个任务里到底算不算够用
经常有人问:4314张图是不是太少?我的判断是:看场景复杂度。
如果你的目标是做通用物品检测,比如COCO那80类,那你确实需要几万张甚至十几万张图。但安全帽、反光衣、靴子、头盔、背心这五类目标,外观相对固定,场景集中在施工区域,背景复杂度比开放世界低得多。4314张图配合ImageNet或COCO预训练权重,再叠加mosaic、mixup、随机仿射这些数据增强手段,训练出一个工程可用的模型是完全现实的。
当然,这里有个前提:4314张图的质量必须靠谱。如果其中有大量错标、漏标、类别语义混乱的标注,那别说4314张,就算给你四万张也是白搭。所以整个流程里,训练前的数据清洗反而是最重要的环节。
2. 解压后的第一件事:核对目录、标签格式与类别分布
2.1 先看目录和文件命名,别急着开训
拿到zip压缩包,第一件事不是解压完就直接扔给训练脚本,而是先把目录结构看清楚。常见的YOLO数据集组织方式无非两种:
# 方式A:全部文件平铺 dataset/ ├── images/ │ ├── 00001.jpg │ ├── 00002.jpg │ └── ... └── labels/ ├── 00001.txt ├── 00002.txt └── ... # 方式B:已经划分好训练集和验证集 dataset/ ├── images/ │ ├── train/ │ ├── val/ └── labels/ ├── train/ ├── val/如果你的压缩包是方式B,恭喜你省了不少事,但我还是要建议你确认一下它划分得是否合理。如果压缩包是方式A,那训练集和验证集的划分就需要自己来做,后面第3节我会详细讲怎么划分才不容易翻车。
另外一个高优先级操作是确认图片和标签文件是否一一对应。用一段小脚本扫描一下:
from pathlib import Path for subset in ['train', 'val']: img_dir = Path(f'images/{subset}') label_dir = Path(f'labels/{subset}') img_names = {p.stem for p in img_dir.glob('*.jpg')} label_names = {p.stem for p in label_dir.glob('*.txt')} print(f'[{subset}] 图片数量: {len(img_names)}, 标签数量: {len(label_names)}') print('有图片没标签:', len(img_names - label_names)) print('有标签没图片:', len(label_names - img_names))我见过不少数据集解压出来之后,莫名多了几个空白txt,或者有几张图对应的标签文件丢了。这种情况你不提前扫一遍,训练时lightning网络会报各种奇怪错误,浪费一晚上时间排查。
2.2 YOLO标签格式检查:5列数据和归一化范围
YOLO格式的标签是纯文本,每行代表一个目标,格式固定为:
<class_id> <cx> <cy> <width> <height>其中cx、cy是目标中心点的归一化坐标,width、height是目标宽高的归一化值,四个数值都在 0 到 1 之间。比如某一行是0 0.5 0.5 0.2 0.3,意思就是类别0的目标,中心点在图片正中,宽度占图片的20%,高度占30%。
这里有个非常隐蔽的坑:某些标注工具输出的标签坐标范围并不是0到1,而是0到图片像素尺寸。如果直接把这类标签当YOLO格式用,训练出来的模型会完全无法收敛。所以我拿到数据集之后,一定会跑一遍格式合法性检查:
from pathlib import Path def check_labels(label_dir): errors = [] class_counter = {} for label_file in label_dir.glob('*.txt'): for line_num, line in enumerate(label_file.read_text().strip().splitlines(), 1): parts = line.split() if len(parts) != 5: errors.append(f'{label_file}:{line_num} 列数不为5') continue cls_id, cx, cy, w, h = parts try: cls_id = int(cls_id) cx, cy, w, h = map(float, (cx, cy, w, h)) except ValueError: errors.append(f'{label_file}:{line_num} 数值无法解析') continue if not (0 <= cx <= 1 and 0 <= cy <= 1): errors.append(f'{label_file}:{line_num} 中心点坐标超出[0,1]') if w <= 0 or h <= 0: errors.append(f'{label_file}:{line_num} 宽高不为正数') class_counter[cls_id] = class_counter.get(cls_id, 0) + 1 return errors, class_counter errors, class_counter = check_labels(Path('labels/train')) print('异常标签数:', len(errors)) for err in errors[:10]: print(err) print('类别分布:', class_counter)这段脚本会帮你在训练前揪出绝大多数标签格式问题。千万别省这一步,YOLO对标签格式的容错率很低,一个异常标签轻则导致该样本失效,重则让loss直接跳到NaN。
2.3 类别统计与语义边界:安全帽/头盔、反光衣/背心不能糊涂
标题里同时出现了“安全帽”“头盔”“反光衣”“背心”这些词,这就涉及一个很实际的语义边界问题。
在中文语境里,“安全帽”和“头盔”经常被人混着说。但严格意义上,工地用的安全帽是硬质带帽檐的防护帽,摩托车头盔、消防头盔、安全头盔在形状上有明显区别。很多公开数据集会用hardhat和helmet同时作为标签,这两类在标注时极其容易混淆。同一顶帽子,今天这个标注员标成hardhat,明天那个标注员标成helmet,模型训练的时候就会学得一头雾水。
反光衣和背心也是类似。有些工人穿的是带高亮反光条的荧光背心,有些穿的是普通工作背心,如果原始标签把两者拆开了,你要先想清楚业务上到底需不需要区分。如果现场只要报警“没穿反光衣”,那这两类合并成一个“反光衣/背心”类别,反而能让模型学得更稳定。
拿到数据集后第一件事,就是抽几十张标签图,把每个类别的实际样例看一遍。用代码把每个类别的图片各抽5到10张拼成一张大图,人工过一眼,确认类别定义是否和你心里的预期一致。不一致的,趁早改标注或者合并类别,别等模型训到一半才反应过来。
2.4 可视化验证:画框截图比什么都直观
格式校验通过、类别语义确认之后,还差一个最直观的检查:把标注框画到图片上,肉眼看。这一步能发现很多脚本发现不了的问题,比如:
- 标注框明显偏离目标,框住了一半背景
- 两个人靠太近,一个框框住了两个人
- 目标特别小,标注框只有几个像素
- 该框的目标漏标了
写一个简单的画框脚本:
import cv2 from pathlib import Path image_dir = Path('images/train') label_dir = Path('labels/train') output_dir = Path('visual_check') output_dir.mkdir(exist_ok=True) class_names = {0: 'safety_helmet', 1: 'reflective_vest', 2: 'boots', 3: 'helmet', 4: 'vest'} for label_file in sorted(label_dir.glob('*.txt'))[:50]: image_path = image_dir / f'{label_file.stem}.jpg' if not image_path.exists(): continue img = cv2.imread(str(image_path)) if img is None: continue h, w = img.shape[:2] for line in label_file.read_text().strip().splitlines(): parts = line.split() if len(parts) != 5: continue cls_id, cx, cy, bw, bh = map(float, parts) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names.get(int(cls_id), str(cls_id)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(str(output_dir / f'{label_file.stem}.jpg'), img)生成的前50张图花两分钟过一遍,你心里就有底了。我个人的经验是,可视化抽检发现问题后,不要急着重新标注所有数据,先把问题分个类:是某个类别的边界定义问题,还是个别样本的标注质量问题,还是框的大小普遍偏小。不同问题的处理方式完全不同。
3. 训前清洗与数据划分:模型的上限在这里决定
3.1 需要重点处理的异常标签
格式检查脚本能抓出坐标越界之类的问题,但有些问题脚本看不出来,只能靠规则或者可视化人工判断。我整理了一个清洗清单,按优先级排列:
| 优先级 | 问题类型 | 处理方式 |
|---|---|---|
| 高 | 标签文件为空或图片无标签 | 删除对应图片 |
| 高 | 图片文件损坏、无法读取 | 删除对应样本 |
| 高 | 坐标越界、宽高为零或负数 | 修复或删除该标签 |
| 中 | 画框后明显偏移目标实际位置 | 人工修正或删除 |
| 中 | 目标过小,框小于图片面积的0.5% | 视场景决定是否保留 |
| 低 | 图片严重模糊、剧烈运动模糊 | 删除或保留作为困难样本 |
这里说一句容易得罪人的话:很多公开数据集并没有做过严格的清理,你要抱着“我不检查它就一定会给我埋雷”的心态去对待。尤其是目标检测这类任务,个别异常标签的影响会被训练过程中的增强操作放大,最后体现在模型输出上,就是毫无道理的误检。
3.2 数据泄漏:同场景图片同时出现在训练集和验证集会骗你
如果说标签格式问题是明面上的坑,那数据泄漏就是暗地里的坑,而且杀伤力更大。
很多安全帽、反光衣数据集是从监控视频里抽帧生成的。同一个工人、同一个动作、同一个背景,在视频里会连续出现几十甚至上百帧。如果做训练集/验证集划分时直接随机打乱,那验证集里大概率会出现和训练集几乎一模一样的目标。模型在训练时已经见过这个目标,验证指标自然会很好看。
这种虚高指标最大的危害是让你误判模型的真实能力,部署到新工地、新场景之后才发现性能断崖式下跌。
正确的做法是:如果数据集目录或文件名里保留了来源视频的信息,就按视频来源分组,同一个视频的画面全部进训练集或全部进验证集,不能跨组混分。如果压缩包里已经分好了train/val,那就先确认它的划分依据是不是按来源分的。很多数据集作者会直接在README里说明划分逻辑,但没人保证你下载的这个版本带README。
手动划分时,推荐用train_test_split的GroupShuffleSplit,或者干脆自己按文件前缀分组写循环。原则只有一个:验证集必须在“场景层面”和训练集分开,而不是只看单张图片。
3.3 类别不平衡:样本少的类别怎么补
五类目标在4314张图里的分布往往不是均匀的。安全帽和反光衣作为核心类别,样本数量可能超过3000,而靴子、头盔这些类别可能只有几百甚至更少。
训练时类别严重不平衡,模型会倾向于把一切都预测成那个类别样本多的类别。处理办法有几个:
- 给样本少的类别提高损失权重。Ultralytics YOLO里可以调整
cls系数,但整体上它不是按类别设权重的,更灵活的方式是修改数据集的类别损失权重逻辑,或者用带类别权重的loss变体。 - 对样本少的类别做针对性的过采样。把包含该类别的图片复制几份放进训练集,配合mosaic增强,能让模型看到更多该类目标。
- 如果差距实在太大,比如某类只有50个标注,那最务实的方案是承认当前数据不足以支撑这一类,暂时把它合并到相近类别或者干脆不训练这一类。
需要特别提醒的是:不要为了追求类别均衡而无脑复制样本,复制太多会导致过拟合。过采样的倍数控制在2到5倍比较合理,同时一定要配合数据增强。
3.4 要不要加背景负样本
另一个值得考虑的操作是往训练集里加一些完全没有目标的“背景图”作为负样本。施工现场的空镜头、未施工区域的画面,让模型学会“没有防护目标的时候就别乱输出框”。
Ultralytics YOLO对空标签文件的支持很好,只需要给这些背景图配一个空的txt文件即可。注意空txt必须是零字节文件,不能写一个只有换行符的文件,否则会被当成无类别目标处理。
我自己测试下来,加入大约5%到10%的负样本,能显著降低现场环境里“凭空出现一个安全帽框”的误检。不过这个比例也要控制,太多会把模型训练推向“所有目标都别检测”的方向,召回率反而下降。
4. yolov8/v5训练配置与调参:从默认参数到一个能用的检测器
4.1 先选模型还是先看显存
YOLO系列的模型尺寸从 n 到 x 依次变大,精度和算力需求也依次上升。对于安全帽、反光衣这类任务,我的建议是不要一上来就追求最大模型:
| 模型 | 参数量 | 推理速度 | 适合场景 |
|---|---|---|---|
| YOLOv8n | 3.2M | 极快 | 低算力边缘设备,实时性优先 |
| YOLOv8s | 11.2M | 快 | 常规边缘盒子/小服务,推荐起步 |
| YOLOv8m | 25.9M | 中等 | 后端服务,精度优先 |
| YOLOv8l | 43.7M | 较慢 | 服务器离线分析 |
先拿s跑通全流程,确认数据没问题之后,再根据效果决定要不要升到m。这比第一天就直接开l模型训练,等了十几个小时才发现数据有问题要高效得多。
显存是很硬的限制。batch size 16 跑 YOLOv8s、640分辨率,大概需要8到12GB显存。如果你的显卡只有6GB,可以 batch=8,或者把 imgsz 降到 512。imgsz 不建议低于512,因为安全帽这类目标在画面里往往不算大,分辨率太低会把小目标直接抹掉。
4.2 data.yaml是第一步,很多报错都出在这里
训练的第一步不是写训练代码,而是确认你的 data.yaml 路径没问题。Ultralytics 的 YOLO 对路径的解析很严格,因为模型训练时会根据这个文件里的路径去定位图片和标签。
一个标准的 data.yaml 长这样:
# dataset.yaml path: /home/user/safety_ppe_dataset # 数据集根目录绝对路径 train: images/train val: images/val names: 0: safety_helmet 1: reflective_vest 2: boots 3: helmet 4: vest最常见的报错就是path写错或者train/val的路径层级不对。我看过非常多的人卡在这一步,反复折腾一晚上,最后只是把路径改成绝对路径就解决了。
需要强调一个细节:Ultralytics YOLO 读取标签文件时,会从图片路径推导对应的标签路径,规则就是图片目录下的images替换成labels。所以如果你的目录结构不是images/train+labels/train这种标准布局,训练会找不到标签,直接报No labels found。
4.3 训练命令与超参数:一组能直接用的配置
数据准备好了,命令行很直接:
yolo detect train \ data=safety_ppe.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ cos_lr=True \ optimizer=SGD这段命令里几个参数解释一下:
model=yolov8s.pt表示加载COCO预训练权重。这个权重已经学到过通用物体的纹理、边缘、颜色特征,迁移到安全帽任务上能大大加速收敛。epochs=100是训练轮数,配合patience=20早停机制。如果连续20个epoch验证指标没有提升,训练会自动停止。cos_lr=True用余弦退火学习率,后半段学习率平滑下降,有利于收敛到更优的局部最优点。optimizer=SGD是我个人更偏好的选择,虽然Adam收敛更快,但SGD往往最终精度更高。
显存不够的时候,先降 batch,再降 imgsz,不要一开始就换更小的模型。batch 太小会导致梯度震荡剧烈,模型训练不稳定,所以 batch=8 是底线,低于这个值就考虑梯度累积或者换模型。
4.4 训练中看什么:loss曲线和验证指标怎么解读
训练启动后,不要干等,要会看曲线。Ultralytics 训练过程会实时输出box_loss、cls_loss、dfl_loss和验证集的mAP50、mAP50-95。
正常的训练曲线有几个特征:
box_loss和cls_loss在前20个epoch快速下降,之后缓慢下降并趋于平稳。- 验证集
mAP50前30个epoch上升明显,后面增速放缓。 - 如果训练集 loss 持续下降但验证集 mAP 停滞甚至下降,说明开始过拟合。这时候可以加大数据增强、增加负样本,或者直接提前结束训练。
- 如果 loss 从第一轮开始就不降反升,或者出现 NaN,大概率是标签格式有异常、学习率过大,或者 data.yaml 里类别数和标签文件里的类别ID对不上。
另外有个小技巧:训练完看一眼results.png,里面有完整的曲线图。不要只看最后一个epoch的 mAP,要看整体曲线趋势。如果曲线一直抖动剧烈,考虑降低学习率或者增大batch。
5. 评估与部署:mAP之外更要盯住这类场景的典型错误
5.1 混淆矩阵:安全帽和头盔不分,模型就是白训
训练完,Ultralytics 会在runs/detect/train/目录下生成confusion_matrix.png。这张图在安全帽、反光衣这类任务里尤其重要。
我见过一个模型,总体 mAP50 能到0.9,看起来已经很强了,但看混淆矩阵才发现:安全帽类别有相当一部分被预测成了头盔。原因就是训练数据里这两个类别的标注本来就互相渗透。看起来整体指标不错,但部署到现场以后,如果告警规则是“没有安全帽框就报警”,那工人明明戴了安全帽却被模型识别成头盔、报警逻辑判定为未佩戴,一天能触发几百次误报。
遇到这种情况,最有效的修复不是增加数据,而是把这两个混淆严重的类别合并成一个类别重新训练。你的业务场景如果只需要知道“有没有戴安全帽”,一个统一的“安全帽”类别,比两个互相打架的细分类别可靠得多。
5.2 现场实拍测试:小目标、逆光、遮挡、密集场景
公开数据集训练出来的模型,拿到现场之后总会有各种“水土不服”。我建议在评估阶段就专门挑几类场景做针对性测试:
- 远距离小目标:人在画面里很小、安全帽只有几十个像素时,模型还能不能召回?这类样本在4314张图里往往不多,但现场恰恰是最容易出现这类情况的时候。
- 逆光和强光:阳光直射时安全帽和反光衣容易过曝,颜色失真。YOLO对颜色特征有依赖,过度曝光后会不会漏检?
- 遮挡和密集:工人弯腰、蹲下、互相遮挡时,安全帽和反光衣被部分遮住,框的定位会不会漂移?
- 密集人群:多个工人走在一起,模型会不会把两个相邻目标框成一个?NMS参数是否需要调整。
单独准备一个“现场hard测试集”,挑个几百张包含这些场景的图,跑一遍评估脚本,统计出来了多少漏检、多少误检,比看一个冷冰冰的总mAP有用得多。
5.3 夜间和低照度:反光衣检测的老大难
如果说安全帽检测在白天场景已经比较成熟,那么夜间低照度环境就是真正的分水岭。
普通RGB摄像头在夜间拍到的画面普遍偏暗,安全帽的颜色信息大量丢失,仅靠轮廓很难稳定识别。至于反光衣,夜间反而有个有意思的现象:反光条在车灯或补光下会高亮反射,模型很容易识别,但这只在光源直接照射时成立。如果工人背对光源或者环境光太暗,反光衣同样会变成一团黑。
如果你的业务场景涉及夜间巡检,建议提前做好两个预案:要么给摄像头配补光灯或红外光源,要么单独收集夜间数据做二次微调。用纯白天数据训练的模型直接扔到夜间场景,AP下降个30个点并不罕见。这个坑我实测踩过,所以特别提醒一句。
5.4 导出ONNX/TensorRT,部署到边缘设备
模型训练完,要落地还得导出成推理引擎能用的格式。Ultralytics 提供了很简单的导出接口:
from ultralytics import YOLO model = YOLO('runs/detect/train/weights/best.pt') model.export(format='onnx', imgsz=640, half=True)half=True会导出FP16版本,体积减半、推理速度提升,代价是极小的精度损失,工程场景基本可以接受。如果需要更高的推理速度,可以继续导出TensorRT的engine格式,在Jetson设备上性能提升非常明显。
导出之后,用ONNX Runtime跑一次批量推理,确认输出结果和PyTorch推理基本一致,再接入RTSP流或本地视频文件做实时检测。要注意的是,导出后的模型输入尺寸必须和训练时保持一致,否则会报维度错误。
6. 我实际踩过的坑和几个效率技巧
6.1 标注不统一是最贵的返工
这个坑排第一,因为我为它付出过最惨痛的代价。一次项目里,我拿到一批标注好的数据,其中“反光衣”的标注规范很模糊:有的人把整件背心框进去,有的人只框荧光条区域,还有的人漏掉了被遮挡的半边身体。模型训完,在验证集上表现尚可,一上现场就一脸懵:逻辑混乱。
所以无论是你自己标注还是别人给你标注,第一批数据一定要人工仔仔细细看一遍,把标注规范钉死。比如“安全帽的框必须包含帽檐”“反光衣的框必须包含整个上衣区域”“只露出一角的目标不标”等等。这些规矩在标注阶段定好,后面能省掉无数返工时间。
6.2 数据增强不要把反光衣的颜色特征改没了
YOLO自带的数据增强里有hsv_h、hsv_s、hsv_v三个参数,默认值分别是0.015、0.7、0.4。对大多数目标检测任务,这个配置没啥问题,但反光衣比较特殊,它本身最核心的特征就是那层高饱和度的荧光色。hsv_s要是调太大,训练时把荧光绿色直接改成了灰绿色,模型反而学不到颜色特征了。
我自己的习惯是在这类安全防护穿戴任务里,把hsv_h降到0.01、hsv_s降到0.4,hsv_v保持0.4左右。牺牲一点对光线变化的鲁棒性,换取对颜色特征的稳定学习,实测下来对反光衣类别的AP是有正向帮助的。
6.3 先用几百张图跑通流程,再全量训练
一个非常实用的效率技巧:正式全量训练之前,先随机抽200到300张图,跑一个epochs=10的迷你训练。目标不是精度,而是验证整个pipeline是否通畅。
这一步能帮你提前发现:标签路径对不对、类别ID有没有错、显存够不够、增强配置会不会报错。如果你一开始就全量训100个epoch,跑了两三个小时才发现某行配置写错了,那时间就浪费得太不值了。先用小数据跑通,再放心大胆地全量训练,是我认为性价比最高的操作习惯。
6.4 置信度阈值和NMS参数要按场景微调
最后聊一个很多人忽略的细节:推理时的置信度阈值不是固定的。
YOLO工具链默认的置信度阈值是0.25,NMS的IoU阈值是0.7。但在安全帽、反光衣这类场景里,现场误报多的时候你可以把置信度阈值提高到0.4到0.5,漏报多的时候降到0.2左右。如果现场多人密集、目标相互遮挡严重,NMS的IoU阈值可以适当降低,抑制掉重复框。
这些参数不应该在部署前拍脑袋定,而是应该根据你第5节做的现场hard测试集的误检漏检统计结果去调。我通常的做法是:在不同阈值下各跑一遍测试集,画一条误检数对漏检数的曲线,然后挑业务上能接受的那个平衡点。
这套流程走下来,一份4314张的YOLO格式安全帽反光衣数据集,就能从一个压缩包的“死数据”变成一个能抗住现场复杂环境的检测模型。数据量虽然不大,但每一步都做到位,实际效果往往超过你的预期。
本文还有配套的精品资源,点击获取