news 2026/10/2 14:38:52

PCB缺陷检测实战:用1297张图与YOLOv5逼近99.8%准确率

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PCB缺陷检测实战:用1297张图与YOLOv5逼近99.8%准确率

简介:PCB电路板缺陷检测识别数据集面向智能制造、质检与深度学习目标检测场景,适用于需要快速获取带标注真实图像来训练缺陷识别模型的工程师和学生。资源共2000个文件,约120.94MB,包含1297个YOLOv5格式的txt标注文件、702张jpg电路板原图以及1个yaml数据配置文件,其中txt记录类别与归一化坐标,yaml定义类别名称和数据集路径,整理后可直接接入YOLOv5训练;图片场景贴近产线,涵盖常见缺陷形态,可用于焊接瑕疵、短路、断路等识别任务。据资源说明,该数据集配套验证达到99.8%识别准确率,能帮助使用者省去自行拍照、标注和格式转换的时间,集中精力做模型调优与实际部署。目前已有470人学习下载,目录划分清晰,适合作为PCB缺陷检测入门练手或算法对比实验的基准数据。

1. 一块1297张图的PCB数据集,凭什么把缺陷检测做到99.8%

产线上PCB板卡缺陷漏检一次,可能整批返工,损失远比一张图能换来的成本大。于是很多人一看到“1297张图片、YOLOv5格式、99.8识别准确率”就会问:是模型好,还是数据集特意筛过?我的判断从来是先看类别构成和测试集怎么切,再看这个数值的置信区间。这个标题真正在讲的是一个少样本工业视觉场景:样本不多,但拍摄条件固定、缺陷形态相对固定,用YOLOv5把漏检压到接近可部署的水平完全可行。适合正在做AOI项目、要训练自己PCB缺陷检测数据集的算法工程师和在校学生。

2. 拆解1297张YOLOv5标注数据:类别、txt坐标和切分原则

2.1 PCB缺陷有哪些类别,1297张图算多还是少

标题里没有给出缺陷类别清单,但结合PCB制程和AOI检测的常见需求,目标类型基本落在短路、断路、缺孔、毛刺、余铜、少锡、锡桥、元件缺失这几类。要特别提醒的是,这里说的“缺陷检测识别”是目标检测,也就是用边界框把缺陷位置框出来,而不是单纯的图像分类;正常电路板区域在模型眼里都是背景。

1297张图算多吗?如果目标是开放世界的自然图像数据集,这个数量连入门都不够。但在AOI产线场景里,情况完全不同:板型固定、光源固定、相机角度固定、来料批次固定,缺陷的形态和分布范围远没有开放场景那么混沌。我的经验是,当目标总数接近5000到10000个、每个类别至少有几十个样本时,1297张图完全能把YOLOv5训练到接近可部署的水平。99.8这个数字在小样本里并不离谱,只是它通常指“在固定测试集、固定置信度阈值下的准确率”,不是全场景无限制的泛化能力。

所以拿到这种数据集后第一件事不是训练,而是打开labels目录统计每个txt里有多少目标、每类目标各多少个。这一步能提前告诉你后面该不该做类别平衡、该用YOLOv5s还是m、增强参数应该保守还是放开。

2.2 YOLOv5标注格式与常用标注工具:LabelImg、CVAT都能导

YOLOv5的标签不是图像分类里那种“一张图一个标签”的CSV,而是每个txt文件与图片同名,文件名只差后缀;txt中每行描述一个目标框:

2 0.5123 0.6879 0.0456 0.0321

五个值依次是:类别序号、边界框中心点x坐标(相对图片宽)、边界框中心点y坐标(相对图片高)、边界框宽度(相对图片宽)、边界框高度(相对图片高)。全部是0到1之间的浮点数,不是像素坐标。很多新手从LabelImg、CVAT或VIA导出时直接拿XML、JSON里的像素坐标往txt里写,结果要么越界被过滤掉,要么框全偏到左上角。这里给一个我从VOC XML转YOLO txt的常用脚本,在工业图像上验证过多轮,可以直接套用:

import xml.etree.ElementTree as ET from pathlib import Path def voc_xml_to_yolo(xml_path: str, out_txt: str, classes: list[str]) -> None: root = ET.parse(xml_path).getroot() img_w = int(root.find('size/width').text) img_h = int(root.find('size/height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in classes: continue cls_id = classes.index(name) b = obj.find('bndbox') x1 = max(0.0, float(b.find('xmin').text)) y1 = max(0.0, float(b.find('ymin').text)) x2 = min(float(img_w), float(b.find('xmax').text)) y2 = min(float(img_h), float(b.find('ymax').text)) # YOLO格式要求中心坐标和宽高都归一化到0-1 x_center = (x1 + x2) / 2 / img_w y_center = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") Path(out_txt).write_text("\n".join(lines), encoding="utf-8") if __name__ == "__main__": classes = ["short", "open", "missing_hole", "burr", "solder_bridge", "component_missing"] voc_xml_to_yolo("annotations/001.xml", "labels/train/001.txt", classes)

这段逻辑很简单:先读取XML里的图片宽高,再把每个目标的xmin、ymin、xmax、ymax换算成YOLO格式。换算完保留6位小数,足够让中心点误差小于百万分之一图片尺寸,不用再纠结精度。这里有两个容易踩的位置:一是classes列表的顺序必须在转换脚本、数据集yaml、模型输出三处完全一致,顺序一旦错位,类别整体漂移;二是有标注工具把坐标写成小数甚至负数,裁剪图经常出现这种情况,所以脚本里用了max和min做边界收敛,避免越界标注在训练时被静默丢弃。

如果数据集是COCO JSON格式,同样道理写一个从annotations里解析出bbox字段的脚本,再做归一化即可。转换完成后把txt放到labels/train,图片放到images/train,两个文件只靠主文件名对齐,YOLOv5训练时会自动匹配。常用的目标检测标注工具里,LabelImg导出VOC、CVAT导出COCO或YOLO格式都方便,但导出后一定要抽查几份txt,别把类别顺序搞错。

2.3 三类切分里最容易掺水分的信息泄漏控制

这个部分专门讲“99.8%到底怎么来的”。少样本训练最常见的错误是直接把1297张图按80/10/10随机打乱,分成train/val/test。这看起来没问题,但工业数据集中常有强相关性的副本:同一块贴片板从不同角度拍了两张、同一次波峰焊产生的同一批板卡、同一个panel区域被切成多个ROI。如果这些高度相似的图片同时进了train和val,模型其实是在“记住”训练图片,而不是“识别”缺陷,换到下一批板卡后掉点会很难看。

我一般做切分前,先给每张图维护一个board_id字段,记录它来自哪一块物理板或哪一个panel批次。然后按board_id做分组,而不是按图片做随机。划分比例仍然建议70%训练、15%验证、15%测试;关键是同一个board_id下的所有图像必须只落在一个集合里。测试集也不要留太少,否则某类缺陷只有一两个样本,最终统计出的99.8%置信区间很宽,换一批图片就可能掉到96%以下。

切分方式适用条件风险
图片随机切分各图之间确实完全独立同板或同批次图片进入两个集合,指标虚高
按board_id分组切分AOI多图拍摄场景必须维护编号映射,不能偷懒
按批次时间切分连续生产批次能防前后时间相关性,但类别分布可能不均

还有一个容易忽略的泄漏源是数据增强。如果在自制复制增强时,把同一张缺陷图复制多份但内容完全一致,模型会把这张图当常量记住;训练和验证里一旦出现同帧画面,指标就虚高。这种在少样本场景非常隐蔽。正确的做法是对复制出来的图做颜色抖动、轻微旋转或尺度变化,让它们成为“伪新样本”,而不是原图重放。

3. 用YOLOv5训练自己的PCB数据集:从dataset.yaml到训练命令

3.1 目录与dataset.yaml这么建

YOLOv5训练自己数据集的目录结构没有想象中复杂,但必须是图片和标签同名同步。我常用的结构是这样:

pcb_1297/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/

然后写一个数据集描述文件pcb.yaml:

path: ../datasets/pcb_1297 train: images/train val: images/val test: images/test nc: 6 names: [ 'short', 'open', 'missing_hole', 'burr', 'solder_bridge', 'component_missing' ]

这里有几个不能让位的点:path是相对yaml文件所在目录的路径,也可以写绝对路径,但Windows下建议统一用正斜杠;train、val、test字段都是相对于path的路径,不要再加labels/前缀,YOLOv5会把image路径里的images替换成labels去查找对应txt。nc必须等于names列表长度,names顺序必须和转换脚本里的classes顺序一致。不要在yaml里单独给几个类别写中文名,显示时字体缺失会乱码,调试时也不直观。

3.2 一条能稳定跑完的训练命令

目录就绪后,进入YOLOv5工程目录,在根目录打开终端。我用过的这组参数在1297张图上效果很稳:

python train.py \ --data pcb.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch 16 \ --epochs 150 \ --patience 20 \ --device 0 \ --hyp data/hyps/hyp.scratch-low.yaml \ --project runs/pcb \ --name def_label_v1 \ --exist-ok

参数含义按顺序说一遍:data指向pcb.yaml;weights在第一次运行前需要放到项目根目录,如果网络受限或离线环境,手动拷进去就行,这个模型给少样本任务提供了COCO预训练的通用纹理特征;img是输入边长,AOI原图通常分辨率不低,用1280保留小缺陷细节;batch是单次迭代的图片数量,batch太小会导致BN层统计量不稳定;epochs给150轮,实际很多项目在60到90轮就触发早停;patience 20表示验证mAP连续20轮不涨就停;hyp指定了一个相对保守的增强超参数文件,后面会讲怎么改。

训练日志要看val/0_mAP_0.5这一栏,别看train loss。train loss下降只代表模型在拟合训练集,val mAP才能反映对真实缺陷的泛化。训练结束后,取runs/pcb/def_label_v1/weights/best.pt,而不是last.pt,best.pt是验证集表现最好的中间权重,last.pt往往是过拟合后的状态。

3.3 类别不平衡:小缺陷类别要不要加权重

1297张图在缺陷检测里有个残酷现实:缺陷类别分布极不均匀。短路、锡桥这类常见缺陷可能有一两千个目标,而断路、缺孔可能只有几十个。为了看清这个问题,我建议训练前先跑一段统计脚本:

from pathlib import Path def count_cls(labels_dir: str) -> dict: counts = {} for txt in Path(labels_dir).glob('*.txt'): for line in txt.read_text().strip().splitlines(): if not line: continue cls = int(line.split()[0]) counts[cls] = counts.get(cls, 0) + 1 return counts print(count_cls('labels/train')) # 输出示例:{0: 1200, 1: 45, 2: 310, 3: 860, 4: 1600, 5: 20}

如果输出里断路只有40个目标,锡桥有1600个,不做处理的话模型会为了压低整体loss而几乎放弃断路这类样本的学习,验证时该类mAP可能不到0.6。我一般不动YOLOv5源码里的box_loss、cls_loss系数,因为那三个权重是给不同预测层用的,改错会同时影响边界框回归和置信度,在少样本项目上副作用很大。

更稳妥的做法是少数类增强:把断路的原图做轻微颜色抖动、平移5个像素以内、缩放0.9到1.1,生成2到3份变体再放进训练集。注意不能直接复制原图,否则程序和上一章说的一样引发增强泄漏。另一种做法是把少数类的标注框在读取时统一外扩几像素,让模型更容易学习到“这个区域有问题”的上下文,而不是只盯着缺陷像素本身。

4. 逼近99.8%的三个真实操作点:模型选型、增强策略和置信度

4.1 预训练模型这么选:还是老老实实用YOLOv5s

模型选型在少样本下不需要复杂。YOLOv5官方提供了n、s、m、l几个档位,PCB缺陷这种工业场景我一般推荐YOLOv5s。n档更快,但小目标丢检率偏高;m档精度确实略好,可对显存和推理时间的压力也同步上升,1297张图的数据量还不足以让m档的额外参数完全发挥。l档在这个数据规模下基本属于浪费,训练慢还容易过拟合。

模型特点适用场景
YOLOv5n轻量、速度快低功耗边缘设备,能接受少量漏检
YOLOv5s精度和速度平衡多数AOI产线项目,推荐
YOLOv5m小目标召回略高显存充足、对漏检极度敏感的离线检测

输入分辨率这块,AOI原图里细小线路缺陷可能只有几个像素宽,直接把1200×900原图缩到640,等于让小缺陷信息丢失大半。我建议训练和推理都固定使用1280输入尺寸。如果显存不够,优先减batch而不是减img,例如batch从16降到8,尽量保证模型能看清楚缺陷纹理。

4.2 增强策略:模拟产线光照,不是乱做数据增强

PCB缺陷检测有一个天然优势:产线成像条件固定。所以数据增强要做的是模拟真实光照、温漂、震动带来的微小变化,而不是把图片旋转90度、随便crop。YOLOv5自带的增强超参在data/hyps/hyp.scratch-low.yaml里,我会单独复制一份再覆盖几个针对性参数,文件名取hyp.scratch-pcb.yaml:

# 继承原 hyp.scratch-low.yaml 后用这几项覆盖 hsv_h: 0.01 hsv_s: 0.5 hsv_v: 0.35 degrees: 5 translate: 0.1 scale: 0.3 shear: 0.5 perspective: 0.0 flipud: 0.2 fliplr: 0.5 mosaic: 0.8 mixup: 0.1

逐个解释:hsv_h色相变化给到0.01,板卡颜色不能乱偏;hsv_s和hsv_v各给一点,模拟光源亮度和颜色差异;degrees只给5度以内,旋转太大会让字符方向失真,模型会把方向和位置信息学坏;scale给0.3控制缩放范围,PCB缺陷本来就是小目标,缩放超过0.5会把缺陷压成一个点,训练信息量反而下降;flipud上下翻转要慎用,如果产线上板卡从没有倒装,模型学了倒着走的特征反而干扰;mosaic降到0.8而不是默认1.0,因为mosaic拼图会把每张原图缩到四分之一,对细小缺陷相当不友好。这些参数看着玄学,但背后都是“贴近真实成像条件”这同一个原则。

改完之后用--hyp data/hyps/hyp.scratch-pcb.yaml重新训练,观察训练结果,而不是只关注loss。

4.3 置信度阈值到底调高还是调低:漏检和误检要分开定价

YOLOv5的评估流程里有个容易混淆的点:训练完成后val.py默认用很低的置信度阈值算mAP,所以99.8%这个数字是在“把大量低置信度检测框也算进去”的前提下得到的。实际部署推理时,detect.py默认置信度阈值是0.25,NMS的IoU阈值是0.45。你的产线到底会把阈值卡在多少,直接影响最终表现。

阈值档位效果偏向适合场景
conf 0.1到0.15召回高、误检多漏检成本高,后续有人工复检
conf 0.25YOLOv5默认值常规评估和量产初调
conf 0.5以上精确率高、漏检上升误检会频繁停机报警的产线

我对漏检敏感场景的建议是先调低到0.15跑一批真实拍图,把所有检测结果输出成带框图片,人工看一遍到底哪类背景被误检了。对误检敏感的产线,把conf调到0.4到0.5再评估,不要照搬论文里的默认值。NMS的IoU阈值也不要动得太狠,0.45是经过YOLOv5官方验证的平衡点,往下调到0.3会出现同一缺陷被后半段漏掉的情况。

5. 避坑指南:小样本PCB缺陷检测的5个常见翻车点

5.1 现象:Loss降了,验证mAP反而掉

训练到第80轮时train loss还在缓慢下降,val mAP却开始波动下跌。这不是“玄学”,而是典型的过拟合提前到达。1297张图的数据量,模型在60到90轮之后已经开始记忆训练集中的缺陷纹理和背景,继续训练只会加大val和train的差距。

解决方式就是开早停:train.py里加patience 20,并在验证不再提升时自动结束。同时始终把best.pt作为最终交付。另一个习惯是每轮都保存tensorboard或csv格式的训练过程,观察mAP曲线,一旦出现连续三轮下降,就应该往回选之前最优的轮数,而不是硬跑完150轮。

5.2 现象:训练启动时提示no labels,loss一直是0

YOLOv5启动后没有报太多错误,但logger里出现labels not found,训练loss纹丝不动。最常见的原因是labels/train目录里的txt与images/train里的jpg文件名没有对齐,或者txt文件全是空文件。还有一种情况是坐标转换时写成了整数像素值,归一化后数值大于1,被YOLOv5当成无效标注直接跳过。

解决方法是写一个十行检查脚本,把所有txt读一遍:

from pathlib import Path for txt in Path('labels/train').glob('*.txt'): for i, line in enumerate(txt.read_text().strip().splitlines()): parts = line.split() if len(parts) != 5: print(f"{txt}: 第{i}行字段数不对") continue x_c, y_c, w, h = map(float, parts[1:]) if not all(0 <= v <= 1 for v in [x_c, y_c, w, h]): print(f"{txt}: 第{i}行坐标越界")

把这段在训练前跑一遍,能拦住80%的少样本训练翻车。训练中也可以用python val.py --data pcb.yaml --weights yolov5s.pt --task val验证数据集目录能不能正常加载,如果val能跑但train不行,优先查文件名称大小写问题。

5.3 现象:同批数据换一个随机种子,准确率从99.8%降到94%

这类现象不是模型黑匣子,而是小样本切分方差太大。1297张图里只要有几十张强相关图片被随机切进不同集合,验证结果就会大幅波动。单次随机切分只能说明“在当前这组train/val划分下模型表现不错”,不能说明模型在下一批板卡上还能保持。

解决方式至少包含三层:固定随机种子让项目可复现;按board_id分组切分切断同板泄漏;用K折交叉验证把最终指标换成多个划分的均值。前两点是底线,第三点在发布技术指标前必须做,否则99.8%只能停留在纸面。

5.4 现象:离线测试接近99.8%,上线第一天就漏检

这种翻车我见过太多次。离线测试用val.py默认的低置信度阈值,pipeline里自动做了letterbox保持宽高比填充。但上线部署时如果直接调用opencv的resize把整个图强行拉成正方形,宽高比被破坏,框的位置全偏,小缺陷被拉伸变形,自然漏检。

解决方式是把在线评测时的参数固定成与部署一致:conf-thres直接设为0.25或0.4,iou-thres设为0.45,resize逻辑严格使用YOLOv5的letterbox函数,不能简化成一次resize。同时在导出模型前后,用同一张图先在PyTorch环境里跑一次,再用部署框架跑一次,对比输出框坐标是否一致。这个对比如果通过,上线才会稳定。

5.5 现象:batch设置太小,模型训练不收敛

少样本项目里有人觉得1297张图太少,想用batch=2或4训练,结果loss一直震荡或者干脆不下降。原因在于YOLOv5大量使用BatchNorm,batch太小导致每个batch内的均值方差统计不稳定,网络学不到稳定的分布特征。

解决方式是把batch至少提到16,如果显存不够,先把img从1280降到640,保持batch不变。如果640分辨率确实影响小缺陷,再换YOLOv5n来降低显存占用。我习惯把批次大小写死在16以上,绝不在PCB缺陷检测里用2、4这样的小batch去考验训练稳定性。

6. 验证“99.8%”是否可信:先做K折再谈部署

6.1 用GroupKFold给1297张图做5折验证,别被单次split骗了

想判断这个数据集上的99.8%是不是真实能力,最可靠的做法是5折交叉验证。先准备一份board_map.csv,每一行记录image_path和board_id,然后用GroupKFold按板卡分组切分:

import pandas as pd from sklearn.model_selection import GroupKFold df = pd.read_csv("board_map.csv") gkf = GroupKFold(n_splits=5) for fold, (tr_idx, va_idx) in enumerate(gkf.split(df, groups=df["board_id"])): train_images = df.iloc[tr_idx]["image_path"].tolist() val_images = df.iloc[va_idx]["image_path"].tolist() # 把train_images和val_images写进train.txt和val.txt # 再在pcb_fold.yaml中用 train: train.txt、val: val.txt 引用这些列表 print(f"fold {fold}: train {len(train_images)}, val {len(val_images)}")

GroupKFold保证同一个board_id的图片不会跨折出现,这是切断信息泄漏的关键。跑完5折后,把每折的mAP均值作为最终报告值,而不是只挑最好的一折。时间紧张也要跑3折,至少你能看到模型对数据切分的敏感度。我拿到任何宣称99.8%的检测方案,都先看测试集怎么切、阈值怎么定、有没有把困难类别单独统计。这三个问题答得越清楚,项目上线就越不慌。这个习惯帮我避开了不少少样本“高精度”陷阱,希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 14:38:18

Win11管理员权限机制深度解析:UAC、令牌完整性与组策略修复

1. 为什么Win11的管理员权限比Win10更“难拿”&#xff1f;——不是系统变坏了&#xff0c;是安全逻辑升级了你双击一个安装包&#xff0c;弹出“需要管理员权限才能继续”&#xff0c;点“是”却没反应&#xff1b;你在资源管理器里右键想删个系统文件夹&#xff0c;提示“拒绝…

作者头像 李华
网站建设 2026/10/2 14:38:14

PyTorch胶囊网络实战:解决小样本与遮挡下的识别鲁棒性问题

简介&#xff1a;本资源是基于PyTorch实现的胶囊网络&#xff08;Capsule Networks&#xff09;完整开源项目&#xff0c;面向深度学习进阶学习者、算法工程师及高校研究者&#xff0c;旨在帮助读者突破传统CNN在空间关系建模上的局限&#xff0c;深入理解Hinton提出的动态路由…

作者头像 李华
网站建设 2026/10/2 14:37:30

SuperPoint红外适配指南:跨模态关键点检测实操六步法

简介&#xff1a;本资源是一套基于SuperPoint深度学习算法的可见光与红外图像关键点检测与对齐开源实现&#xff0c;面向计算机视觉方向的研究者、多模态图像处理开发者及深度学习进阶学习者&#xff0c;解决跨模态图像配准这一典型工业与安防场景中的核心难题。压缩包共46个文…

作者头像 李华
网站建设 2026/10/2 14:37:04

2080 Ti、3090与A100:深度学习硬件选型的本质逻辑

1. 这三张卡不是“代际升级”&#xff0c;而是面向完全不同的战场很多人点开这篇内容&#xff0c;心里想的是&#xff1a;“我该买哪张卡来跑自己的深度学习项目&#xff1f;”——这个出发点本身就有问题。2080 Ti、3090 和 A100 看似都是“NVIDIA显卡”&#xff0c;但把它们放…

作者头像 李华
网站建设 2026/10/2 14:36:19

华为ENSP AAA权限报错:level should not higher than current user‘s解析

1. 这个报错不是配置错了&#xff0c;是权限逻辑被误解了在ENSP里敲下aaa命令、刚想给用户授权就弹出那句经典的红色提示&#xff1a;"the level should not higher than current users"——很多人第一反应是“我输错了命令”“密码填错了”“设备没连上”&#xff0…

作者头像 李华
网站建设 2026/10/2 14:35:42

SkeyeWebPlayer多分屏原理:WebRTC流调度与WebGL渲染深度解析

1. SkeyeWebPlayer多分屏功能的本质&#xff1a;不是UI堆砌&#xff0c;而是流媒体调度逻辑的重构SkeyeWebPlayer这个播放器名字里带“Web”&#xff0c;但实际用起来你会发现它根本不像传统H5视频标签那样简单——它底层是基于WebAssemblyWebRTC自研解码内核的混合架构&#x…

作者头像 李华