资讯动态

PCB缺陷检测数据集:VOC/YOLO标注转换与分组切分避坑

发布时间:2026/10/25 12:47:08 来源:尧图企业网站定制
简介面向电路板PCB缺陷检测的视觉数据集适合目标检测学习者、课程实验与算法验证使用覆盖假铜、缺孔、鼠咬、开路、针孔、划痕、短路、毛刺等8类常见缺陷。压缩包共1个docx文件约3.38MB文档内提供百度云下载地址及数据集说明便于获取图片与标注文件。数据包含9666张jpg图像并配有等量Pascal VOC格式xml和YOLO格式txt标注共51549个矩形框采用labelImg按类别勾画可直接用于模型训练、验证与格式转换。其中还包含较多椒盐噪声增强图片可用于考察模型在噪声条件下的鲁棒性或作为数据增强对照实验素材。目前已有232人学习适合需要现成PCB缺陷标注数据、快速搭建检测基线或开展课程设计的中初级读者。1. 电路板 PCB 缺陷检测数据集9666 张图、8 类缺陷双格式标注省掉了哪一半活拿到一份 PCB 缺陷检测数据集最怕的情况是图有框也有但类别名对不上、坐标归一化错位、图里还混着一批合成噪声图分不清是缺陷还是干扰。这份 9666 张图、8 类缺陷、51549 个标注框的 PCB 缺陷检测数据集同时给了 Pascal VOC 的 xml 和 YOLO 的 txt等于把“人工画框”和“格式转换”这两件最耗时间的活都做完了。它适合三类人想快速验证 AOI 视觉方案可行性的小团队、做课程设计需要跑通检测全流程的同学、以及手上有产线图但缺标注、想先拿公开数据做预训练和对照实验的工程师。需要提前说清一点资源说明里明确写了数据集中有大量通过椒盐噪声生成的增强图片。这不是缺点但它会直接影响你怎么切分训练集和验证集也会让验证指标看起来比现场表现好一大截。第 5 章会把这个问题拆开讲。2. 拆开 51549 个框8 类缺陷的物理含义与标签字段对齐2.1 八类缺陷各自长什么样框数分布是否失衡先看类别本身。这 8 个类名是英文工业术语不是随便起的每一个都对应 PCB 制程里的一道工序缺陷理解它们的形态才能判断模型该给多大输入分辨率。类别名中文习惯叫法典型成因与形态标注框数占比falsecopper余铜 / 多余铜蚀刻不净残留铜箔形状不规则、边界毛糙48529.41%missinghole缺孔 / 漏钻孔该钻的导通孔或定位孔没钻出来整块圆缺失47439.20%mousebite鼠咬板边成型铣边出现锯齿状缺口常贴板边788315.29%opencircuit开路走线断裂断口两侧本应连通803315.58%pinhole针孔镀层或阻焊层上的微小孔洞面积极小745314.46%scratch划伤表面线状划痕长宽比大、方向随机554310.75%shortcircuit短路多余连接把两条本不该通的线连上673713.07%spur毛刺 / 凸起走线边缘多出来的铜刺细长且小630512.23%合计 51549 个框9666 张图平均每张图 5.3 个框。最多类 opencircuit 与最少类 missinghole 的比例约 1.69:1这个失衡程度在检测任务里算是相当温和的不需要做重采样或类别加权直接训练即可。真正麻烦的不是数量失衡而是尺度失衡pinhole、spur 这类目标在原图上可能只有十几个像素opencircuit、mousebite 往往横跨几十上百像素同一张图里大小目标混在一起这才是后面调 imgsz 和增强策略的主要依据。另外一个容易忽略的细节这批标注全部是矩形框没有分割掩码。资源说明里也提到VOC 部分只给了 xml 和 jpg不包含分割路径的 txt。也就是说这份数据只能做目标检测别指望直接拿去做像素级缺陷分割如果业务要求输出缺陷轮廓得自己补 mask 或者换弱监督路线这一点在选型阶段就要想清楚别训到一半才发现方向不对。2.2 xml 与 txt 的字段对应关系一个框在两种格式里怎么表示同一张图、同一个框VOC 和 YOLO 的写法完全不同。VOC 用绝对像素坐标、左上右下的顺序而且是 1-basedYOLO 用归一化到 0~1 的中心点加宽高0-based。下面是一个 xml 片段和它对应的 txt 行。annotation filenamepcb_00231.jpg/filename size width1024/width height1024/height depth3/depth /size object nameopencircuit/name bndbox xmin412/xminymin268/yminxmax455/xmaxymax301/ymax /bndbox /object /annotation对应的 YOLO txt 只有一行3 0.423340 0.277832 0.041992 0.032227这五个数字的含义依次是类别 id、中心点 x 归一化、中心点 y 归一化、框宽归一化、框高归一化。手算一遍就能对上中心 x (412455)/2 433.5除以宽 1024 得 0.423340中心 y (268301)/2 284.5除以高 1024 得 0.277832宽 43/1024 0.041992高 33/1024 0.032227。类别 id 是 3对应 names 列表里的第四项 opencircuit。关键就在这个 id 上。YOLO 的类别 id 不是从名字推出来的而是严格依赖你训练时 data.yaml 里 names 的排列顺序。这份资源给的 8 个类名恰好接近字母序很多人就想当然地按字母序写 yaml结果一旦某个类名拼写或顺序有出入整份标签全错位而且训练照样能跑、loss 照样下降只是模型学到的全是错的。血泪经验是names 列表直接从标注里统计出来不要手打。2.3 用一段脚本做全量体检别信“应该没问题”在动手训练之前我一般会先跑一遍体检脚本把三个目录的文件一一对应关系、框数一致性、坐标合法性、类别名拼写全部过一遍。这一步花不了两分钟但能省掉后面几小时的排查。import os, glob from collections import Counter from xml.etree import ElementTree as ET ROOT pcb_dataset IMG_DIR os.path.join(ROOT, JPEGImages) XML_DIR os.path.join(ROOT, Annotations) TXT_DIR os.path.join(ROOT, labels) legal_names {falsecopper, missinghole, mousebite, opencircuit, pinhole, scratch, shortcircuit, spur} cls_counter Counter() # 各类别真实框数 bad [] # 异常记录 tiny 0 # 面积过小的框计数 for xp in glob.glob(os.path.join(XML_DIR, *.xml)): stem os.path.splitext(os.path.basename(xp))[0] tp os.path.join(TXT_DIR, stem .txt) ip os.path.join(IMG_DIR, stem .jpg) # 1) 三件套是否齐全 if not os.path.exists(tp): bad.append((missing_txt, stem)); continue if not os.path.exists(ip): bad.append((missing_img, stem)); continue root ET.parse(xp).getroot() size root.find(size) W int(size.find(width).text) H int(size.find(height).text) objs root.findall(object) with open(tp, encodingutf-8) as f: lines [l for l in f.read().splitlines() if l.strip()] # 2) xml 框数与 txt 行数必须一致 if len(objs) ! len(lines): bad.append((count_mismatch, stem, len(objs), len(lines))) for o in objs: name o.find(name).text.strip() if name not in legal_names: bad.append((illegal_name, stem, name)) cls_counter[name] 1 b o.find(bndbox) x1, y1 float(b.find(xmin).text), float(b.find(ymin).text) x2, y2 float(b.find(xmax).text), float(b.find(ymax).text) # 3) 坐标必须落在图内且框不为负 if x2 x1 or y2 y1 or x1 0 or y1 0 or x2 W or y2 H: bad.append((bad_box, stem, x1, y1, x2, y2)) if (x2 - x1) * (y2 - y1) 16: # 小于 4x4 像素的框基本是噪声 tiny 1 print(类别框数:, cls_counter.most_common()) print(总框数:, sum(cls_counter.values())) print(异常条数:, len(bad), bad[:10]) print(过小框数:, tiny)代码逻辑不复杂但每一步都对应一个真实的坑。第一段做三件套对齐因为资源包解压后经常出现图片和标签数量不等的情况原因可能是后缀大小写、编号跳号或者中间某个文件夹漏拷。第二段比对 xml 与 txt 的框数能抓出转换脚本中途报错留下的半截文件。第三段检查坐标是否越界标注工具在图片边缘画框时偶尔会写出超出宽高的 xmax这种框转成 YOLO 后会得到大于 1 的归一化值训练框架有的会静默截断、有的直接抛异常。最后统计过小框如果 pinhole 和 spur 里有大量小于 4×4 像素的框说明这批数据本身对小目标不友好后面要么提高输入分辨率要么在评估时对这两类单独看。跑完之后把cls_counter的结果和资源说明里给的每类框数对一遍数字能对上就说明标签文件没有被改动过。对不上也不用慌先确认是不是自己解压不完整。2.4 把类别顺序固化下来写进 classes.txt体检通过之后第一件事是把类别顺序固化。除了 data.yaml建议在数据集根目录再放一份 classes.txt每行一个类名顺序和 yaml 里的 names 完全一致。这个文件不参与训练但它是团队协作时的唯一事实来源别人拿到你的数据集看一眼 classes.txt 就知道 id 0 到 7 分别是什么不用去翻聊天记录。cat pcb_dataset/classes.txt EOF falsecopper missinghole mousebite opencircuit pinhole scratch shortcircuit spur EOF顺序一旦定下就不要改。中途往数据集里加类、或者调整顺序等于让所有已经训好的权重作废必须从头再来。这个代价我吃过一次后面每次建数据集都先把 classes.txt 定死再动手。3. 从原始包到可训练目录分层切分、命名对齐与 data.yaml3.1 YOLO 的目录约定为什么不能随手改文件夹名YOLO 系列在加载数据时是根据标签路径反推图片路径的它把路径里的/labels/替换成/images/再把.txt换成.jpg。这意味着图片和标签的目录层级必须严格对称文件名主干必须完全一致。常见的正确结构是这样pcb_dataset/ ├── images/ │ ├── train/ pcb_00001.jpg ... │ └── val/ pcb_08001.jpg ... ├── labels/ │ ├── train/ pcb_00001.txt ... │ └── val/ pcb_08001.txt ... ├── classes.txt └── pcb.yaml有三点必须注意。第一图片和标签的文件名主干要一模一样pcb_00001.jpg只能配pcb_00001.txt不能出现pcb_1.jpg配pcb_00001.txt这种编号位数不一致的情况。第二VOC 那套JPEGImages、Annotations的命名不要直接搬进 YOLO 目录框架不认。第三不要把所有图平铺在一个目录里再用 txt 列表去指定那样每换一台机器路径就得改一遍。3.2 分层切分脚本让椒盐噪声增强图和它的原图待在同一边切分是这份数据集最容易翻车的地方。资源说明里明确提到有大量椒盐噪声生成的增强图片这些图和原图内容高度相似只是加了噪声。如果随机切分很可能同一块板子的原图进了训练集、噪声版进了验证集模型在验证集上表现很好一到现场就原形毕露。这不是模型的问题是数据泄漏。处理办法是按“源图分组”切先把疑似同源的图片归到一个组再以组为单位整体分到训练集或验证集。判断同源最实用的依据是文件名前缀噪声增强图通常在原名后加后缀。import os, glob, random, shutil, re from collections import defaultdict SRC_IMG pcb_dataset/JPEGImages SRC_LBL pcb_dataset/labels DST pcb_yolo RATIO (0.8, 0.1, 0.1) # 训练 / 验证 / 测试 random.seed(42) def group_key(stem): 把噪声、增强、副本等后缀剥掉得到源图分组键 s re.sub(r(_noise|_salt|_pepper|_aug|_copy|_\d)$, , stem, flagsre.I) return s groups defaultdict(list) for ip in glob.glob(os.path.join(SRC_IMG, *.jpg)): stem os.path.splitext(os.path.basename(ip))[0] groups[group_key(stem)].append(stem) keys sorted(groups.keys()) random.shuffle(keys) n len(keys) n_train int(n * RATIO[0]) n_val int(n * RATIO[1]) split_map {} for i, k in enumerate(keys): part train if i n_train else (val if i n_train n_val else test) for stem in groups[k]: split_map[stem] part for stem, part in split_map.items(): for sub in (images, labels): os.makedirs(os.path.join(DST, sub, part), exist_okTrue) shutil.copy(os.path.join(SRC_IMG, stem .jpg), os.path.join(DST, images, part, stem .jpg)) lp os.path.join(SRC_LBL, stem .txt) if os.path.exists(lp): shutil.copy(lp, os.path.join(DST, labels, part, stem .txt)) print({p: sum(1 for v in split_map.values() if v p) for p in (train, val, test)})参数上RATIO我一般用 8:1:1这份数据量接近一万张验证集留 900 多张足够评估稳定测试集单独留着只在最后验收时用一次平时调参绝对不碰。group_key里的正则要根据实际文件名调整如果增强图是原名_1、原名_2这种编号后缀就把_\d留在里面如果文件名完全无规律退而求其次的做法是对图片算感知哈希做近重复聚类成本高但更保险。random.seed(42)是为了让切分结果可复现团队里两个人跑出同样的划分排查问题时才不会互相甩锅。切完之后建议再跑一次统计确认三个子集里的类别分布没有严重偏移。如果某一类在验证集里只有几十个框那这个类的 AP 波动会非常大看到指标掉了先别急着改模型。3.3 data.yaml 的写法与路径陷阱配置文件很短但错一个字就白跑。path: /data/pcb_yolo # 数据集根目录绝对路径最稳 train: images/train val: images/val test: images/test nc: 8 names: 0: falsecopper 1: missinghole 2: mousebite 3: opencircuit 4: pinhole 5: scratch 6: shortcircuit 7: spurpath用绝对路径相对路径在不同工作目录下启动训练时会解析成不同的位置表现是“昨天还能训今天就说找不到图片”。train、val是相对path的子路径不要重复写根目录。names写成字典形式比列表更直观能一眼看出 id 和类名的绑定关系但要注意字典的键必须从 0 开始连续中间缺一个 id 框架会报错或者错位。如果手头只有 VOC 格式、想自己转一份 YOLO 出来做交叉验证转换公式是cx (xmin xmax) / 2 / Wcy (ymin ymax) / 2 / Hw (xmax - xmin) / Wh (ymax - ymin) / H。转换时务必把结果 clamp 到 [0, 1]并且跳过宽或高小于 1 像素的框否则会写出负数或者大于 1 的值。def voc_to_yolo(x1, y1, x2, y2, W, H, cls_id): cx (x1 x2) / 2.0 / W cy (y1 y2) / 2.0 / H w (x2 - x1) / float(W) h (y2 - y1) / float(H) # 越界裁掉避免框架静默截断引入误差 cx, cy min(max(cx, 0.0), 1.0), min(max(cy, 0.0), 1.0) w, h min(max(w, 0.0), 1.0), min(max(h, 0.0), 1.0) if w * W 1 or h * H 1: return None return f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}保留 6 位小数是够用的1024 像素的图上 1e-6 的精度远小于 1 个像素。有些转换脚本只保留 4 位小目标上会引入可见的框抖动pinhole 这种十几像素的缺陷尤其明显。4. 小目标 椒盐噪声输入分辨率与增强开关怎么定4.1 先量框尺寸再决定 imgsz 设多少输入分辨率不该拍脑袋。640 是通用默认值但 PCB 缺陷里有一批十几像素的目标缩到 640 之后可能只剩 5 到 8 个像素网络根本学不到有效特征。正确做法是先统计一遍框的像素宽高分布。import glob, os, numpy as np from xml.etree import ElementTree as ET ws, hs, ratios [], [], [] for xp in glob.glob(pcb_dataset/Annotations/*.xml): root ET.parse(xp).getroot() size root.find(size) W, H int(size.find(width).text), int(size.find(height).text) for o in root.findall(object): b o.find(bndbox) w float(b.find(xmax).text) - float(b.find(xmin).text) h float(b.find(ymax).text) - float(b.find(ymin).text) ws.append(w); hs.append(h); ratios.append(max(w, h) / max(min(w, h), 1)) for name, arr in ((宽, ws), (高, hs), (长宽比, ratios)): a np.array(arr) print(name, P10%.1f P50%.1f P90%.1f max%.1f % (np.percentile(a, 10), np.percentile(a, 50), np.percentile(a, 90), a.max()))跑完看 P10 和 P50。如果 P10 的宽高在 20 像素上下而原图是 1024×1024那么用 640 训练时这些小目标会被压到 12 像素左右基本宣判死刑。这种情况我会把 imgsz 提到 1024显存不够就降 batch 或者用梯度累积顶上去。反过来如果 P50 已经超过 150 像素640 完全够用硬上 1024 只是白白拖慢训练速度。长宽比那一列也有用。如果 P90 的长宽比超过 8说明数据里有一批极端细长的目标scratch 和 spur 很可能就是默认 anchor 的覆盖会偏弱YOLOv8 这类 anchor-free 的框架影响小一些但仍建议在评估时按尺寸分组看 AP别只看总体数字。4.2 增强开关这份数据已经带了噪声别再叠一层训练配置里最容易照抄默认值的地方就是增强。这份数据集的特殊之处在于它本身就包含大量椒盐噪声图如果你再在训练管线里加高斯噪声、ISONoise 之类的增强等于在已经退化的图像上继续退化模型要么学成噪声鲁棒但过拟合噪声分布要么直接学不动。我一般这么设mosaic保留但把close_mosaic设在最后 10 到 15 个 epoch 关闭让模型在收尾阶段见到不被拼接截断的真实分布hsv_h、hsv_s、hsv_v可以小幅保留PCB 图像受打光影响确实有色偏degrees控制在 0 到 5 度旋转太大会让走线方向失真opencircuit 这类依赖走向的类别会受影响flipud直接关掉fliplr可以开但要在验证集上对比一次开了和没开的差别有些板型的丝印和定位孔左右不对称。还有一个容易忽略的点mixup和copy_paste对小目标数据集并不总是正收益。copy_paste 会把缺陷实例抠出来贴到别处对 pinhole 这种本身就模糊的小目标粘贴边缘的伪影可能比缺陷本身还显眼模型学到的可能是伪影特征。建议先用默认关闭跑一版基线再单独开一项做消融别一上来全开。4.3 一套可复现的训练命令与超参基线配置如下表适合单卡 16GB 显存的机器跑 9666 张图大约几小时量级。参数取值说明modelyolov8s先用 s 跑通再考虑 m/limgsz1024依据 4.1 的框尺寸统计确定epochs100配合 patience30 早停batch81024 输入下显存吃紧时的折中值optimizerSGD检测任务上比 AdamW 更稳lr00.01batch 变小时按比例下调mosaic1.0训练前期开启close_mosaic15最后 15 轮关闭拼接degrees3.0小幅旋转fliplr0.5水平翻转flipud0.0关闭避免上下语义失真patience30连续 30 轮无提升则停命令本身很短yolo detect train \ modelyolov8s.pt \ data/data/pcb_yolo/pcb.yaml \ imgsz1024 \ epochs100 \ batch8 \ optimizerSGD \ lr00.01 \ degrees3.0 \ fliplr0.5 \ flipud0.0 \ close_mosaic15 \ patience30 \ projectruns_pcb \ namebaseline_s_1024project和name指定输出目录养成习惯之后每次实验的权重、曲线、混淆矩阵都归档在独立文件夹里回头对比不同 imgsz、不同增强组合时不会翻车。batch8如果显存溢出先降 batch 再用accumulate补等效批大小不要直接砍 imgsz砍分辨率对小目标的影响比降 batch 大得多。训练过程中重点盯两个东西一是分类损失的下降曲线是否平滑如果前 10 轮震荡剧烈多半是 lr0 偏大或者标签有问题二是验证集上 pinhole 和 spur 的 AP这两类最能反映输入分辨率够不够。总体 mAP 到 0.8 但 pinhole 只有 0.4说明模型在偷懒只学会了大目标。5. 避坑排查五处让 mAP 虚高、训练静默出错的现场5.1 验证集 mAP 冲到 0.9换一批现场图就大面积漏检现象训练日志里验证 mAP50 稳定在 0.9 以上混淆矩阵也漂亮但拿几十张产线新拍的图一跑漏检严重尤其划伤和针孔几乎全丢。原因验证集里混进了和训练集同源的椒盐噪声增强图。这些图和训练图只差一层噪声模型见过几乎一模一样的样本指标自然虚高。另外噪声图和真实产线图在噪声分布、打光、背景上都不一样模型学到的是“带椒盐噪声的缺陷”不是“缺陷”。解决回到 3.2 的分组切分把同源图锁在同一边。切完再补一步单独留一组完全不含合成噪声的原图做验证用这组数字作为对外汇报的口径。合成噪声图可以全部放进训练集当数据增强用这样既用上了这批数据又不会污染评估。5.2 模型把 scratch 认成 shortcircuit混淆矩阵里两类互相吞现象训练能跑完loss 正常下降但混淆矩阵显示 scratch 和 shortcircuit 之间有大量互错人工看预测框位置又是对的。原因类别 id 错位。xml 里的类名是对的但转 txt 时用的映射表和 data.yaml 里的 names 顺序不一致导致 id 3 在某些文件里是 opencircuit、在另一些文件里是 scratch。这种错误不会报错因为 id 都在 0 到 7 的合法范围内。解决写一个反向校验脚本从每张图的 txt 里读 id再到对应 xml 里读类名比对id - name的映射是否全局一致。from collections import defaultdict from xml.etree import ElementTree as ET import glob, os mapping defaultdict(set) # id - 出现过的类名集合 for tp in glob.glob(pcb_dataset/labels/*.txt): stem os.path.splitext(os.path.basename(tp))[0] xp fpcb_dataset/Annotations/{stem}.xml if not os.path.exists(xp): continue names [o.find(name).text.strip() for o in ET.parse(xp).getroot().findall(object)] with open(tp, encodingutf-8) as f: lines [l for l in f.read().splitlines() if l.strip()] if len(names) ! len(lines): continue for line, name in zip(lines, names): mapping[int(line.split()[0])].add(name) for k in sorted(mapping): print(k, mapping[k])正常情况下每个 id 后面只应该有一个类名。如果出现3 {opencircuit, scratch}这种立刻停下来修映射表别继续训。5.3 训练报 “invalid label”或者坐标出现 1.0003现象训练启动阶段抛出标签非法的异常日志里能看到某个 txt 行的数值略大于 1。原因VOC 转换时 xmax 或 ymax 等于图片宽高除以宽高后正好是 1.0浮点误差或者先加后除的顺序问题会让它变成 1.0000001。也有些标注工具在边缘画框时直接写出了超出图片尺寸的坐标。解决转换脚本里统一做 clamp并且跳过退化框这一点 3.3 的代码已经处理了。如果标签是别人给的、不方便重转可以写一个清洗脚本把每行读出来、裁剪到 [0,1]、再写回同时记录被修改的行数。清洗完一定要重新统计一遍每类框数确认没有因为裁剪把框剪成零面积。5.4 把空的 txt 当成坏文件删掉结果模型在纯背景图上疯狂误检现象数据集里有一部分 txt 是 0 字节看着像坏文件删掉之后训练集里全是带缺陷的图模型上线后在空板上频繁报缺陷。原因空 txt 在 YOLO 里是合法样本表示这张图里没有目标也就是负样本。负样本对抑制误检非常重要尤其 PCB 场景里良品板远多于缺陷板全给正样本会让模型倾向于“总要找出点什么”。解决保留空 txt只在体检脚本里单独统计数量。如果空 txt 比例太低比如不到 2%可以从良品图里再补一批纯背景图进来作为负样本扩充。补的时候注意图片和空 txt 要成对出现别只放图不放标签文件。5.5 训练不报错但一张图都没加载mAP 全程为 0现象训练正常启动loss 有变化但验证 mAP 一直是 0或者日志里显示的数据集数量远小于实际数量。原因文件名后缀大小写不一致。资源包里图片可能是.JPG、.jpeg而标签路径推导时按.jpg去找框架找不到就静默跳过不报错。另一种情况是图片放在images/train标签却放到了labels/train/xxx/多一层子目录路径替换后对不上。解决切分脚本里统一把后缀改成小写重命名一遍再拷贝。检查办法很直接训练启动后看日志里打印的 train 和 val 图片数量和你切分时统计的数字对不上就立刻停。这个坑不显眼但一旦踩上可能白跑一整晚。6. 验收与进阶用三个动作判断这份标注值不值得信训练跑通只是开始真正决定这份数据能不能用的是标注质量。我习惯在正式投入前做三个动作成本都不高。第一个动作是随机抽 30 张图做可视化叠框检查。把 xml 里的框画回原图重点看三件事框是否贴紧缺陷边缘、有没有把整块板子框进去的巨型框、同一处缺陷有没有被重复标两次。重复标注在 PCB 数据里很常见尤其是划伤这种细长目标标注的人容易在两端各标一个。第二个动作是看分类型 AP而不是总体 mAP。下面这张表是我每次实验都会填的填完一眼就能看出哪类拖后腿。类别AP50AP50-95框数验证集结论falsecoppermissingholemousebiteopencircuitpinholescratchshortcircuitspur如果 pinhole 和 spur 的 AP50 明显低于其他类先怀疑分辨率不够把 imgsz 从 640 提到 1024 再跑一版对比提到 1024 还不行就去看这几类的标注框是不是偏大或偏小有时候是标注口径不统一造成的。反过来如果 opencircuit 这种大类 AP 反而低多半是样本内部形态差异太大得考虑加数据或者拆分细分。第三个动作是用一个小模型快速过一遍高 loss 样本。训 10 个 epoch 就够了把验证集里 loss 最高的前 50 张图导出来人工看。这批图里通常藏着标注错误框标偏了、类别标错了、或者图本身就是模糊到无法判断的废图。把它们清理掉再重训指标往往能涨几个点比调参见效快。最后一个习惯每次改完数据集我都会把 2.3 的体检脚本、5.2 的映射校验脚本连着跑一遍两个都过了才允许启动训练。这个动作看起来繁琐但它拦下过至少三次类别错位和两次文件缺失每次都省掉了一整晚的无用功。数据集这种东西宁可前期多花半小时核对也别指望从训练曲线里反推问题出在哪。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑