
简介这份资源是面向工业质检与机器视觉方向的焊接件表面缺陷检测数据集适合从事缺陷检测算法训练、模型验证及课程实践的研究者与工程师使用。数据采用Pascal VOC与YOLO双格式标注包含2292张jpg图片并配有等量的xml与txt标注文件覆盖crease、crescent_gap、inclusion、oil_spot、punching、rolled_pit、silk_spot、waist_folding、water_spot、welding_line共10类典型焊接缺陷可直接用于目标检测模型的训练与评估。压缩包为7z格式共约2000个文件以xml标注文件为主另含一份使用前必读说明整体大小约256.38MB目录结构清晰便于按类别检索与批量处理。目前已有913人学习下载读者可借此快速搭建焊接缺陷检测实验流程省去自行采集与标注的成本并对照多类别样本理解各类缺陷的形态差异与标注规范。1. 焊接件表面缺陷检测数据集2292张VOC与YOLO双格式的选型逻辑拿到一个标注好的焊接件表面缺陷数据集第一反应往往不是“赶紧训练”而是“这10个类别到底能不能撑起产线质检”。焊接件表面缺陷检测数据集VOCYOLO格式2292张10类别这个标题里藏着三个关键信息数据量不大、双格式兼容、类别数偏多。2292张图分到10个类别平均每类不到230张如果分布再偏一点某些类别可能只有几十张。这意味着直接拿YOLOv8默认参数硬训大概率会在小样本类别上翻车。适合谁用适合做焊接质检原型验证、课程设计、算法对比实验的从业者不适合直接上产线替代人工复判。VOC格式适合做数据清洗和可视化分析YOLO格式适合直接喂给YOLOv5/v8/v11系列。先搞清楚这两个格式怎么互转、类别怎么映射比急着调模型重要得多。2. 从VOC到YOLO2292张焊接缺陷数据的格式转换与类别映射2.1 VOC的XML结构拆解与焊接缺陷的标注特点VOC格式每张图对应一个XML文件核心字段是filename、size、object下的name和bndbox。焊接件表面缺陷的标注有个特点缺陷区域往往细长、边缘模糊标注框容易画得偏大或偏小。拿到数据集后先别转格式用脚本统计一下每个类别的框数量和宽高分布这一步能提前暴露类别不平衡和标注异常。import xml.etree.ElementTree as ET import os from collections import defaultdict def parse_voc_annotations(anno_dir): 统计VOC标注中每个类别的框数量和宽高分布 stats defaultdict(list) for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) xmax float(bbox.find(xmax).text) ymin float(bbox.find(ymin).text) ymax float(bbox.find(ymax).text) w xmax - xmin h ymax - ymin stats[name].append((w, h)) for name, boxes in stats.items(): ws [b[0] for b in boxes] hs [b[1] for b in boxes] print(f{name}: {len(boxes)} boxes, favg_w{sum(ws)/len(ws):.1f}, avg_h{sum(hs)/len(hs):.1f}, fmin_w{min(ws):.1f}, max_w{max(ws):.1f}) return stats stats parse_voc_annotations(./Annotations)这段代码遍历所有XML按类别收集边界框的宽高。重点看两个指标如果某个类别的avg_w或avg_h小于20像素说明缺陷目标很小YOLO默认的输入尺寸640可能不够需要考虑切图或放大输入。如果max_w和min_w差距超过10倍说明同类缺陷的尺度变化大数据增强时要加多尺度训练。焊接缺陷里“气孔”通常是小目标“裂纹”可能是细长目标这两类的锚框设计要区别对待。2.2 转换脚本VOC转YOLO的归一化与类别索引YOLO格式要求每张图对应一个txt文件每行是class_id x_center y_center width height全部归一化到0到1之间。转换时最容易出错的是类别索引映射——VOC里类别名是字符串YOLO需要从0开始的整数。建议单独维护一个classes.txt顺序固定训练和推理都用同一份。import xml.etree.ElementTree as ET import os CLASSES [crack, porosity, slag_inclusion, lack_of_fusion, undercut, overlap, spatter, burn_through, incomplete_penetration, surface_scratch] def voc_to_yolo(anno_dir, img_dir, out_dir): 将VOC XML转换为YOLO txt归一化坐标 os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, xml_file)) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: continue cls_id CLASSES.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) xmax float(bbox.find(xmax).text) ymin float(bbox.find(ymin).text) ymax float(bbox.find(ymax).text) # 归一化并限制在0-1之间防止标注越界 x_center max(0, min(1, (xmin xmax) / 2 / img_w)) y_center max(0, min(1, (ymin ymax) / 2 / img_h)) w max(0, min(1, (xmax - xmin) / img_w)) h max(0, min(1, (ymax - ymin) / img_h)) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_name xml_file.replace(.xml, .txt) with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines)) voc_to_yolo(./Annotations, ./JPEGImages, ./labels)CLASSES列表的顺序就是最终模型输出的类别顺序一旦确定不要中途改动。归一化时用max(0, min(1, ...))做截断因为焊接缺陷标注中偶尔会出现框超出图像边界的情况不截断会导致YOLO训练时loss异常。转换完成后随机抽10张图用labelImg或labelme可视化验证重点看细长裂纹的框是否贴合。2.3 数据集划分2292张图怎么分才不浪费2292张图按8:1:1分训练集1834张、验证集229张、测试集229张。但焊接缺陷类别多随机分可能导致某些类别在验证集里一张都没有。用分层抽样保证每个类别在三个子集里的比例接近。import os import random from collections import defaultdict import shutil def split_dataset(img_dir, label_dir, out_dir, ratios(0.8, 0.1, 0.1)): 按类别分层抽样划分数据集 random.seed(42) # 收集每张图包含的类别 img_classes defaultdict(set) for txt_file in os.listdir(label_dir): if not txt_file.endswith(.txt): continue img_name txt_file.replace(.txt, .jpg) with open(os.path.join(label_dir, txt_file)) as f: for line in f: cls_id int(line.split()[0]) img_classes[img_name].add(cls_id) # 按主类别分组 class_imgs defaultdict(list) for img, classes in img_classes.items(): main_cls min(classes) # 用最小类别ID作为主类别 class_imgs[main_cls].append(img) train, val, test [], [], [] for cls_id, imgs in class_imgs.items(): random.shuffle(imgs) n len(imgs) n_train int(n * ratios[0]) n_val int(n * ratios[1]) train.extend(imgs[:n_train]) val.extend(imgs[n_train:n_train n_val]) test.extend(imgs[n_train n_val:]) for subset, imgs in [(train, train), (val, val), (test, test)]: for sub in [images, labels]: os.makedirs(os.path.join(out_dir, subset, sub), exist_okTrue) for img in imgs: shutil.copy(os.path.join(img_dir, img), os.path.join(out_dir, subset, images, img)) txt img.replace(.jpg, .txt) shutil.copy(os.path.join(label_dir, txt), os.path.join(out_dir, subset, labels, txt)) print(ftrain: {len(train)}, val: {len(val)}, test: {len(test)}) split_dataset(./JPEGImages, ./labels, ./dataset)分层抽样的关键是main_cls的选取这里用最小类别ID简化处理。如果某张图包含多个类别按主类别归组后仍可能造成其他类别分布不均更严格的做法是对每个类别独立分层再取并集。划分完成后检查每个子集的类别分布如果验证集里某个类别少于5张考虑合并到训练集或调整比例。3. YOLOv8训练焊接缺陷检测参数配置与显存优化3.1 数据配置文件与预训练模型选择YOLOv8的数据配置文件weld_defect.yaml需要指定训练、验证、测试路径和类别名。路径用绝对路径或相对于ultralytics根目录的路径避免训练时找不到文件。# weld_defect.yaml path: /data/weld_defect/dataset train: train/images val: val/images test: test/images nc: 10 names: 0: crack 1: porosity 2: slag_inclusion 3: lack_of_fusion 4: undercut 5: overlap 6: spatter 7: burn_through 8: incomplete_penetration 9: surface_scratchnc必须和names长度一致顺序和转换时的CLASSES完全对应。预训练模型选yolov8s.pt或yolov8m.pt2292张图用s版本足够m版本容易过拟合。如果显存只有8GB用yolov8n.pt并把imgsz降到512。焊接缺陷里小目标多imgsz不建议低于512否则气孔和裂纹细节丢失严重。3.2 训练命令与关键参数含义yolo detect train \ dataweld_defect.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience30 \ augmentTrue \ mosaic1.0 \ mixup0.1 \ copy_paste0.1 \ device0 \ projectweld_runs \ nameexp1epochs150配合patience30如果30轮验证指标不升就早停。lr00.01是初始学习率lrf0.01是最终学习率系数余弦退火到初始的1%。mosaic1.0开启马赛克增强对多类别小目标有效但焊接缺陷里细长裂纹被拼接后可能断裂如果发现裂纹类别AP低把mosaic降到0.5。mixup0.1和copy_paste0.1是额外增强copy_paste对实例分割有效检测任务里作用有限可以关掉。batch16在8GB显存下跑640尺寸的s模型刚好如果OOM就降到8并开ampTrue。3.3 训练过程监控与显存不足的降级方案训练启动后看三个指标box_loss是否稳定下降、cls_loss是否震荡、mAP50是否在50轮后开始爬升。焊接缺陷数据量小前30轮cls_loss震荡正常如果100轮后还震荡检查标注里是否有类别名拼写不一致。显存不足时按这个顺序降级先降batch到8再降imgsz到512最后换yolov8n.pt。不要一上来就换n模型s模型的特征提取能力对焊接缺陷的细微纹理更重要。from ultralytics import YOLO model YOLO(yolov8s.pt) results model.train( dataweld_defect.yaml, epochs150, imgsz640, batch8, ampTrue, cacheTrue, # 缓存图像到内存加速训练 workers4, projectweld_runs, nameexp2 )cacheTrue把解码后的图像缓存到内存2292张图约占用2-3GB内存能显著减少数据加载瓶颈。workers4根据CPU核心数调整太多反而导致IO竞争。训练结束后用model.val()在测试集上跑一次看混淆矩阵里哪些类别互相误判——焊接缺陷里lack_of_fusion和incomplete_penetration容易混如果混淆严重考虑合并这两个类别或增加区分性标注。4. 焊接缺陷检测的避坑与排查从标注到推理的5个翻车点4.1 类别不平衡导致小样本类别AP为0现象训练完看各类别APburn_through和surface_scratch的AP低于0.1其他类别正常。原因这两个类别在2292张图里可能只有30-50个实例YOLO的损失函数对小样本类别梯度贡献少模型倾向于预测多数类。解决在weld_defect.yaml里加cls_pw参数给少数类加权或直接用过采样——把包含少数类的图复制2-3倍加入训练集。更彻底的做法是用copy_paste把少数类实例粘贴到其他图上但焊接缺陷的背景纹理要匹配否则引入噪声。4.2 标注框越界导致训练loss为NaN现象训练几个epoch后box_loss变成NaN整个训练崩溃。原因VOC转YOLO时没有做坐标截断某些标注框的xmax超过图像宽度归一化后x_center大于1YOLO计算loss时出现除零或log负数。解决转换脚本里加max(0, min(1, ...))截断转换后用脚本检查所有txt文件确保每行的四个数值都在0到1之间。发现越界框后回溯原XML手动修正或删除该标注。4.3 验证集mAP高但测试集崩盘现象验证集mAP50到0.85测试集只有0.5。原因划分数据集时没有分层抽样测试集里某些类别的样本和训练集分布差异大或者测试集里混入了训练集的重复图。解决用感知哈希去重确保训练集和测试集没有相同或高度相似的图。分层抽样后打印每个子集的类别分布直方图如果测试集某个类别占比和训练集差超过5%重新划分。4.4 推理时置信度阈值设0.25漏检严重现象模型在测试集上mAP不错但实际推理时漏检多尤其是小目标气孔。原因YOLO默认conf0.25小目标的置信度普遍偏低0.25的阈值把很多正确检测过滤掉了。解决推理时把conf降到0.1同时把iou从0.7降到0.5观察漏检和误检的平衡。如果误检增加太多用agnostic_nmsTrue合并跨类别重叠框。焊接质检场景宁可误检不可漏检conf0.1是合理起点。4.5 数据增强过度导致裂纹类别退化现象开了mosaic1.0和mixup0.2后裂纹类别的AP反而比不开增强时低。原因裂纹是细长连续目标马赛克拼接把裂纹截断成几段模型学到的裂纹特征不完整。mixup混合两张图后裂纹边缘模糊进一步恶化。解决对裂纹类别单独处理——要么把mosaic降到0.3要么在增强时排除包含裂纹的图。更精细的做法是用copy_paste模式只粘贴非裂纹类别裂纹图保持原样训练。5. 用混淆矩阵和PR曲线定位焊接缺陷检测的边界训练完不是看一个mAP就结束。焊接件表面缺陷检测的10个类别里lack_of_fusion和incomplete_penetration在视觉上都是“未熔合”的表现模型很容易把两者混判。打开weld_runs/exp1/confusion_matrix.png看非对角线上的数值——如果lack_of_fusion被预测成incomplete_penetration的次数超过真实样本的30%说明这两个类别在标注阶段就没有明确边界。我一般会做两件事第一把混淆严重的两个类别合并成一个“未熔合”大类重新训练一版对比mAP第二如果业务上必须区分就找标注规范里两者的定义差异在数据里补充边界样本。PR曲线看每个类别的曲线下面积重点看曲线在recall0.8时的precision值。焊接质检的底线是召回率优先如果某个类别在recall0.8时precision低于0.5说明误检太多需要调整该类别的置信度阈值或补充负样本。YOLOv8的val()会输出PR_curve.png用matplotlib可以单独提取某个类别的曲线做对比。import matplotlib.pyplot as plt from ultralytics import YOLO model YOLO(weld_runs/exp1/weights/best.pt) metrics model.val(dataweld_defect.yaml, splittest, conf0.1, iou0.5) # 打印每个类别的P/R/mAP for i, name in enumerate(metrics.names): p, r, ap50 metrics.box.p[i], metrics.box.r[i], metrics.box.ap50[i] print(f{name}: P{p:.3f}, R{r:.3f}, AP50{ap50:.3f})这段代码在测试集上以conf0.1重新评估输出每个类别的精确率、召回率和AP50。如果某个类别的召回率低于0.6优先补该类别的训练样本如果精确率低于0.5检查是否有其他类别的框被误判成该类。焊接缺陷检测没有“训练完就完事”的说法混淆矩阵和PR曲线是调参的后悔药每次改完增强参数或类别定义都重新跑一遍。我自己的习惯是拿到任何焊接缺陷数据集先花半天做格式转换和分布统计再花半天调增强参数最后才开训练。2292张图不算多但10个类别的标注质量决定了模型上限。如果标注里“气孔”和“夹渣”的边界模糊再好的模型也学不明白。希望帮到你。本文还有配套的精品资源点击获取