ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

焊接缺陷检测数据集实战:YOLOv5格式解析与训练避坑指南

焊接缺陷检测数据集实战:YOLOv5格式解析与训练避坑指南 简介面向焊接表面与金属加工质检场景的YOLOV5格式目标检测数据集涵盖穿孔、折痕、压痕、内含物等10类焊接缺陷图像为2048×1000分辨率的RGB画面并已拆分出训练集与验证集适合缺陷检测模型训练、迁移学习及算法效果验证的开发者直接使用。资源包共2000个文件主体为1999个txt标注文件另含1个Python可视化脚本可随机读取图像并绘制边界框无需修改即可运行压缩包总计约918.87MB。数据按训练集与验证集分目录存放并附10类缺陷的txt类别清单由于采用YOLOV5目录结构组织标签文件可直接与图像路径对应无需额外转换即可接入训练流程。可视化脚本的加入也便于逐样本检查标注质量降低数据集验收与排错成本。目前已有234人学习下载对有工业质检样本需求或想快速拿到标准YOLO格式数据的学习者能显著节省整理和预处理时间非常适合快速验证检测算法或开展工业视觉相关实践。1. 焊接缺陷检测数据集拿到手先确认这三件事做焊接质检的目标检测数据集很多人解压后第一件事就是丢进 YOLOv5 里开训然后被一堆报错卡住。这个 YOLOV5 目录格式的焊接表面缺陷数据集一共 10 个类别穿孔、折痕、压痕、内含物都在里面训练集和验证集已经按 images/labels 的约定分好图片是 2048×1000 的 RGB 大图总量接近 1GB。它解决的是缺陷检测场景里最耗时的数据整理问题——不用你再去写格式转换脚本、配目录、对齐标签。适合正在用 YOLOv5 训练目标检测模型、需要马上能用的质检数据的人。真正要留神的不是网络结构而是目录约定、标签坐标的换算逻辑以及大图怎么喂给 640 输入的训练流程。下面按我实际拆这个数据集的顺序把每一步的关键点和坑位写清楚。2. YOLOv5 目录格式解析从文件夹布局到标签坐标换算2.1 目录结构与读取顺序数据集解压后是标准的 YOLOv5 布局第一次用这类数据的人容易在路径上翻车。目录结构大致是这样datasets/ ├── images/ │ ├── train/ # 1836 张 2048×1000 RGB 图片 │ └── val/ # 458 张验证图片 ├── labels/ │ ├── train/ # 与 images/train 同名的 .txt 标签 │ └── val/ # 与 images/val 同名 ├── classes.txt # 10 行一行一个类别名 └── show.py # 可视化脚本训练时 data yaml 里的 train 和 val 指向 images 路径YOLOv5 会自动去同级 labels 目录找同名 txt。这套约定和 VOC、COCO 完全不同后者要额外写转换脚本这个资源直接省掉那一步。我一般会先打开 classes.txt 确认类别行数再随机挑一个标签文件看内容这两步能过滤掉一半的“训练时标签读不进来”问题。三种目标检测数据集格式的差别一张表说清楚格式标注内容坐标类型是否归一化常见后缀YOLO txt类别 中心点 宽高相对图片比例是.txtVOC XML左上角 右下角像素绝对值否.xmlCOCO JSONbbox / 多边形像素偏移否.jsonYOLO 的归一化坐标意味着标签和分辨率解耦换输入尺寸不用改标签。但代价是手工验证时不直观0.5 这种数字很难一眼判断在图上什么位置所以必须靠脚本来兜底。2.2 标签 txt 的坐标含义与手工校验标签文件里每行是五个数类别编号、中心点 x、中心点 y、宽度 w、高度 h全部是相对图片宽高的比例值。拿一个验证集标签做解析通常是这样写with open(datasets/labels/val/001852.txt, r, encodingutf-8) as f: lines f.read().strip().split(\n) for line in lines: c, cx, cy, bw, bh map(float, line.split()) print(fclass{int(c)} center({cx:.4f}, {cy:.4f}) size({bw:.4f}, {bh:.4f}))这里 c 是整数类别编号对应 classes.txt 的行号cx、cy 是归一化中心点bw、bh 是归一化宽高。打印出来如果发现 cx 大于 1 或者 bw 大于 1说明标签本身越界这种样本要直接剔除。把归一化坐标换回 2048×1000 原图像素坐标公式是像素中心 x 等于 cx 乘 2048像素中心 y 等于 cy 乘 1000框宽乘 2048框高乘 1000。注意宽度只能乘图片宽度高度只能乘图片高度很多人栽在这——用 2048 算高度或者用 1000 算宽度画出来的框全歪。我习惯按这四步做批量校验第一步统计所有标签的类别编号是否在 0 到 9 之间第二步检查是否有 cx、cy 超出 0 到 1第三步检查 bw、bh 是否出现负数第四步随机抽三张图跑可视化和原图对照。这套流程走完标签层面的问题基本清零。注意2048×1000 的宽高比接近 2:1焊接缺陷里折痕、压痕这类目标通常细长坐标稍微偏一点画出来的框就会和缺陷主体错位这也是后面可视化脚本要先跑通的原因。3. show.py 可视化脚本拆解边界框绘制的关键计算3.1 单张可视化随机抽图的脚本逻辑资源里附带的 show.py 设计得很省事随机抽一张图读对应标签画框后保存到当前目录。逻辑上就是先加载 classes.txt再随机选图然后按标签绘制。核心代码拆开看是这个思路import cv2, glob, random import numpy as np classes open(classes.txt, encodingutf-8).read().strip().split(\n) img_path random.choice(glob.glob(datasets/images/val/*.jpg)) txt_path img_path.replace(images, labels).replace(.jpg, .txt) img cv2.imread(img_path) h, w img.shape[:2] colors np.random.randint(0, 255, (len(classes), 3)).tolist() for line in open(txt_path, encodingutf-8): c, 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) cv2.rectangle(img, (x1, y1), (x2, y2), colors[int(c)], 2) cv2.putText(img, classes[int(c)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[int(c)], 2) cv2.imwrite(vis_result.jpg, img)逻辑说明random.choice 从验证集图片里随机挑一张replace 把 images 目录换成 labels 目录、jpg 后缀换成 txt这样能保证图和标签严格同名对应。坐标换算的关键在 x1、y1、x2、y2 这四行中心点减半宽得到左上角 x减半高得到左上角 y加半宽半高得到右下角。线宽设 2 是因为 2048 宽的原图缩小后细线会看不清文本字号 0.6 同理。这段代码里最容易忽略的是变量命名txt 里的 w 是归一化宽度图片的 w 是像素宽度如果不加区分很容易把 bw 和 bh 弄混。脚本默认随机选图想固定看某一张就把 random.choice 换成具体的文件名或者直接删掉随机逻辑改成传参。3.2 批量可视化把整个验证集过一遍单张可视化只能抽查要排查整个验证集 458 张图有没有标签错位最好批量跑一遍把所有带框的验证图像输出到单独目录里人工扫一遍。常见做法是写一个循环遍历 labels 目录下所有 txtimport cv2, glob, os classes open(classes.txt, encodingutf-8).read().strip().split(\n) os.makedirs(vis_out, exist_okTrue) for txt_path in glob.glob(datasets/labels/val/*.txt): img_path txt_path.replace(labels, images).replace(.txt, .jpg) img cv2.imread(img_path) if img is None: print(missing image:, img_path) continue for line in open(txt_path, encodingutf-8): c, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * img.shape[1]) y1 int((cy - bh / 2) * img.shape[0]) x2 int((cx bw / 2) * img.shape[1]) y2 int((cy bh / 2) * img.shape[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, classes[int(c)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) if img.shape[1] 1024: scale 1024 / img.shape[1] img cv2.resize(img, (1024, int(img.shape[0] * scale))) cv2.imwrite(fvis_out/{os.path.basename(img_path)}, img)这段代码比单张版本多做了两件事一是 img 为 None 时打印缺失文件名避免图片和标签目录对不齐时静默跳过二是输出前把超过 1024 宽的图等比缩小方便快速浏览。批量看图的目的是发现规律性的错位比如所有框都偏左上、所有标签类别都和实物差一位。如果只想看某一个类别在循环里加一个判断if int(c) 3 再画框。批量可视化还有一个隐藏用法把输出图和原图放在两个窗口左右对比能直接发现标注框是否漏掉了一个缺陷或者框是否把背景噪声也包进去了。焊接表面本身纹理复杂如果出现大量背景框说明标注质量有问题训练前要处理。4. 训练配置与参数调优2048×1000 图像如何喂给 YOLOv54.1 dataset.yaml 与类别映射YOLOv5 训练前要写一个 data yaml把数据路径、类别数、类别名告诉训练脚本。这个资源是 10 类别classes.txt 里十行类名yaml 可以这样写train: D:/datasets/weld/images/train val: D:/datasets/weld/images/val nc: 10 names: 0: 穿孔 1: 折痕 2: 压痕 3: 内含物 # 4-9 按 classes.txt 里的顺序继续填编号不允许跳train 和 val 指向 images 目录YOLOv5 会自动映射同级 labels。nc 是类别总数必须和 classes.txt 行数一致。names 列表顺序就是标签文件里编号对应的类名标签里写 3names 里下标 3 那个名字才是它对应的类别。最常见的翻车来自这里classes.txt 里 10 个类手写 names 时漏了一个、序号整体错位训练出来的模型预测框类别全错。提示类名建议用英文或者拼音中文类名在训练日志和导出 ONNX 时会出现编码兼容问题。可以先用中文看数据训练前统一替换成英文。我一般会写一段小脚本把 yaml 里的 names 和 classes.txt 逐行 diff 一遍确认顺序完全一致再开工。这一步 30 秒能省掉训练完才发现类别错位的后悔药。4.2 输入尺寸、batch 与训练命令2048×1000 的原图直接送进默认 640×640 的训练管线会先被缩到 640×640。宽高比从 2:1 变成 1:1所有目标被横向压扁折痕、压痕这类细长缺陷特征直接变形。我的做法是用 1280 作为训练尺寸同时开矩形训练让 batch 里的图按接近原始宽高比做填充python train.py --img 1280 --batch 8 --epochs 100 \ --data datasets/weld.yaml --cfg models/yolov5s.yaml \ --weights yolov5s.pt --rect参数说明--img 1280 是训练输入的长边1280×641 左右的实际输入比 640 保真度高很多代价是显存占用翻倍--batch 8 对 1280 输入是保守值24GB 显存可以升到 168GB 直接降到 4--rect 开启矩形训练按 batch 内图片近似宽高比分组填充减少无效像素--epochs 100 配合 yolov5s 预训练权重适合两千多张的中小数据集。如果显存紧张可以改成 --img 960 --rect速度提升明显但细长缺陷的 recall 会掉。参数推荐值默认值说明img1280640保持 2:1 宽高比缺陷特征更完整batch816大图 大 batch 是 OOM 主因rectTrueFalse矩形训练减少黑边填充mosaic0.51.0降低拼接避免细小缺陷被缩没fliplr0.50.5左右翻转对焊接缺陷语义影响小flipud0.00.0上下翻转可能改变缺陷形态建议关闭数据增强方面mosaic 默认 1.0 会把四张图拼成一张焊接缺陷本身细小拼接后再缩放容易变成几个像素。我把 mosaic 调到 0.5保留一部分但不过度。fliplr 左右翻转对折痕、压痕这类缺陷问题不大flipud 建议关掉因为焊接方向性缺陷上下翻转后形态语义可能变化。这些超参在 models/hub/yolov5s.yaml 或 data/hyps/hyp.scratch-low.yaml 里改。这个配置下我实际跑过类似数据集前 20 个 epoch loss 下降正常30 个 epoch 后 mAP 开始拉起来100 epoch 大概需要三到五小时取决于显卡。如果发现验证集 mAP 一直上不去优先回头看标签质量而不是换网络结构。5. 避坑指南目录、标签、显存相关的五个踩坑记录5.1 数据侧的三个坑现象一训练日志疯狂刷 “WARNING: No labels in ...”loss 基本不动。原因通常是 images/train 路径配对了但 YOLOv5 找不到对应 labels 目录或者标签文件里混了类别编号 10而 classes.txt 只有 10 行编号最大只能到 9。解决训练前跑一段校验遍历所有标签文件检查类别编号范围同时确认每个图片文件都有同名 txt。我习惯用一个简短脚本一次性完成这两项检查python - PY import glob, os for split in [train, val]: txts glob.glob(fdatasets/labels/{split}/*.txt) for txt in txts: for line in open(txt): if int(line.split()[0]) 9: print(类别越界:, txt, line.strip()) img txt.replace(labels, images).replace(.txt, .jpg) if not os.path.exists(img): print(缺图:, img) print(split, 标签数, len(txts)) PY现象二show.py 画出来的框全挤在图片左上角或者框小到看不见。原因是把归一化坐标直接当像素坐标用了cx0.8 被画成距左边 0.8 个像素。解决严格按第 3 章的换算公式x、y 分别乘宽和高不能混。另外注意 bw、bh 也要乘对应轴的像素数不是乘以图片对角线或者总面积。现象三图片和标签对不上号画出的框落在另一张图的目标上。原因出在文件名补零上001852.txt 这种 6 位编号某些工具转格式时把前导零丢了变成 1852.txt排序后错位。解决不要手动拼路径统一用 glob 按目录匹配让脚本用 txt 文件名去反查图片是否存在发现缺失立即提醒自己重新解压原始压缩包。5.2 训练侧的坑现象四训练时报 CUDA out of memory程序直接崩。2048×1000 的图如果不用 --img 限制YOLOv5 会按原图尺寸做前向计算一张图的特征图就吃掉大量显存batch 稍微调大就爆。解决先把 --img 降到 1280batch 降到 8再不行就 960 配合 rect。还有一招是关闭 mosaic 和 mixup这两个增强在训练过程中会临时拼接多张图峰值显存额外增加 20% 左右。现象五验证集 mAP 很高但实际拍一段焊接视频测试折痕、内含物大量漏检。原因是类别不平衡10 个类别里有的类只有几十个标注框模型把所有注意力放在样本多的类别上样本少的类别 recall 极低。mAP 被大类拉高掩盖了小类的问题。解决训练前先统计每个类别的框数量如果发现有类别框数不到总量的 5%就要考虑复制粘贴增强、对少样本类别提高 loss 权重或者干脆合并语义相近的类别。不要指望加 epochs 解决样本不平衡那是数据分布问题不是拟合问题。6. 进阶验证写个统计脚本检查类别分布与标签质量网络结构可以抄参数可以抄但一张图里到底有几个框、每类多少样本、框子占整图多大比例这些问题只有你自己跑一遍才知道。我的习惯是拿到任何目标检测数据集先跑一段统计脚本把家底摸清楚再决定训练策略import glob from collections import Counter stats Counter() areas {i: [] for i in range(10)} num_boxes {} for split in [train, val]: num_boxes[split] 0 for txt in glob.glob(fdatasets/labels/{split}/*.txt): for line in open(txt): c, cx, cy, bw, bh map(float, line.split()) c int(c) stats[(split, c)] 1 num_boxes[split] 1 areas[c].append(bw * bh) print(每张图平均框数 / 每类样本数 / 平均框面积占比:) for split in [train, val]: avg num_boxes[split] / len(glob.glob(fdatasets/images/{split}/*.jpg)) print(split, boxes_per_img, round(avg, 2)) for c in range(10): avg sum(areas[c]) / len(areas[c]) if areas[c] else 0 print(class, c, train, stats[(train, c)], val, stats[(val, c)], avg_area, round(avg, 5))这段脚本输出三组关键信息每张图的平均框数判断标注密度每类的 train/val 样本数判断类别平衡程度平均框面积占比判断目标尺度。焊接缺陷里折痕这类目标往往细长且面积占比小如果某类平均面积低于 0.001训练输入尺寸就要往 1280 走640 输入下这种目标只有几个像素模型基本学不到特征。如果发现 val 里某类样本数为 0训练过程会直接跳过这个类的 recall 评估mAP 再高都是假象。从那以后我每拿到一个数据集都是这个顺序先跑统计脚本、再批量可视化抽查、最后才写 yaml 开训练。这套流程帮我把“训练完才发现数据有问题”这种事提前扼杀在第一个 epoch 之前也让我在调参时不至于把时间浪费在跟数据较劲上。希望帮到你。本文还有配套的精品资源点击获取
返回列表