☰
PCB缺陷数据集实战:YOLO与VOC双格式训练及避坑指南
2026/10/10 18:43:22 网站建设 项目流程

简介:这份资源面向从事PCB表面缺陷检测的算法工程师、高校研究者与深度学习学习者,提供一套可直接用于目标检测训练与验证的标注数据集,帮助解决工业质检场景中样本获取难、标注成本高的问题。压缩包共1386个文件,由693张jpg图像与693个xml标注文件组成,整体约907.52MB,标签同时提供VOC的xml格式与YOLO的txt格式,便于在Faster R-CNN、YOLO等主流框架间灵活切换。缺陷覆盖Missing_hole、Mouse_bite、Open_circuit、Short、Spur、Spurious_copper六类,标注精确、类别分布均匀,可支撑较稳定的模型拟合效果。目前已有2904人学习下载,适合作为PCB缺陷识别项目的训练底库或算法对比基准,也可用于数据增强、类别均衡与检测精度调优等实验。

1. 从一批spurious_copper文件名说起:这份 PCB 缺陷数据集到底能干什么

如果你手头正好有一批04_spurious_copper_12.jpg、04_spurious_copper_16.jpg这样的文件名,大概率你拿到的就是一份按缺陷类别前缀命名的 PCB 缺陷数据集。这份资源的核心是 2700 余张 PCB 板面图像,覆盖 6 类典型缺陷:Missing_hole(缺孔)、Mouse_bite(鼠咬)、Open_circuit(开路)、Short(短路)、Spur(毛刺)、Spurious_copper(余铜)。每张图同时提供 YOLO 格式的 txt 标签和 Pascal VOC 格式的 xml 标签,意味着你既可以直接喂给 YOLO 系列做检测训练,也能走 VOC 路线接 Faster R-CNN、SSD 这类框架。它解决的是「手头没有工业缺陷样本、自己标注成本高、类别不均衡」这三个最现实的问题,适合做 PCB 质检方向的学生、做工业视觉落地的算法工程师,以及需要快速搭一个缺陷检测 baseline 的从业者。需要提前说明的是,受上传体积限制,公开部分约 693 张,剩余部分作者通过邮箱补发,这一点在动手前要心里有数。

2. 数据集结构与两种标签格式:先看懂目录再谈训练

拿到压缩包后别急着解压完就丢进训练脚本,先把目录结构和标签格式吃透,否则后面报错你连是哪一层出的问题都定位不到。这一章把「文件怎么摆、两种标签怎么读、怎么互相转」讲清楚,这是后面所有训练动作的地基。

2.1 目录组织与文件命名规律

这类数据集常见的组织方式是按缺陷类别分文件夹,或者图像和标签平铺在同一目录靠文件名前缀区分。从给出的文件名看,命名规则是「编号_类别名_序号.jpg」,比如04_spurious_copper_12.jpg表示第 4 组、余铜缺陷、第 12 张。这种命名有个好处:即使标签文件丢了,你也能从文件名反推类别,做数据校验时非常省事。

我一般拿到手先跑一遍统计,确认每类数量、有没有空标签、有没有图像和标签对不上的情况。下面这段脚本就是干这个的,放在数据集根目录执行即可。

import os from collections import Counter root = "./PCB_DATASET" # 换成你的实际路径 img_dir = os.path.join(root, "images") lbl_dir = os.path.join(root, "labels") # 统计每类图像数量(按文件名前缀里的类别名) classes = ["Missing_hole", "Mouse_bite", "Open_circuit", "Short", "Spur", "Spurious_copper"] counter = Counter() for f in os.listdir(img_dir): if not f.lower().endswith((".jpg", ".png")): continue for c in classes: if c in f: counter[c] += 1 break print("每类图像数量:", dict(counter)) # 检查图像与标签是否一一对应 imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbls = {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} print("有图无标签:", imgs - lbls) print("有标签无图:", lbls - imgs)

逻辑说明:第一段按类别名做子串匹配统计分布,能快速看出六类是否均衡;第二段用集合差集找出配对异常。参数上,root要改成你解压后的真实路径,classes列表必须和文件名里的类别拼写完全一致,大小写敏感。如果输出里「有图无标签」不是空集,说明这批数据需要先清洗,别硬着头皮训练,否则 YOLO 会直接跳过这些图,你以为训了 2700 张,实际可能只用了 2000 张。

2.2 YOLO txt 与 VOC xml 的字段对照

两种格式描述的是同一件事——目标框位置和类别,但坐标系和存储方式完全不同。YOLO 的 txt 每行一个目标,格式是class_id x_center y_center width height,全部归一化到 0~1;VOC 的 xml 用绝对像素坐标,存xmin ymin xmax ymax。搞混这两个是新手最常见的翻车点,训练时 loss 不降、框全跑到角落,八成就是坐标系用错了。

维度YOLO txtVOC xml
坐标类型归一化中心点+宽高绝对像素左上+右下
类别表示数字 id(0~5)字符串 name
单文件目标数多行,每行一个多个<object>节点
常用框架YOLOv5/v8/v11Faster R-CNN、SSD
是否需要类别映射文件需要(classes.txt)不需要,name 直接写

类别 id 的映射顺序必须固定,建议按这个顺序建classes.txt:Missing_hole、Mouse_bite、Open_circuit、Short、Spur、Spurious_copper,对应 0 到 5。一旦训练中途改了顺序,之前训的权重就废了,这是血泪经验。

2.3 两种格式互转的脚本

实际项目里经常遇到「只有 VOC 但要训 YOLO」或者反过来。下面这个转换脚本覆盖两个方向,读图尺寸拿真实宽高做归一化,避免用错基准。

import os import xml.etree.ElementTree as ET from PIL import Image classes = ["Missing_hole", "Mouse_bite", "Open_circuit", "Short", "Spur", "Spurious_copper"] cls2id = {c: i for i, c in enumerate(classes)} def voc2yolo(xml_path, img_path, out_txt): tree = ET.parse(xml_path) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in cls2id: continue bb = obj.find("bndbox") xmin = float(bb.find("xmin").text) ymin = float(bb.find("ymin").text) xmax = float(bb.find("xmax").text) ymax = float(bb.find("ymax").text) # 转成归一化中心点+宽高 xc = (xmin + xmax) / 2 / w yc = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls2id[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") with open(out_txt, "w") as f: f.write("\n".join(lines)) def yolo2voc(txt_path, img_path, out_xml): img = Image.open(img_path) w, h = img.size root = ET.Element("annotation") ET.SubElement(root, "filename").text = os.path.basename(img_path) size = ET.SubElement(root, "size") ET.SubElement(size, "width").text = str(w) ET.SubElement(size, "height").text = str(h) with open(txt_path) as f: for line in f: if not line.strip(): continue cid, xc, yc, bw, bh = map(float, line.split()) xmin = int((xc - bw / 2) * w) ymin = int((yc - bh / 2) * h) xmax = int((xc + bw / 2) * w) ymax = int((yc + bh / 2) * h) obj = ET.SubElement(root, "object") ET.SubElement(obj, "name").text = classes[int(cid)] bb = ET.SubElement(obj, "bndbox") for k, v in zip(["xmin", "ymin", "xmax", "ymax"], [xmin, ymin, xmax, ymax]): ET.SubElement(bb, k).text = str(v) ET.ElementTree(root).write(out_xml)

逻辑说明:voc2yolo从 xml 读绝对坐标,除以图像宽高做归一化,中心点用(xmin+xmax)/2算;yolo2voc反过来,先乘回像素再取整。参数上要注意两点,一是cls2id的顺序必须和训练时的classes.txt一致,二是yolo2voc里int()截断可能让框比原图小一像素,对精度要求高的场景建议用round()。转换完务必抽查几张,用可视化脚本画框确认,别信脚本不验证。

3. 用这份数据训一个 YOLO 检测模型:从环境到出权重

数据看懂了,接下来就是把它跑起来。这一章按「装环境 → 配数据 → 改配置 → 开训 → 看结果」的顺序走,每一步都给出可抄的命令和参数含义,新手照着能出权重,熟手可以对照检查自己的配置有没有埋雷。

3.1 环境搭建与依赖版本

YOLO 系列对 PyTorch 和 CUDA 版本比较敏感,版本对不上轻则警告重则直接崩。我一般用 conda 建独立环境,避免污染主环境。

conda create -n pcb_yolo python=3.10 -y conda activate pcb_yolo # 按你的 CUDA 版本装 torch,下面以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pillow lxml

逻辑说明:python=3.10是当前 YOLOv8/v11 兼容性最好的版本;torch 的 index-url 要和你显卡驱动支持的 CUDA 版本匹配,用nvidia-smi看右上角 CUDA Version。ultralytics一个包就把 YOLOv8/v11 的训练、验证、导出全带了,比早年克隆仓库改代码省事得多。装完跑一句yolo checks确认环境,它会打印 torch、CUDA、设备信息,这一步别省。

3.2 数据集配置文件与目录规范

YOLO 训练要求固定的目录结构,图像和标签分开放,且路径要在 yaml 里声明。建议整理成这样:

pcb_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── pcb.yaml

pcb.yaml内容如下:

path: ./pcb_dataset train: images/train val: images/val nc: 6 names: ['Missing_hole', 'Mouse_bite', 'Open_circuit', 'Short', 'Spur', 'Spurious_copper']

逻辑说明:path是数据集根目录,train/val是相对路径;nc是类别数,必须和names长度一致,写错了训练时类别索引会越界。划分比例常见做法是 8:2,但 PCB 缺陷样本少,建议按类别分层抽样,保证 val 里六类都有,否则验证指标会失真。划分脚本用sklearn.model_selection.train_test_split加stratify参数即可,这里不展开。

3.3 训练命令与关键参数

配置齐了就能开训。下面这条命令是我在 8G 显存上跑通的最小可用配置:

yolo detect train \ data=./pcb_dataset/pcb.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/pcb \ name=exp1

逻辑说明:model=yolov8n.pt用 nano 版先跑通流程,显存不够就降到batch=8;imgsz=640是默认输入尺寸,PCB 缺陷目标偏小,可以试imgsz=1024但显存翻倍;lr0=0.01是初始学习率,小数据集别调太大否则 loss 震荡;patience=20表示 20 轮没提升就早停,省时间。训练日志里重点看box_loss和mAP50,前者持续不降说明标签有问题,后者上不去可能是类别不均衡或学习率不对。

3.4 推理与结果验证

训完在runs/pcb/exp1/weights/下会有best.pt和last.pt,用 best 做推理:

yolo detect predict \ model=runs/pcb/exp1/weights/best.pt \ source=./pcb_dataset/images/val \ conf=0.25 \ save=True

逻辑说明:conf=0.25是置信度阈值,低于它的框不显示,工业场景漏检代价高可以降到 0.1 再人工复核;save=True把画框结果存到runs/detect/predict。验证时别只看 mAP,一定要肉眼看几张漏检和误检的图,PCB 的 Short 和 Spurious_copper 在低分辨率下容易混,这是数据本身的难点,不是模型的问题。

4. 避坑与排查:这份数据集最容易翻车的五个地方

数据、代码、环境都摆好了,真正让人卡住的往往是些不起眼的细节。这一章把我在类似 PCB 缺陷数据集上踩过的坑按「现象 → 原因 → 解决」列出来,遇到问题先对照排查,能省下大量瞎试的时间。

4.1 训练 loss 不降,框全堆在图像中心

现象:开训几轮后box_loss卡在高位不动,可视化预测框全挤在图像中央。原因:YOLO 标签坐标系用错,把 VOC 的绝对像素坐标直接当归一化坐标喂进去了,数值远大于 1,模型学不到有效位置。解决:确认 txt 里每个数值都在 0~1 之间,用 2.3 的转换脚本重新生成标签,转换后抽查数值范围。

4.2 类别索引错位,预测全标成同一类

现象:模型能框出目标,但类别几乎全是Missing_hole。原因:classes.txt或 yaml 里names的顺序和生成标签时的cls2id不一致,比如生成时 Short 是 3,配置里写成了 2。解决:全流程统一一份类别顺序文件,生成标签、训练配置、推理后处理都读同一个来源,别手写多份。

4.3 显存溢出 CUDA out of memory

现象:训练刚开始就报 OOM。原因:batch或imgsz超过显存容量,PCB 图分辨率高时尤其明显。解决:先把batch降到 8 或 4,再考虑降imgsz;也可以用yolo detect train ... amp=True开混合精度,显存能省三成左右。

4.4 验证集 mAP 虚高但实际漏检严重

现象:mAP50到 0.9 以上,实际推理却漏掉很多小缺陷。原因:训练集和验证集划分时没分层,验证集里恰好都是大目标或简单样本。解决:按类别分层抽样重划数据集,确保 val 里六类都有且包含小目标样本,重新评估。

4.5 图像与标签数量对不上,训练悄悄丢样本

现象:明明有 2700 张图,训练日志显示的样本数却少了几百。原因:部分图像没有对应标签文件,YOLO 默认跳过。解决:用 2.1 的配对检查脚本找出缺失项,要么补标签,要么把这些图移出训练集,别让它们静默消失。

5. 进阶技巧:把这份数据用到极致的两三个动作

跑通 baseline 只是起点,真正决定模型能不能落地的是后面这些细节。这一章讲几个我常用的进阶动作,包括数据增强策略、小目标处理和模型导出,都是能直接提升实际效果的。

5.1 针对 PCB 缺陷的数据增强配置

PCB 缺陷里 Spur 和 Spurious_copper 目标很小,默认增强可能不够。YOLO 支持在训练命令里直接调增强参数:

yolo detect train \ data=./pcb_dataset/pcb.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=1024 \ batch=8 \ mosaic=1.0 \ scale=0.5 \ fliplr=0.5 \ hsv_h=0.015 \ hsv_s=0.7

逻辑说明:mosaic=1.0开启马赛克增强,对小目标检测帮助明显;scale=0.5允许缩放,模拟不同拍摄距离;fliplr=0.5水平翻转,PCB 缺陷方向不敏感可以放心用;hsv_h和hsv_s调色调饱和度,应对不同光照的工业相机。注意imgsz=1024时batch要相应降,否则 OOM。

5.2 小目标检测的切片推理

PCB 整板图里缺陷占比很小,直接缩到 640 会丢细节。常见做法是切片推理(SAHI 思路),把大图切成带重叠的小块分别检测再合并。核心参数是切片尺寸和重叠比例:

参数建议值说明
slice_size640与训练输入一致
overlap0.2切片间重叠,避免边缘目标被切断
conf0.1切片后置信度可调低,靠 NMS 合并

切片推理的代价是速度变慢,一张整板图可能切成几十块,推理时间线性增长。如果产线节拍要求高,得在精度和速度之间权衡,我一般先测切片方案的召回提升幅度,提升不明显就不上。

5.3 模型导出与部署前验证

训练完的 pt 权重不能直接上产线,通常要导出成 ONNX 或 TensorRT。导出命令:

yolo export model=runs/pcb/exp1/weights/best.pt format=onnx opset=12 simplify=True

逻辑说明:opset=12兼容性较好,simplify=True会做图优化去掉冗余节点。导出后务必用同一批验证图对比 pt 和 onnx 的推理结果,确认精度没有明显下降再部署。我见过导出后框偏移的案例,原因是预处理里的归一化参数没对齐,所以这一步的验证不能省。

从那以后我每次拿到新数据集,都强制先跑一遍配对检查和类别分布统计,再动手训练。这份 PCB 缺陷数据集的价值在于它把六类典型缺陷和两种标签格式都备齐了,省掉了最耗时的标注环节,剩下的就是你怎么把它用好。希望帮到你。

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

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

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

立即咨询