
简介面向PCB缺陷检测与工业视觉质检场景的数据集适合使用YOLOv9等目标检测框架进行模型训练与验证的算法工程师、研究人员及竞赛团队。该数据集包含1297张PCB电路板图片每张图片均配有对应的YOLOv9格式txt标注文件整体识别准确率达到99.8%覆盖焊点缺陷、线路划伤、元件偏移等常见瑕疵类型可直接用于缺陷检测模型的训练、评估与迭代省去人工标注的时间成本。压缩包共2000个文件其中702个jpg为原始电路板图像1297个txt为边界框与类别标注1个yaml为数据集配置文件包体大小约120.79MB结构简洁、无需额外格式转换即可接入训练流程。目前已有436人学习下载适合希望快速搭建PCB缺陷检测基线、验证算法效果或扩充训练样本的开发者与研究者使用。1. PCB缺陷检测数据集1297张图片、yolov9格式99.8%准确率真的可信吗PCB缺陷检测是电子制造里最让人头疼的环节。一条产线每天过几千块板每个板上有上百个焊盘、线路和过孔人工目检盯久了必然疲劳漏检。于是很多人想用yolo这类目标检测模型来替代部分人工。这时候遇到一个号称“yolov9格式标注、1297张图片、识别准确率99.8%”的PCB缺陷检测数据集自然会心动。但做过工业视觉的人都会先打个问号1297张图真的够吗99.8%的准确率是在什么测试集上算出来的yolov9格式的标注到底长什么样这篇笔记不吹不黑直接照着这个标题拆开看告诉你这个数据集能用、怎么用以及那些藏在数字后面的坑。2. 读懂yolov9格式数据集长什么样标签怎么写的2.1 数据目录和文件结构拿到一个带“yolov9格式”字样的PCB数据集最常见的组织方式就是images和labels成对出现。目录树如下datasets/pcb/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ ├── 0002.jpg │ │ └── ... │ └── val/ │ ├── 1001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ ├── 0002.txt │ │ └── ... │ └── val/ │ └── 1001.txt ├── data.yaml └── classes.txt这里的关键点是images里每张jpg在labels里必须有一个同名的txt而且txt文件名要去掉扩展名。比如images/train/0001.jpg对应labels/train/0001.txt。yolov9的训练逻辑默认会遍历图片再去同名目录或由路径替换规则找标签。如果你把标签全放在一个文件夹或者命名成0001_jpg.txt模型会一直提示“WARNING: No labels found”而完全没法训练。我拿到数据集的第一个动作是用一个脚本检查图片和标签的对应关系。下面这段脚本把所有缺失标签或空标签的图片打印出来import os, cv2 img_dir datasets/pcb/images/train lab_dir datasets/pcb/labels/train for img_name in os.listdir(img_dir): stem os.path.splitext(img_name)[0] lab_path os.path.join(lab_dir, stem .txt) if not os.path.exists(lab_path): print(f[缺少标签] {img_name}) continue with open(lab_path) as f: lines [l.strip() for l in f if l.strip()] if not lines: print(f[空标签] {img_name})这段代码的逻辑很直接遍历图片目录拼接出预期的标签路径检查存在性和是否为空。参数说明stem是去掉后缀的文件名strip()用于过滤空白行。空标签文件有几种来源一是负样本图片本身没有缺陷二是标注工具生成了0字节文件三是数据发布时漏掉了。对目标检测来说空标签可以作为背景样本加入训练但必须确保图片里真的没有缺陷否则模型会把缺陷当成背景学掉这是最危险的坑。接着我会统计所有标签里每个类别的框数量。PCB缺陷数据普遍存在类别不均衡这个统计能直接告诉你后续训练要不要做采样优化。from collections import Counter counter Counter() for split in [train, val]: lab_dir fdatasets/pcb/labels/{split} for txt_name in os.listdir(lab_dir): with open(os.path.join(lab_dir, txt_name)) as f: for line in f: if line.strip(): cid int(line.split()[0]) counter[cid] 1 print(counter)Counter统计的是每个类别id出现的次数。输出类似Counter({0: 1200, 2: 850, 1: 400, 3: 220, 4: 180, 5: 60})这时候你就知道“短路”类样本明显偏少。这个脚本不涉及任何模型但能为后面的参数设置提供依据。2.2 标签内容详解yolov9格式的标签每张图对应一个txt每行是“类别id 中心点x 中心点y 宽 高”。中心点和宽高都除以原图宽高做归一化。一个标签文件可能是这样的0 0.4823 0.6214 0.0312 0.0417 2 0.2145 0.3548 0.0583 0.0234 5 0.7612 0.4855 0.0121 0.0189第一列含义要查classes.txt。比如classes.txt里顺序为missing_hole mouse_bite open_circuit short spur spurious_copper那么第一行0是missing_hole第二行2是open_circuit第三行5是spurious_copper。注意类别id是下标从0开始的不是从1开始。很多人写data.yaml时把nc设为5但标签里有5就是索引越界训练直接崩溃。这里必须强调一个坐标转换。yolov9格式使用的是中心点坐标不是左上角和右下角。如果你手里的标注是COCO或VOC格式从(x1, y1, x2, y2)转换成yolo格式的公式是# x1, y1, x2, y2是像素坐标img_w, img_h是原图宽高 x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h width (x2 - x1) / img_w height (y2 - y1) / img_h反过来从yolo格式变回像素框x1 int((x_center - width/2) * img_w) y1 int((y_center - height/2) * img_h) x2 int((x_center width/2) * img_w) y2 int((y_center height/2) * img_h)这些转换看着简单但方向很容易搞反。我见过有人转换时忘记除以宽高导致框坐标变成几千像素训练时全部预测到图外。所以在切数据集之前最好先做一次可视化。用opencv把标注框画回原图抽查十个文件确认框的位置、大小是否合理。另一个容易忽略的是空标签图片。有些图片本身是完好的PCB没有任何缺陷。它们的txt文件是空文件。在yolov9训练中空标签是合法的但需要注意如果训练集中全是带缺陷的图没有背景样本模型会对整张板的随机噪点产生高置信度误检。所以保留一定比例的空标签图很必要。理想比例是10%到20%背景图。如果这个数据集里没有空标签建议用负样本板卡补充。2.3 1297张图的性价比和99.8%准确率的真相1297张图听起来不少但摊到训练和验证两个集合里其实紧张。如果按8:2划分训练集1038张验证集259张。PCB缺陷检测通常每张图有几十个目标框259张图也有几千个框。看似不少但模型在训练时见过大量同一块板子的不同区域验证时很容易“见到老朋友”。所以数据划分方式比划分比例更影响最终指标。这是一个非常常见的划分策略train: 板卡ID 1-800 的所有图像 val: 板卡ID 801-1000 的所有图像而不是随机抽样。为什么PCB图像往往来自同一批次板卡相邻板卡之间背景、光照高度相似随机划分会让验证集里混入训练图片的近亲导致mAP虚高。如果数据集里没有提供板卡ID那么至少按图片文件名前缀或拍摄批次划分。我一般会写一个分组脚本按文件名前几位比如board_01_xxx.jpg中的board_01分组先分板卡再分图片。99.8%准确率这个数字我在很多数据集描述里见过。做目标检测的人应该先问这是图像级准确率还是像素级还是框级mAP不同定义差距巨大。图像级准确率是“整张图有没有被正确分类”只要模型能识别出任意缺陷就能达到高分而框级mAP要求框的位置和类别都正确难度大得多。很多号称高准确率的数据集实际上只做了图像级二分类不能照搬到目标检测任务里用。合理的做法是拿到数据集后自己重新计算一次“验收指标”。保留一个从未参与训练的板卡批次训练结束后用模型在这个批次的全部图片上跑一遍然后按目标检测的P、R、F1逐类列出来。不要只看总mAP。总mAP会被多数类拉高掩盖“短路”类完全检不出来的问题。这一步做完你对这个数据集的真实水平心里就有底了。3. 用yolov9训练PCB缺陷模型环境、命令与参数3.1 环境准备与模型选择训练一个yolov9模型不需要一整条产线一台带NVIDIA GPU的机器就够了。官方仓库的依赖以PyTorch为核心建议Python 3.9或3.10版本。安装命令通常是git clone yolov9仓库地址 cd yolov9 pip install -r requirements.txt拉取仓库后先确认torch版本。同一个仓库在torch 1.13和2.x下的运行行为有差异有些算子优化在不同版本上会导致训练速度不同但一般不影响最终精度。我的习惯是用conda创建一个干净环境避免和别的项目互相污染conda create -n yolov9 python3.10 conda activate yolov9 pip install torch2.0.0 torchvision0.15.0版本号不必跟我完全一致但注意torch和torchvision要匹配。CPU可以跑通流程但训练速度太慢1297张图跑几百轮可能要一天一夜。如果你只有CPU建议先买云GPU或借用公司的卡。显存上yolov9-c模型在batch-size16、imgsz640时大概需要16G显存8G卡就把batch-size降到8。模型选型上yolov9有多个规格。PCB缺陷检测有个特点目标小、数量多、背景纹理复杂。小模型t/s速度快但小目标召回率偏低大模型c/e精度高但推理慢。对产线来说c规格通常是最优折中。你不必一上来就训练c可以先用s跑通数据流和代码逻辑确认没问题后再换c训练。这也是一个避免浪费时间和显卡的好习惯。3.2 配置数据文件data.yaml训练前要写一个yaml文件告诉模型去哪里找图片、一共有几个类别、类别叫什么。内容如下train: /home/user/datasets/pcb/images/train val: /home/user/datasets/pcb/images/val nc: 6 names: [ missing_hole, mouse_bite, open_circuit, short, spur, spurious_copper ]有几个点容易出错。第一train和val的路径要写到images目录而不是labels目录。yolov9会根据images路径自动推断labels路径规则是把images换成labels、把扩展名换成txt。如果图片路径是.../images/train/0001.jpg标签路径会被推断为.../labels/train/0001.txt。所以目录必须和前面一致。第二names列表的顺序要和labels里的类别id一一对应不能随意排序。比如你希望id0是short那names列表第一项必须是“short”。第三nc的值是类别个数等于最大id加1因为id从0开始。如果标签里出现id6nc至少要7否则训练会报索引越界。我们在写代码时最好自动检查一下标签里的最大id。我经常用一个脚本来自动判断import os max_id -1 for split in [train, val]: lab_dir fdatasets/pcb/labels/{split} for txt in os.listdir(lab_dir): with open(os.path.join(lab_dir, txt)) as f: for line in f: if line.strip(): cid int(line.split()[0]) if cid max_id: max_id cid print(类别数应为, max_id 1)这个脚本不复杂但能省去手动数类别数的麻烦。如果结果和yaml里写的nc不一致务必以脚本结果为准。3.3 训练命令与关键参数yolov9官方仓库的训练脚本是train.py。训练时核心命令如下python train.py \ --data data/pcb.yaml \ --cfg models/detect/yolov9-c.yaml \ --weights yolov9-c.pt \ --batch-size 16 \ --epochs 300 \ --imgsz 640 \ --device 0 \ --hyp hyp.scratch-high.yaml这条命令里每个参数都值得细看。--data指定我们刚写好的data.yaml。--cfg指定模型结构配置models/detect/yolov9-c.yaml定义了c规格的层数和通道数。--weights是预训练权重路径通常用yolov9-c.pt你可以在官方仓储的weights目录里找到。如果没有预训练权重也可以去掉这个参数但训练时间会变长最终精度也可能低一些。--batch-size是每批图片数量。它和显存直接相关。显存溢出时先减半batch-size而不是调小模型。--epochs是训练轮数。300轮是起步值配合下面要说的早停策略如果模型长时间不提升会自动停止。--imgsz是训练输入尺寸。PCB缺陷中有很多小目标把imgsz从640提到1280mAP往往能涨3到5个点。代价是训练时间变成原来的四倍推理时间也变长。产线上如果既要速度又要精度建议训练用1280部署时再用640或896但这样会有训练/推理分辨率不一致导致的精度损失需要自行权衡。--device 0指使用第一张GPU。没有GPU就写--device cpu。--hyp是超参数文件。hyp.scratch-high.json里包含mosaic、mixup、hsv等增强参数其中mosaic和mixup对防止小样本过拟合很有用。新手不要自己手写hyp用默认文件起步后面再微调。还有一个参数是--workers指数据加载线程数。默认8如果Windows系统直接设为0否则容易出现“DataLoader worker exits unexpectedly”的报错。这个错不会导致模型崩溃但会中断训练浪费之前练过的epoch。我习惯在命令里显式加上--workers 4或--workers 0。训练启动后终端会滚动打印每轮的loss、mAP。这时不要急着挂断先看前10轮有没有报错。最容易报错的是类别索引越界、图片读取失败、显存不足。前10轮如果loss一直是nan基本可以放弃继续跑先回头检查标注标签和data.yaml。3.4 训练结果怎么看训练结束或提前停止后输出目录runs/train/expN下生成best.pt和last.pt还有result.png和混淆矩阵。best.pt是验证集mAP最高的权重last.pt是最后一轮的权重。正式使用应该选best.pt。result.png里有多条曲线重点关注box_loss、cls_loss和val/box_loss、val/cls_loss。训练loss持续下降是健康的验证loss如果先降后升说明过拟合已经开始。PCB小数据集很容易过拟合通常在第100轮之后验证loss会缓慢上升。这时候不要等到300轮跑完而是应该用早停机制。yolov9默认有patience参数比如--patience 30表示验证集指标30轮不提升就停止。这样能省大量时间也避免保存一个过拟合的权重。混淆矩阵能帮你判断类间混淆。比如“鼠咬”和“毛刺”这类形态相近的缺陷在混淆矩阵上会有明显的串类。看到这种结果先别着急加数据。优先确认标注框是否准确有没有框偏移导致一个人为的混淆。然后考虑合并类把难以区分的类别合并成一个“缺陷”类别等模型基础能力上来后再细分。这个策略在工业场景很实用因为很多缺陷类别的差异本身就很模糊。最后打开终端打印出的每类AP列表。如果有一两个类AP特别低检查一下这类样本数量。通常最少的类AP最低。针对它做复制粘贴增强或者在验证时单独降低置信度阈值。这一步是提升整体性能最关键的地方不要只看总的mAP。4. 避坑指南训练PCB缺陷检测时的5个常见陷阱4.1 现象报表准确率99.8%实际产线误报率居高不下现象拿这个数据集训练后验证集mAP很高显示99.8%试产时机器把正常的PCB标注成缺陷一条产线每分钟误报几十次。原因数据划分方式泄漏。1297张图可能来自同一块板或同批次板随机划分导致验证集与训练集高度相似。另外99.8%可能是图像级准确率不是框级mAP一种缺陷都没检出来也能有很高的图像级准确率。解决按板卡划分数据确保验证集经过严格分离。在实际产线部署前留存一批从未见到的板卡图像做验收。评估指标用mAP或F1并逐类计算。如果发现某类缺陷完全没检出调整该类别的置信度阈值或补充训练样本。4.2 现象小缺陷漏检焊盘上的微短路完全看不见现象缩放到640x640后短路缺陷只有2-3像素模型输出结果里完全没有框。原因yolo的下采样倍数很大小目标在深层特征图里可能只剩一个点甚至被丢弃。默认anchor对细小目标匹配率低。此外训练时如果原图缺陷区域占比太小loss对小目标的梯度信号微弱。解决把imgsz提高到1280甚至1536。切片推理将原图按网格切块每块单独送入检测。训练时开启mosaic和copy-paste让模型多见到不同位置的小缺陷。有时还可以把大图按缺陷密度裁剪成patch训练避免整图缩放过度。4.3 现象loss下降但mAP纹丝不动现象box_loss、cls_loss都在下降但mAP在几十轮内锯齿式上升始终达不到预期。原因学习率过高使模型在局部震荡或者anchor与缺陷框大小不匹配。PCB缺陷长宽比极端而默认anchor偏正方形导致IoU匹配时正样本太少。如果使用固定anchor没有开启自动计算模型很难学准。解决把学习率从0.01降到0.001或0.0005训练更稳定。开启autoanchor在训练启动时让yolov9重新聚类anchor。检查类别id是否从0开始类别数是否一致。一个隐藏bug是标签文件的坐标顺序写错让模型用宽当高导致loss下降但检测框变形。4.4 现象训练集太小验证集提升但真实场景泛化差现象训练结束后在验证集上mAP高达99%去另一个批次PCB上测试准确率降到80%以下。原因1297张图只覆盖了有限的板卡类型、光照条件和元器件布局。模型把某些背景噪声当成了缺陷的特征过拟合到了训练集特有的纹理上。解决使用数据增强模拟光照变化、旋转、缩放。但更根本的是用自己的产线图片扩充训练集。这个数据集适合做迁移学习起始点而不是终局。部署前必须采集现场图片做域适应。同时在训练时保留一定比例的空标签背景图降低误检。4.5 现象类别极不均衡少数类AP为零现象missing_hole检出很好但是short类AP只有0.0验证集里一次都没预测中。原因标签分布统计显示short类只占总框数的3%模型训练时见得太少决策面完全偏向多数类。置信度阈值固定为0.25时少数类预测的置信度整体低于阈值全部被过滤。解决先统计类别数量对少数类做过采样或复制增强。修改验证和推理时的置信度阈值单独为少数类设置低保真度阈值。在训练中启用类别权重或焦点损失。如果样本数实在太少考虑把short类归并入“缺陷”大类等数据量足够再细分。5. 从99.8%到稳定上线验证方法与进阶技巧5.1 用“验收板”验证模型泛化无论数据集描述多精彩最终上线前必须做一个动作挑一块从未参与训练的板卡把它所有图像存到一个单独文件夹标记为“验收板”。在模型训练和调参过程中这块板卡的图像不能以任何形式进入训练集、验证集或测试集。只有当你选定了候选权重之后才把模型跑一遍统计每块板上的漏检和误检。这个数字才是真实的预估准确率。我习惯把验收板的检测结果按缺陷类别做成一张表格记录每类的TP、FP、FN然后计算精确率和召回率。如果某类FN过高说明模型没有学会这种缺陷。此时再返回去补充数据或调整阈值。这个流程会反复几次但都能在真正上线前暴露问题。5.2 三个立竿见影的进阶技巧第一TTA。把推理时的增强打开yolov9的检测脚本里--augment参数能启用水平和垂直翻转、多尺度测试。TTA通常能提升1到2个点召回率代价是推理时间变为原来的4到8倍。适合离线抽检和节拍较慢的工位。第二把模型导出为ONNX再用TensorRT做FP16推理。yolov9的export.py支持导出ONNX然后用TensorRT生成engine。FP16在精度上损失不大但推理速度能提升2到3倍。产线部署时GPU算力有限这一步往往是必须的。第三后处理时不要守着默认置信度阈值。在验收板上画出不同阈值下的Precision-Recall曲线选一个平衡点。如果缺陷漏检造成的损失远大于误检就把阈值从0.25降到0.1换取更高的召回率。这个调整不需要重新训练但经常是上线后第一个救命操作。# 在yolov9推理结果中动态调整阈值 results model(img)[0] conf_mask results[:, 4] 0.1 # 将阈值设为0.1 detections results[conf_mask]这段代码示意yolov9输出的tensor中第4列是置信度通过布尔掩码过滤。实际使用还要考虑类别置信度但这个思路简单实用。参数说明阈值降低会增加误检阈值升高会增加漏检。具体数值需要根据产线要求反复试。5.3 一个老工程师的教训我第一次做PCB缺陷检测时就是用了一个类似的数据集验证集mAP接近99%。我当时兴奋地把它部署到测试设备上结果当天就被反馈“误判太多没法用”。后来才发现那个数据集的验证图片和训练图片来自同一块板的相邻区域模型记住的是背景而不是缺陷。从那之后我给自己定了一条规矩每个项目必须预留一个不参与调参的验收板所有的超参数调整都不能看这块板的输出。这个习惯让我避开了很多次翻车。说到底1297张图的数据集不是不可以用而是要用对方法先用它跑通流程、调好模型结构和训练管线再逐步加入真实产线数据最后用验收板定阈值、量化部署。希望这篇笔记能帮你少走这几步弯路也希望帮到你。本文还有配套的精品资源点击获取