YOLOv5实战:PCB元件目标检测数据集构建与训练全指南

发布时间:2026/9/15 6:26:46
YOLOv5实战:PCB元件目标检测数据集构建与训练全指南
简介这是一份面向目标检测学习者和电路板质检场景的YOLO格式数据集包含电路板上电感器、变压器、传感器等45类电器元件的标注图像按YOLOv5目录规范划分训练集约1800张、验证集60张、测试集40张并附有类别class文件下载后即可直接用于yolo检测训练。压缩包共2000个文件以1981个txt标注文件为主配合18张jpg原图与1个可视化py脚本整体仅53.66MB轻量便于快速上手。可视化脚本无需修改参数随机传入一张图片即可绘制边界框并保存到当前目录方便直观检查标注效果与模型输出。资源目前已有119人学习浏览适合正在做电路元件识别、PCB故障检测或需要现成YOLO训练数据的开发者直接使用。1. 电路板元件目标检测的难点与这套YOLO数据集的构成PCB质检里最典型的麻烦是一个0805封装的贴片电阻在五百万像素的板卡图上可能只占十几个像素目标小、背景杂、元件反光再加上电感器和变压器这类外观相近的类别直接拿COCO预训练模型硬训漏检率会非常难看。小目标检测不是单纯的模型问题数据组织和标签质量往往先决定了下限。这套数据集按YOLOv5目录规范整理训练集约1800张、验证集约60张、测试集约40张覆盖电感器、变压器、传感器等45类电器元件图片与同名txt标签一一对应根目录还带了classes.txt和一个无需改参数即可运行的数据可视化脚本。适合两类人一是想快速把PCB元件检测跑通YOLO训练流程的工程师二是做小目标检测改进、需要一份可复现数据的研究者。但验证集只有60张评估波动会很明显后文会专门讲怎么处理。2. 数据集目录结构与YOLO标签格式从文件夹布局到classes.txt的映射关系2.1 目录布局与YOLOv5的期望结构解压数据集后第一件事不是打开图片而是确认images和labels是否严格同名配对。这套数据沿用了Roboflow导出的YOLOv5结构根目录下是images/train、images/val、images/test和labels/train、labels/val、labels/test文件同名一一对应。文件名里带.rf.和一段hash是平台导出的痕迹不影响训练。确认目录有没有错位用tree和循环统计最直接tree -L 2 /data/pcb18 for d in train val test; do img$(ls /data/pcb18/images/$d/*.jpg | wc -l) lab$(ls /data/pcb18/labels/$d/*.txt | wc -l) echo $d: images$img labels$lab donetree看整个目录层级是否完整循环分别统计三个子集里jpg和txt数量正常应当完全相等。YOLOv5在训练时读取的是images路径标签路径通过在路径里把images替换为labels、把.jpg替换为.txt推导出来所以文件名必须严格配对多一个空格都不行。如果数量不一致常见原因是某些图片没有标签或导出时漏了txt这类图要在训练前剔除否则YOLOv5会跳过它等于少了一条数据。2.2 classes.txt45类元件的ID映射规则classes.txt是数据集的“字典”每一行是一个类别名行号从0开始这个行号就是标签txt里的第一列数字。比如第0行是电感器那么所有电感器标注的txt里第一列都写0而不是1。nl -ba /data/pcb18/classes.txt wc -l /data/pcb18/classes.txtnl -ba给每行加连续行号方便直接看类别IDwc -l确认总行数是否45。类别名可能是中文终端里直接cat容易乱码用Python按UTF-8读更稳。45类里除了电感器、变压器、传感器还有不少外观接近的小型元件类别边界模糊是后面评估时AP差距拉开的根源。只看classes.txt还不够还要看类别分布。小数据集最忌讳长尾所以我一般先跑一条命令cat /data/pcb18/labels/train/*.txt | awk {print $1} | sort -n | uniq -c | sort -nr | head -20这条命令把训练集所有标签的第一列类别ID取出来sort -n按数字排序uniq -c统计每个ID出现次数sort -nr按数量降序head -20看样本最多的前20类。执行完通常会发现某些类别只有几张图这种类别几乎学不到特征会直接影响后续损失函数的收敛表现和mAP。2.3 标签txt的归一化格式与坐标还原YOLO标签每行五个数字class_id, x_center, y_center, width, height全部是相对图片宽高的0到1浮点数。注意后四个数里前两个是中心点不是左上角后两个是宽高不是右下角。从VOC或COCO格式转过来时很多人在这里写错。写一个最小解析脚本把归一化坐标还原成像素坐标import cv2 image_id pcb18rec1_jpg.rf.fe6528260b430b34217765e080843a34 img_path fimages/train/{image_id}.jpg label_path flabels/train/{image_id}.txt img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f: cid, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) print(int(cid), (x1, y1), (x2, y2))逻辑说明cx, cy乘以图片宽高得到中心像素坐标减去半宽半高得到左上角加上得到右下角。这段代码同时是“体检工具”如果x2大于宽或y2大于高说明标注越界如果bw或bh小于等于0说明存在坏框需要删掉对应行。实际使用建议用os.path.join拼接路径避免手写路径在Windows或者Linux上分隔符不一致的问题。3. 数据可视化脚本随机抽图绘制边界框的实现与参数说明3.1 一键可视化随机图片渲染边界框再规范的标签也不如直接画在图上看得直观。附带的数据可视化脚本核心逻辑是随机从images/train选一张图按同名txt把框和类别ID画上去保存到当前目录。我常用的版本拆开讲可以直接拿去用。import cv2 import glob import os import random import numpy as np IMG_DIR images/train LABEL_DIR labels/train CLASS_FILE classes.txt def load_classes(path): with open(path, r, encodingutf-8) as f: return [line.strip() for line in f if line.strip()] def draw_image(img_path, classes): name os.path.splitext(os.path.basename(img_path))[0] .txt txt_path os.path.join(LABEL_DIR, name) img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: continue cid int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color tuple(np.random.RandomState(cid).randint(0, 255, 3).tolist()) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) label f{cid}:{classes[cid] if cid len(classes) else unknown} cv2.putText(img, label, (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) out os.path.join(os.getcwd(), vis_ os.path.basename(img_path)) cv2.imwrite(out, img) print(fsaved: {out}, boxes: {len(lines)}) if __name__ __main__: classes load_classes(CLASS_FILE) pick random.choice(glob.glob(os.path.join(IMG_DIR, *.jpg))) draw_image(pick, classes)代码里用os.path.splitext去掉图片后缀再拼txt避免直接把路径里的images替换成labels造成误伤。画框用cv2.rectangle类别文字用putText颜色由RandomState(cid)生成保证同一个类每次显示的颜色相同。2是框线粗细0.5是字体缩放1是字体线宽。如果元件小、框密建议把粗细改成1、字体改成0.4否则框会糊在一起。打印的boxes如果为0说明这张图没有有效标注需要回查txt内容。3.2 可视化结果异常怎么判断标签是否脏了把可视化脚本跑出来的图与原图对照出现下表情况时按对应方式处理现象可能原因处理方式图片上完全没有框txt为空或文件名配对错位用2.1节的循环统计核对文件名框明显偏左或偏上标签写成了左上角坐标没转中心点重新导出或转换标签格式框超出图像边界坐标越界或width/height写成了右下角用2.3节脚本过滤越界行类别文字乱码或显示unknownclasses.txt编码不是UTF-8或cid越界转编码检查classes.txt行数小框密集看不清元件本身小属于正常现象调大图片分辨率后再可视化可视化是发现脏数据最高效的路径。数据集文件多时不要逐张看随机抽三五十张有异常再批量修。要是发现某个类的框一半都偏那说明标注源头有问题直接训练只会把模型带偏。3.3 扩展脚本指定图片和类别过滤随机抽样看整体指定单图看细节。给脚本加两个参数更实用python viz.py --img path/to/pcb57rec1_jpg.rf.fbba315ab7096a734a35e0f5f098df61.jpg --class-id 3实现方式不复杂用argparse接住参数--img给定时不走random.choice--class-id给定时在画框循环里加一行if cid ! args.class_id: continue。这样只想查传感器这一类标注得准不准不必把45类全部画出来。保存文件名建议拼上类别ID例如vis_3_pcb57rec1.jpg整理错误样本时好归档。4. 用这个数据集训练YOLOv5数据配置、超参数调整与小目标改进点4.1 写data.yaml路径、类别数、类别名拿到数据集后第一步是写pcb.yaml。YOLOv5训练靠它定位图片和类别信息路径是相对path字段的不是相对当前终端目录。path: /data/pcb18 train: images/train val: images/val test: images/test nc: 45 names: 0: inductors 1: transformer 2: sensor # 从classes.txt复制顺序必须完全一致path指向数据集根目录train/val/test是相对路径nc必须等于classes.txt行数names的索引顺序必须和标签txt里第一列数字一一对应。如果自作聪明把names按字母排序训练出来的模型类别全部错位推理结果就是“框都对类全错”。建议写data.yaml时直接复制classes.txt内容不要手打。写完先用wc -l classes.txt对比一下nc是否一致。4.2 训练命令与模型选型先跑baseline我一般从yolov5s开始。命令cd yolov5 python train.py --data pcb.yaml --weights yolov5s.pt \ --img 640 --batch 16 --epochs 100 \ --project runs/pcb --name baseline这段命令用COCO预训练权重yolov5s.pt初始化输入分辨率640批量16训练100轮。--project runs/pcb --name baseline把日志输出到runs/pcb/baseline后续实验换--name即可对比。如果显存不足先降--batch到8其次降--img到512不要一上来就换更小的模型。模型选择参考这张表模型参数量640分辨率GFLOPs适用场景YOLOv5s7.2M16.5快速验证后续要部署到边缘设备YOLOv5m21.2M49.0精度优先训练时间可接受YOLOv5l46.5M109.1离线实验看上限不看速度PCB元件公开数据很少训练集约1800张直接上l很容易过拟合。我的做法是s跑通流程m做最终模型l只在需要压榨精度时再试。4.3 小目标检测改进anchor、输入尺寸与损失函数这个数据集大量元件是小目标。默认anchor是在COCO上统计得到的对十几个像素的贴片元件不一定合适。YOLOv5训练开始时会自动执行AutoAnchor根据标签做k-means聚类重新生成anchor日志里出现AutoAnchor: ... anchors generated就是正在做。想关闭自动anchor给训练命令加--noautoanchor。更直接的提升手段是输入分辨率。把--img 640改成--img 1280小目标像素面积放大四倍代价是显存和训练时间同步上升。我一般先跑640看baseline再用1280微调。损失函数方面YOLOv5默认是CIoU加上obj和cls三项。网上大量改进集中在把CIoU换成SIoU或Wise-IoU对小目标定位有帮助。修改入口是utils/loss.py里的bbox_iou常见做法是增加一个mode参数默认返回原版ciou需要对比时改成siou。不要一开始就动loss先确认baseline稳定再换损失做对照。YOLOv5的数据增强对PCB场景基本适用但有几个参数要收着调。data/hyps/hyp.scratch-low.yaml里我通常这样改参数默认值建议值说明hsv_h0.0150.01PCB颜色变化小色相扰动略降degrees0.015加一点旋转但小目标会转到边界外translate0.10.2平移增强小目标场景有效scale0.50.5尺度扰动保持默认mosaic1.00.5降低mosaic比例减少拼接导致的半截目标mixup0.00.2样本少时缓解过拟合但训练变慢可以直接用sed快速改sed -i s/^mosaic: 1.0/mosaic: 0.5/ data/hyps/hyp.scratch-low.yaml sed -i s/^mixup: 0.0/mixup: 0.2/ data/hyps/hyp.scratch-low.yamlsed -i是就地替换文件内容^锚定行首避免误替换其他含mosaic的项。改完用grep mosaic data/hyps/hyp.scratch-low.yaml确认结果。修改增强参数后观察训练集loss是否正常下降如果验证loss震荡明显先把mixup调回0。4.4 验证集只有60张怎么处理AMD显卡怎么跑验证集只有60张直接后果是每次val的mAP波动大可能同一模型两次评估差2到3个点。处理办法是固定验证集文件列表不要每次随机抽训练早期看P和R曲线是否同步上升不要单独抓某一次val结果。要做严谨对比可以把训练集和验证集合并后做k-fold交叉验证每次用其中60张验证最后取平均。1800张训练图不算大CPU训练也只是多等几小时。如果你的机器是AMD显卡跑yolo要注意PyTorch需要安装ROCm版本不能直接装CUDA版。YOLOv5里的NMS等算子在ROCm下效率一般训练还能接受推理速度会打折。实在跑不动就转CPU训练s模型在主流CPU上100轮大约半天起步具体看核数和内存。5. 验证与扩展逐类AP排查、迁移到YOLOv8/v115.1 用val.py看每个类别的AP而不是只看mAP训练结束后不要只看总mAP。我一般用这个命令评估最佳权重python val.py --data pcb.yaml \ --weights runs/pcb/baseline/weights/best.pt \ --img 640 --task val --verbose 21 | tee val.log--verbose会逐类打印AP50和AP50:95tee val.log同时存文件。然后执行grep -E Class|all val.log查看关键行。验证集只有60张某些类别可能只有一两张图单类AP统计意义有限重点看样本量大的几个类是否稳定以及AP50和AP50:95相差是否过大。如果AP50还行但AP50:95很低说明框与标签重合度不够优先检查标注框是不是太松或太小。5.2 用predictions.json做快速漏检分析YOLOv5的val.py支持--save-json会输出COCO格式的predictions.json。结合验证集标签可以快速对比每个类别的预测数量和GT数量python val.py --data pcb.yaml --weights best.pt --save-json --save-txtimport json import glob from collections import Counter with open(runs/val/exp/predictions.json) as f: preds json.load(f) pred_cnt Counter(p[category_id] - 1 for p in preds if p[score] 0.25) gt_cnt Counter() for txt in glob.glob(labels/val/*.txt): for line in open(txt): gt_cnt[int(line.split()[0])] 1 for cid in sorted(set(pred_cnt) | set(gt_cnt)): print(cid, pred:, pred_cnt[cid], gt:, gt_cnt[cid])predictions.json里的category_id是COCO格式从1开始所以要减1才对应classes.txt的ID。score 0.25是过滤低分框的阈值可根据实际精度调整。预测数不等于真正例但它能快速暴露问题如果某类GT有30个、预测只有5个说明这个类基本没学会不需要怀疑是阈值问题。5.3 标签格式直接迁移到YOLOv8/v11想在YOLOv8或v11上复现不需要改标签。两个版本使用相同的txt格式只需安装ultralytics把data.yaml里的nc和names原样搬过去pip install ultralytics yolo detect train datapcb.yaml modelyolov8s.pt epochs100 imgsz640 batch16换YOLO v11把modelyolov8s.pt改成modelyolo11s.pt即可。两个版本都会读pcb.yaml里的names做类别映射所以再次强调txt里的整数ID必须和names顺序一致不要改classes.txt后再copy。AMD显卡用ultralytics如果出现loss为nan在命令里加ampFalse通常就正常。遇到验证集AP突然波动大优先检查训练集和验证集是否混入了同一块PCB板的裁剪数据泄露造成的高分没有意义先拆干净数据再回来调anchor和损失函数。本文还有配套的精品资源点击获取