ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

焊接表面缺陷检测实战:YOLOv5目录格式数据集全流程解析

2026/10/1 5:49:08 拓冰建站 浏览量
焊接表面缺陷检测实战:YOLOv5目录格式数据集全流程解析 简介面向工业质检与目标检测模型训练提供一套YOLOV5目录格式的焊接表面缺陷图像数据集覆盖穿孔、折痕、压痕、内含物等10个类别图像为2048×1000的RGB图片。数据按YOLOV5标准文件夹组织可直接送入YOLO系列模型训练与验证免去数据整理与格式转换步骤。压缩包内共2000个文件以TXT标注文件为主1999个每个标签与图像一一对应另附1个Python可视化脚本可随机读取图片并绘制边界框便于快速检查标注效果资源包总大小约918.87MB。数据集包含训练集1836张、验证集458张10个TXT类别文件清晰列出所有类别名称方便模型类别配置。当前已有233人学习/下载适合需要快速获取工业缺陷数据的算法工程师、研究人员及目标检测初学者用于缺陷检测模型的训练、验证与可视化调试。1. 焊接缺陷检测为什么绕不开一份YOLOv5目录格式的数据集拿到“目标检测数据集(YOLOV5目录格式)焊接表面缺陷图像目标检测10类别包含训练集、验证集”这个标题第一反应不是“又一份数据集”而是“这正好补上了焊接质检项目里最耗时的一环”。焊接表面缺陷检测在工业视觉里属于典型的高频小目标场景气孔、咬边、未熔合、裂纹这些缺陷在6000×4000的大图上往往只占几十个像素点标注成本高想复现论文里的mAP 50又不愿意自己从零标注最现实的路就是找一份已经按YOLOv5目录结构分好训练集和验证集的焊接数据直接改数据配置跑起来做基线。这份数据集的核心价值在于它少了两个常见的拦路活目录结构和类别划分。YOLOv5训练时对数据路径、标签格式和类别编号的顺序极其敏感自己转格式踩坑能踩一下午而它把10个焊接缺陷类别、训练集和验证集都按YOLOv5约定铺好了你要做的事变成三件确认标签格式、改data yaml里的路径和类别名、看训练曲线决定要不要做数据增强。这篇笔记就按照“格式拆解 → 目录落地 → 标签检测 → 训练参数 → 踩坑记录 → 结果验证”的顺序把焊接表面缺陷检测从一份YOLOv5目录格式数据集到能出效果的路走一遍。适合正被检测精度卡住、或刚开始入坑工业缺陷检测的视觉工程师。2. 拆解YOLOv5目录格式背后的约定:焊接缺陷的10类别怎么落到文件夹里2.1 YOLOv5的目录约定images和labels必须一一对应YOLOv5训练时并不扫描目录里所有图片而是通过data yaml里的train和val路径去找对应的txt标签。它内部用os.path.splitext把图片文件名后缀替换成.txt再去labels目录下找同名文件。这意味着images和labels两个目录里的文件名必须完全相同连大小写都不能差。焊接缺陷数据集如果按这个约定组织标准结构长这样weld-defect-dataset/ ├── images/ │ ├── train/ │ │ ├── 00001.jpg │ │ ├── 00002.jpg │ │ └── ... │ ├── val/ │ │ ├── 01001.jpg │ │ └── ... ├── labels/ │ ├── train/ │ │ ├── 00001.txt │ │ ├── 00002.txt │ │ └── ... │ ├── val/ │ │ ├── 01001.txt │ │ └── ... ├── data.yaml └── README.mdYOLOv5的dataloader在加载时会通过img2label_paths函数把images/train路径里的/images/替换成/labels/后缀换成.txt。所以目录层级有讲究images和labels的父目录层级关系不能乱images/train必须对应labels/train一旦数据集被放到别的层级里/images/字符串替换不命中就会报AssertionError: Label class x exceeds ncx或者找不到标签文件。对于这份焊接表面缺陷数据集要做的事首先是用tree命令核实目录层数和文件名匹配# 核实目录结构与文件数量 find weld-defect-dataset/images/train -name *.jpg | wc -l find weld-defect-dataset/labels/train -name *.txt | wc -l # 对比文件名是否一一对应 ls weld-defect-dataset/images/train/ | sed s/\.jpg$// | sort /tmp/img_list.txt ls weld-defect-dataset/labels/train/ | sed s/\.txt$// | sort /tmp/lbl_list.txt diff /tmp/img_list.txt /tmp/lbl_list.txtdiff无输出说明图片和标签一一对应这是YOLOv5训练能跑通的第一道保险。如果diff有输出优先检查是不是有多余的.txt残留常见原因是标注工具在导出时保留了临时文件。2.2 与VOC/COCO格式相比YOLO格式对焊接检测的利弊焊接表面缺陷数据集的标注格式不止YOLO一种常见还有VOC的XML和COCO的JSON。标题里已经明确锁定YOLOv5目录格式但了解差异才能用好它。YOLO格式的标签是class_id x_center y_center width height前两个是归一化后的中心点坐标后两个是归一化后的宽高全部是0到1之间的小数——这意味着YOLO格式丢掉了图像原始尺寸信息换来了推理时不需要读图尺寸、预处理路径更短的优势。对焊接缺陷这类工业图像YOLO格式有四个实际好处一是训练时直接读txt不需要像COCO那样解析JSON再转数组二是做数据增强Mosaic、MixUp时可以同步平移和缩放归一化坐标不会出现坐标和原图尺寸比例失配的问题三是单张图上多缺陷类别互不干扰一个txt里可以同时有气孔、夹渣、咬边三类目标的多行记录每行独立、互不遮挡。但YOLO格式也有一个明显短板标签里的长度信息是归一化的相对值不记录真实物理尺寸而且没有类别名称文本只有一个数字编号。这就要求data yaml里的类别顺序必须与标注txt里的class_id严格一致。焊接缺陷数据集的标注顺序一旦和data yaml里的names列表错位训练出的模型就会出现“标注是气孔、预测成咬边”的串类别问题。检查类别编号顺序最好的办法是统计训练集标签里出现的class_id集合# 抽取训练集标签中所有出现的类别编号 cat weld-defect-dataset/labels/train/*.txt | awk {print $1} | sort -n | uniq # 期望输出: 0 1 2 3 ... 9输出的最大编号是9对应10个类别。如果输出是0到10或者0到9中间有缺失说明标签文件里有脏数据需要在训练前清洗。2.3 10个焊接缺陷类别的常见定义与类别顺序敏感性焊接表面缺陷检测里的“10类别”一般不是随意凑出来的常见命名集合包括气孔porosity、咬边undercut、未熔合lack of fusion、未焊透incomplete penetration、夹渣slag inclusion、裂纹crack、焊瘤overlap、凹陷suck-back、飞溅spatter、正常焊道good weld / normal。其中“正常焊道”作为第10类来处理是工业界的常见做法理由是缺陷检测的最终目标不只是报缺陷还要把无缺陷样本排除掉让误报率可控。10类别的顺序不是随机的它决定两件事训练时loss在类别维度的one-hot编码顺序以及推理时detect.py输出框前的类别字符串映射顺序。焊接缺陷类别编号写进txt里之后如果训练脚本里用的是names: 0: porosity 1: undercut 2: lack_of_fusion 3: incomplete_penetration 4: slag_inclusion 5: crack 6: overlap 7: suck_back 8: spatter 9: good_weld那么标签txt里第一行开头的0就代表porosity而不是crack。这个顺序必须和训练时的一致否则模型训练完毕输出推理结果全是张冠李戴。标题里没有给出具体的类别清单这是每份数据集自带的data.yaml要解决的头号问题。拿到数据集的第一步不是开训练而是把data.yaml里的names列表打开一条一条核对确认和标注txt里的class_id对应关系。若data.yaml缺失或未提供names则需要手工从标签中提取类别频次按编号顺序人工映射。3. 用脚本把焊接缺陷数据整理成YOLOv5能吃的目录转换、划分、检查三步走3.1 将VOC XML或已有标注转换成YOLO txt格式的实用脚本很多焊接缺陷数据集原始交付物是VOC XML格式需要转换成YOLO格式再落到目录结构里。转换脚本的思路不复杂核心是把XML里的bndbox像素坐标转为归一化中心点与宽高。下面这个脚本一次处理一个标注文件夹输出到目标labels目录import os import xml.etree.ElementTree as ET from pathlib import Path def voc_xml_to_yolo(xml_file, out_txt_file, class_names): 将VOC格式的XML标注转换为YOLOv5的txt标签。 class_names: 类别清单索引即class_id必须与data.yaml中names一致。 tree ET.parse(xml_file) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text # 类别名不在清单里则跳过避免脏数据进入训练 if name not in class_names: print(f[跳过] 未知类别: {name} in {xml_file}) continue class_id class_names.index(name) xml_box obj.find(bndbox) x_min int(float(xml_box.find(xmin).text)) y_min int(float(xml_box.find(ymin).text)) x_max int(float(xml_box.find(xmax).text)) y_max int(float(xml_box.find(ymax).text)) x_center (x_min x_max) / 2.0 / img_width y_center (y_min y_max) / 2.0 / img_height box_width (x_max - x_min) / img_width box_height (y_max - y_min) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) with open(out_txt_file, w) as f: f.write(\n.join(lines)) xml_dir Path(annotations) out_dir Path(labels) out_dir.mkdir(exist_okTrue) class_names [porosity, undercut, lack_of_fusion, incomplete_penetration, slag_inclusion, crack, overlap, suck_back, spatter, good_weld] for xml_path in xml_dir.glob(*.xml): out_txt out_dir / (xml_path.stem .txt) voc_xml_to_yolo(xml_path, out_txt, class_names)这个脚本的转换逻辑有一个需要特别说明的细节x_center和y_center用的是(x_min x_max) / 2而不是x_min (x_max - x_min) / 2两种写法数学上等价但前者减少一次除法和一次括号运算批量转换大文件时更快。而box_width和box_height使用x_max - x_min这里没有做防0处理实际中如果某个标注框的x_max等于x_min生成的width会是0YOLOv5训练时直接报ZeroDivisionError应在转换前先行过滤宽度或高度小于1像素的框。3.2 按比例划分训练集和验证集避免集中划分破坏YOLOv5的分布假设有了标注后第二步是把图片和标签划分成训练集、验证集。焊接缺陷图像通常来自不同的焊件批次、不同光照条件如果直接把前80%划给训练、后20%划给验证会导致验证集里全是某一种光照或某种焊件形状的样本训练集和验证集的分布差异过大训练时variance高、验证指标虚高或偏低都没有参考价值。常见做法是先把图片按全局随机打乱再进行比例切分我这里习惯用sklearn的train_test_split配random_state固定种子来保证可复现import random from pathlib import Path from shutil import copyfile image_dir Path(images) # 原始图像目录 label_dir Path(labels) # 转换后的标签目录 train_img_dir Path(images/train) val_img_dir Path(images/val) train_lbl_dir Path(labels/train) val_lbl_dir Path(labels/val) for d in [train_img_dir, val_img_dir, train_lbl_dir, val_lbl_dir]: d.mkdir(parentsTrue, exist_okTrue) all_images sorted(image_dir.glob(*.jpg)) random.seed(42) random.shuffle(all_images) val_ratio 0.2 val_count int(len(all_images) * val_ratio) val_images all_images[:val_count] train_images all_images[val_count:] for img_path in train_images: lbl_path label_dir / (img_path.stem .txt) if not lbl_path.exists(): print(f[警告] 缺少标签: {img_path.name}) continue copyfile(img_path, train_img_dir / img_path.name) copyfile(lbl_path, train_lbl_dir / lbl_path.name) for img_path in val_images: lbl_path label_dir / (img_path.stem .txt) if not lbl_path.exists(): print(f[警告] 缺少标签: {img_path.name}) continue copyfile(img_path, val_img_dir / img_path.name) copyfile(lbl_path, val_lbl_dir / lbl_path.name) print(f训练集图片: {len(train_images)} 张) print(f验证集图片: {len(val_images)} 张)这里有几个细节要说明。第一random.seed(42)必须在shuffle之前设定且每次运行脚本都用同样的种子这样得到的训练集/验证集划分是稳定的后续做超参数对比时不会因数据划分不同导致结果不可比。第二copyfile比shutil.move更安全因为原始图片还保留一份后面发现划分不合理可以重新划分对数据集操作尽量别用破坏性命令。第三脚本里对缺少标签的图片打印警告而不是直接退出因为工业数据集里个别图像确实没有标注正确做法是跳过而不是删除保留这些样本会让后续数据检查阶段有据可查。3.3 用统计脚本检查每类缺陷的目标数量和图像尺寸分布目录结构搭完之后立刻要跑一个目标分布统计脚本来确认类别均衡性。焊接缺陷数据集里最常见的分布问题是气孔类目标数量可能占70%而裂纹类只有不到2%。类别不均衡会导致模型对少数类召回率极低训练时这类缺陷几乎不贡献loss梯度。提前统计分布为后续做类别权重或采样策略提供依据from collections import Counter, defaultdict from pathlib import Path from PIL import Image label_dirs [labels/train, labels/val] class_counter Counter() image_sizes defaultdict(list) class_names [porosity, undercut, lack_of_fusion, incomplete_penetration, slag_inclusion, crack, overlap, suck_back, spatter, good_weld] for label_dir in label_dirs: for txt_path in Path(label_dir).glob(*.txt): img_path Path(label_dir.replace(labels, images)).with_suffix(.jpg) try: img Image.open(img_path) image_sizes[img.size].append(txt_path.stem) except FileNotFoundError: print(f[缺失] {img_path}) with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) 5: print(f[警告] 不完整标注: {txt_path}) continue class_id int(parts[0]) if class_id len(class_names): print(f[错误] class_id越界: {class_id} in {txt_path}) continue class_counter[class_names[class_id]] 1 print(每类目标数量:) for name, count in class_counter.most_common(): print(f {name}: {count}) print(\n图像尺寸分布:) for size, count in sorted(image_sizes.items(), keylambda x: -len(x[1])): print(f {size}: {len(count)} 张)这个脚本的统计口径是目标框数量而不是图像数量因为缺陷检测关心的核心是每个类别的目标级召回情况。运行后如果发现某个类别数量级比均值少10倍以上这个类别的AP会很难看后续训练时至少要开数据增强里的mosaic和hsv_h、hsv_s来补样本多样性而不是盲目调学习率。4. 用YOLOv5训练焊接缺陷模型:data yaml改写、训练命令与三个必调参数4.1 修改data.yaml:路径、类别名、类别数一个都不能错目录结构合格后训练的第一步是正确传入data.yaml。YOLOv5的data yaml不是一份固定的配置它会同时告诉dataloader三件事训练集和验证集的图片路径、类别数量、类别名称列表。焊接缺陷数据集对应的data.yaml最小可用写法如下# weld_defect_data.yaml train: ./weld-defect-dataset/images/train val: ./weld-defect-dataset/images/val nc: 10 names: [porosity, undercut, lack_of_fusion, incomplete_penetration, slag_inclusion, crack, overlap, suck_back, spatter, good_weld]这个文件里最隐蔽的坑是train和val的路径写法。YOLOv5在训练时会根据路径末尾是否含/来拼接查找标签且对路径做os.path.sep规范化。建议一律用./开头且目录末尾不加斜杠。如果你把数据集放在机械硬盘根目录普遍存在中文路径的工程目录下还会遇到YOLOv5的LoadImages打不开图片原因是部分版本的OpenCV对非ASCII路径支持不完善检测到报cv2.error时优先把数据集移到纯英文路径。nc: 10和names列表里实际标注的类别数必须严格一致差一个都不行。YOLOv5在parse_model阶段会依据nc来构建最后一层卷积的输出通道names的数量可以少于nc少了无所谓只是推理时显示布尔索引缺失但nc少了会导致最后一个类别的目标在训练时索引越界。这个错误的表现是训练到中途突然IndexError: index 9 is out of bounds for axis 0 with size 9排查时优先检查data.yaml的nc。4.2 训练命令与三个关键超参数:img size、batch size、epochYOLOv5官方仓库的train.py参数很多但针对焊接缺陷这个场景最值得先盯住的是--img、--batch和--epoch这三个参数决定了训练能跑起来的最基本前提。焊接缺陷是典型的小目标密集场景建议先用--img 640起步而不是直接用1280原因有二一是原始图像如果是6000×4000的大图直接缩到640会把气孔这类小目标缩小到只有4×4像素但YOLOv5的检测头本身有尺度适应能力先用640跑通基线、再用1280细调是更稳的做法二是1280分辨率下显存占用约为640的4倍多数单卡12GB显存会OOM。cd yolov5 python train.py \ --data weld_defect_data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache ram \ --project runs/weld_defect \ --name baseline_640--cache ram在焊接缺陷数据集上值得推荐。这类数据集单张图片可能达到12MB以上从机械硬盘读取再解码成为训练瓶颈缓存进内存后训练速度能提升3倍以上。如果机器内存少于训练集总大小不要开--cache ram改成--cache disk预加载到本地缓存目录。--batch 16是单卡12GB显存下的保守值。批量大小影响BN层的统计量焊接缺陷数据集的图像内容相对单一都是焊道表面批量太小BN容易震荡批量太大又容易OOM。如果训练日志里loss曲线高频抖动优先缩小batch而不是调低学习率。跑通第一轮后还需要把预训练权重从COCO的80类适应到焊接缺陷10类YOLOv5的autoanchor会在每轮epoch开始时自动计算基于训练集标签的anchor。这个自动anchor过程在焊接缺陷上经常会产生极端宽高比anchor因为焊缝本身是条状气孔缺陷又是圆形两种形状的宽高比差异极大。如果损失值长时间不降可以加--noautoanchor并手动在模型yaml里调整anchor的宽高比分布。4.3 训练曲线怎么盯:焊接缺陷的小目标loss和过拟合判断训练过程中不要只盯box_loss和obj_loss这两个数值更要关注val/box_loss和val/obj_loss的变化趋势。焊接受焊面缺陷的样本背景高度相似模型很快就能把背景和缺陷区分开box_loss会在前20个epoch快速下降然后进入平台期这时候要判断是不是过拟合了——看验证集loss是否出现先降后升的拐点如果训练集loss还在降、验证集loss开始回升说明模型开始把训练集里的噪声焊纹当成特征记住此时应当停止训练回到数据层面做增强而不是继续调epoch。YOLOv5会在每个epoch结束后输出指标到runs/weld_defect/目录用tensorboard或wandb观察更方便tensorboard --logdir runs/weld_defect然后浏览器打开local host:6006看P/R/mAP曲线。焊接缺陷数据集的验证曲线有一个典型特征P精确率曲线上升速度远快于R召回率因为缺陷区域在整图中占比太小模型倾向于保守预测宁可少框也不框错。如果R值长期低于0.5意味着大量缺陷没被框出来应对策略不是加epoch而是下调推理时的置信度阈值或使用更小的--conf在测试阶段让模型输出更多候选框。5. 焊接表面缺陷目标检测常见的5个坑:现象、原因、解决记录5.1 标签坐标越界导致训练中断用这份数据集训练时最容易遇到的第一个报错是训练到某个epoch突然崩溃报错信息类似Invalid box coordinates或RuntimeError: zero-size tensor。现象是dataloader在加载某张图的标签时发现归一化坐标里有负数或超过1的值。原因是标注人员在手工标注时把框拉到了图像边缘之外或者转换脚本对边界值做了像素坐标到归一化坐标的除法但没有约束范围。解决的硬办法是在训练前统一过滤越界标签# 用awk检查所有标签中是否有坐标小于0或大于1的情况 awk {if ($2$3 $20 $31 $40 $51) print $0} labels/train/*.txt | wc -l # 和原始标签总行数对比 cat labels/train/*.txt | wc -l两次输出差异较大时说明有标签坐标异常。处理方式是把越界的坐标直接裁剪到[0, 1]区间而不是删掉整行因为删除整行会丢失一个缺陷目标裁剪只是把框拉回图像边界内损失很小。5.2 类别编号错位导致验证集指标失真我曾遇到过训练日志显示mAP 50 0.93精度看起来很高但打开验证集的可视化结果发现气孔缺陷全被标成了咬边。这种错误发生在标注txt里的类别编号顺序和data.yaml里的names顺序不一致时而模型本身的检测框位置是正确的。原因是数据集交付时标注脚本用了A顺序训练时data.yaml却用了B顺序两者对不上。判断方法很简单随机挑三张验证集图片用detect.py推理同时用labelImg打开对应的txt标签人工比对几个框的类别名称是否对得上。解决方法是统一一个类别顺序标准然后重跑标注转换脚本而不是在训练代码里做映射补丁。5.3 小目标缺陷漏检:模型学了但框不出来焊接表面缺陷中气孔的尺寸经常只有32×32甚至更小当输入分辨率640时缩小后目标只占7×7像素YOLOv5的P3检测层80×80特征图能勉强覆盖但权重占比低。表现是训练loss正常下降但验证集的小缺陷召回率低。常见处理是开Mosaic增强并把输入分辨率提到960或1280python train.py --img 1280 --mosaic 1.0 --batch 8分辨率提高后单卡12GB显存连--batch 8都吃力时就要考虑用--rect矩形推理减少背景填充。要注意的是--img 1280不保证一定有效需要对比--img 640的验证集AP差值若AP反而下降则说明模型容量不够应换yolov5m或yolov5l。5.4 训练集和验证集数据泄漏焊接缺陷数据集常见的另一个隐蔽分布问题是同一张原图被轻微裁剪或旋转后出现在训练集和验证集里。因为焊件表面纹理高度相似模型会通过记忆背景纹理来“识别”缺陷而不是真正的缺陷形状导致验证集指标偏高部署后现场数据一换就全崩。检查方法是计算训练集与验证集图像的内容相似度imagededup等工具可以基于CNN特征做相似度哈希阈值设在0.85以上时视为重复并手动清理pip install imagededup imagededup_hash --dataset images/train --dataset2 images/val --threshold 0.85对比后把重复样本从训练集里移出。这个操作一定会降低验证集mAP但那是真实水平比虚假的高分有用。5.5 图像过曝和反光导致某一类别识别率偏低焊接表面常见金属反光图像里有大量高光区域这些区域在标注时往往被标成气孔或咬边但在模型看来高光背景和缺陷的纹理非常相似。现象是spatter类别飞溅的精确率在训练10个epoch后停滞在低位波动剧烈。解决方法是手动调整数据集里的过曝样本比例——不是删掉高光图而是在数据增强里把hsv_s和hsv_v的增强幅度加大让模型对光照变化不过敏感:python train.py --hyp data/hyps/hyp.scratch-low.yaml --hsv_h 0.0 --hsv_s 0.7 --hsv_v 0.5过曝样本在焊接数据集里属于真实分布的一部分直接删除会让训练分布偏离部署分布调整增强策略比清洗数据更稳妥。6. 焊接缺陷检测模型训练完怎么验证:混淆矩阵、可视化分析与数据迭代闭环6.1 用混淆矩阵判断10个类别里哪两个最容易混淆训练完成后第一件事不是看总mAP而是看runs/weld_defect/exp*/confusion_matrix.png。这张图会暴露两类问题哪两个类别互相误判最多、哪个类别被大量漏检背景列数值大。焊接缺陷场景里最常见的混淆对是crack和lack_of_fusion因为两者在视觉上都是线状暗区域形状和纹理极其相似。如果混淆矩阵显示crack被大量判成lack_of_fusion解决方向不是继续训练而是回到数据集层面确认标注规范是否统一是否存在同一张图里同一条缺陷被不同标注员一个标成crack、另一个标成lack_of_fusion的情况。检查方法是对训练集标签按类别分别做裁剪可视化抽100张crack样本、100张lack_of_fusion样本人工看边缘形状特征差异是否足够明显。若确实标注口径混乱需要重新校准标注规范并修正标签这不是模型能学回来的。6.2 推理结果可视化:把检测框叠加到原图上找漏检规律用detect.py跑验证集输出带框图片重点观察两类样本:一是置信度低于0.3的漏检区域二是置信度在0.5到0.7之间的模糊区域。具体命令是:python detect.py \ --weights runs/weld_defect/exp/weights/best.pt \ --source weld-defect-dataset/images/val \ --conf 0.1 \ --save-txt \ --project runs/weld_defect_vis把--conf设到0.1是为了让模型把所有有嫌疑的区域都框出来可视化后能看清模型对哪些缺陷类型是“压根没框”哪些是“框了但置信度低”。按我的经验焊接缺陷里“压根没框”的区域往往是面积很小的点状气孔因为P3层特征图感受野覆盖不了太小的目标“框了但置信度低”的则多半是带反光的未熔合背景纹理干扰了特征提取。6.3 按缺陷类别拆分的AP矩阵决定下一步投入方向YOLOv5训练结束会在验证集上输出每个类别的P、R、mAP50、mAP50-95把这些数值整理成表:类别PRmAP50mAP50-95porosity0.910.880.920.61undercut0.820.760.840.52crack0.700.510.660.33lack_of_fusion0.680.450.610.28...............如果crack的mAP50-95显著低于porosity说明模型在精确定位这类细长缺陷上还没有学到位下一步应该做针对性数据增强对crack这类目标做旋转和轻微形变增强而不是对全数据集做统一增强。因为这类线状缺陷的方向性很强水平裂纹在训练集占多数模型对垂直裂纹的泛化就差。我个人的习惯是每轮训练结束固定跑一次这套验证流程把各类别AP变化记录成表再决定下一轮投入的是标注更多数据、改增强策略、还是换更大的模型。这个闭环比单纯堆epoch有效得多。焊接缺陷检测没有一劳永逸的模型只有不断补数据和调整分布才能让现场误报率压下来。希望这份笔记能帮你在焊接表面缺陷数据集上少走几趟弯路把更多时间花在真正有用的迭代上。本文还有配套的精品资源点击获取