简介:本资源为焊接处缺陷检测数据集,面向从事工业质检、焊缝缺陷识别的研究人员与算法工程师,可用于目标检测模型的训练、验证与算法对比实验。数据集同时提供Pascal VOC与YOLO两种标注格式,包含jpg图片及对应的xml、txt标注文件,省去格式转换环节,便于直接接入主流检测框架。压缩包共2000个文件,以1999个xml标注文件和1个说明txt为主,整体约372.78MB,标注类别共8类,涵盖气孔、咬边、断弧、裂纹、空心珠、重叠、夹渣、未熔合等典型焊接缺陷,总标注框数达6783个,样本量与类别覆盖较为均衡。目前已有835人学习下载,适合需要快速构建焊缝缺陷检测基线、验证模型泛化能力或扩充工业缺陷数据集的读者参考使用。
1. 焊接处缺陷检测数据集:3400 张 VOC+YOLO 双格式到底能干什么
拿到一个标注好的焊接缺陷数据集,第一反应往往不是「太好了」,而是「这 3400 张到底够不够训出一个能上产线的模型」。焊接处缺陷检测在工业视觉里属于典型的细长目标 + 低对比度场景:气孔、裂纹、咬边、焊瘤这些缺陷在灰度图上和母材、飞溅的边界经常糊在一起,人眼都要凑近看,模型更别指望靠几张大图就能学会。这个数据集给的是 VOC 和 YOLO 双格式、8 个类别、3400 张,意味着你既可以用 Pascal VOC 那套 XML 标注走两阶段检测器,也可以直接喂给 YOLO 系列做单阶段训练,省掉格式转换的折腾。它适合三类人:想快速验证焊接缺陷检测可行性的算法工程师、需要给产线做 PoC 的视觉从业者、以及拿它当 YOLO 入门练手但不想用 COCO 那种通用数据的人。真正要判断的是:8 类缺陷的样本分布是否均衡、小目标占比多少、VOC 的 XML 和 YOLO 的 txt 是否一一对应——这些决定了你是直接开训,还是得先做一轮数据清洗和重采样。
2. 焊接缺陷 8 类别与 VOC/YOLO 双格式的对应关系
2.1 先搞清楚 8 个类别在标注里长什么样
焊接缺陷检测的类别命名在不同数据集里差异很大,常见做法是按缺陷形态分:气孔(porosity)、裂纹(crack)、咬边(undercut)、焊瘤(overlap)、未熔合(lack of fusion)、未焊透(lack of penetration)、夹渣(slag inclusion)、飞溅(spatter)。这个数据集标了 8 类,你拿到手第一件事不是写训练脚本,而是把类别名和标注文件里的英文标签对齐。VOC 格式下每张图对应一个 XML,<name>字段就是类别名;YOLO 格式下每张图对应一个 txt,每行是class_id x_center y_center width height,坐标全部归一化到 0~1。两套标注必须能互相印证,否则训练时会出现「VOC 里有的框 YOLO 里没有」这种脏数据。
我一般会先跑一个统计脚本,把 VOC 的 XML 解析一遍,输出每个类别的框数量和每张图的框数量分布。这一步能直接暴露三个问题:类别是否严重不均衡、有没有空标注图、有没有框超出图像边界。
import os import xml.etree.ElementTree as ET from collections import Counter # 指向 VOC 格式的 Annotations 目录 anno_dir = "VOCdevkit/VOC2007/Annotations" class_counter = Counter() boxes_per_image = [] 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() objs = root.findall("object") boxes_per_image.append(len(objs)) for obj in objs: name = obj.find("name").text.strip() class_counter[name] += 1 print("类别分布:", class_counter) print("每图平均框数:", sum(boxes_per_image) / len(boxes_per_image)) print("空标注图数量:", boxes_per_image.count(0))这段脚本的逻辑很直接:遍历 Annotations 下所有 XML,用findall("object")拿到每张图的标注框,累计类别名并记录每图框数。参数上唯一要注意的是anno_dir要指向 VOC 结构里的 Annotations,不是 JPEGImages。跑完你会得到类似{'crack': 420, 'porosity': 980, ...}的分布。如果某一类低于 100 个框,训练时就要考虑过采样或者类别权重;如果空标注图超过 5%,说明这批图里有大量无缺陷样本,要么单独拿出来做负样本,要么直接剔除。
2.2 VOC 转 YOLO:坐标归一化和类别映射的两个关键点
数据集虽然号称双格式,但实际用的时候经常需要自己再转一遍,尤其是你想换 YOLOv8 或 YOLOv11 这种要求特定目录结构的框架。VOC 的坐标是绝对像素值xmin, ymin, xmax, ymax,YOLO 要的是归一化中心点加宽高。转换公式不复杂,但有两个坑:一是图像尺寸要从 XML 的<size>里读,不能假设所有图都是 640×640;二是类别 id 必须从 0 开始连续,且和你的data.yaml里的names顺序严格一致。
import os import xml.etree.ElementTree as ET # 类别名到 id 的映射,顺序必须和 data.yaml 一致 classes = ["crack", "porosity", "undercut", "overlap", "lack_of_fusion", "lack_of_penetration", "slag", "spatter"] class_to_id = {name: i for i, name in enumerate(classes)} anno_dir = "VOCdevkit/VOC2007/Annotations" out_dir = "labels/train" os.makedirs(out_dir, exist_ok=True) 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") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in class_to_id: continue # 跳过未定义类别,避免训练时报错 bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 裁剪到图像边界,防止越界框 xmin, ymin = max(0, xmin), max(0, ymin) xmax, ymax = min(w, xmax), min(h, ymax) xc = (xmin + xmax) / 2.0 / w yc = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{class_to_id[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") txt_name = os.path.splitext(xml_file)[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: f.write("\n".join(lines))逻辑说明:先读 XML 的<size>拿真实宽高,再对每个 object 做坐标裁剪和归一化。class_to_id的顺序就是最终训练时names的顺序,一旦定了就不要改,否则模型学到的 id 和标签对不上。参数上xc/yc/bw/bh保留 6 位小数足够,YOLO 内部会再处理。裁剪那两行是后悔药:有些标注框会超出图像边界几个像素,不裁的话归一化后会出现负值或大于 1 的值,训练时 loss 直接变 NaN。
2.3 目录结构怎么摆才能让 YOLO 直接开训
YOLO 系列对目录结构有固定要求,常见做法是images/train、images/val、labels/train、labels/val四个目录,外加一个data.yaml。3400 张按 8:2 切,训练集 2720 张、验证集 680 张。切分时要注意:同一个焊接件的多张图不能同时出现在训练和验证集里,否则验证指标会虚高。如果数据集里没有工件 id 信息,至少按文件名前缀做分组切分。
# data.yaml path: ./weld_defect train: images/train val: images/val names: 0: crack 1: porosity 2: undercut 3: overlap 4: lack_of_fusion 5: lack_of_penetration 6: slag 7: spatter这个 yaml 里path是数据集根目录,train和val是相对路径。names的键必须从 0 连续,和转换脚本里的class_to_id完全一致。摆好之后用yolo detect train data=data.yaml model=yolov8n.pt epochs=100 imgsize=640就能跑起来。第一次跑建议先用yolov8n这种小模型验证流程通不通,别一上来就上大模型,不然数据有问题的话你连排查方向都找不到。
3. 用 YOLOv8 在 3400 张焊接缺陷上跑通训练的最小闭环
3.1 环境安装与第一次前向推理验证
训练之前先确认环境和数据能对上。安装 ultralytics 之后,别急着 train,先做一次推理验证:拿一张训练图喂给预训练模型,看输出格式对不对。这一步能排除掉 90% 的「数据路径写错」和「类别数不匹配」问题。
pip install ultralytics yolo detect predict model=yolov8n.pt source=weld_defect/images/train/001.jpg save=True这条命令会用 COCO 预训练的 yolov8n 对单张图做推理,输出保存在runs/detect/predict。注意这里模型输出的是 COCO 的 80 类,不是你的 8 类焊接缺陷,所以框的位置可能对但类别全是错的。这一步的目的不是看检测效果,而是确认图片能正常读取、ultralytics 能正常跑。如果这一步就报错,先检查图片路径和格式,别往下走。
3.2 训练参数怎么设:从 epochs 到 imgsz 的取舍
正式训练时,参数设置直接决定你能不能在一晚上拿到可用的模型。3400 张 8 类,属于小数据集,我一般会这么设:
| 参数 | 建议值 | 理由 |
|---|---|---|
| model | yolov8s.pt | n 太小欠拟合,m/l 容易过拟合 |
| epochs | 150 | 小数据集收敛快,150 轮足够观察趋势 |
| imgsz | 640 | 焊接缺陷目标偏小,再小会丢细节 |
| batch | 16 | 显存 8G 以上都能跑,不够就降到 8 |
| lr0 | 0.01 | 默认值,小数据集可降到 0.005 |
| patience | 30 | 30 轮没提升就早停,省时间 |
yolo detect train \ data=weld_defect/data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=30 \ project=weld_runs \ name=exp1参数说明:imgsz=640是焊接缺陷检测的常用分辨率,再高到 1280 会显著增加显存和训练时间,但小目标召回可能提升;patience=30是早停,验证集 mAP 连续 30 轮不涨就停,避免过拟合。project和name决定输出目录,方便你对比多次实验。训练过程中重点看mAP50和mAP50-95两条曲线,如果 mAP50 涨到 0.7 以上但 mAP50-95 很低,说明框的位置不够准,可能是标注框偏大或偏小。
3.3 训练完怎么验证:混淆矩阵和单类 AP 才是重点
训练结束后 ultralytics 会自动生成混淆矩阵和 PR 曲线,但很多人只看一个总 mAP 就结束了。焊接缺陷 8 类里,裂纹和未熔合这种细长缺陷的 AP 往往比气孔低很多,必须单独看每一类的 AP。在runs/detect/exp1下找到confusion_matrix.png和results.csv,重点看两类:一是混淆矩阵里有没有某一类被大量误判成背景,二是results.csv里每一列的metrics/mAP50-95分项。
import pandas as pd df = pd.read_csv("weld_runs/exp1/results.csv") # 打印每一轮的总 mAP 和各类 AP 的列名 print(df.columns.tolist()) # 取最后 5 轮的平均值,看稳定后的表现 print(df.tail(5)[["metrics/mAP50(B)", "metrics/mAP50-95(B)"]].mean())这段代码读results.csv,先看列名确认有哪些指标,再取最后 5 轮平均。如果总 mAP50 在 0.75 左右但某一类 AP 低于 0.3,说明这一类样本太少或标注质量差,需要针对性补数据或重新标注。验证集上的可视化也很重要,用yolo detect predict model=weld_runs/exp1/weights/best.pt source=weld_defect/images/val save=True跑一遍,肉眼看看漏检和误检集中在哪些缺陷上。
4. 焊接缺陷检测训练避坑:5 个血泪踩坑记录
4.1 现象:训练 loss 正常下降但 mAP 一直是 0
原因:data.yaml里的names顺序和 YOLO 标签里的class_id对不上。比如你把crack放在 names 的第 0 位,但转换脚本里crack的 id 是 3,模型学到的类别和验证时计算的类别完全错位。解决:用 2.2 的转换脚本重新生成一遍标签,确保class_to_id和data.yaml的names逐字一致,然后重新训练。
4.2 现象:验证集 mAP 很高,但拿产线新图测试全漏检
原因:训练集和验证集来自同一批工件、同一光照条件,模型学到了背景特征而不是缺陷特征。3400 张如果全是一个焊接场景下拍的,泛化能力会很差。解决:切分数据时按工件或拍摄批次分组,验证集尽量包含不同光照、不同角度的图。如果数据集本身多样性不足,训练时开mosaic=1.0和hsv_h=0.015做增强,但增强不能替代真实多样性。
4.3 现象:训练到一半 loss 突然变 NaN
原因:标注框越界或宽高为 0。VOC 转 YOLO 时如果没做边界裁剪,归一化后会出现负值或大于 1 的值,YOLO 内部计算 IoU 时直接除零。解决:在转换脚本里加max(0, xmin)和min(w, xmax)的裁剪,并且过滤掉xmax <= xmin或ymax <= ymin的无效框。训练前用脚本扫一遍所有 txt,确认每行 5 个值且都在 0~1 之间。
4.4 现象:小目标缺陷(气孔、裂纹)召回率极低
原因:640 分辨率下,几个像素宽的气孔在特征图上只剩一两个点,YOLO 的 P3 层也救不回来。解决:把imgsz提到 1024 或 1280,同时把batch降下来;或者在数据增强里加scale=0.5让模型多见小目标。如果显存不够,用yolov8s换yolov8n省显存,但别指望 n 模型能检好小目标。
4.5 现象:某一类缺陷 AP 始终为 0,但标注里明明有
原因:这一类样本数量太少,比如 8 类里有一类只有 20 个框,YOLO 在计算 loss 时正样本被其他类淹没。解决:先确认这一类框数,低于 100 的话考虑过采样(把含该类的图复制多份)或在 loss 里加类别权重。ultralytics 不直接支持类别权重,简单做法是把这一类图单独抽出来多训几轮,或者用copy_paste增强合成更多样本。
5. 从 3400 张到产线可用:提升焊接缺陷检测精度的三个进阶技巧
5.1 用 SAHI 切片推理救小目标
焊接缺陷里气孔和裂纹在整图上占比极小,640 分辨率直接推理很容易漏。SAHI(Slicing Aided Hyper Inference)的思路是把大图切成重叠的小块分别推理再合并,对小目标召回提升明显。安装sahi后,用 YOLOv8 的权重做切片推理:
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type="yolov8", model_path="weld_runs/exp1/weights/best.pt", confidence_threshold=0.25, device="cuda:0" ) result = get_sliced_prediction( "test_weld.jpg", detection_model, slice_height=320, slice_width=320, overlap_height_ratio=0.2, overlap_width_ratio=0.2 ) result.export_visuals(export_dir="sahi_out/")参数说明:slice_height/width=320是把原图切成 320×320 的小块,overlap_ratio=0.2是块间重叠 20%,防止缺陷正好落在切缝上被切断。切片推理的代价是速度慢 3~5 倍,适合离线抽检或对召回要求高的场景。如果产线要求实时,可以只在低置信度区域做二次切片。
5.2 用验证集上的错误样本做定向补标
模型训完之后,把验证集里漏检和误检的图挑出来,按缺陷类别归类。焊接缺陷检测里最常见的错误是「裂纹被标成未熔合」和「飞溅被当成气孔」,这两类在灰度图上确实像。把错误样本单独存一个目录,重新标注后加入训练集,再跑 50 轮微调。我一般会做两轮这样的迭代,mAP50 通常能再涨 3~5 个点。注意微调时lr0要降到 0.001,否则会把之前学到的特征冲掉。
5.3 导出 ONNX 做产线部署前的速度验证
训练完的.pt权重在产线上一般要转成 ONNX 或 TensorRT。导出 ONNX 的命令很简单,但导出后一定要用onnxruntime跑一遍速度测试,确认在目标硬件上能满足节拍。
yolo export model=weld_runs/exp1/weights/best.pt format=onnx imgsz=640 opset=12导出后重点看两个指标:单张推理耗时和显存占用。如果产线是 T4 这类卡,640 分辨率下 YOLOv8s 的 ONNX 推理大概在 10~15ms 一张,8 类焊接缺陷的吞吐量足够覆盖多数抽检工位。如果节拍要求更高,可以考虑 TensorRT FP16 量化,但量化后要重新在验证集上确认 mAP 掉不超过 2 个点。我自己踩过的坑是:ONNX 导出时opset版本和推理引擎不匹配,导致加载失败,后来固定用opset=12就没再翻车。部署前一定在目标机器上实测,别只在开发机上跑通就上线。
希望帮到你。
本文还有配套的精品资源,点击获取