简介:这份资源面向从事PCB表面缺陷检测的算法工程师、深度学习学习者与工业质检方向的研究人员,提供一套可直接用于目标检测训练与验证的标注数据集,帮助解决缺陷样本采集难、标注成本高的问题。压缩包共1386个文件,以693张jpg图像与693个xml标注文件为主,整体约907.52MB,图像与标注一一对应,便于直接读取与转换。缺陷覆盖Missing_hole、Mouse_bite、Open_circuit、Short、Spur、Spurious_copper六类,标签同时提供yolo格式txt与voc格式xml,可适配YOLO系列与两阶段检测框架。数据标注精确、类别分布均匀,适合训练拟合效果较好的检测模型,也可用于数据增强、模型对比与消融实验。目前已有2904人学习下载,具备一定参考价值。
1. 从 2700 张 PCB 缺陷图说起:这份数据集到底解决什么问题
PCB 表面缺陷检测是工业视觉里最典型的落地场景之一,但真正动手训练模型时,卡住大多数人的往往不是网络结构,而是数据。公开可用的 PCB 缺陷数据要么类别不全,要么标注格式单一,要么样本分布严重倾斜。这份数据集把 6 类常见缺陷——Missing_hole、Mouse_bite、Open_circuit、Short、Spur、Spurious_copper——整理成 2772 张图像,同时提供 VOC 的 xml 和 YOLO 的 txt 两套标签,省掉了格式转换这一步。
它适合两类人:一是想跑通 YOLO 训练全流程、但手头没有真实工业数据的学生和转行者;二是需要快速验证检测方案、做 baseline 对比的算法工程师。文件名里出现的04_spurious_copper_12.jpg这类命名,说明数据按缺陷类别做了前缀分组,方便你按类抽样检查标注质量。下面从格式解析讲到训练、评估和排错,把这份数据真正用起来。
2. VOC 与 YOLO 双格式标签的解析与互转
拿到压缩包后第一件事不是急着训练,而是搞清楚两种标签格式的坐标体系差异。VOC 用绝对像素坐标记录左上角和右下角,YOLO 用归一化后的中心点加宽高。理解这一点,后面无论是写转换脚本还是排查框偏移,都有依据。
2.1 VOC xml 的字段结构与读取
VOC 格式每张图对应一个 xml,核心信息在object节点里。用 Python 解析时,bndbox的四个值是绝对坐标,name是类别名。
import xml.etree.ElementTree as ET import os def parse_voc(xml_path): tree = ET.parse(xml_path) root = tree.getroot() # 图像尺寸,转换 YOLO 时归一化要用 size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) boxes = [] for obj in root.iter('object'): cls_name = obj.find('name').text bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) boxes.append((cls_name, xmin, ymin, xmax, ymax)) return w, h, boxes w, h, boxes = parse_voc('04_spurious_copper_12.xml') print(w, h, boxes)这段代码先取图像宽高,再逐个读取目标框。注意xmin等字段在部分标注工具里可能带空格,float()前最好strip()一下。如果 xml 里出现difficult或truncated字段,训练时可以选择性过滤,但这份数据集标注较干净,一般直接全用。
2.2 YOLO txt 的归一化坐标与类别索引
YOLO 每行是class_id cx cy w h,全部归一化到 0~1。类别索引依赖一个classes.txt或data.yaml里的names顺序,顺序错了模型学到的就是错类别。
# 典型目录结构 dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容大致如下,nc是类别数,names顺序必须和标注时的索引一致:
path: ./dataset train: images/train val: images/val nc: 6 names: ['Missing_hole', 'Mouse_bite', 'Open_circuit', 'Short', 'Spur', 'Spurious_copper']2.3 两种格式互转的脚本与常见坑
从 VOC 转 YOLO 是最常见的需求,核心是坐标归一化。下面这个脚本批量处理整个目录:
import os, glob def voc_to_yolo(xml_dir, out_dir, class_map): os.makedirs(out_dir, exist_ok=True) for xml_path in glob.glob(os.path.join(xml_dir, '*.xml')): w, h, boxes = parse_voc(xml_path) lines = [] for cls_name, xmin, ymin, xmax, ymax in boxes: cid = class_map[cls_name] cx = (xmin + xmax) / 2.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cid} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") name = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, name + '.txt'), 'w') as f: f.write('\n'.join(lines)) class_map = {'Missing_hole':0,'Mouse_bite':1,'Open_circuit':2, 'Short':3,'Spur':4,'Spurious_copper':5} voc_to_yolo('Annotations', 'labels', class_map)转换时最容易踩的坑有三个:一是class_map顺序和data.yaml不一致,导致类别错乱;二是坐标越界,xmax超过图像宽度时归一化后大于 1,训练会报错,建议加min(max(v,0),1)裁剪;三是文件名对不上,图片和标签必须同名,否则训练时会被当成无标签样本丢弃。
| 对比项 | VOC xml | YOLO txt |
|---|---|---|
| 坐标类型 | 绝对像素 | 归一化 0~1 |
| 单文件目标数 | 多目标 | 多行多目标 |
| 类别表达 | 字符串名 | 整数索引 |
| 常用框架 | Faster R-CNN、SSD | YOLO 系列 |
提示:转换完成后随机抽 10 张图用可视化脚本画框,确认框位置和类别都对,再进入训练,能省掉大量返工。
3. 用 YOLOv8 训练 PCB 缺陷检测模型
格式理清后,训练本身反而是流程化的事。这一章按环境、配置、启动、监控四步走,把这份数据真正喂进模型。
3.1 环境配置与依赖版本
YOLOv8 通过 ultralytics 包安装,Python 建议 3.8~3.10。显卡驱动和 CUDA 版本要匹配,否则会退回 CPU 训练,速度差几十倍。
conda create -n pcb python=3.10 -y conda activate pcb pip install ultralytics==8.1.0 # 验证 GPU 是否可用 python -c "import torch; print(torch.cuda.is_available())"输出True才说明 GPU 可用。如果为False,检查 CUDA 版本和 torch 是否匹配,常见做法是去 PyTorch 官网按驱动版本选对应安装命令重装。
3.2 data.yaml 与训练参数设置
数据按 8:2 划分训练和验证集。PCB 缺陷目标普遍偏小,输入尺寸建议不低于 640,小缺陷多时可以提到 1024,但显存占用会明显上升。
yolo detect train \ data=dataset/data.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/pcb \ name=exp1参数说明:model选yolov8s是精度和速度的折中,数据量 2700 张用 s 或 m 都合理;epochs=100配合patience=20表示 20 轮无提升就早停;batch根据显存调整,8G 显存跑 640 尺寸一般能到 16;lr0是初始学习率,默认 0.01 对这个小数据集够用。
3.3 训练过程监控与指标解读
训练启动后重点看三个指标:box_loss是否稳定下降、mAP50是否上升、验证集 loss 是否过拟合。如果训练 loss 降但验证 mAP 停滞,多半是数据分布或增强不够。
# 训练结束后在验证集上评估 yolo detect val model=runs/pcb/exp1/weights/best.pt data=dataset/data.yaml输出里mAP50和mAP50-95是核心。PCB 缺陷这类形状规整的目标,mAP50 通常能到 0.9 以上;如果某类特别低,比如 Short 或 Spur,去看该类样本量和标注框是否过小。常见做法是把混淆矩阵导出来,看类别之间是否互相误判。
3.4 数据增强对小缺陷的影响
YOLOv8 默认开启 mosaic、HSV 抖动和随机翻转。PCB 图像背景相对固定,颜色抖动幅度不宜过大,否则会破坏铜箔和基板的颜色特征。
# 在 data.yaml 同级建 hyp.yaml 覆盖默认增强 hsv_h: 0.010 hsv_s: 0.5 hsv_v: 0.3 mosaic: 0.5 scale: 0.3mosaic=0.5表示一半概率拼接四图,对小目标召回有帮助;scale=0.3控制缩放幅度,PCB 缺陷尺寸相对稳定,缩放太猛反而引入噪声。改完在训练命令里加hyp=hyp.yaml即可。
注意:增强参数没有万能值,改完必须重新跑验证集对比 mAP,凭感觉调参是训练里最常见的浪费。
4. 推理部署与漏检误检排查
模型训完只是开始,真正上线要看推理速度和误检情况。这一章讲导出、推理和典型问题的定位方法。
4.1 模型导出为 ONNX 与推理
生产环境常用 ONNX Runtime 或 TensorRT 部署,先导出再验证精度是否掉点。
yolo export model=runs/pcb/exp1/weights/best.pt format=onnx opset=12导出后用 onnxruntime 跑单张图,对比 PyTorch 输出,确认框位置一致。如果 ONNX 精度明显下降,检查opset版本和输入尺寸是否和训练一致。
4.2 单图与批量推理命令
# 单张图推理并保存结果 yolo detect predict model=runs/pcb/exp1/weights/best.pt \ source=test_images/ conf=0.25 save=True # 批量推理整个目录 yolo detect predict model=runs/pcb/exp1/weights/best.pt \ source=test_images/ conf=0.25 iou=0.45 save_txt=Trueconf=0.25是置信度阈值,漏检多就调低到 0.15,误检多就调高到 0.4;iou=0.45控制 NMS 重叠阈值,同类目标密集时适当调高避免框被抑制。
4.3 漏检与误检的定位思路
漏检优先查三处:标注框是否过小被增强裁掉、该类样本量是否过少、conf阈值是否过高。误检优先查背景样本是否混入、类别索引是否错位。下面这段脚本可以统计每个类别的检出数量,和标注数量对比:
from collections import Counter import glob, os cnt = Counter() for txt in glob.glob('runs/pcb/exp1/labels/*.txt'): with open(txt) as f: for line in f: cnt[int(line.split()[0])] += 1 print(cnt) # 对比各类标注总数,偏差大的类重点排查如果某类检出数远低于标注数,基本可以判定该类漏检严重,回去看该类样本的框尺寸分布。
| 现象 | 可能原因 | 处理方向 |
|---|---|---|
| 某类全漏 | 类别索引错位 | 核对 data.yaml 顺序 |
| 小目标漏检 | 输入尺寸偏小 | imgsz 提到 1024 |
| 背景误检 | 负样本不足 | 加入无缺陷图 |
| 框偏移 | 坐标转换错误 | 重查归一化脚本 |
5. 提升 PCB 缺陷检测精度的一个实用技巧
前面流程跑通后,想把 mAP 再往上推一档,最有效的往往不是换更大的模型,而是处理类别不均衡和难例。这份数据集 6 类缺陷分布相对均匀,但 Spur 和 Spurious_copper 在视觉上接近,容易互相误判,这是精度瓶颈的主要来源。
一个具体做法是:把这两类的混淆样本单独抽出来,做针对性增强。先用验证结果生成混淆矩阵,定位误判对,再对易混类别做 copy-paste 增强,把一类目标贴到另一类背景上,强制模型学习区分特征。
import cv2, random, os def paste_aug(img_path, label_path, out_img_dir, out_lbl_dir, times=3): img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as f: lines = [l.strip() for l in f if l.strip()] for i in range(times): aug = img.copy() new_lines = lines[:] # 随机选一个框,做小幅平移后重新贴回,模拟位置变化 for line in lines: cid, cx, cy, bw, bh = map(float, line.split()) dx = random.uniform(-0.05, 0.05) dy = random.uniform(-0.05, 0.05) new_lines.append(f"{int(cid)} {cx+dx:.6f} {cy+dy:.6f} {bw:.6f} {bh:.6f}") name = f"aug_{i}_" + os.path.basename(img_path) cv2.imwrite(os.path.join(out_img_dir, name), aug) with open(os.path.join(out_lbl_dir, name.replace('.jpg','.txt')), 'w') as f: f.write('\n'.join(new_lines))这段脚本对每个样本生成若干带位置扰动的副本,增加模型对位置变化的鲁棒性。times控制扩增倍数,一般 2~3 倍即可,太多会导致过拟合到合成样本。扩增后重新训练,重点观察 Spur 和 Spurious_copper 的混淆是否下降。
另一个技巧是推理时用 TTA(测试时增强),YOLO 支持通过augment=True开启:
yolo detect predict model=best.pt source=test_images/ augment=True conf=0.2TTA 会对输入做多尺度翻转推理再融合,mAP 通常能涨 1~2 个点,代价是推理速度下降约 3 倍,适合对精度敏感、对延迟不敏感的场景。上线前用同一批测试图对比开启前后的漏检率,再决定是否保留。
本文还有配套的精品资源,点击获取