
简介面向PCB电路板缺陷检测识别任务的YOLOv9格式数据集主要服务于工业视觉质检、自动化检测等方向的工程师与研究者可应用于缺陷标注、模型训练、算法评估等核心环节。整个压缩包共2000个文件其中jpg图像702张、txt标注文件1297个、yaml配置文件1个整体大小约120.79MBtxt中记录了与图像对应的目标类别和位置信息yaml则用于定义类别名称与数据路径可直接接入YOLOv9训练流程使用。已有434人学习下载。该数据集覆盖了不同光照、拍摄角度和板面状态下的PCB实物图像标注类别贴近常见缺陷类型据资源描述其识别准确率达到99.8%。借助这份数据可以省去自行采集与标注的预处理时间快速开展模型训练、批次对比和检测效果的复现适合作为缺陷检测算法验证、课程设计以及入门实训的参考数据集。1. PCB缺陷检测数据集1297张图片真的能撑起99.8%准确率吗一个做PCB产线质检的工程师拿到一份“PCB电路板缺陷检测识别数据集yolov9格式标注99.8%识别准确率1297张图片”的资料第一反应通常是怀疑这个数有点玄。1297张图片对深度学习目标检测来说不到一个零头动辄上万张的公开数据集都未必敢承诺99.8%。但如果你真做过PCB缺陷检测项目大概率会停下来认真看一眼——PCB板的缺陷检测恰好是少样本能出高精度的少数场景之一。缺陷类别清晰、背景高度单一、拍摄角度固定模型不需要花精力学环境不变性1297张高质量标注图配合合理的训练策略跑出接近99%的准确率并不违背常理。这里就拿这个标题当线索把数据集类别构成、yolov9标注格式、训练复现流程和最容易翻车的地方一次讲透。2. 数据集构成与yolov9标注格式一张缺陷图装了多少信息2.1 从裸板到缺陷样本PCB制造缺陷检测的行业背景PCB缺陷检测主要出现在两条业务线上一是PCB裸板出厂前的AOI自动光学检测二是SMT贴片回流焊后的AOI。标题里这个数据集对应的是前者检测对象是裸板——没有贴装元器件只有铜箔线路、焊盘、过孔和阻焊层。缺陷类型集中在铜箔加工环节的工艺偏差上。常见的缺陷类别整理如下类别英文标注名中文习惯叫法典型形态标注框特点missing_hole缺失孔应钻孔位未钻透小圆形框mouse_bite鼠咬铜线边缘弧状缺口细长框紧贴边缘open_circuit开路铜线断裂长条窄框short短路相邻导线连到一起不规则四边形拟合矩形框spur毛刺/尖刺走线边缘锐利突出狭长小框spurious_copper铜渣基板表面残铜不规则块状pinhole针孔铜面微小孔洞极小框可能只占几个像素PCB设计和制造工程师看到这几类词会很熟悉鼠咬多半来自蚀刻过度开路和短路来自曝光或蚀刻不均毛刺和铜渣是残留铜没清理干净。这些缺陷的共性是面积小、对比度低、与正常走线纹理高度相似所以检测算法必须依赖局部纹理差异而不是全局形状。这也是为什么很多通用目标检测模型在PCB数据上表现一般——它们把太多参数花在学“背景是什么”上而PCB背景几乎不变。做PCB layout的工程师还会发现一个有趣的点这类缺陷和设计规则检查DRC里查的东西不是一回事。DRC查的是线宽、间距、环宽这些设计约束而这里查的是工艺过程产生的物理缺陷。数据里的“short”和“open”如果出现在设计阶段是设计错误出现在制造阶段就是缺陷——这也是标注时容易让新人困惑的地方。2.2 yolov9标注格式逐行拆解五个归一化数字yolov9沿用了yolov5以来的标注规范每张图片对应一个同名txt文件每一行表示一个目标class_id x_center y_center width height五个字段全部是归一化数值。x_center和y_center是缺陷框中心点相对图片宽高的比例width和height是框宽高相对图片宽高的比例取值范围都在[0,1]区间。class_id从0开始计数和配置文件里的类别顺序一一对应。读取这类标注的Python脚本非常简单import os def parse_yolo_label(txt_path, img_w, img_h): 将yolov9格式的txt解析为像素坐标框用于可视化与越界检查。 参数img_w、img_h是原图宽高标注中的归一化坐标以它们为基准。 boxes [] if not os.path.exists(txt_path): return boxes with open(txt_path, r) as f: lines [l.strip() for l in f.readlines() if l.strip()] for line in lines: parts line.split() cid int(parts[0]) # 类别ID从0开始 x_c, y_c, w, h map(float, parts[1:]) # 均为0~1归一化值 x1 (x_c - w / 2) * img_w y1 (y_c - h / 2) * img_h x2 (x_c w / 2) * img_w y2 (y_c h / 2) * img_h boxes.append((cid, x1, y1, x2, y2)) return boxes # 用法示例检查某张图的标注是否越界 boxes parse_yolo_label(labels/train/missing_hole_01.txt, 640, 640) for cid, x1, y1, x2, y2 in boxes: if x1 0 or y1 0 or x2 640 or y2 640: print(f越界标注: class{cid}, box({x1:.1f},{y1:.1f},{x2:.1f},{y2:.1f}))这段脚本的核心意义有两个一是从归一化坐标反推像素级坐标用来画框检查二是校验标注是否越界。归一化坐标本来不应该出现负数或超过1的值但手工标注或格式转换脚本难免产生非法值。yolov9训练时遇到标注框中心点落在图片外会直接跳过这个标注或者报错中断提前巡检一遍能省下不少调试时间。另一个值得注意的细节标注的归一化是相对原始图片的宽高而不是相对训练时resize后的640×640。letterbox填充不会改变归一化数值的含义但有些工程师会把标注文件和resize图像混在一起看产生“框偏了”的错觉。检查标注正确性的可靠方式是直接画在原图上对比不要画在letterbox后的图上。2.3 1297张图片如何切分训练、验证、测试三层分配1297张图对评估结论的影响很大程度上取决于切分方式。随机切分听着省事但PCB缺陷类别分布极不均匀pinhole和spur这类缺陷可能只占很小的样本比例随机切分极容易把稀有类别全部切进训练集或验证集。一旦稀有类别在验证集里完全消失总mAP会显得很高但真实场景里模型一遇到针孔就漏检。可靠做法是按类别做分层抽样。思路是为每张图片构造一个“是否包含某类别”的二进制向量基于这个向量做切分尽量保证每个类别在训练集和验证集都有分布。切分结果要固定下来用随机种子保存保证每次训练在相同的数据分布上评估。数据集目录结构建议如下pcb_defect/ ├── images/ │ ├── train/ # 约1000张 │ ├── val/ # 约150张 │ └── test/ # 约150张训练期间不触碰 ├── labels/ │ ├── train/ # 对应同名txt标注 │ └── val/ └── pcb.yaml # 数据配置文件把test单独拆出来的原因只有一个最后评估时需要一个完全没有参与过训练和调参的集合。如果只看训练集和验证集上的指标很容易被验证集调参过拟合带偏。血泪经验是很多项目在验证集上刷到99%一上真实产线立刻掉到90%——多半是切分时把测试分布混进了验证集。分层切分的参考脚本import os from sklearn.model_selection import train_test_split image_dir images # 全量图片 label_dir labels # 全量标注 image_names [] label_rows [] # 每个样本的类别存在性向量 all_class_ids list(range(7)) for img in sorted(os.listdir(image_dir)): txt os.path.join(label_dir, img.replace(.jpg, .txt)) if not os.path.exists(txt): continue with open(txt) as f: class_ids set(int(line.split()[0]) for line in f if line.strip()) image_names.append(img) label_rows.append([1 if c in class_ids else 0 for c in all_class_ids]) train_imgs, val_imgs train_test_split( image_names, test_size0.15, stratifylabel_rows, random_state42 )切分时test_size按样本量定1297张图取0.15比较合适再大验证集就太薄。stratify参数要求传入的标签是二维数组或复合标签sklearn会尽量保持每个类别比例一致。random_state固定后多次运行得到相同切分方便后续复现。切分完成后建议用labelimg或cvat这类常用的目标检测标注工具随机抽查几十张图确认txt里的框确实落在缺陷上这一步能拦截大部分格式层面的低级错误。3. 用yolov9训练PCB缺陷检测模型从环境到权重的完整步骤3.1 训练环境准备PyTorch、CUDA与显存预算yolov9的训练环境依赖PyTorch和CUDA常见组合是Python 3.8、PyTorch 1.13到2.x、CUDA 11.x或12.x。显存8GB以上的显卡可以开batch-size 16配合640×640输入4GB显存则需要把batch-size降到8或者把训练输入尺寸降到416。PCB缺陷是小目标为主的任务输入尺寸不建议低于416否则pinhole这类几个像素的缺陷在特征图上可能只剩一个点。克隆官方yolov9训练仓库并安装依赖# 以官方yolov9实现为例克隆后安装训练依赖 git clone https://github.com/WongKinYiu/yolov9.git cd yolov9 pip install -r requirements.txt如果机器上有多个显卡用--device 0,1指定如果只有CPU训练池建议只用于验证代码流程不要真的用CPU跑100个epoch那会等到怀疑人生。这步最常见的问题不是环境装不上而是PyTorch的CUDA版本和驱动不匹配。安装前先跑一句python -c import torch; print(torch.cuda.is_available())输出False就先去装对应CUDA的PyTorch预编译包别在依赖冲突里硬耗。3.2 数据配置文件yaml里的类别顺序决定训练成败yolov9读取数据配置时nc类别数量和names类别名列表必须和标注文件里的class_id完全对应。以下是一个可用的pcb.yaml# pcb.yaml path: /data/pcb_defect # 数据集根目录绝对路径最稳 train: images/train # 相对path的训练图片目录 val: images/val # 相对path的验证图片目录 test: images/test # 可选通常不参与训练 nc: 7 # 类别数量务必与txt里的class_id范围匹配 names: [missing_hole, mouse_bite, open_circuit, short, spur, spurious_copper, pinhole]这段配置的关键参数是names数组的下标位置。比如names第0位是missing_hole那么标注txt里class_id为0的那一行必须对应缺失孔class_id为6对应pinhole。如果数据集作者把pinhole排在names第4位而这里写成第6位训练不会报错但推理时模型会把针孔当成毛刺最后准确率直接崩掉。路径写法是个容易忽视的细节。path用绝对路径最稳妥用相对路径要保证启动训练命令时的工作目录在数据集根目录之外指定。很多训练“莫名其妙找不到图片”的报错不是路径不存在而是yolov9在读相对路径时拼接错了基准目录。3.3 启动训练关键超参数与训练过程监控数据配置准备好后启动训练的命令如下python train.py \ --data pcb.yaml \ --weights yolov9-c.pt \ --batch-size 16 \ --epochs 100 \ --imgsz 640 \ --device 0参数的直观说明参数建议值说明--datapcb.yaml数据配置路径错了后面全错--weightsyolov9-c.pt预训练权重也可以用COCO版yolov9-c或者不传从零训练--batch-size8~16由显存决定PCB小目标多batch别太小--epochs100~2001297张图100轮够收敛太少学不到小缺陷纹理--imgsz640小于416时小目标丢失严重训练开始后重点关注两类输出一是loss曲线train loss应持续下降val loss在中间可能出现波动二是mAP0.5曲线如果验证集mAP不涨反跌通常是数据增强过猛或学习率没降下来。yolov9训练过程中会周期性输出验证指标不用额外写脚本。TensorBoard这类网页可视化工具可以顺带开着但不是必须。关于预训练权重常见做法是使用yolov9-c在COCO上的预训练模型做迁移学习。PCB缺陷和COCO自然图像差异很大但预训练权重仍然提供边缘和纹理特征的初始化收敛速度和最终精度都好于从零训练。如果训练100轮后mAP0.5还不到0.9先别急着调模型回头检查数据增强配置。yolov9的数据增强在超参数文件里控制最常见的调节点是hsv_h、hsv_s、hsv_v颜色扰动和degrees旋转角度。PCB图片本身颜色稳定过强的颜色扰动反而让模型学到错误的颜色不变性。一个相对稳妥的起点是把颜色扰动降到默认值的一半旋转角度限定在±10度以内等模型能收敛了再逐步放开。训练中断是常态yolov9支持从最近的checkpoint恢复python train.py --data pcb.yaml --weights runs/train/exp/weights/last.pt --resume注意resume时必须保证数据集路径、配置文件不变否则会加载失败。另外last.pt和best.pt的差异不要混淆——best.pt是验证集指标最优的权重last.pt是最后一个epoch的权重。1297张图这种小规模训练单卡8GB显存跑完100轮通常只需要两到四小时时间可控直接用best.pt做后续推理验证就够了。4. 避坑PCB缺陷检测训练中常见的五个翻车点4.1 现象训练loss正常下降但mAP一直为零这个问题在换数据集时出现频率极高。原因是标注文件里的类别ID和pcb.yaml里的names顺序不一致。举例来说标签txt里class_id5对应spurious_copper但names列表第5位写的是pinhole模型训练时学到的类别语义全错位验证集评估自然为零。解决方法是写一个小脚本交叉核对随机抽20个标注txt打印class_id范围再对照names第几个位置确认一一对应。更直观的办法是用labelimg或cvat打开一张图把txt转回像素坐标画在原图上看框的类别名是否和缺陷形态吻合。这个校验应该在训练前做不要等训练完看曲线才发现。4.2 现象训练完模型对微小缺陷漏检严重pinhole和spur这类缺陷框只有十几个像素yolov9在数据增强阶段默认会对图片做随机缩放、裁切和马赛克增强小目标很容易被缩放到特征图上只剩一个点。解决方向有三个按性价比排一是把输入尺寸从640提到768或960小目标像素数量多了模型更容易学二是在超参数配置里关闭或降低mosaic强度比如mosaic0.5马赛克四图拼接会让小目标在拼贴边界被切掉三是单独用一份只含小缺陷的样本做二次微调把漏检类别的权重补回来。这三个手段可以叠加使用但每加一个都要在验证集上看mAP变化不能盲调。4.3 现象验证集指标很好看测试集一测就崩这是典型的切分泄漏问题。如果切分前没有打乱或者同一张板子的不同区域图片被同时分进训练和验证集验证集里就会出现训练样本的近亲图片。PCB缺陷数据集的原始照片通常是一张大板分成多个小图同一块板的缺陷纹理高度相似这些图会被模型“背下来”而不是“学会”。解决方法是按板子ID分组切分保证同一块板的所有局部图只在训练集或只在验证集。判断是否泄漏可以看一个细节训练集mAP比验证集mAP高很多且验证集mAP在训练中段突然跳高多半是泄漏。4.4 现象原图分辨率五花八门标注框画出来是歪的1297张图片如果来自不同批次或不同相机分辨率可能从500×500到1600×1200都有。标注归一化数值不受影响但如果同一个小缺陷在有的图里占15像素、在有的图里占60像素模型会被迫学习一个“平均尺度”两头不讨好。解决方法是先统一分辨率较稳妥的方案是把所有图缩放到一个统一的长边尺寸比如1024同时保持宽高比再同步更新标注。如果发现标注坐标在缩放后出现偏移尽量写脚本批量重算不要手动改手动改1297张图的坐标纯属给自己挖坑。4.5 现象不同人复现“99.8%”得到的数字差很多“识别准确率”这个词有歧义。有人把Precision当准确率有人把Accuracy当准确率还有人直接把验证集mAP0.5当成准确率。在PCB检测的场景里公开数据集和论文通常报告的是每个类别的Precision、Recall和mAP。99.8%大概率指某个类别或加权平均的Precision而不是全体缺陷的mAP。如果要在自己的测试集上复现这个数字必须先定义清楚统计口径是框级别的IOU0.5命中率还是图片级别的有无缺陷分类准确率。两个口径算出来的数字可以差好几个点说不清楚口径的99.8%不具备可比性。建议让评测代码输出Precision、Recall、mAP0.5、mAP0.5:0.95四列按表对比谁都能复现。5. 复现99.8%从指标解读到模型验证5.1 用分层K折交叉验证得到一个可信的准确率1297张图只切一次得到的结果方差很大。如果想让99.8%这个数字站得住常见做法是跑一遍分层5折交叉验证。把数据按类别分层切成5份每次取4份训练、1份验证轮5次最终指标取平均值。这比单次切分更能反映数据集质量。缺点是要训练5次但1297张图的小数据量单次训练耗时可控值得做。交叉验证的统计口径建议与最终交付口径一致。生产环境关心的是“流入检测工位的板子中有多少被正确分拣”对应图片级Accuracy或缺陷级Precision。评测代码要固定IOU阈值比如0.5固定置信度阈值比如0.25固定NMS参数否则结果不可比。注意交叉验证的每一折都要重新训练5折就是5次完整训练。1297张图单次训练时间不长但如果算上调参整体耗时放大5倍时间预算要提前规划。5.2 错误分析哪种缺陷在拖后腿训练完一个baseline后把验证集上所有被漏检或被误检的样本单独拎出来按类别统计。通常你会发现80%的漏检集中在1到2个类别上——要么是pinhole要么是spur。这时候可以做两件事一是针对该类别收集或生成更多样本继续训练二是给该类别单独调置信度阈值让模型对低置信度的小缺陷更敏感。PCB检测里漏检的代价远大于误检因为缺陷板流到客户手里是要赔钱的而误检只是多了一次人工复判。一个实用的错误分析脚本框架# 统计验证集每类的漏检数和误检数 from collections import Counter false_neg Counter() # 标注存在但模型没检出 false_pos Counter() # 模型检出但标注不存在 for pred, gt in zip(predictions, ground_truths): for cls in gt: if cls not in pred: false_neg[cls] 1 for cls in pred: if cls not in gt: false_pos[cls] 1 print(漏检TOP:, false_neg.most_common()) print(误检TOP:, false_pos.most_common())执行错误分析前先把预测结果的置信度统一记下来。因为阈值一旦下调漏检会变少、误检会变多两个数量是此消彼长的不看阈值谈漏检率没有意义。5.3 从数据增强到难例挖掘补短板的具体手段对PCB缺陷数据集来说最有效的数据增强不是颜色扰动或翻转而是多尺度训练和复制粘贴。多尺度训练让模型适应不同放大倍率下的缺陷尺寸复制粘贴则是把同一张图上切出的小缺陷目标贴到正常板图的多个位置既增加样本数又不破坏PCB背景的真实性。yolov9超参数里的copy_paste选项就是干这个的先加到0.3试试mAP如果提升就继续加跌了说明粘贴位置太生硬模型学到了假纹理。难例挖掘的落地方式是“错题本”。把每轮验证集里漏检的图片单独存到一个目录训练完下一轮时把这个目录里的样本复制一份重新放回训练集并提高它们的采样权重。这套做法在PCB缺陷检测里效果比盲目增加新数据还明显因为漏检样本基本集中在同一类形态变异上。另外指标的计算只讲Precision和Recall太粗最终建议把PR曲线画出来选择曲线上Precision和Recall交叉的平衡点作为调试基准。很多模型在默认阈值下Precision很高但Recall很低——它确实没怎么误报但漏了一半缺陷。PCB质检场景宁可接受低一点Precision也不能牺牲Recall除非你有其他手段兜底。6. 部署阈值与数据集迭代让模型适配产线节拍6.1 推理端置信度阈值与NMS调参训练时的超参数和部署时的推理参数是两回事。yolov9推理时默认置信度阈值0.25、NMS阈值0.45在PCB产线场景通常需要下调查信度到0.1甚至0.05换取更低漏检率。代价是单张图的检测框数量上升需要在后端加一个人工复判或规则过滤环节。建议先在100张不重复的真实产线图上扫描PR曲线找到Recall接近0.98对应的置信度阈值用它作为部署初值。6.2 从yolov9格式迁移到yolov8或yolov5生态yolov9的标注格式和yolov8/yolov5完全兼容迁移工作几乎为零。很多项目从yolov9训练换到yolov8部署因为推理框架和TensorRT工具链对yolov8支持更齐全。如果之前是用yolov8训练自己的数据集这份yolov9格式数据可以直接喂给yolov8的官方训练脚本只需要保留同一套data.yaml和类别顺序。需要注意yolov9官方实现和yolov8官方实现里--imgsz参数的单位和默认值略有差异启动前重新确认一遍别把416当成640传进去。6.3 数据迭代闭环用难例把数据集越养越厚第一批模型上线后要把产线复判为NG的图片定期收集起来经过标注和筛选后回流到训练集。1297张图的起点够了但产线光照变化、板型更换、铜厚差异都会让旧数据逐渐失效。按周或按月做一轮数据回流每次只抽新增难例标注成本可控。这是让99.8%从一次实验变成长期指标的真正关键数据集的厚度决定模型寿命。我习惯在每次增补数据后记录三样东西新增图片数、类别分布变化、验证集mAP变化。这个记录比训练曲线更值得长期保存。希望帮到你。本文还有配套的精品资源点击获取