☰
乱堆物料检测数据集实战:VOC/YOLO格式与训练避坑全解析
2026/10/10 0:11:18 网站建设 项目流程

简介:乱堆物料检测数据集包含1143张真实场景图片及对应标注,面向目标检测算法训练与评估,适用于识别沙堆、混凝土堆等堆放物料的计算机视觉项目。数据集采用Pascal VOC与YOLO两种格式,每张图片均配有xml和txt标注文件;类别仅有一类pile,标注框总数1174个,由labelImg工具按矩形框规则绘制,少量样本涉及箱子等杂物,有助于增强模型对非典型乱堆物的泛化能力。整个压缩包共2000个文件,以1143个xml和857个txt标注文件为主体,大小约90.23MB,目录结构简洁,可直接用于YOLO、Faster R-CNN等常见检测框架的训练与验证。该数据集已有530人学习,适合目标检测入门者练习格式转换与标注规范,也可用于工地安全、物料监管等场景下的模型微调与基准测试。

1. 乱堆物料检测数据集:一个类别为什么让检测模型集体翻车

先说结论:这个「乱堆物料检测数据集 VOC+YOLO 格式 1143 张 1 类别」的压缩包,训练门槛比很多人想象的低得多,但踩坑空间一点不小。乱堆物料不是「人」「车」那种刚体目标,它是钢筋、砖块、碎石、废料混压在一起的聚合体,边界含糊、密度不均、透视远近差异大,一个框里经常套着好几个互不相干的物体。1143 张对深度学习来说不算多,1 个类别听起来也简单,实际跑起来你会发现:模型 loss 降得很漂亮,到了现场推理却把一堆物料框成七八个碎框。VOC 和 YOLO 两种格式都已配好,省掉了最磨人的标注格式转换环节,可以直接喂给常见训练框架。适合正在做智慧工地、仓储巡检、环保监管方向,想快速验证「普通检测模型到底能不能搞定乱堆物料」的工程师。花一个周末把训练跑通、把坑踩一遍,比看十篇综述都值。

2. 先看压缩包:VOC 与 YOLO 两套标注到底差在哪

拿到 .7z 压缩包,第一步不是双击解压拖到桌面,而是先看清楚目录结构。很多训练事故的根源,就是解压后想当然地按记忆里的目录去写路径,结果模型读到的训练图片和标签根本对不上。命令行解压是最稳的,我一般会先把包名改成英文再处理,因为中文包名在某些 Linux 发行版的终端里会出编码问题,解压出来的目录名变成乱码,后边所有脚本都要跟着遭殃。

mv 乱堆物料检测数据集VOC+YOLO格式1143张1类别.7z ./scattered.7z sudo apt install p7zip-full # Debian/Ubuntu 先装 7z 支持 7z x scattered.7z -o./scattered_dataset cd ./scattered_dataset tree -L 2

解压命令里-o指定输出目录,注意-o和路径之间没有空格。装 p7zip-full 是因为 .7z 格式用的是 LZMA 压缩算法,系统的 unzip 解不了。解压完成后立即用tree -L 2看两层目录结构,这一步能避免后边至少一半的路径错误。数据集选择 7z 而不是 zip 也很合理,标签里的 XML 和 TXT 文本压缩率不高,但图片部分压缩优势明显,整体体积能小不少。

2.1 解压与目录结构:1143 张图该有哪些文件

VOC 和 YOLO 两种格式的数据组织方式完全不同。VOC 格式脱胎于早期目标检测竞赛,它的标准布局是 JPEGImages 放图、Annotations 放 XML、ImageSets/Main 放训练集和验证集的划分清单;YOLO 格式则把图和对应标签拆成 images 和 labels 两大块,各自再按 train/val 分目录。这 1143 张图在两种格式下会出现两份标注文件,一份是 XML,一份是 TXT,图片本身是同一批。

scattered_dataset/ ├── VOC/ │ ├── Annotations/ # 1143 个 .xml,每个对应一张图 │ ├── JPEGImages/ # 1143 张 .jpg │ └── ImageSets/Main/ # train.txt / val.txt,写的是图片文件名 └── YOLO/ ├── images/ │ ├── train/ │ ├── val/ └── labels/ ├── train/ └── val/

拿到压缩包后先对照这个清单数一下文件数,ls -1 | wc -l看一眼每个目录是不是都齐了。缺 XML 或者缺 TXT 都是常见问题,少一个标注文件,训练时那一条数据就会被框架静默跳过,而你不会得到任何报错。VOC 格式下,ImageSets/Main 里的 train.txt 和 val.txt 不是标签,是图片文件名清单,训练框架靠这个清单去找对应的 XML;YOLO 格式下,目录结构本身就是划分,train 目录里的图片自动进训练集。这两种逻辑不要搞混,尤其当你想自己重新划分数据集时,改错地方会导致验证集和训练集重叠而不自知。

2.2 VOC 的 XML 与 YOLO 的 txt:同一张图的两种叙事

VOC 的 XML 把一张图的所有标注信息写在一个文件里,最核心的是 size 和 object 两段。size 记录图片原始宽高和通道数,object 里是每个目标的名称和 bndbox 边界框,边界框坐标是像素绝对值——不管图片是 1920×1080 还是 640×480,xmin、ymin 都是真实像素位置。

<annotation> <filename>IMG_0001.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>scattered_material</name> <bndbox> <xmin>321</xmin> <ymin>456</ymin> <xmax>1432</xmax> <ymax>987</ymax> </bndbox> </object> </annotation>

YOLO 的 txt 则每一行只描述一个目标,格式是「类别ID x中心 y中心 宽度 高度」,五个数字用空格分隔,且全部是归一化到 0~1 之间的浮点数——除以图片宽高之后的结果。这个「归一化」是整套格式里最容易出事的点。VOC 转 YOLO 时,分子分母搞错、或者忘了除以图片宽高,训练时会出现两种情况:要么训练进程直接崩溃,要么模型学出一个永远往左上角偏的框。我自己见过最隐蔽的一种错误,是转换脚本里把 bndbox 的 xmax 减 xmin 当成宽,但除以的是归一化之后的 1,等于把像素值直接当成了比例,模型居然还收敛了,只是收敛到一个完全错误的坐标空间里。

VOC 坐标转 YOLO 的公式没有玄学,就是四行除法:

# 假设 XML 解析出的 bndbox 为 xmin, ymin, xmax, ymax # 图片真实宽高为 img_w, img_h x_center = ((xmin + xmax) / 2) / img_w y_center = ((ymin + ymax) / 2) / img_h box_w = (xmax - xmin) / img_w box_h = (ymax - ymin) / img_h

中心点坐标是「左边界加右边界取平均」再除以宽,不是「右边界减左边界再除以二」。宽高才是右边界减左边界。这两组公式我每次写转换脚本都要在草稿纸上重新推一遍,因为中心点和宽高的算法太像了,上手就写必错一次。转换时尤其要注意拿来做分母的 img_w、img_h 必须和图片真实分辨率完全一致,很多标注工具在 XML 里写的 size 跟实际图片文件的像素尺寸不一致,转出来的 txt 就是错位的。

验证这个是否一致很简单,跑一段脚本把每张图的真实分辨率读出来,和 XML 里的 size 比对,不一致的全部标出来。

2.3 用脚本校验两套标注对齐:坐标对不上是一切训练事故的源头

既然压缩包里 VOC 和 YOLO 两种格式都给齐了,一个聪明的做法不是信任它们、而是交叉验证它们——如果两套标注从同一批原始标注转换而来,那么把 YOLO 的归一化坐标乘以图片宽高,应该精确还原出 VOC 的像素坐标。这一步能过滤掉数据制作环节的绝大部分低级错误。

import xml.etree.ElementTree as ET from pathlib import Path def parse_voc(xml_path): """解析 VOC XML,返回 (图片宽, 图片高, [(xmin, ymin, xmax, ymax), ...])""" root = ET.parse(xml_path).getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) boxes = [] for obj in root.iter("object"): bnd = obj.find("bndbox") boxes.append(( float(bnd.find("xmin").text), float(bnd.find("ymin").text), float(bnd.find("xmax").text), float(bnd.find("ymax").text), )) return img_w, img_h, boxes def parse_yolo(txt_path): """解析 YOLO txt,返回归一化坐标 [(xc, yc, w, h), ...]""" boxes = [] with open(txt_path) as f: for line in f: parts = line.strip().split() if len(parts) < 5: continue # 空行或格式损坏的行直接跳过 boxes.append(tuple(float(x) for x in parts[1:6])) return boxes # 遍历所有图片,逐张比对 voc_xml_dir = Path("scattered_dataset/VOC/Annotations") yolo_label_dir = Path("scattered_dataset/YOLO/labels/train") for xml_path in sorted(voc_xml_dir.glob("*.xml")): img_w, img_h, voc_boxes = parse_voc(xml_path) txt_path = yolo_label_dir / (xml_path.stem + ".txt") yolo_boxes = parse_yolo(txt_path) assert len(voc_boxes) == len(yolo_boxes), f"{xml_path.name} 目标数量不一致" for (xmin, ymin, xmax, ymax), (xc, yc, bw, bh) in zip(voc_boxes, yolo_boxes): # 还原 YOLO 归一化坐标为像素坐标 rx1 = (xc - bw / 2) * img_w ry1 = (yc - bh / 2) * img_h rx2 = (xc + bw / 2) * img_w ry2 = (yc + bh / 2) * img_h # 允许 1 像素误差,毕竟 float 运算有精度损失 assert abs(rx1 - xmin) < 1 and abs(ry1 - ymin) < 1 \ and abs(rx2 - xmax) < 1 and abs(ry2 - ymax) < 1, f"{xml_path.name} 坐标偏移"

脚本逻辑分三步:先解析 XML 拿到像素绝对坐标和图片尺寸,再解析对应 TXT 拿到归一化坐标,最后把归一化坐标按图片尺寸还原回像素值去比对。parts[1:6]取了 5 个元素,包含了 x_center、y_center、width、height 这 4 个值,类别 ID 在parts[0],因为 1 个类别所以 ID 只能是 0,但是校验时不用管它。跑完没有任何断言报错,才说明这份数据的 VOC 和 YOLO 标注是同一批原始标注转换出来的,可以放心拿去做训练。

这一步千万别跳过。数据集的制作过程中,转换脚本写错一次然后批量产出错误标签的情况太常见了,而且错误标签不会导致训练报错,它们只会让模型「学得很努力但学了个错误的东西」。交叉校验是唯一的后悔药。

3. 用 YOLO 格式跑通第一次训练:最小配置与必看输出

格式校验通过之后,训练本身反而简单了。现在的开源训练框架已经把数据加载、增强、损失计算都封装成了标准流程,你真正需要亲手写的内容就两个部分:一个数据配置文件,一条训练命令。下面以 YOLO 系列最常见的开源训练框架为例来写,你用其他的框架时核心逻辑一样,只是 YAML 的字段名略有差异。

3.1 数据 YAML 怎么写到不出路径歧义

YOLO 训练框架读取数据集靠的是一个 YAML 配置。这个文件决定了模型到哪儿找图片、到哪儿找标签、有几个类别、类别叫什么名字。写它的时候最容易犯的错就是路径用相对路径,而且对自己的「当前工作目录」没有概念。训练命令在终端里执行时,进程工作目录是终端所在位置,YAML 里的相对路径是相对于这个位置的;但如果你用 IDE 跑,工作目录可能就是项目根目录了,同样的相对路径结果完全不同。

# scattered_data.yaml # 放在训练项目根目录,不要放在数据集目录里 path: /home/you/scattered_dataset/YOLO # 数据集绝对路径 train: images/train # 训练图片目录,相对于 path val: images/val # 验证图片目录,相对于 path nc: 1 # 类别数量:只有乱堆物料 1 类 names: ["scattered"] # 类名列表, 索引 0 对应 labels 里的类别 ID 0

path写成绝对路径是最省心的做法,代价是换机器训练时要改一次。train和val是相对于path的目录名,框架会自动到path/images/train下找图,再到path/labels/train下找同名 TXT 标签。names里的类名建议用英文小写加下划线,不要用中文,部分框架在可视化输出和导出时对中文类名的支持不稳定,遇到编码问题排查起来很浪费时间。nc必须和 labels 目录里 TXT 文件标注的类别 ID 对得上——这里是 1 类别,TXT 中每行的第一个数字必须是 0,出现了 1 就说明标签文件有问题。

配置完 YAML,强烈建议在训练前先跑一次验证加载的命令,确认框架能正确读到每张图和对应的标签。这个习惯能帮你把「数据集路径错误」和「模型训练不好」这两类问题彻底隔离开,不至于训练了三个小时才发现模型压根没读到数据。

3.2 训练命令与 6 个关键参数

训练命令本身不长,难的是理解每个参数在这个特定数据集的含义。乱堆物料检测和通用目标检测最大的区别在于:类别极端不平衡(背景占画面比例极高)、目标边界不清晰、同一堆物料在不同距离下尺度差异巨大。这些特点决定了你不能照搬网上随手抄来的训练参数。

yolo train \ data=/home/you/scattered_data.yaml \ model=yolov8n.pt \ epochs=120 \ imgsz=640 \ batch=16 \ patience=20 \ project=run_scattered \ name=baseline

model=yolov8n.pt表示加载 nano 级别的预训练权重。乱堆物料只有 1 个类别,nano 参数量足够起步,先跑通再换大模型;如果显存紧张,yolov8n.pt在 6GB 显存上都能跑起来。epochs=120是乱堆场景的经验值,这个任务特征比较简单,但收敛慢,100 到 150 轮之间比较合适。imgsz=640是输入分辨率,也是参数量之外影响显存的最大因素。batch=16是 8GB 显存的常见起步值,如果你的卡是 24GB,可以开到 32。patience=20表示验证集指标连续 20 轮不提升就自动停止,防止无效训练烧时间。

project和name两个参数决定输出目录,框架会在run_scattered/baseline/下生成权重、曲线和验证结果。宁可每次实验换一个name,也不要让它自动递增或者复用同一个名字,否则后边对比实验时会发现自己分不清哪次是哪次的。

3.3 训练完成后先看这三个文件,而不是 mAP

训练结束后,模型会自己打印一个 mAP 分数。但干这行久了你就知道,「mAP 高」和「你的场景能用」之间隔着一条巨大的鸿沟。我拿到训练输出后的第一件事永远是打开输出目录看三个文件:results.csv、confusion_matrix.png、val_batch_pred.jpg。

results.csv是训练过程的完整记录,每一行是一个 epoch 的损失值和指标。重点看 train_loss 和 val_loss 两条曲线的走势,train_loss 持续下降而 val_loss 在第 40 轮之后掉头向上,说明过拟合已经开始,早停参数可能没起作用。confusion_matrix.png看的是误检情况——因为只有 1 个类别,这个矩阵只可能有两种错:把乱堆物料检成背景,或者把背景检成乱堆物料,前者是漏检、后者是误报,你要根据现场需求决定容忍哪一边。val_batch_pred.jpg是验证集图片上的预测可视化,直接看框的贴合程度,比任何数值指标都直观。

这三张图看完再回头看 mAP 才有意义。数值指标是压缩过的信息,可视化输出能告诉你模型具体错在哪张图上、错的形态是什么,这是黑匣子之外唯一可见的手感来源。

4. 避坑:乱堆物料数据集的五个典型翻车现场

这个数据集我在类似项目上跑过不止一次,包括模拟项目X里的堆料识别,也帮某公司调过一段智慧工地巡检的模型。乱堆物料场景的翻车模式高度统一,下面五个坑几乎每次都出现,按出现频率排序。

4.1 loss 降了 mAP 不涨:模型学的是纹理还是堆

现象:训练日志里 loss 一路下降,看起来非常健康,但验证集 mAP 在 0.1 附近徘徊,怎么调都不涨。把验证图的预测结果可视化出来,发现框确实画了,但位置东一块西一块,完全不贴物料堆。

原因:乱堆物料的视觉特征不是「边界」而是「纹理统计」。一堆碎砖和一堆碎石在颜色上可能很像,在纹理粗糙度上有差异,模型如果用低层特征去匹配局部纹理,学到的是「这种质感的东西」而不是「这一片区域是堆料」。loss 下降是因为模型在训练集上确实拟合了这些纹理模式,但一到验证集,纹理稍有变化就失效。

解决:不要动 loss,先动输入分辨率和模型结构。把imgsz从 640 提到 960,让模型在更高分辨率下看到堆体的整体轮廓;同时把模型从 nano 换成 small 或 medium,让网络容量匹配任务复杂度。如果还不行,检查一下标注框是不是太紧凑,导致框内几乎只有细节纹理没有上下文,这种情况需要放宽标注范围。

4.2 预测框整体偏移:坐标归一化除错了分母

现象:模型训练正常收敛,指标也不差,但预测框总是整体往左上方偏上一截,框的下边界和右边界贴合得很好,上边界和左边界明显压到了目标内部。

原因:这是 2.2 节讲的坐标转换问题的典型症状。标注制作时把 bndbox 的坐标除以了错误的图片尺寸,比如 XML 里 size 写的是 1920×1080,但实际图片被压缩到了 1280×720,原始像素坐标没变,归一化时却用了旧的宽高,导致所有转换出来的 YOLO 标签等比缩小。

解决:跑一遍 2.3 节的交叉校验脚本,逐张图对比 VOC 还原坐标和 YOLO 还原坐标的偏差。找到偏差规律后,写一个批量修正脚本,把 YOLO 标签全部重新生成。教训是:标注数据的生产线越自动化,越要留一个独立校验环节。我也是在一次部署现场看到框整体偏了一个身位,回去查才发现是 XML 里的 size 和图片实际分辨率不一致。

4.3 CUDA out of memory:不是显存不够,是输入太大

现象:训练到某个 epoch 突然报RuntimeError: CUDA out of memory,前面几十轮都好好的,调小 batch 后又能跑,但跑一会儿又炸。

原因:乱堆物料图片往往来自现场巡检相机,原始分辨率非常高。框架在训练时会做随机缩放和裁剪,但 Mosaic 增强会把四张图拼在一起再缩放,如果某几张原始图特别大,增强后叠加的中间张量会瞬间超出显存。训练前期碰巧没抽到这几张图,后期抽到了就崩。

解决:直接在训练命令里加cache=True配合imgsz=640,让数据加载时统一缩放和缓存。另外显存不是只能靠调 batch 解决,batch=8加上imgsz=640在 8GB 卡上通常能稳定跑完整个训练。如果还是崩,把workers=4降到 2,数据加载线程异常也会导致显存波动。

4.4 验证集 mAP 虚高:划分前少做了一个去重

现象:训练时验证集 mAP 在 0.9 以上,模型表现堪称完美,部署到现场新拍的图片上直接崩盘,漏检误检一大堆,和验证集表现完全不是同一个模型。

原因:数据集划分阶段没有做图片去重。很多巡检数据集是按时间顺序连续拍摄的,同一堆料从不同角度、不同距离拍了多张,如果只是按文件名随机划分,相邻帧很容易一张进了训练集、另一张进了验证集。验证集里全是训练集见过的场景,mAP 虚高是必然的。这个坑在 1143 张的小数据集上尤其危险,因为样本量少,重复比例可能很高。

解决:划分 train/val 之前,先对所有图片计算感知哈希或 MD5,把内容重复的图片归为一组,确保同一组图片不会同时出现在训练集和验证集。对时间序列巡检数据,更保险的做法是按拍摄时间切分,前 80% 时间段做训练、后 20% 时间段做验证。我先校验再划分的习惯就是这次踩坑之后养成的,算是最有价值的血泪经验之一。

4.5 一堆物料被框成七八个小框:NMS 压不住

现象:模型训练指标正常,推理时一张图上同时出现六七个小框,每个框都只框住物料堆的一部分,置信度还都不低。把置信度阈值调到 0.5 以上,小框消失了一部分,但真正的物料堆也被漏掉了。

原因:乱堆物料内部存在大量高对比度局部区域——钢筋的亮面、砖块的棱角、塑料布的褶皱,这些局部特征被模型当成独立目标激活了。NMS 默认的 IoU 阈值在刚体目标上够用,但堆料这种「框里套框」的几何形态会让 NMS 误以为这几个框是不同目标。

解决:推理时把 NMS 的 IoU 阈值从默认的 0.45 调到 0.3,让重叠度更高的框被合并掉;置信度阈值从 0.25 调到 0.35 到 0.4 之间,过滤掉低置信度的碎片框。如果还压不住,优先检查是不是 imgsz 太低导致堆体细节在缩放过程中被过度放大,先提高到 960 再重新训练,NMS 参数只是兜底。

5. 把准确率再往上提:乱堆场景的模型与增强策略

跑通基线之后,接下来就是精度优化。乱堆物料这个任务有个奇怪的特点:模型结构的影响远大于超参调优的影响。很多通用检测的经验在这里会失效,你需要针对「堆」这种非刚体目标做专门调整。

5.1 为什么乱堆怕小框,以及什么时候换 P6 检测头

默认的目标检测模型通常设计来检测 8×8 到 64×64 像素范围的目标,在 640 分辨率输入下,一个物料堆如果占了 400×400 像素,属于超大目标。默认检测头对大目标的特征提取用的是高层语义信息,但乱堆物料的「语义」恰恰不明确——没有固定的形状、没有固定的颜色,语义信息里唯一稳定的是「这是一个堆」的上下文。

这导致低层特征反而成了关键:物料堆的边界通常靠纹理突变和颜色分布来界定,这些信息集中在浅层特征图里。开源框架自带的多尺度检测头已经在不同层上做预测,真正需要干预的是输入分辨率的上限。如果现场摄像头离得远,一个小料堆在画面里可能只有 100×100 像素,这时把输入分辨率从 640 提到 960 带来的收益比换大模型更明显。

我一般会在被标注框的像素面积普遍小于 64×64 时,换用带 P6 检测头的配置。P6 检测头增加了一个更浅层的特征图,专门负责小目标。但代价是推理速度下降约 30%、显存占用上升一个档次,所以只在确认小目标确实是瓶颈时才上,不要一开始就堆配置。

5.2 数据增强怎么开:Mosaic、随机遮挡与翻转的取舍

现代训练框架默认开启的增强策略整体上适合乱堆场景,但有三项需要单独调整。

第一是 Mosaic 增强,它把四张图拼接成一张再喂给模型,本质是提高单张图片的目标多样性。乱堆物料场景下 Mosaic 有一个副作用:四张图拼接处的堆料边界会互相污染,模型可能学到「拼接缝也是边界」。解决办法是不完全关闭它,而是把 Mosaic 概率从默认的 1.0 降到 0.7 左右,保留多样性收益,减少伪边界干扰。

第二是随机遮挡和 Cutout,它对乱堆场景是正向的。物料堆天然存在互相遮挡——前面的钢筋挡住后面的砖块,特征不完整才是常态。主动裁掉一小块区域,反而能逼模型学习更鲁棒的堆体判断。

第三是翻转。左右翻转一般安全,因为物料的物理形态不依赖左右方向;但上下翻转要谨慎,如果现场摄像头是固定俯仰角安装,上下翻转会让模型看到现实中不存在的拍摄视角,泛化反而变差。保留左右翻转、关闭上下翻转,是我在这个场景里的默认配置。

5.3 迁移学习与超参:1 个类别不需要从零开始

乱堆物料不在主流预训练数据集的类别清单里,但预训练权重依然有巨大价值。模型前几层学到的是边缘、拐角、纹理基元这类通用特征,这些特征在任意视觉任务里都可复用。区别只在于后几层需要更多时间去适配「堆」这个新概念。

我的习惯是分两阶段训练。第一阶段冻结 backbone 的全部参数,只训练检测头 30 轮,让损失函数先在一个相对稳定的特征空间里找到合理的框位置;第二阶段解冻全部参数,用较低的学习率再训练 90 轮。这种做法的好处是避免训练一开始就大范围扰动预训练特征,因为你的数据集只有 1143 张,从零学特征很容易过拟合。

学习率方面,nano 和 small 模型的起步学习率建议在 0.01 左右,配合余弦退火衰减。动量保持默认即可。需要注意的另一个参数是mosaic关闭后的预热轮数,框架默认会先用小学习率跑几轮再进入主训练,这是在迁移学习场景下最容易被忽略、但对稳定性影响最大的设置。

6. 部署前的最后一关:在现场图片上验证模型学到了「堆」

训练指标再漂亮,都不如拿现场真实图片试一遍。这里说的现场图片,指的是训练集和验证集里完全没有出现过、由现场摄像头直接拍回来的原始画面。找一个周末,让现场同事随手拍 20 到 30 张不同光线、不同堆放状态的照片,拷回来放到一个单独目录里,跑一次推理。

yolo predict \ model=run_scattered/baseline/weights/best.pt \ source=/path/to/field_test_images/ \ conf=0.35 iou=0.3 \ save=True

推理的时候,我会把置信度阈值conf故意调低到 0.3,这样能暴露出模型的「犹豫区」——那些刚好压在阈值边缘的检测结果,往往能告诉你模型到底依赖什么特征来判断堆料。如果低置信度预测框虽然多,但都集中在真正的物料堆区域,说明模型学对了只是不够自信;如果低置信度框散布在背景草地、墙壁、阴影里,说明模型学到的特征有明显漏洞。

最值得做的一个验证,是统计所有预测框的面积占整张图的比例,画一个直方图。乱堆物料在巡检画面里通常占画面 10% 到 60% 的区域,如果直方图显示模型输出了一大堆面积占比在 0.5% 以下的碎框,哪怕这些框的置信度很高,也要警惕——它学到的可能不是「堆」这个整体,而是「堆里面有这种纹理的小块」。这时候回到第 5 章,调整 P6 检测头或增强策略,比盲目调超参更有效。

把面积占比分布纳入验收指标,是我在部署阶段学到的最大教训。当时一个模型 mAP 到了 0.86,但现场画面里到处都是小碎框,后排的同事都在问这模型是不是坏掉了。后来发现是验证集里漏检的目标没有计入惩罚,指标看起来一切正常,实际部署效果和指标完全不相关。从那以后,我坚持在验证脚本里加一行面积占比统计代码,把它当作跟 mAP 并列的验收标准。经验是:检测模型的指标多到足够让任何问题都被掩盖,但直方图不会撒谎。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询