首页 / 资讯中心 / 文章详情

680张图也能训好YOLO:PCB缺陷检测实战指南

680张图也能训好YOLO:PCB缺陷检测实战指南 ★ FEATURED ARTICLE
简介一份面向工业视觉入门与电子质检场景的 PCB 缺陷检测数据集包含 680 张产线实拍高清图像覆盖桥接、缺失、短路、断路、偏移、极性错误 6 类典型瑕疵标注遵循 YOLOv5/v8/v9 格式按 7:2:1 划分训练、验证与测试集可直接接入 Ultralytics 训练流程。压缩包共 1376 个文件含 680 张 jpg 原图、693 个 txt 标注文件及 data.yaml 配置与缓存文件整包 77.85MB轻量易用适合快速完成目标检测实训。已有 26 人学习。资源额外提供中文 readme说明采集设备、标注规范与训练命令并附带缺陷可视化分析工具可生成类别分布、标注密度等报告经 YOLOv8n 实测 mAP0.5 达 86.3%能支撑从数据加载、模型微调到性能评估的全流程实践。1. 只有680张图的PCB缺陷数据集凭什么能训出能用的YOLO模型PCB缺陷检测数据集6类680张图像YOLO格式这个规模在工业质检项目里其实是常态不是特例。产线上短路、开路、缺孔、鼠咬、多余铜、毛刺六类缺陷形态固定但真出现时往往一批板子也就留得下几十张有效图像。这份数据集的姿势很直接txt标注、归一化坐标、类别编号齐全拿到手就能往YOLO训练脚本里填。680张图听着少配合YOLO的预训练权重迁移、数据增强和合理的验证集划分按常见路径推进时mAP能稳定在0.85以上对刚接触缺陷检测的工程师这是一份能快速跑通全流程的最小数据集对熟手它是测试增强策略和调参手感的试验场。接下来从数据格式、训练配置到翻车现场一步步拆开讲。2. 先读懂PCB六类缺陷与YOLO标注格式再动手训练2.1 六类缺陷定义与成像特征从类别名到标注规则拿到数据集第一件事不是急着跑训练先把labels目录打开看一遍。这份数据集把PCB缺陷分成六类它们的成像特征差异明显标注规则也各有侧重。class_id类别名成像特征典型成因0missing_hole焊盘中央该有的孔却没有反光形状和正常孔明显不同钻孔偏移、钻头磨损1mouse_bite导线边缘出现半月形缺口像被老鼠咬过蚀刻过度、铜箔损伤2open_circuit铜箔导线中间断裂信号通道不连续蚀刻断裂、搬运划伤3short两条导线之间的铜箔粘连间距被桥接蚀刻不足、残铜未清4spurious_copper非线路区域残留的零星铜箔形状不规则显影残留、蚀刻不均5spur导线边缘突出的尖角状铜箔细长且尖锐蚀刻方向异常、铜箔毛刺这张表不只是给人看的它直接决定后续增强策略。比如mouse_bite和spur在成像上都是导线边缘的缺损模型很容易混淆所以标注时框要紧贴缺陷像素不能把整段导线框进去。我见过不少团队在标注阶段图省事框比实际缺陷大两三倍模型学到的变成“导线边缘”而不是“缺陷本身”。open_circuit的断裂点往往只有几个像素宽标注偏移一个像素IoU就掉到0.5以下这也正是很多PCB检测项目在验证集上mAP始终上不去的隐形原因。标注框还有个容易被忽略的规则如果一个缺陷横跨两条导线比如short桥接了相邻导线应该用一个框把整个桥接区域框住而不是分别标两个小框。一个框对应一个独立缺陷这是YOLO格式处理检测任务的基本约定。如果你拿到手的数据集里发现同一个缺陷被切成多个框训练前要合并回去否则模型学到的是残缺目标推理时会出现一个缺陷打出两三个框的现象NMS后检出的置信度也会被摊薄。2.2 YOLO格式的txt标注归一化坐标与类别编号对应关系这份数据集的标注是YOLO格式每张图像对应一个同名txt文件。txt里每一行代表一个缺陷框内容格式是class_id center_x center_y width heightcenter_x和center_y是缺陷框中心点相对于图像宽高的比例width和height是框宽高相对图像宽高的比例。全部是0到1之间的浮点数不是像素坐标。之所以用归一化坐标而不直接用像素值是因为YOLO训练时会做随机缩放和裁剪归一化坐标在resize后不需要重新计算框架内部通过比例映射就能还原真实位置。labels目录里的txt文件打开长这样0 0.5234 0.6125 0.0432 0.0287 3 0.3108 0.4257 0.0185 0.0154第一行是类别0missing_hole的缺陷框中心点在图像横向52.34%、纵向61.25%的位置框宽占图像宽度4.32%框高占图像高度2.87%。第二行是类别3short的缺陷框数值含义相同。看这个数值分布就知道缺陷框的尺寸占比很小最大的框也不到图像面积的5%这是PCB缺陷数据集最典型的特点也决定了训练时必须把imgsz设到640以上否则小目标直接就消失在特征图里。第一次跑YOLO时我踩过坐标写错的坑标注脚本把center_x算成了左上角x导致所有框偏移了半个图像宽度训练出来的模型在验证集上mAP直接归零。所以训练前我会先写一段脚本检查标注和图像是否对得上import cv2 import os def verify_annotation(image_path, label_path, min_size5): 检查YOLO标注是否在图像边界内且尺寸正常 img cv2.imread(image_path) if img is None: print(f图像读取失败: {image_path}) return False h, w img.shape[:2] ok True with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: print(f格式错误: {label_path} - {line.strip()}) continue cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) if not (0 cls_id 6): print(f类别编号越界: {label_path} - {cls_id}) ok False x1 (cx - bw / 2) * w y1 (cy - bh / 2) * h x2 (cx bw / 2) * w y2 (cy bh / 2) * h if x1 0 or y1 0 or x2 w or y2 h: print(f越界框: {label_path} - cls{cls_id}) ok False if (x2 - x1) min_size or (y2 - y1) min_size: print(f过小框: {label_path} - cls{cls_id}) ok False return ok base /data/pcb_defect for split in [train, val]: img_dir os.path.join(base, images, split) lbl_dir os.path.join(base, labels, split) for file in os.listdir(img_dir): name file.rsplit(., 1)[0] img_path os.path.join(img_dir, file) lbl_path os.path.join(lbl_dir, name .txt) if not os.path.exists(lbl_path): print(f缺少标注: {lbl_path}) continue verify_annotation(img_path, lbl_path)这段脚本只用了OpenCV和os两个标准库没有额外依赖。verify_annotation函数做的事有三件检查每行是否正好五个字段检查类别编号是否在0到5之间把归一化坐标反算回像素坐标确认框没有越界、没有小于5像素的废框。之所以要在训练前跑一遍这个脚本是因为这类数据集经常从转换工具或人工标注平台导出归一化坐标在四舍五入时会出现x2比图像宽度多一个像素的情况。图像边缘的缺陷框一旦越界YOLO训练时Dataloader做马赛克拼接会把这些框裁掉模型等于丢失了一部分正样本。而小于5像素的框经过640×640下采样后在特征图上不到一个像素基本学不到任何特征相当于噪声标注直接过滤掉反而干净。我每次检查完这个脚本多多少少都能挑出几条问题标注这份680张图像的数据集也不例外。如果你训练时发现某些类别AP偏低回头先跑一遍这个脚本把越界框和过小框清理掉AP往往能回升三到五个点。这是处理数据集用于yolov8训练的第一步也是被跳过得最多的一步。2.3 680张图像的目录划分随机切还是按组切目录划分直接决定了训练评估的可信度。常见做法是按8:1:1随机切但我不建议直接随机切。PCB缺陷图往往来自同一批板卡的不同拍摄位置如果随机切同一块板的不同区域会同时出现在训练集和验证集模型“见过”的语义信息被带进了验证集mAP会虚高两到三个点等部署到新板卡上立刻露馅。如果680张图像是按板卡编号命名的先按编号分组保证同一组图像不会被切到两个集合里。划分逻辑用Python实现很简单import os import random from collections import defaultdict base /data/pcb_defect img_dir os.path.join(base, images) img_paths os.listdir(img_dir) # 按文件名中板卡编号分组假设文件名格式为 board_001_patch_1.jpg groups defaultdict(list) for p in img_paths: board_id p.split(_)[0] groups[board_id].append(p) random.seed(42) group_list list(groups.keys()) random.shuffle(group_list) train_ratio, val_ratio 0.8, 0.1 n_train int(len(group_list) * train_ratio) n_val int(len(group_list) * val_ratio) train_groups group_list[:n_train] val_groups group_list[n_train:n_train n_val] test_groups group_list[n_train n_val:] # 输出划分结果后续按这个清单移动图像和标注 for split, gids in [(train, train_groups), (val, val_groups), (test, test_groups)]: for gid in gids: for f in groups[gid]: print(f{split}\t{f})注意代码里的board_id提取逻辑是按文件名格式写的实际使用时要改成你数据集的实际命名规则。如果文件名里没有板卡编号退一步的做法是按patch的坐标位置分组——比如文件名包含图像在整板上的行列号那么同一行相邻patch也属于同板区域应尽量归到同一个集合。这个细节决定了验证集mAP的可信度值得花十分钟处理。划分之后记得检查每个集合里的类别分布。680张图总缺陷框数大约在1200到1500之间如果test集里missing_hole只有两三个框那测试结果就没有统计意义。我会手动从val或者train里调几张包含missing_hole的图像进test保证每类在test里至少有5个框。另外划分时不要只划图像要同步移动labels目录下的同名txt。一个常见错误是移动了jpg但漏了txtYOLO训练时会反复警告image without labels浪费排查时间。3. 用YOLOv8训练这份数据集yaml配置、命令与参数调优3.1 数据集yaml配置文件路径结构、nc与namesYOLOv8训练的第一步是写数据集配置yaml。这个文件告诉ultralytics框架去哪里读图像、标注有几类以及类别名称。yaml写在数据集根目录下内容如下# pcb_defect.yaml path: /data/pcb_defect # 数据集根目录 train: images/train # 训练图像相对路径 val: images/val # 验证图像相对路径 test: images/test # 测试图像相对路径 nc: 6 # 缺陷类别数 names: 0: missing_hole 1: mouse_bite 2: open_circuit 3: short 4: spurious_copper 5: spurpath字段是绝对路径train/val/test写相对于path的路径。yaml里的类别编号和顺序必须与txt标注文件里的class_id一一对应这是最常见的配置错误来源。举例来说如果yaml里把short写成第3类但txt标注文件里short的class_id是2训练时模型会把两类缺陷完全学反验证集mAP看起来却不会太差因为你拿错标注去比错了检测结果整个评估失去意义。ultralytics框架启动时会解析yaml并拼接图像路径所以保证images目录下的jpg文件和labels目录下的txt文件数量一致、文件名一致。用以下命令快速核对ls /data/pcb_defect/images/train | wc -l ls /data/pcb_defect/labels/train | wc -l两个数字必须相等。如果labels比images少说明有图没标反过来则是有标注没图都要先补齐。yaml里我习惯不写test字段只在验证时用valtest留到最终评估时单独写避免训练过程误用test做早停判断。3.2 训练启动命令imgsz、batch与epochs的选择逻辑配置好yaml后训练命令非常简洁。如果你装好了ultralytics包最小可运行的训练命令是yolo detect train \ data/data/pcb_defect/pcb_defect.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ project/output/pcb_yolov8 \ namerun1这段命令里每个参数都值得细说。modelyolov8n.pt是预训练权重n代表nano是YOLOv8系列里参数量最小的模型约310万参数。680张图的数据量用nano是合理起点——用yolov8x这种大模型训练几乎必然过拟合验证集mAP反而更差。imgsz640表示训练时把图像统一缩放到640×640PCB缺陷是小目标这个分辨率不能降降到416的话missing_hole这种几个像素宽的缺陷就彻底消失了。batch16是每次迭代同时处理16张图显存足够就设16不够就下调用梯度累积兜底。epochs150是训练轮数配合早停一般足够收敛。我通常还会额外追加几个参数让训练更可控yolo detect train \ data/data/pcb_defect/pcb_defect.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ patience30 \ lr00.005 \ seed42 \ project/output/pcb_yolov8 \ namerun1patience30是早停策略连续30轮验证集mAP没有提升就停止训练防止后段过拟合。lr00.005是初始学习率比默认0.01低一半。迁移学习场景下用小学习率可以避免预训练权重被大幅破坏PCB图像和COCO图像差异大学习率稍低能让模型在稳定区域内微调。seed42固定随机种子让每次训练结果可复现。参数调优时如果不固定随机种子你改了某个参数但效果变化可能只是随机波动根本无法判断哪个改动起了作用。训练完成后验证命令是yolo detect val \ data/data/pcb_defect/pcb_defect.yaml \ model/output/pcb_yolov8/run1/weights/best.pt验证输出会给出mAP50、mAP50-95以及每个类别的Precision和Recall。看到哪些类别AP明显低于均值再回去查该类别的样本量和标注质量。3.3 迁移学习与预训练权重小数据集训练的胜负手680张图像直接从头训练YOLO基本不可行。CNN特征提取器需要大量数据才能学会基础纹理、边缘和颜色特征两三百张图训练出来的backbone学到的全是噪声。所以必须用预训练权重做迁移学习。YOLO预训练模型下载渠道很成熟yolov8n.pt是COCO数据集上预训练好的权重COCO有80类自然图像虽然和PCB缺陷域差异很大但底层卷积核学到的边缘、纹理、颜色渐变特征是可以迁移的。训练时冻结前几层是一个常见调优手段。前几层学到的是通用特征在PCB图像上同样适用冻结它们不更新梯度训练更稳定、显存占用更小。实际操作中用freeze参数指定冻结层数yolo detect train \ data/data/pcb_defect/pcb_defect.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ freeze10 \ lr00.003 \ project/output/pcb_yolov8 \ namerun_freeze10freeze10表示冻结前10层主要是backbone前段的卷积模块。我处理PCB缺陷数据时一般从freeze10起步然后对比验证集mAP再决定加大还是减小。如果PCB图像是在暗场、低对比度下拍摄的和COCO自然图像的特征分布差得远冻结层反而限制了特征提取需要把freeze降到5甚至0。这里有一个判断技巧训练到30轮左右时看一眼验证集的Precision曲线如果曲线持续很低且不抬头多半是冻结层数过多导致模型学不到PCB的底层特征果断减少freeze层数重跑。预训练权重属于典型的“黑匣子”依赖用之前记录版本和来源。不同版本的yolov8n.pt结构一致但权重数值不同混用可能导致效果偏差。我会在模型目录里写一个weights_notes.txt记录权重文件名、下载时间、训练配置和对应mAP换权重时方便回溯。4. 680张不够用数据增强与伪标签撑起泛化能力4.1 离线增强用albumentations扩充缺陷样本680张原始图像直接训练模型见过的缺陷形态太有限。离线增强是最直接的办法在训练前用图像处理库生成扩充样本。albumentations是目前工业视觉最常用的增强库支持同步变换图像和边界框YOLO格式的归一化坐标可以直接传入。下面是一段针对PCB缺陷数据的增强脚本按常见做法把小样本训练里最有效的几类增强都配置好了import albumentations as A import cv2 import os aug A.Compose([ A.RandomBrightnessContrast(p0.8, brightness_limit0.15, contrast_limit0.15), A.HueSaturationValue(p0.5, hue_shift_limit5, sat_shift_limit10, val_shift_limit10), A.RandomGamma(p0.5, gamma_limit(80, 120)), A.ShiftScaleRotate(p0.5, shift_limit0.05, scale_limit0.1, rotate_limit15), A.GaussNoise(p0.3, var_limit(10.0, 40.0)), A.CLAHE(p0.3, clip_limit2.0, tile_grid_size(8, 8)), ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels], min_visibility0.3, min_area4 )) def augment_one_image(img_path, label_path, save_dir, idx): img cv2.imread(img_path) boxes, labels [], [] with open(label_path) as f: for line in f: cls, cx, cy, bw, bh map(float, line.strip().split()) boxes.append([cx, cy, bw, bh]) labels.append(cls) augmented aug(imageimg, bboxesboxes, class_labelslabels) out_img augmented[image] out_name faug_{idx}.jpg cv2.imwrite(os.path.join(save_dir, out_name), out_img) with open(os.path.join(save_dir, out_name.replace(.jpg, .txt)), w) as f: for bbox, cls in zip(augmented[bboxes], augmented[class_labels]): cx, cy, bw, bh bbox f.write(f{int(cls)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}\n)代码里的bbox_params是核心。formatyolo告诉albumentations输入的是归一化坐标它会在内部转换成像素坐标做变换再转回归一化坐标输出。min_visibility0.3表示变换后如果边界框可见面积少于30%这个标注就丢弃避免生成一半缺陷被裁掉的无效样本。min_area4过滤掉变换后面积小于4像素的框和训练前的检查脚本逻辑一致。增强的每一项都有针对性。RandomBrightnessContrast模拟工厂不同班次的光照差异ShiftScaleRotate的rotate_limit15限制了旋转角度PCB缺陷中的short和spurious_copper对旋转方向没有严格依赖但旋转过大会引入大量背景干扰GaussNoise模拟工业相机低照度下的传感器噪点。离线增强只对训练集做val和test保持原始图像这样评估结果才真实反映泛化能力。生成增强样本后建议把原图和增强图混在一起比例控制在1:2以内增强图太多了模型会过度适应噪声分布。4.2 在线增强YOLOv8内置增强参数调整YOLOv8本身集成了在线增强训练时动态变换图像不额外占磁盘空间。ultralytics的增强参数可以在训练命令里直接覆盖默认值。参数名默认值推荐值作用hsv_h0.0150.02色调增强强度hsv_s0.70.8饱和度增强强度hsv_v0.40.5亮度增强强度degrees0.05.0随机旋转角度translate0.10.1平移比例scale0.50.6缩放比例fliplr0.50.5水平翻转概率mosaic1.00.8Mosaic增强概率mixup0.00.2Mixup增强概率对应训练命令yolo detect train \ data/data/pcb_defect/pcb_defect.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ hsv_h0.02 \ hsv_s0.8 \ hsv_v0.5 \ degrees5 \ scale0.6 \ mosaic0.8 \ mixup0.2这些推荐值基于PCB缺陷数据的特点。PCB图像背景通常是绿色阻焊层色调单一所以hsv增强可以适当放开。degrees5是因为PCB板在夹具里可能有轻微旋转但不会大角度倾斜超过10度的旋转会产生明显背景变形。mosaic从默认1.0降到0.8要说明一下PCB缺陷面积很小mosaic拼接四张图后单个缺陷的像素占比进一步缩小不利于小目标学习适度降低mosaic概率反而有助稳定收敛。如果离线增强已经扩充了大量样本在线增强的强度要相应调低两者叠加太多会让模型面对的变换过于剧烈训练难度增大且收敛变慢。我一般离线增强做一套保底在线增强用偏保守的参数两条路都走但都不走极端。4.3 伪标签半监督让未标注PCB图像参与训练产线最不缺的就是未标注的图像。标注680张图要一两天但拍几千张未标注图只要半天。伪标签pseudo labeling的思路是先用680张标注数据训练一个初始模型再用这个模型给未标注图像打标注把高置信度的检测结果当成训练数据。操作流程分两步。第一步推理生成伪标签yolo detect predict \ model/output/pcb_yolov8/run1/weights/best.pt \ source/data/pcb_unlabeled/images \ conf0.6 \ save_txtTrue \ save_confTrue \ project/data/pcb_unlabeled/pseudo \ nameround1conf0.6是伪标签的置信度阈值这个值很关键。阈值高于0.8保留的标注太少扩展作用有限低于0.4大量误检框被当成标注污染训练数据。PCB缺陷背景单一模型置信度往往呈两极分化0.6到0.7是可接受区间。save_txtTrue把检测结果存成和源图像同名的txt文件格式和数据集标注一致可以直接进训练。save_confTrue会在txt每行末尾附加置信度方便后续筛选低质量伪标签。第二步把伪标签混入训练集重新训练。我一般按伪标签真实标签1:2.5的比例混合伪标签占比太高会把模型的偏差固化。第二轮训练完成后用新模型回到第一步生成更高质量的伪标签形成自训练循环。迭代三轮左右mAP通常还能再提升三到五个百分点。这个收益在小数据集上比任何超参调优都明显代价只是多跑几轮训练时间。如果预算允许更稳的做法是让模型挑出低置信度样本人工快速复核后加入标注集。这属于人机协同标注比纯伪标签更可靠人工成本也远低于从零标注。5. 小样本训练避坑680张图像上最常见的五个翻车现场5.1 Loss降到0.2但mAP只有0.3模型学了个寂寞现象训练150轮后训练loss降到0.2以下但验证集mAP只有0.3出头Precision和Recall都惨不忍睹。原因这是典型的过拟合。680张训练图像经过150轮迭代模型参数量足以把训练样本“背”下来却没有任何泛化能力。验证集图像和训练集只要有一点光照或角度差异检测性能立刻崩塌。解决先看训练日志里val_loss和train_loss的差距如果val_loss始终是train_loss的两倍以上从三个方向同时下手——增强离线增强强度尤其是光照和噪声类、增加数据多样性用第4.4.3节的伪标签扩展、把freeze层数加到10让浅层特征更通用。还有一个立竿见影的土办法把epochs从150砍到80配合patience20早停让模型没有足够时间去背样本。5.2 验证集mAP震荡剧烈每隔10轮波动超过5个点现象训练日志里mAP50曲线像锯齿上一轮0.7下一轮0.62再一轮又回到0.71完全无法判断模型收敛状态。原因两个因素叠加。一是batch size太小梯度估计噪声大二是验证集图像太少。680张图切出的val通常只有68张这68张里如果恰好包含几张角度偏差大的异常图mAP就会随批次不同剧烈波动。batch size4时BN层的统计量不稳定训练过程本身也在震荡。解决val集扩容到100张以上从train匀一部分图过来。学习率降到0.002以下让训练更平滑。如果显存允许batch size提到32。还有一个评估层面的调整用mAP50代替mAP50-95作为主要监控指标后者对框的精确位置更敏感在小验证集上更容易震荡。5.3 short和spurious_copper几乎检测不到其他四类正常现象验证结果显示missing_hole和open_circuit的AP都在0.7以上但short和spurious_copper的AP只有0.2接近随机猜测。原因两类缺陷的像素面积小是典型的小目标。在640×640输入下原始图像里只有几十个像素的缺陷经过下采样后在特征图上可能只剩一两个点。其次这两类缺陷的样本量往往最少模型没有足够的正样本去学习形态特征。解决针对小目标缺陷把imgsz从640提高到960甚至1280代价是训练速度变慢、显存占用翻倍但小目标的AP通常能提升十个百分点以上。如果不方便提分辨率用切片推理策略——训练时保持640推理时把原图切成多个640×640的patch分别检测再合并结果。样本量层面对这两类做针对性离线增强把已有样本随机粘贴到背景板的其他位置生成新样本。5.4 显存只有8GBbatch size只能设4训练又慢又不稳现象显卡显存不够batch size设到16直接OOM只能用4硬撑。训练速度慢而且验证集mAP波动明显。原因batch size过小时BN层的均值和方差估计不准梯度更新方向噪声大模型不收敛。解决最常见做法是梯度累积让模型每积累多次反向传播的梯度再更新一次参数等效于增大batch size。在ultralytics中通过nbs参数实现yolo detect train \ data/data/pcb_defect/pcb_defect.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch4 \ nbs16 \ lr00.003 \ project/output/pcb_yolov8 \ namerun_gradaccnbs是normalizer batch size当batch4而nbs16时框架会自动累积4次梯度再更新参数等效于batch size16。学习率要相应调低这里lr00.003是配合nbs16的取值。显存实在不够时把imgsz降到480效果通常比硬撑着batch2要好。小分辨率模型初始精度略低但收敛更稳定后期可以再微调到640。5.5 用了预训练权重反而比从头训练效果更差现象加载yolov8n.pt预训练权重训练150轮mAP只有0.4而不加载权重从头训练反而能到0.55。原因预训练模型的BN层参数适应了COCO数据分布在PCB图像上微调时如果学习率过高或冻结层数不合理BN统计量没有被正确更新特征分布错乱模型反而被“带偏”了。解决把freeze降到5以下让backbone后面的层更快适应PCB图像。同时把lr0降到0.001或0.0005用小梯度步长微调。另一个容易被忽略的问题是图像通道PCB图像如果是灰度图预训练模型的第一卷积层是3通道输入和灰度单通道不匹配。解决方法是读图时把灰度图复制成3通道用np.repeat(img, 3, axis-1)或者用cv2.cvtColor转成BGR。如果数据集的图像本身就是三通道但颜色单一这个问题不存在但要确认所有图像通道数一致避免训练中途报shape不匹配的错。6. 验证与落地从mAP曲线到产线质检的最小闭环6.1 混淆矩阵哪两类缺陷最容易互相误判训练完成后不要只看整体mAP。打开ultralytics输出目录里的confusion_matrix.png看哪两类缺陷互相误判最严重。PCB六类缺陷中mouse_bite和spur在形态上都是边缘缺损误判率通常最高。这里有个容易误解的细节YOLO的混淆矩阵默认不画背景类漏检的目标被归到background列所以每一行总量不等于该类真实框数。看到行列对不上先别慌是框架的统计口径不是bug。如果spur被大量预测成mouse_bite优先检查标注质量而不是继续调参。6.2 导出ONNX从训练服务器到产线相机训练好的模型要部署到产线ONNX是最通用的中间格式yolo export \ model/output/pcb_yolov8/run1/weights/best.pt \ formatonnx \ imgsz640 \ simplifyTrue导出后交给ONNX Runtime或TensorRT推理。这里有一个踩过的坑导出时的imgsz必须和训练时一致否则推理性能明显退化。训练用640导出就是640不要图快改成416。如果现场相机是2000万像素的整板图像推理前先做切片切成640×640的patch逐块检测再把检测框映射回原图坐标避免小目标在整图缩放中丢失。6.3 置信度阈值漏检和误报的权衡产线部署时的置信度阈值需要单独调。默认conf0.25针对自然场景但PCB缺陷检测中漏检成本远高于误检——一个缺陷漏过去就是整批板子报废。我一般先用conf0.1跑一遍验证集看能检出多少真实缺陷再逐步提高阈值在漏检率和误报率之间找平衡。缺陷率低于0.5%的产线宁可把误报放宽到3%到5%也要把漏检率压到0.1%以下。最后说一个我的习惯每次训练完best.pt和last.pt都保留两者对比能发现是不是早停太早这相当于给自己留了一颗后悔药。验证集的fail case截图归档到固定目录下次训练前先翻一遍历史失败案例确认它们有没有被修复再决定调参方向。调参很多时候是玄学但记录每一次实验的心得之后玄学慢慢会变成手感这套流程跑顺之后680张图的数据集真的够撑起一条初步的PCB缺陷检测产线。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站