
简介面向毕业设计、课程设计与项目开发场景这套基于Python与Faster-RCNN的PCB元器件缺陷检测项目提供完整源码、开发文档与项目解析。项目已通过严格测试支持在VOC0712数据集上训练也可切换到自定义数据集进行训练、预测与评估覆盖数据预处理、模型搭建、迭代训练、推理检测和指标评测等完整环节。压缩包共79个文件以35个Python源码和39个编译生成的pyc文件为主另含Markdown说明文档、工程说明文本、gitignore与许可证文件整体仅214KB结构轻量清晰。内容按功能模块划分涵盖数据扩充与标注转换、基于多种骨干网络的FasterRCNN实现、训练预测入口、mAP评估工具以及必要的说明与QA文档方便按图索骥开展二次开发与复现实验。目前已有381人学习浏览适合计算机视觉方向的课题研究、课程设计或毕业设计参考可直接运行亦可作为功能扩展的基础框架。1. PCB元器件缺陷检测Faster R-CNN 源码包能解决什么PCB元器件缺陷检测真正卡住人的往往不是模型结构而是数据准备和训练流程。板面密集、元器件尺寸小一阶段检测器漏检率偏高做毕业设计或课程设计时更愿意用两阶段网络把检测过程拆开讲清楚。这份基于 Python 的 Faster R-CNN 工程把 VOC 数据准备、训练、预测、评估整条链路都补齐了VOC0712 训练标准、anchor 聚类、mAP 评估脚本全部成套你可以直接用它跑公开数据集练手也可以换上自己的 PCB 缺陷图片做迁移训练。它适合三类人毕业设计需要完整训练流程和文档支撑的学生课程设计里想快速交付一套可演示系统的开发者以及刚接触检测落地、想照着完整工程学参数配置的从业者。2. 认识源码包Faster R-CNN 的组件、文件角色与 VOC 数据约定2.1 Faster R-CNN 为什么适合 PCB 元器件缺陷检测PCB 板上的常见缺陷比如缺件、偏移、焊桥在图像里往往只有几十到上百个像素板面上又有大量铜箔走线和焊盘纹理做背景前景和背景的边界非常模糊。Faster R-CNN 的两阶段设计正好踩中这个场景的痛点RPN 先决定“哪里有可能是目标”第二阶段再决定“这是什么缺陷、框怎么调”。相比 YOLO、SSD 这类一阶段检测器直接回归类别和位置的做法两阶段结构在密集小目标上的漏检率更低这也是很多 PCB 检测课题在开题时就选 Faster R-CNN 的原因。在实际项目里“可解释性”是另一个容易被忽略的价值。训练时 loss 不降你可以单独看 RPN 输出的候选框置信度判断问题出在目标没被框出来还是分类没学好拿到一张漏检图也能从 NMS 阈值和 anchors 尺寸往上排查而不是对着整个网络黑匣子瞎调参数。对毕设答辩来说这意味着你能把 RPN、ROI 池化、分类回归头拆成三块讲清楚整个工程不再是说不清的“玄学”。2.2 源码包文件角色训练、数据、预测、评估四条链路拿到压缩包先别急着跑训练我习惯按功能把文件分成四组对照 README.md 看一遍再动手。README 里通常写的就是项目简介、训练步骤、预测步骤和评估步骤qa.md 里还汇总了常见问题答疑这两份文档本身就是最权威的开发文档。分组文件作用数据准备voc_annotation.py读取 VOC XML 标注按 ImageSets 划分生成训练、验证的 txt 索引数据准备order_name.py按文件名排序避免多线程读取时图片名与标注错位数据准备data_expansion.py离线数据增强扩充 PCB 小缺陷样本模型结构nets/resnet50.py、vgg16.py、resnet101.py三种常见骨干网络模型结构nets/resnet50_FPN.py、rpn.py、classifier.py带特征金字塔的主干、RPN 与分类回归头训练train.py训练启动入口汇总数据集、类别数等配置训练FasterRCNN_train.py训练主流程控制正向传播、反向传播和日志输出训练utils/dataloader.py、utils/utils_fit.py数据加载与训练回调封装训练utils/anchors.py、kmeans_anchors.py默认 anchors 生成与聚类重算工具预测predict.py、frcnn_predict.py、Suggestion_box.py单图预测、批处理调用与建议框可视化评估get_map.py、utils/utils_map.py客户端评估入口与 mAP 计算核心工具summary.py打印模型结构、参数量供论文或答辩截图这张表对应的是压缩包里的真实布局每个文件都不是摆设。刚开始时把注意力放在 vic_annotation.py、FasterRCNN_train.py、predict.py、get_map.py 这四个入口上其他文件等功能跑通了再回来看效率会高很多。2.3 环境与数据约定VOC0712 与自制数据集目录这个工程遵循 VOC 数据格式。VOC0712 训练意味着用 VOC2007 的 trainval 加 VOC2012 的 trainval 一起训练再拿 VOC2007 的 test 做评估这是许多 Faster R-CNN 实现里反复验证过的数据组合样本量足够大类别划分也标准。第一次接触这套工程的开发者直接用公开数据集把流程跑通比一上来就换自己数据更稳妥。VOC 数据集的目录结构是固定的VOCdevkit/ VOC2007/ JPEGImages/ # 图片jpg 或 png Annotations/ # 同名 XML 标注 ImageSets/Main/ # train.txt、val.txt、test.txt自制数据集时按这个结构建目录图片文件名要和 XML 文件名完全一致ImageSets/Main 里只写不带扩展名的文件名。标注文件至少要包含 object 节点、name 标签和 bndbox 四个坐标Faster R-CNN 的 dataloader 只认这几项少了 name 或者坐标字段训练时立刻报 KeyError。提示在 Python 3.8 或 3.9 的虚拟环境里安装依赖最省心别在过新的 Python 版本上硬跑旧工程。装深度学习框架前先确认机器上的 CUDA 版本GPU 训练时这个环节最容易出错。依赖方面压缩包里的 requirements.txt 列出了运行时依赖安装顺序建议是激活虚拟环境 → 先装基础图像库numpy、Pillow、opencv-python→ 再按 requirements.txt 安装剩余项 → 最后单独验证框架能否调用 GPU。如果机器是 CPU 环境也能训练只是迭代速度会慢很多batch_size 建议直接设成 1 或 2。3. 数据准备从 VOC 标注到生成训练列表的四步操作PCB 缺陷检测的坑十有七成出在数据上不在网络上。模型结构可以照搬数据一旦名称错位、坐标没跟图片走训练出来就是“看着 loss 在降、实际效果全乱”的假象。为了避免这种假象建议严格按 voc_annotation.py → order_name.py → data_expansion.py → 检查 txt 的顺序操作。3.1 voc_annotation.py把 XML 标注批量转成训练列表这个脚本是整个数据链路的第一步。它读取 Annotations 里的 XML提取每个目标的名字和 bndbox 坐标再按照 ImageSets/Main 里的划分生成形如 2007_train.txt 的训练列表文件。训练数据加载器之后只读 txt不再直接解析 XML好处是数据格式在训练前一次性校验不用在 dataloader 里重复做 XML 解析速度更快问题也更好定位。python voc_annotation.py跑完之后用 wc -l 检查生成文件的行数wc -l 2007_train.txt正常情况下行数应该和 ImageSets/Main/train.txt 完全一致。差了行数说明有图片或标注缺失行数一致还要再抽查几行内容确认坐标没有出现 0 或负数。检查通过之后这份 txt 就会作为后续训练和聚类脚本的数据源所以这一步值得多花两分钟。注意脚本顶部通常有一份 classes 列表把缺陷类别按固定顺序填进去比如 missing、offset、solder_bridge。这个顺序必须在 voc_annotation.py、训练配置、预测脚本三处保持一致改了一个另外两个不改模型输出的类别编号就会整体错位排查起来很费劲。3.2 order_name.py给文件名排序杜绝图与标注错位很多自制数据集是从现场直接拷出来的图片文件名可能是 IMG_0231.jpg、IMG_97.jpg 这种混排。dataloader 准备时按目录顺序读一旦操作系统返回顺序和标注列表顺序不一致图像和标注就会错位训练出来的模型在测试集上表现会很差而且很难定位问题。order_name.py 的作用就是把图片文件按文件名排序后重新生成总表保证图片和标注一一对应。更换过数据集、追加过新图片之后我都习惯先跑一次python order_name.py脚本内部的排序逻辑通常是字符串排序如果想让 IMG_2 排在 IMG_10 前面就得在文件名数字部分做补零处理。排序本身不是训练的必要环节但这几分钟的时间能在后面省出几小时的排查时间属于典型的“低投入、高回报”操作。我见过太多人跳过这步最后发现训练集里的图对应的是上一批数据的标注白白浪费十几个小时的显存。3.3 data_expansion.py缺陷样本不够时的离线增强PCB 缺陷数据集普遍存在两个问题总量少、类别不均衡。缺件样本可能只有几十张而正常板子占绝大多数。data_expansion.py 做的是离线增强对原图做随机翻转、旋转、亮度扰动再把缺陷区域提取出来做适当放大生成一批新图片和新标注。python data_expansion.py --input ./origin --output ./augmented --times 3这里的--times表示每张原图额外生成几张图。需要特别注意翻转和旋转的增强必须同步修改 XML 里的 bndbox 坐标不能只增强图片不管标注。增强完成之后用标注工具随机抽几张查看框的位置是否跟着移动这一步一定要做否则坐标错位的脏数据会被当成训练数据吃进去。离线增强和训练时的在线增强on-the-fly augmentation并不冲突两者可以同时开。当类别严重不平衡时优先用离线增强把少类样本数量补上来在线增强负责提高整体泛化能力两条腿走路。3.4 自制 PCB 缺陷数据集的目录模板如果你的项目没有现成公开数据集规范建目录是第一件事mkdir -p PCB_data/JPEGImages mkdir -p PCB_data/Annotations mkdir -p PCB_data/ImageSets/Main touch PCB_data/ImageSets/Main/train.txt touch PCB_data/ImageSets/Main/val.txt把图片放进 JPEGImages同名 XML 放进 Annotations在 train.txt、val.txt 里写上对应的文件名不带扩展名然后在 voc_annotation.py 里把数据根路径从 VOCdevkit 指到 PCB_data重新跑一次。标注工具我用得最多的是 LabelImg另存为 VOC XML 格式和这套工程的数据加载方式完全匹配。要注意 PCB 原图通常尺寸很大直接标注整张 3000x3000 的板子训练时缩放到 600 像素缺陷可能被压到几乎没有像素。常见做法是先把大图裁剪成若干个 512x512 或 1024x1024 的小图再逐块标注。裁剪时 bndbox 坐标必须做相应的偏移换算不能在缩放原图之后直接复用原坐标。这一条是自制数据集最容易忽略的细节适用范围也不只是这个工程所有基于 VOC 格式的检测项目都一样。4. 训练与评估参数档位、启动命令和 mAP 怎么读4.1 train.py 与 FasterRCNN_train.py 的分工train.py 是训练入口承担配置职责数据集路径、类别数、batch_size、学习率、epoch 数都在这里集中修改跑起来之后它会创建模型、加载数据、调用训练循环。FasterRCNN_train.py 是另一层实现负责训练主流程——正向传播、计算 RPN 损失和分类回归损失、反向传播、更新学习率以及打印日志。把入口和主流程拆开的好处是调参的时候只动 train.py改网络细节的时候才动 FasterRCNN_train.py互不干扰。这套工程里有一个常见且稳妥的训练策略先冻结骨干网络的权重训练 RPN 和分类头若干轮让候选框先稳定下来再解冻整个网络做微调。冻结阶段的训练更稳定因为 backbone 初始权重来自预训练模型前期不参与更新网络参数搜索空间小loss 不会出现大幅震荡等 RPN 生成的候选框质量稳定下来解冻微调阶段的学习率要同步下调避免在收敛边缘又把权重推飞。4.2 关键训练参数怎么调参数常见档位调节建议batch_size2 / 4 / 8由显存决定PCB 大图经过缩放后仍很大先小后大冻结阶段学习率1e-4常见初始值loss 猛烈震荡时降为 5e-5解冻阶段学习率1e-5解冻后必须下调否则训练过程明显反弹冻结轮数50数据量大可延长到 80数据少可缩到 30总轮数100–150以验证集 mAP 不再上涨为准第一轮训练时batch_size 从 2 开始是稳妥的。用 nvidia-smi 观察显存占用率如果训练中接近 90% 以上就该调小 batch 或缩小输入图片尺寸而不是硬撑着跑因为显存不足时的随机 OOM 会打断训练甚至损坏权重文件。学习率再强调一遍冻结阶段用 1e-4解冻后降到 1e-5这是我在多个检测工程里验证过不容易出问题的档位。训练命令按 README 和 train.py 里的实际形式来常见做法是修改脚本里的配置项后在项目根目录直接执行python train.py我一般会先把总轮数改小、只加载十分之一数据跑 5 个 epoch 做冒烟测试确认 loss 能降、权重文件能正常保存再开全量训练。这一步能筛掉“代码路径不对、数据格式报错、显存超限”这三类基础问题比直接跑一个 100 epoch 的完整训练划算得多。4.3 预测predict.py 与 frcnn_predict.py 的两种用法训练得到权重之后predict.py 负责对单张图片做推理并可视化输出适合一张张看检测效果判断某个缺陷类别有没有被识别出来。frcnn_predict.py 走的是封装调用路线适合在自己的脚本里 import 后做批量推理或者嵌到检测流程里当模块用。用预训练权重做预测前先确认两个地方类别顺序要和训练时完全一致num_classes 要和模型分类头一致。预测脚本里通常还会有置信度阈值、NMS 阈值这类开关PCB 缺陷检测场景下置信度阈值我一般设为 0.5 起步密集目标出现重复框的时候再往上调到 0.6相反如果发现漏检就把阈值往下降。python predict.py --image ./test/board_01.jpg --model_path ./logs/best_epoch_weights.pth这里的--image和--model_path是这套脚本常用的参数形式具体键名以源码里的 argparse 定义为准。预测阶段特别建议打开“框上显示置信度”的选项肉眼观察输出图时可以直接看到模型对自己判断的信心比只输出一个 mAP 数字直观得多。4.4 get_map.py 评估mAP 只是及格线召回率才是 PCB 场景的生死线get_map.py 调 utils_map.py 计算 mAP。评估流程是把所有验证图片的预测结果和真值收集起来按置信度排序之后做 IoU 匹配再画 PR 曲线。mAP 反应的是分类和定位的综合能力但它会掩盖某些单一类别的问题所以评估时我会特别关注每个类的 AP 和召回率。在 PCB 缺陷检测里漏检比误检代价更大漏掉一个焊桥可能整板报废多画几个误检框反而容易人工复核。所以看 get_map.py 的输出时要重点看每个缺陷类别的召回率够不够高而不只是总 mAP 数值。如果某一类的召回率明显偏低大概率是 anchors 尺寸没覆盖到这类小目标回到 kmeans_anchors.py 重算 anchors比盲目加训练轮数有效得多。提示mAP 计算会比较耗时数据量大时每轮验证会拖慢整体进度。训练期间可以把验证比例调小或者每 5 个 epoch 评估一次最后再跑全量精确评估。5. 避坑与常见问题训练 PCB 缺陷检测最容易翻车的五个点训练过程真正花时间的永远不是跑批量而是查报错。下面五类问题是我在这种 Faster R-CNN 工程里反复撞过的每一条都按“现象 → 原因 → 解决”记录。5.1 训练一开始就报 shape mismatch 或维度越界现象train.py 启动后很快就抛出维度不匹配的错误错误堆栈指到分类头或者回归头的位置。有时候错误信息还会直接显示 expected 和 actual 两个维度数字看起来像是某个矩阵乘法的形状对不上。原因类别数没有对齐。voc_annotation.py 里少写一个缺陷类别但 train.py 里的 num_classes 还是旧数字导致分类头输出维度和标签编号对不上。这个问题在公开数据集上很少出现但换到自己数据集时几乎必踩一次。解决把 classes 列表做成唯一数据源voc_annotation.py、train.py、predict.py 三处全部 import 同一份配置改完类别列表后删除旧生成的 txt 重新运行 voc_annotation.py。不要手动改一个副本留一个旧副本这是整个项目里最常见的低级事故。排查顺序也固定先看类别列表长度再看 txt 行数最后看模型分类头输出维度三步走完基本能定位。5.2 loss 前几十轮不降或者降一半就卡死现象训练到第五十个 epochloss 仍然贴着初始值偶尔小幅波动另一种更迷惑的是 loss 先降到某个平台之后无论怎么训都不再下降看起来像是正常收敛但验证集上的 mAP 始终上不去。原因anchors 尺寸和 PCB 缺陷目标不匹配。默认 anchors 是按公开数据集尺度设计的锚框最小边长可能依然远大于几十像素的缺件目标RPN 里的正样本数量太少后续分类器根本没有机会学出有效特征。loss 卡在平台期说明模型已经把能学的都学了剩下的问题出在候选框生成层面。解决用 kmeans_anchors.py 统计训练集中所有标注框的宽高聚类出 9 组锚框尺寸替换工程里的默认 anchors 常量。替换后不要立刻改学习率先保持冻结阶段参数重新训练几十轮观察 RPN 的 loss 是否有明显下降趋势。这一步改动往往比调学习率带来的收益更明显。5.3 Windows 下跑 voc_annotation.py 突然报错现象在 Windows 环境运行数据准备脚本出现路径找不到或者 UTF-8 解码失败同样的代码在 Linux 下单跑没有问题。错误信息里可以看到系统把反斜杠和斜杠混在一起拼接路径导致文件定位失效。原因Windows 路径分隔符是反斜杠Linux 是斜杠脚本硬编码了其中一种另外 XML 里有中文内容默认 GBK 编码读不出来。很多人把 Linux 上跑通的工程直接拉到 Windows 就报这个错。解决脚本内部用 os.path.join 组合路径避免手写斜杠打开 XML 时显式声明 encodingutf-8。如果是从现场拷贝的标注文件建议先统一转成 UTF-8再跑数据生成脚本。这个坑本身很小但它卡在数据准备的第一步会让很多人误以为是模型代码有问题。5.4 GPU 显存不足一喂 batch 就 OOM现象程序加载数据后报 CUDA out of memory有时是训练到中途随机出现并非每轮都发生。这种随机性最难受因为你很难判断是数据太大还是权重累积导致的峰值占用。原因PCB 原图尺寸大即使 resize 到固定大小显存占用也高于普通检测数据集batch_size 设置偏大或者验证时一次性喂入过多图片。另外训练中验证和训练交替进行时验证过程临时分配的显存也可能把峰值推上去。解决第一步把 batch_size 降到 2第二步把数据加载器里的目标尺寸从 600 缩到 512 或 416第三步再把验证集的图片量减半。梯度累积可以当“后悔药”用但不要一开始就依赖它因为梯度累积改变了 batch 内部的统计量对 BatchNorm 层不友好。改完用 nvidia-smi 重新观察一轮训练确认显存占用稳定在 80% 以下再继续。5.5 加载预训练权重报 unexpected key 或 missing key现象加载权重时日志里刷出一堆 unexpected key或者训练能跑但初始 loss 异常高收敛速度极慢。更隐蔽的情况是模型没有报错但前几十个 epoch 的 loss 比从零开始训练还奇怪说明权重根本没有被正确加载。原因模型分类头的类别数不同导致最后一层权重参数数量对不上或者把不同骨干网络的权重文件混用resnet50 的权重加载到了 vgg16 的结构上。骨架网络名字对不上时特征提取层和检测头的 key 全部错位训练就等于从随机初始化重新开始。解决加载权重时跳过分类头和回归头的层只恢复 backbone 权重这是迁移学习里的标准做法。如果代码里设置 strictFalse也要逐条看加载日志确认跳过的只是最后一层而不是整个特征提取层都被忽略了。断点续训的时候同理保存的权重文件里应带上 optimizer 和 epoch 信息否则恢复后学习率可能还是初始值。6. 进阶换 backbone、重算 anchors 与低成本验证顺序6.1 换 backbone 的改动点nets 目录里提供了 vgg16、resnet50、resnet101、resnet50_FPN、resnet50_ECA_FPN 等多套骨干网络。换的时候只需要改模型构建处的主干定义RPN、ROI 池化和分类头不动Faster R-CNN 的框架天然支持这种替换。PCB 缺陷目标尺寸差异大FPN 结构通过多尺度特征融合对小目标更友好我一般首选 resnet50_FPN如果追求更高精度再加 ECA 注意力代价是训练时间变长。换完 backbone 之后预测脚本里的权重路径和 num_classes 也要跟着换否则又会出现上一章的 key 不匹配问题。6.2 用 kmeans_anchors 重算 anchors改完数据集的第一个动作就是重算 anchors不要让工程默认值替你做决定python kmeans_anchors.py --file 2007_train.txt --num_cluster 9脚本会用聚类算法统计训练列表里所有目标框的宽高比输出新的 9 组锚框值。拿到结果以后回到工程里把 anchors 常量替换掉打印检查一次确认输出值和标注框的尺度分布基本一致。6.3 我固定跑的验证顺序从那以后我每次拿到新数据都强制走一遍同样的顺序先跑 voc_annotation 生成新 txt再用 kmeans_anchors 重算锚框接着提取十分之一的数据跑 5 个 epoch 做冒烟测试确认 loss 有下降趋势后再上全量训练。这个流程能在半天内筛掉数据错位、anchors 不合理、路径配置错误这几类最常见的问题而不是等一个 100 epoch 的长训练跑完才发现数据从一开始就是脏的。重算 anchors、确认 loss 能降这两步是这套工程里最值得花时间的“预防针”。希望帮到你。本文还有配套的精品资源点击获取