
简介这是一份面向电子制造质检与计算机视觉方向研究者的PCB表面元器件缺陷检测数据集可用于训练和评估YOLO系列目标检测模型帮助解决电路板元器件缺陷自动识别问题。压缩包共1219个文件约57.41MB其中608张jpg图像与609个txt标签一一对应另含1个py可视化脚本和1个png说明图标签采用YOLO格式并已完成数据增强可直接投入训练。数据集划分了训练集与验证集class文件列出7个缺陷类别涵盖Miss、mask defect、defect等类型便于按类别分析模型表现。配套show脚本可将预测或标注的边界框绘制在图像上直观核对缺陷位置辅助调试与效果验证。目前已有391人学习下载适合需要快速搭建缺陷检测基线、开展数据增强对比实验或进行模型排错的中高级开发者参考使用。1. PCD表面元器件缺陷检测600张图的数据集到底能跑出什么PCB 表面贴装产线上最让人头疼的不是贴片机抛料而是回流焊之后那些肉眼难辨的缺陷——立碑、偏移、少锡、虚焊、异物残留。传统 AOI 设备靠规则模板比对换一个板型就要重新调参遇到反光、丝印遮挡、焊点形态差异就频繁误报。我见过一条产线因为 AOI 误报率太高最后干脆把阈值调到最松结果漏检的虚焊流到功能测试才暴露整批返修。PCDPrinted Circuit Board Defect表面元器件缺陷检测数据集就是冲着这个场景来的超过 600 张实拍图像配套标注文件覆盖元器件贴装后的常见表面缺陷类别。它的价值不在于“大”而在于“真”——工业现场的光照不均、板面反光、元件密集排列这些让模型翻车的因素它基本都保留了。适合两类人一是想用 YOLO 系列做工业质检落地、但手头没有真实产线数据的算法工程师二是做 AOI 二次开发、想验证深度学习方案是否比传统视觉更抗干扰的设备厂商。600 张不算多但作为迁移学习的起点和 pipeline 验证集够用了。2. 数据集拆开看600张图里藏着哪些标注门道2.1 缺陷类别与标注格式的对应关系拿到一个目标检测数据集第一件事不是急着训练而是把标注文件打开搞清楚类别定义和边界框的粒度。PCD 这类工业缺陷数据集标注粒度直接决定模型能学到什么。常见做法是每张图对应一个同名.txtYOLO 格式或.xmlVOC 格式每行/每个对象记录类别索引和归一化坐标。我一般会先跑一段统计脚本把类别分布和框的尺寸分布拉出来import os from collections import Counter label_dir labels/train class_names [missing, shift, solder_bridge, tombstone, foreign] counter Counter() box_sizes [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) w float(parts[3]) h float(parts[4]) counter[class_names[cls_id]] 1 box_sizes.append((w, h)) print(类别分布:, counter) if box_sizes: avg_w sum(b[0] for b in box_sizes) / len(box_sizes) avg_h sum(b[1] for b in box_sizes) / len(box_sizes) print(f平均框尺寸(归一化): w{avg_w:.4f}, h{avg_h:.4f})这段脚本做三件事统计每个类别的实例数量、计算归一化框的平均宽高、暴露类别不平衡问题。参数上class_names必须和数据集自带的data.yaml或classes.txt顺序严格一致否则训练出来的模型会把“立碑”认成“偏移”。如果发现某个类别实例数不到总数的 5%就要考虑过采样或 focal loss不然模型会直接摆烂——这是血泪经验。2.2 图像分辨率与增强策略的匹配600 张图的原始分辨率决定了你能不能用小目标检测的常规套路。工业相机拍 PCB常见分辨率在 200 万到 500 万像素之间单张图里元器件可能只占几十个像素。如果直接 resize 到 640×640 送进 YOLO小缺陷的像素信息会被进一步压缩召回率掉得厉害。我的做法是先读一批图看尺寸分布再决定输入尺度。import cv2 import glob import numpy as np img_paths glob.glob(images/train/*.jpg)[:50] sizes [] for p in img_paths: img cv2.imread(p) if img is not None: sizes.append(img.shape[:2]) sizes np.array(sizes) print(高度范围:, sizes[:, 0].min(), -, sizes[:, 0].max()) print(宽度范围:, sizes[:, 1].min(), -, sizes[:, 1].max()) print(中位尺寸:, np.median(sizes, axis0))如果中位尺寸远大于 640比如 2048×1536那训练时用imgsz1024或1280更合理但显存要跟上。另一个选择是切图把大图裁成重叠的小块每块单独标注和训练。切图能显著提升小目标召回但会引入边界截断问题——一个元件被切成两半标注框就不完整了。常见做法是切图时保留 10%15% 重叠区域并在后处理阶段用 NMS 合并跨块检测结果。注意切图之前一定确认标注文件是否已经按原图坐标归一化。如果标注是绝对坐标切图后必须重新计算每个框在新图里的位置否则标签全错。2.3 训练集/验证集划分的坑600 张图如果随机按 8:2 划分很容易出现某个缺陷类别在验证集里一个实例都没有的情况。工业缺陷数据集的类别分布天然不均衡随机划分就是给自己埋雷。我一般用分层抽样先按每张图包含的主要缺陷类别分组再在组内按比例抽验证集。import random from collections import defaultdict def split_dataset(label_dir, val_ratio0.2, seed42): random.seed(seed) groups defaultdict(list) for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: classes set() for line in f: parts line.strip().split() if parts: classes.add(int(parts[0])) key tuple(sorted(classes)) groups[key].append(fname) train, val [], [] for key, files in groups.items(): random.shuffle(files) n_val max(1, int(len(files) * val_ratio)) val.extend(files[:n_val]) train.extend(files[n_val:]) return train, val这段代码按“缺陷类别组合”分组保证每个组合在验证集里都有代表。val_ratio在数据量少的时候可以调到 0.15把更多样本留给训练。seed固定住方便复现。如果某个组合只有 12 张图max(1, ...)会强制抽一张进验证集虽然训练集少了一张但验证阶段能暴露该类别的真实表现比自欺欺人强。3. 用 YOLOv8 跑通 PCD 缺陷检测的最小闭环3.1 环境准备与数据配置文件YOLOv8 是目前工业缺陷检测落地最顺手的框架之一安装和训练接口都足够简洁。我一般用 conda 建一个干净环境避免和系统里的 OpenCV、PyTorch 版本打架。conda create -n pcd_defect python3.10 -y conda activate pcd_defect pip install ultralytics opencv-python pyyaml装完之后在数据集根目录建一个pcd.yaml内容如下path: /data/pcd_defect train: images/train val: images/val nc: 5 names: 0: missing 1: shift 2: solder_bridge 3: tombstone 4: foreignpath写绝对路径train和val写相对路径。nc是类别数names的顺序必须和标注文件里的类别索引一一对应。这个文件写错一个数字训练就会把类别映射搞乱loss 看着在降但推理结果全是错的——这种翻车现场我见过不止一次。3.2 训练命令与关键参数最小训练命令就一行但参数怎么设决定了你能不能跑出可用的模型yolo detect train \ datapcd.yaml \ modelyolov8s.pt \ epochs150 \ imgsz1024 \ batch8 \ lr00.001 \ lrf0.01 \ patience30 \ augmentTrue \ mosaic0.5 \ mixup0.1 \ projectruns/pcd \ nameexp1逐项说明modelyolov8s.pt是预训练权重600 张图从零训练基本没戏迁移学习是必须的。imgsz1024是为了保留小缺陷的像素细节如果显存不够降到 640但要有召回率下降的心理准备。batch8在 1024 尺度下对显存要求不低8GB 卡可能只能跑 4。lr00.001是初始学习率配合lrf0.01做余弦退火比固定学习率稳。patience30是早停验证集 30 轮不提升就停省时间。mosaic0.5和mixup0.1是增强强度工业缺陷检测里 mosaic 太强会引入不真实的拼接边界0.5 是个折中。训练过程中重点盯三个指标metrics/mAP50、metrics/mAP50-95和每个类别的instances。如果某个类别的 instances 一直是 0说明验证集里没有这个类回去检查划分脚本。3.3 推理与结果可视化训练完在runs/pcd/exp1/weights/best.pt拿到权重推理命令yolo detect predict \ modelruns/pcd/exp1/weights/best.pt \ sourceimages/val \ conf0.25 \ iou0.45 \ saveTrue \ projectruns/pcd_predictconf0.25是置信度阈值工业场景宁可多报不可漏报的话可以降到 0.15但误报会明显增加。iou0.45是 NMS 的 IoU 阈值元件密集排列时调低到 0.35 能减少框重叠导致的漏检。推理结果会保存成带框的图但真正要看的不是图好不好看而是每个类别的误报和漏报分布。我习惯把预测结果和真值放一起按类别算混淆矩阵找出模型到底在哪个缺陷上犯糊涂。4. 小目标与类别不平衡PCD 缺陷检测的两个硬骨头4.1 小目标检测的输入尺度与特征融合PCD 数据集里最难的类别通常是“少锡”和“异物”——目标像素面积小和背景对比度低。YOLOv8 的 P3 特征图 stride 是 8在 1024 输入下对应 128×128 的网格一个 20×20 像素的缺陷在 P3 上只有 23 个格子响应信息量非常有限。提升小目标召回的几个实操手段第一把输入尺度拉到 1280 甚至 1536代价是显存和推理时间线性增长。第二在pcd.yaml里确认没有把imgsz设得过小。第三如果框架支持开启 P2 特征层——YOLOv8 默认不输出 P2但可以通过修改模型配置加回来stride 变成 4小目标响应区域翻倍。第四训练时关掉或减弱 mosaic因为 mosaic 会把小目标缩得更小反而害了小目标学习。from ultralytics import YOLO model YOLO(yolov8s.pt) model.train( datapcd.yaml, epochs150, imgsz1280, batch4, mosaic0.0, scale0.3, fl_gamma1.5, projectruns/pcd, nameexp_small )这里mosaic0.0关掉拼接增强scale0.3限制随机缩放幅度fl_gamma1.5开启 focal loss 缓解类别不平衡。focal loss 对易分类样本降权让模型更关注难样本但 gamma 设太大比如 3.0会导致训练不稳定1.5 是工业场景常用的起点。4.2 类别不平衡的采样与损失加权600 张图里“偏移”可能有两千个实例“立碑”只有几十个。这种长尾分布下模型会倾向于预测高频类别低频类别的召回率惨不忍睹。除了 focal loss还有两个手段过采样和类别权重。过采样不是简单复制图片而是对包含稀有类别的图做额外增强——旋转、亮度扰动、局部裁剪——再喂给模型。类别权重则是在损失函数里给稀有类别更高的惩罚系数。YOLOv8 没有直接暴露类别权重参数但可以通过自定义 dataset 或修改 loss 实现。更简单的做法是在验证集里单独看稀有类别的 mAP如果低于 0.3就说明需要干预了。import cv2 import os import random rare_class_id 3 # tombstone rare_images [] for fname in os.listdir(labels/train): if not fname.endswith(.txt): continue with open(os.path.join(labels/train, fname)) as f: for line in f: if line.startswith(str(rare_class_id)): rare_images.append(fname.replace(.txt, .jpg)) break for img_name in rare_images: img cv2.imread(os.path.join(images/train, img_name)) if img is None: continue for i in range(3): angle random.uniform(-15, 15) h, w img.shape[:2] M cv2.getRotationMatrix2D((w//2, h//2), angle, 1.0) rotated cv2.warpAffine(img, M, (w, h)) cv2.imwrite(fimages/train/aug_{i}_{img_name}, rotated)这段脚本对稀有类别图片做小角度旋转增强生成额外样本。角度控制在 ±15 度以内因为 PCB 上的元件方向有物理约束转太多会引入不真实的样本。增强后的图需要同步复制标注文件否则训练时找不到标签。提示过采样之后一定要重新划分训练/验证集确保增强样本不会泄漏到验证集里。否则验证指标虚高上线就翻车。5. 避坑与排查PCD 缺陷检测训练中最容易翻车的 5 个点5.1 现象loss 正常下降但 mAP 始终为 0原因标注文件的类别索引和pcd.yaml里的names顺序不一致或者标注坐标没有归一化。YOLO 格式要求坐标是相对于图像宽高的浮点数01如果标注是绝对像素坐标模型学到的框会全部跑到图像外面。解决写一个校验脚本遍历所有标注文件检查坐标是否在 01 之间类别索引是否小于nc。发现越界就说明格式不对需要转换。def validate_labels(label_dir, nc): errors [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: errors.append((fname, i, 字段数不对)) continue cls_id int(parts[0]) coords [float(x) for x in parts[1:]] if cls_id nc: errors.append((fname, i, f类别越界: {cls_id})) if any(c 0 or c 1 for c in coords): errors.append((fname, i, 坐标未归一化)) return errors5.2 现象验证集 mAP 很高但拿产线新图推理全是误报原因训练集和验证集来自同一批拍摄条件模型过拟合了特定光照和背景。PCD 数据集如果全部在一个工位、一种光源下采集模型学到的是“这个背景下的缺陷”而不是“缺陷本身”。解决在训练增强里加入亮度、对比度、色调扰动模拟不同光照。如果条件允许留出一部分不同批次、不同光照的图做测试集哪怕只有几十张也能暴露泛化问题。model.train( datapcd.yaml, epochs150, imgsz1024, hsv_h0.015, hsv_s0.7, hsv_v0.4, degrees10, translate0.1, scale0.3, fliplr0.5, flipud0.0 )hsv_v0.4是亮度扰动幅度工业场景光照变化大这个值可以拉到 0.5。flipud0.0是因为 PCB 上下翻转不符合物理实际开了反而引入噪声。5.3 现象训练到一半 loss 突然变成 NaN原因学习率太大或者某批数据里出现了损坏的图像/标注。工业数据集里偶尔会有空标注文件或截断的 JPEGYOLO 的 dataloader 遇到这些会产出异常梯度。解决训练前用脚本过滤掉空标注和无法读取的图像。学习率方面lr00.001对 batch8 是安全的如果 batch 更小学习率要相应降低。import cv2 def filter_bad_samples(img_dir, label_dir): bad [] for fname in os.listdir(img_dir): img_path os.path.join(img_dir, fname) label_path os.path.join(label_dir, fname.replace(.jpg, .txt)) img cv2.imread(img_path) if img is None: bad.append(fname) continue if not os.path.exists(label_path) or os.path.getsize(label_path) 0: bad.append(fname) return bad5.4 现象推理时同一个缺陷被框了多次原因NMS 的 IoU 阈值设得太高或者模型对同一目标输出了多个高置信度框。元件密集排列时相邻元件的框容易重叠NMS 分不清是同一个目标还是不同目标。解决把iou从默认 0.7 降到 0.45 甚至 0.35同时观察是否出现漏检。如果降 IoU 导致漏检增加说明模型定位精度不够需要回到训练阶段提升box_loss的收敛质量。5.5 现象换了新板型后模型完全失效原因PCD 数据集的 600 张图覆盖的板型和元件类型有限模型没有见过新板型上的元件排列和焊点形态。这是工业缺陷检测的固有难题——缺陷定义随产品变化。解决把模型当作特征提取器在新板型上只标注几十张图做微调。冻结 backbone只训练检测头学习率设小一点lr00.0001通常 2030 轮就能适应新板型。如果新板型的缺陷类别不同需要修改nc和names并重新初始化检测头。6. 从 600 张到产线可用微调策略与验证习惯600 张图训练出来的模型直接上线大概率是不够的。我的习惯是把它当作一个“预训练起点”而不是最终交付物。具体做法分三步第一步用 PCD 数据集训练一个基模型确认 pipeline 跑通、指标合理第二步在目标产线上采集 100200 张真实图人工标注后做微调学习率降到基模型训练时的十分之一第三步用留出的产线测试集算每个类别的召回率和误报率定一个可接受的阈值。微调时有个细节容易忽略冻结层数。YOLOv8 的freeze参数可以指定冻结前 N 层工业场景我一般冻结前 10 层让 backbone 的低层特征保持通用高层特征和检测头适应新数据。如果新板型和原数据集差异极大比如从消费电子板换到汽车电子板冻结层数要减少甚至不冻结。yolo detect train \ datapcd_new.yaml \ modelruns/pcd/exp1/weights/best.pt \ epochs50 \ imgsz1024 \ batch8 \ lr00.0001 \ freeze10 \ patience15 \ projectruns/pcd_finetune验证阶段我坚持看两个东西一是按类别拆分的 PR 曲线二是误报样本的视觉抽查。PR 曲线告诉你模型在什么置信度下能达到多少召回视觉抽查告诉你模型到底把什么认错了。我见过一个模型把丝印上的白色圆点认成“异物”PR 曲线看着还行但抽查误报样本时一眼就发现问题。这种错误只能靠人眼抓指标抓不到。最后一个习惯每次训练完把权重文件、pcd.yaml、训练命令和验证指标写进一个experiment_log.md放在项目根目录。过两个月回头看你能准确知道哪个模型是用什么数据、什么参数跑出来的。没有这个日志三个月后你面对一堆best.pt文件根本分不清哪个是哪个——这是后悔药提前吃。希望帮到你。本文还有配套的精品资源点击获取