简介:面向工业视觉与电子制造质检场景,这套PCB缺陷检测数据集覆盖桥接、缺件、短路、断路、偏移、极性错误六类典型线路板瑕疵,共680张已标注图像,可直接用于YOLO系列模型训练与算法验证,适合高校《机器视觉》课程实训、电子制造企业质检算法预研及入门学习者实践。包体内共1376个文件,693个txt为YOLO格式标注框,680张jpg为原始图像,另含data.yaml配置与训练缓存文件,整体压缩后77.85MB,目录结构清晰便于批量读取。每张图像均采用归一化五元组标注,按7:2:1划分训练、验证与测试集,类间分布均衡,并附中文使用指南、MD5校验清单及缺陷分布分析脚本,可帮助快速完成从数据加载、模型微调到结果可视化的完整流程。此数据集在YOLOv8n模型上测试集mAP达到86.3%,可作为入门基准参考。目前已有26人学习下载,是轻量级、可直接落地的PCB缺陷检测实训数据集。
1. PCB缺陷检测数据集:为什么680张图也能撑起一个YOLO项目
做过产线AOI相关的朋友都懂,真正麻烦的不是算法选型,是手里根本没有像样的缺陷样本。PCB板上的开路、短路、鼠咬这类缺陷,良品率一高,想攒一套带标注的数据比登天还难。所以当我看到这套6类、680张的PCB缺陷检测数据集时,第一反应是:量不大,但够用了。它是标准的YOLO格式,每张图对应同名txt标注文件,类别覆盖了PCB缺陷检测里最常碰到的六种情况,拿到就能直接喂给YOLOv5或YOLOv8训练,不用自己费劲去转格式。适合谁?如果你是做课程设计、算法预研、或者想验证某个缺陷检测思路能不能跑通,这套数据正好卡在"麻雀虽小五脏俱全"的档位上。下面我从数据格式讲起,把训练、推理、踩坑一条龙过一遍。
2. 数据集结构与YOLO标注格式:先搞懂文件布局再动手
2.1 六类缺陷分别是什么:类名与典型特征
这套数据集的类别设计沿用了PCB缺陷检测里最经典的六分类体系,分别对应板卡制造过程中最常见的六类异常。识别这六类缺陷靠的是图像上的形态差异:断路(open)表现为本该连通的走线中间出现断开缺口;短路(short)则是两条不该碰到的走线粘连在一起;鼠咬(mousebite)是走线边缘出现规则的小半圆缺口,像被老鼠啃过;毛刺(spur)是走线上多出来一小段尖锐凸起;铜面异物(copper)是铜箔区域出现多余的铜渣或异常沉积;针孔(pin-hole)是铜面上出现极小的圆孔,反射特征和周围明显不同。
从检测难度上看,open和short这类缺陷面积相对大、纹理结构清晰,YOLO模型学起来比较容易;而pin-hole和mousebite目标尺寸小、特征弱,是拉低mAP的主要来源。680张图里这六类并不是均匀分布的,open和short的样本量通常明显多于其他四类,这一点在训练之前心里要有数,后面避坑章节我会专门讲类别不平衡的处理。至少拿到数据后第一件事,不是直接开训,而是按下面脚本统计一下每个类到底有多少框。
2.2 YOLO标签文件解读:归一化坐标不是像素坐标
YOLO格式的标签文件是纯文本,每行代表一个目标框,格式是五个字段:class_id x_center y_center width height。这里最容易翻车的地方是:后面四个数不是像素值,而是相对图像宽高的归一化比例。比如一行标签是2 0.5234 0.3187 0.0821 0.0458,意思是这个目标属于类别2(鼠咬),框的中心点落在图像宽度52.34%、高度31.87%的位置,框的宽度占图像宽度8.21%、高度占图像高度4.58%。
为什么YOLO非要这么设计?因为训练时dataloader会把图像统一缩放到imgsz尺寸,如果标注存的是原始像素坐标,缩放后坐标全错乱了;归一化坐标是比例值,图像不管缩放到多大,比例关系不变。这一点如果你之前用过VOC格式的XML标注(存的是绝对像素坐标),转YOLO时容易踩坑。常用的转换公式是:center_x = (x_min + x_max) / 2 / img_width,center_y = (y_min + y_max) / 2 / img_height,width = (x_max - x_min) / img_width,height = (y_max - y_min) / img_height。四舍五入保留6位小数足够训练用了。
在动手训练之前,建议把标签文件打开看几行。一般数据集目录结构是images/放原图、labels/放同名txt、classes.txt里按行写六类名称、data.yaml里配置路径和类别数。拿到资源后先把目录结构敲一遍确认,别直接往训练代码里灌路径。
2.3 标签质量校验:一个脚本看出所有问题
我拿到任何标注数据,第一步永远不是训练,而是写脚本全量扫一遍标签。680张图说多不多,但手工标注难免有框出界、类别号错了、宽高为零等问题。这些脏数据直接进YOLO训练,轻则loss异常,重则训练直接崩。下面这个脚本可以统计每类框数量、检查坐标越界和宽高非法,建议放在数据集根目录下运行。
from pathlib import Path from collections import Counter def validate_labels(label_dir: Path, img_w: int = 640, img_h: int = 640): issues = [] class_counter = Counter() total_boxes = 0 for txt_path in sorted(label_dir.glob("*.txt")): for line_no, line in enumerate(txt_path.read_text(encoding="utf-8").splitlines(), 1): parts = line.split() if len(parts) != 5: issues.append((txt_path.name, line_no, f"字段数不是5,实际为{len(parts)}")) continue try: cls_id, cx, cy, w, h = map(float, parts) except ValueError as e: issues.append((txt_path.name, line_no, f"float解析失败: {e}")) continue class_counter[cls_id] += 1 total_boxes += 1 if not (0 <= cls_id <= 5): issues.append((txt_path.name, line_no, f"class_id越界: {int(cls_id)}")) if w <= 0 or h <= 0: issues.append((txt_path.name, line_no, f"宽高非正: w={w:.4f}, h={h:.4f}")) if max(abs(cx - 0.5), abs(cy - 0.5)) > 0.52 or w > 1.02 or h > 1.02: issues.append((txt_path.name, line_no, f"归一化坐标疑似越界: {line}")) print(f"总框数: {total_boxes}") for cls_id in sorted(class_counter.keys()): print(f"类别 {int(cls_id)}: {class_counter[cls_id]} 个框") if issues: print(f"发现 {len(issues)} 个问题:") for name, line_no, desc in issues[:30]: print(f" {name}:{line_no} - {desc}") else: print("所有标签文件检查通过") return class_counter, issues if __name__ == "__main__": validate_labels(Path("labels"))这段脚本的核心逻辑是逐行解析五个字段,先检查字段数量是否等于5,再做float转换,最后做三类合法性判断:类别号是否在0到5之间、框宽高是否大于0、归一化坐标是否严重越界。第三类判断用了中心点偏离阈值和宽高上限,因为实际标注时偶尔会有轻微越界(比如框贴边超出0.5%),超过2%的越界基本就是标注失误了,这类样本要么退回重标,要么手动改掉。脚本跑完会输出每类的框数量和前30条问题记录,方便定位具体是哪个文件哪一行出错。
跑完这个脚本,你对这份数据集的健康状况就有底了。如果open和short占了过半的框,剩下四类每类只有几十个框,那训练策略就得往数据增强和类别平衡方向倾斜。
3. 基于YOLOv8的PCB缺陷检测实战:从训练到推理全流程
3.1 环境准备:ultralytics版本与显卡选型
训练这套数据集推荐直接用ultralytics库,YOLOv8和YOLO11都支持,接口一致。装环境这一步很简单但容易出幺蛾子,我习惯用Python 3.9以上的干净虚拟环境来装。执行下面的命令就能把推理和训练依赖一次装齐。
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple如果只是训练这个680张的小数据集,CPU也能跑,但一个epoch可能要几分钟到十几分钟,100个epoch下来很折磨。建议有NVIDIA显卡就装上CUDA版的PyTorch,显存6GB以上跑yolov8n和yolov8s都够。装完用下面命令确认GPU可用:
python -c "import torch; print(torch.cuda.is_available())"输出True说明CUDA环境没问题,输出False就先去装对应版本的PyTorch。注意PyTorch的CUDA版本要跟显卡驱动匹配,别上来就装最新版,老卡配新CUDA容易翻车。
3.2 数据集划分与data.yaml配置
数据集里的680张图,默认不会给你分好train/val/test。我的习惯是用脚本按8:1:1划分,划分前先pip install scikit-learn,用train_test_split做分层随机抽样,确保每个子集里六类框的比例跟全集接近。
import shutil from pathlib import Path from sklearn.model_selection import train_test_split base_dir = Path(".") images = sorted((base_dir / "images").glob("*.jpg")) train_imgs, val_imgs = train_test_split(images, test_size=0.2, random_state=42) val_imgs, test_imgs = train_test_split(val_imgs, test_size=0.5, random_state=42) for split in ["train", "val", "test"]: (base_dir / split / "images").mkdir(parents=True, exist_ok=True) (base_dir / split / "labels").mkdir(parents=True, exist_ok=True) def move_files(img_list, split): for img in img_list: label = base_dir / "labels" / (img.stem + ".txt") shutil.move(str(img), str(base_dir / split / "images")) shutil.move(str(label), str(base_dir / split / "labels")) move_files(train_imgs, "train") move_files(val_imgs, "val") move_files(test_imgs, "test")划分完了之后,项目目录结构就变成train/images、train/labels、val/...、test/...这样。random_state=42是为了让你的随机结果可复现,方便和别人对实验结论;如果你第一次划分后模型效果很差,换一个seed重新划分对比是个可行的调优思路。
接下来写data.yaml配置文件。注意路径建议写绝对路径,省的每次换机器都要改。文件内容如下:
path: /data/pcb_defect # 数据集根目录的绝对路径 train: train/images val: val/images test: test/images nc: 6 names: 0: open 1: short 2: mousebite 3: spur 4: copper 5: pin-holenc是类别总数6,names里的顺序必须和标签txt里的class_id严格对应。如果classes.txt的排序是另一回事,务必改data.yaml的映射,而不是去改几百个txt文件。
3.3 训练参数:为什么从yolov8n开始
很多新手一上来就选yolov8x,觉得模型越大约好,结果680张图训出来的效果甚至不如yolov8n。小数据集配大模型,模型参数远多于样本蕴含的信息量,妥妥过拟合。我的做法是以yolov8n.pt为起点,它只有约300万参数,训练速度快,还能加载COCO预训练权重做迁移学习。
yolo detect train \ data=pcb.yaml \ model=yolov8n.pt \ epochs=120 \ imgsz=640 \ batch=16 \ patience=20 \ lr0=0.005 \ save_dir=runs/pcb_train训练命令的参数解释如下:epochs=120给足迭代空间,配合patience=20让它在验证集连续20轮不涨时自动早停,省时间也防过拟合;imgsz=640是标准输入分辨率,PCB缺陷属于中小目标,这个尺寸够用;batch=16是显存限制下的保守值,6GB显存跑yolov8n毫无压力;lr0=0.005比默认的0.01低一半,小数据集用稍低学习率能避免前期震荡。
训练过程要盯着两个指标:训练loss和验证集mAP。loss稳步下降说明模型在拟合,mAP在验证集上逐步上升说明泛化在变好。如果train_loss降很快但val_mAP一直不动,就是过拟合信号,提前停或换更小的模型。训练结束后所有结果在runs/pcb_train/目录下,weights里同时有best.pt(验证集最优)和last.pt(最后一轮),实际用best.pt。
3.4 推理验证:单张图和批量测试
训练完用best.pt对测试集跑一轮推理,看看模型在没参与训练的数据上表现如何。
yolo detect predict \ model=runs/pcb_train/weights/best.pt \ source=test/images \ conf=0.35 \ iou=0.45 \ save=Trueconf=0.35是对应置信度阈值,低于这个值的检测结果会被过滤掉;iou=0.45是NMS去重阈值,同一位置多个重叠框时只保留分数最高的。这两个参数不是固定的,缺陷检测场景如果漏检多,就把conf调低到0.25;如果误检多,就调到0.5试。save=True会把标好框的图片存到runs/detect/predict/。
推理完一定要人眼看图,尤其是测试集里的每张图都翻一遍。YOLO的mAP是数值指标,但落在具体图片上可能有些框位置偏得离谱。我见过mAP挺高但所有pin-hole框都往右下偏移半个身位的情况,这种系统误差只能靠看图发现。
4. 避坑:680张小数据集最容易翻车的五个场景
4.1 类别不平衡:mAP好看,某一类全漏
现象:训练完看results.png里mAP@0.5有0.78,画面上挺好看,但翻开每类的PR曲线,mousebite的recall只有0.3,pin-hole甚至0.2都不到。原因:680张图里open和short占了将近一半框,而mousebite和pin-hole一共只有几十个框,模型把大部分容量花在了学大类特征上。解决:第一选择是补充小类样本,没有渠道的话就用增强硬顶。ultralytics里把copy_paste开大一点,数据loader会在线做随机复制粘贴增强,把小类目标复制到其他图上,等于变相扩充了小类样本量。训练完看confusion_matrix.png而不是只看results.png,能直观看出哪些类互相混。
4.2 标签坐标越界导致训练异常
现象:训练到中途突然报AssertionError或loss变成nan,而且报错每次出现在不同epoch。原因:标签txt里存在越界框,归一化坐标超过了0~1范围,比如手工标注时把框拖到了画布外面,存下来cx=1.12。dataloader在做目标匹配时遇到这种异常数据就崩了。解决:训练前先跑一遍2.3节的校验脚本,把越界报错的文件全部找出来。修复方案是把越界坐标clip到合法范围内,对每个框执行cx = min(max(cx, 0), 1),注意clip完之后w和h可能超出边界,需要同步修正中心点。这种问题在买来的数据里不罕见,必须提前扫。
4.3 模型过拟合:train_loss掉得飞快,val不再涨
现象:train_loss大概第30轮就降到0.05,但val mAP卡在0.6左右再也上不去,后边甚至往下掉。原因:680张图的信息量有限,yolov8m以上规模的模型参数量远超过数据所能约束的范围,就开始背训练集细节。解决:先从yolov8n开始,这是我在小数据集上屡试不爽的起点;同时用早停,patience=20,防止在过拟合区间空跑。如果n模型mAP正常,再考虑按顺序试s、m,每换一个型号必须对比val指标,不是越大越好。
4.4 图像尺寸不一:模型稳定但检测飘忽
现象:训练过程loss正常,没有报错,但检测效果总是不稳定,同一块板子换个角度测就漏检。原因:数据集里混了不同分辨率的图片,有的640x640,有的1920x1080,训练时dataloader统一缩放到640后,小图上清晰的目标在大图上被严重压缩,特征丢失。解决:训练前把所有图片统一处理,推荐批量resize到640x640再启动训练,这一步顺便把标注的相对坐标验证一遍。做法是遍历images目录,用OpenCV的resize统一尺寸,标签不用动(归一化坐标天然适配resize)。
4.5 数据增强参数过猛:把PCB纹理都改了
现象:训练loss收敛得漂亮,但实际拍的新板子检测效果差一截。原因:增强参数设置太激进,hsv_h=0.5把铜面的色调随机改得面目全非,mosaic=1.0把四张图拼在一起时缺陷框被裁掉或缩小。PCB缺陷检测场景和自然图像不同,铜面颜色和纹理是检测的重要线索,过度增强会把特征抹掉。解决:把hsv增强调低,hsv_h=0.01、hsv_s=0.3左右,mosaic降到0.5到0.6,开启close_mosaic=10让最后10轮训练不用mosaic,用完整图像做精调。这套参数在PCB铜面缺陷上比默认增强稳得多。
5. 把680张图用出性价比:迁移学习半冻结与验证习惯
5.1 半冻结训练:先训head,再全量微调
小数据集从头训YOLO风险太大,但直接全量微调预训练权重也不是最优。我会先冻结backbone,只更新检测头跑30轮,等loss降到平稳区域,再解冻全部层用更低学习率微调。这个策略在ultralytics里通过代码控制比较方便,它在训练小数据集时能把过拟合风险压到很低的水平。我一般在跑这个PCB数据集时会单独做一组对比:半冻结策略和直接全量训练,后者val mAP通常低3到5个点。
5.2 增强参数的定量参照
给一组经过验证的参数参考,适合PCB铜面缺陷这类小目标场景:hsv_h=0.01、hsv_s=0.2、hsv_v=0.2、degrees=15、scale=0.3、mosaic=0.5、copy_paste=0.3、close_mosaic=10。这套参数主要是保住PCB板原始的铜面和纹理特征,同时通过mosaic和copy_paste补足小类样本。旋转角度控制在15度以内是因为PCB走线有强方向性,转过90度后"断路"和"短路"的特征语义会发生混淆,训练出来的模型更容易误判。
5.3 验证时的三个习惯
我跑完一轮训练不会直接下结论。第一,打开results.png和confusion_matrix.png,对比每一类的PR和误判方向;第二,从测试集里挑一个batch的推理结果图,用肉眼确认框的位置和尺寸是否贴合缺陷轮廓;第三,单独统计每一类的mAP而不是只看整体平均——整体mAP被大类拉高之后,小类缺陷的漏检问题很容易被掩盖。这个习惯是吃过亏才养成的。有一次我训练完看到mAP有0.76就兴冲冲上线实测,结果pin-hole一个都没抓住,返工了大半天才意识到单类指标才是关键。从那以后我每次跑完PCB缺陷检测训练,都强制走一遍"校验脚本、单类PR曲线、肉眼抽查推理图"的流程,一步都不省。希望帮到你。
本文还有配套的精品资源,点击获取