尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

行李箱缺陷检测:650张小样本数据集的YOLO实战指南

行李箱缺陷检测:650张小样本数据集的YOLO实战指南 简介面向行李箱外观质检与缺陷检测场景的标准化目标检测数据集适合计算机视觉初学者及工业质检项目开发者直接用于YOLO系列或Faster R-CNN等模型的训练与评估。压缩包共1952个文件包含650张清晰JPG原图、650个VOC格式XML标注文件以及650个YOLO格式TXT标签文件另含2个辅助说明TXT整体约25.11MB数据提供damaged与good_condition两类标签矩形框标注共936个其中缺陷框199个、完好框737个图片未做增强便于复现和二次处理。已有126人学习下载数据格式规范、目录清晰省去人工转换格式的麻烦适合快速搭建行李箱缺陷检测基线亦可作为目标检测教学与算法对比的实验数据。1. 行李箱缺陷检测数据集650张2类值不值得动手行李箱出厂前的质检传统靠人工肉眼翻看箱壳、拉杆和拉链区域一小时几百个箱子看下来漏检率很难压住。想用 YOLO 做一版自动缺陷检测卡住你的往往不是模型而是没数据。这个标题给到的是一个 650 张、2 类缺陷、同时带 YOLO 和 VOC 两种标注格式的行李箱缺陷检测数据集。先说结论650 张在工业小样本场景里完全够启动一个验证项目配合迁移学习和针对性增强能做出一条能上测试线的基线。这个数据集的价值不只在“能不能用”更在于它同时给了两种格式省去你自己在 labelimg 和代码之间来回转换的重复劳动。接下来要判断的是标签干不干净、两类缺陷分别是什么、怎么划分训练集才不虚高。本文按“拆数据 → 转格式 → 训练 → 避坑 → 验证”的顺序把从解压到跑通的完整路径写清楚。适合手里有产线需求、正准备拿它试水的工程师也想帮想用一张工业数据集练 YOLO 全流程的入门者省点时间。2. 拆解这份数据集的真实结构目录、标签格式与两类缺陷的组织方式2.1 650 张小数据集到底能不能喂给 YOLO先说一个反直觉的结论650 张对于目标检测来说不算“绝对不够”关键看类别复杂度。2 类缺陷的区分度通常比 10 类高不少迁移学习预训练权重也能把起点拉高。做过产线项目的都知道真实场景里能拿到 500 张高质量缺陷图已经算顺利了很多时候是几十张硬着头皮做。650 张足够你跑通流程、得到一条可信的 baseline而不是拿它和几万张的公开数据集比精度。如果解压后目录里同时存在 images、labels、annotations 三个顶层目录那一半是作者按 YOLO 官方仓库的习惯组织的。YOLO 格式的标签放在 labels 下和 images 下的图片一一对应annotations 下是 VOC 格式的 xml 文件文件名与图片同名。先确认是这类结构后面所有脚本都以它为输入来写。2.2 YOLO 标签和 VOC 标签并存目录差异与命名习惯解压后看到的结构常见是这样路径内容说明images/*.jpg / *.png原始图像一般已按 train/val 分好labels/*.txtYOLO 格式归一化标签一行一个目标annotations/*.xmlVOC 格式标签含类别名和像素坐标这里有个第一眼容易忽视的细节YOLO 的 txt 里 class 是数字 id0、1而 VOC 的 xml 里 class 是字符串名称比如 scratch、dent。类名和数字 id 的对应关系在训练配置 data.yaml 里定义顺序错一位就全错。拿第一张图的 txt 和 xml 对照着看一遍比任何脚本都直观。我当时拿到双格式数据集做的第一件事不是写转换代码而是数清楚两边的文件数。labels 和 annotations 的文件数量必须和 images 一致多一个少一个都是脏数据信号尤其要警惕那种只有 xml 没有 txt、或者 txt 里是空文件的图。2.3 同一张图两种标签的核心区别归一化坐标与像素坐标YOLO 标签是归一化坐标每行五个数类别 id、中心点 x、中心点 y、宽 w、高 h四个数值都在 0 到 1 之间。VOC 的 xml 则是绝对像素坐标 xmin、ymin、xmax、ymax。两者换算很机械但容易在图尺寸上翻车如果图片被缩放或裁剪过像素坐标直接失效归一化坐标也要重新计算。格式坐标形式取值范围YOLO txtclass_id xc yc w h0~1 归一化VOC xmlname xmin ymin xmax ymax绝对像素值从 VOC 转 YOLO 的公式是xc (xmin xmax) / 2 / img_ww (xmax - xmin) / img_w。注意 img_w 必须是 xml 对应原图的真实宽度不是你想当然的 640。很多坑就出在这里图片是 1920 宽你按 640 归一化坐标全错训练出来框全部偏移。2.4 先跑一个检查脚本过滤脏标签不管数据来自哪里训练前先做一轮标签体检。下面这个脚本检查 YOLO 格式标签的类别号越界、坐标越界、图片缺失三类基础问题是最小的卫生检查。import os from pathlib import Path img_dir Path(images/train) label_dir Path(labels/train) num_classes 2 # 跟你的类别数保持一致 for label_file in sorted(label_dir.glob(*.txt)): img_candidates list(img_dir.glob(label_file.stem .*)) if len(img_candidates) 0: print(f[缺失] 标签存在但图片缺失: {label_file}) continue for i, line in enumerate(label_file.read_text().strip().splitlines()): parts line.split() if len(parts) ! 5: print(f[格式错] {label_file.name} 第{i1}行字段数不对: {line}) continue cls_id, xc, yc, w, h map(float, parts) if int(cls_id) num_classes: print(f[越界] {label_file.name} 第{i1}行类别号 {int(cls_id)} 超出范围) left, right xc - w / 2, xc w / 2 top, bottom yc - h / 2, yc h / 2 if left -0.001 or right 1.001 or top -0.001 or bottom 1.001: print(f[越界] {label_file.name} 第{i1}行框超出图像边界)这段脚本的逻辑很简单文件名对不上就报缺失字段数不是 5 个就报格式错框的左边界或右边界超出 1 或者小于 0 就报越界。-0.001和1.001是容差因为有些标注工具会留 1 个像素的浮点误差标准太死会把好标签误杀。跑完一遍如果输出为空说明标签基本干净。如果有输出优先处理“图片缺失”和“类别号越界”这两种会导致训练直接报错坐标越界轻微的话 YOLO 能容忍但会拉低收敛速度。别跳过这步这是整个流程里性价比最高的一道工序。3. 从 VOC 到 YOLO 的转换与训练准备脚本、划分与参数3.1 VOC 格式转 YOLO一段能直接用的转换脚本虽然数据本身带双格式你仍然大概率需要自己写一遍转换。原因很简单你后面用 labelimg 补标的新缺陷图存出来的多半是 VOC 格式必须转成 YOLO 才能和这份数据集合并训练。下面是通用的转换函数。import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h, class_names): tree ET.parse(xml_path) root tree.getroot() out_lines [] for obj in root.findall(object): name obj.find(name).text difficult obj.find(difficult) if difficult is not None and int(difficult.text) 1: continue # 困难样本跳过训练早期会被当成噪声 bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) xc (xmin xmax) / 2 / img_w yc (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h cls_id class_names.index(name) out_lines.append(f{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) return \n.join(out_lines) # 示例调用class_names 的顺序必须和训练配置完全一致 # class_names [scratch, dent] # txt_content voc_to_yolo(annotations/0001.xml, 1920, 1080, class_names)转换过程中有一个关键参数容易被忽略difficult字段。VOC 格式里人工标注时常把模糊、遮挡严重的框标成 difficult1意思是“这框太难了别算错误”。转到 YOLO 时如果不过滤这些困难样本会变成噪声标签干扰损失计算。上面脚本直接跳过这是我在实践里确认更稳的做法标签量少时尤其重要。另一个关键点class_names列表的顺序就是训练时 txt 里数字 id 的映射。它必须和后面 data.yaml 里的 names 完全一致。比如这里写[scratch, dent]那么 txt 里的 0 对应 scratch1 对应 dent。如果你把顺序搞反损失照样收敛但预测结果会把两类缺陷互调这是最隐蔽的坑之一。3.2 YOLO 标签转回 VOC给别的工具留条退路反向转换用得少但要有。典型场景是你训练完发现某个类别效果差想用 labelimg 人工复核标签而 labelimg 的标注界面默认读 VOC 或 YOLO 都可以有些团队内部工具只认 VOC。写一个简短的逆向函数需要时直接调用。import xml.etree.ElementTree as ET def write_voc_xml(out_path, filename, img_w, img_h, objs): # objs: [(class_name, xmin, ymin, xmax, ymax), ...] annotation ET.Element(annotation) ET.SubElement(annotation, filename).text filename size ET.SubElement(annotation, size) ET.SubElement(size, width).text str(img_w) ET.SubElement(size, height).text str(img_h) ET.SubElement(size, depth).text 3 for name, xmin, ymin, xmax, ymax in objs: obj ET.SubElement(annotation, object) ET.SubElement(obj, name).text name box ET.SubElement(obj, bndbox) ET.SubElement(box, xmin).text str(int(xmin)) ET.SubElement(box, ymin).text str(int(ymin)) ET.SubElement(box, xmax).text str(int(xmax)) ET.SubElement(box, ymax).text str(int(ymax)) ET.ElementTree(annotation).write(out_path, encodingutf-8, xml_declarationTrue)注意 YOLO 的归一化坐标在转回像素坐标时xc - w/2和xc w/2可能算出负数或超过图宽的数值。原因是原始标注时框本身就越界YOLO 格式允许这种框存在但 VOC 的像素坐标必须裁到图像范围内。这也是为什么 2.4 的检查脚本要写在转换脚本前面顺序反了会带着脏数据一路污染到训练。3.3 训练集验证集划分别在目录里直接剪贴如果这份数据集解压后已经分了 train/val可以直接用但建议先看一眼划分比例。如果全部 650 张都在一个目录里需要自己划分。常见的做法是 8:2 或 9:1针对这个量级我更倾向 9:1验证集 65 张足够看趋势。下面是按文件名随机划分的脚本。import random from pathlib import Path import shutil images sorted(Path(images_all).glob(*.jpg)) val_ratio 0.1 random.seed(42) # 固定种子保证每次划分结果一致 random.shuffle(images) val_count int(len(images) * val_ratio) val_images set(images[:val_count]) for img in images: if img in val_images: sub val else: sub train shutil.copy(img, fimages/{sub}/{img.name}) # 同步拷贝对应标签txt 或 xml 二选一 txt Path(labels_all) / (img.stem .txt) if txt.exists(): shutil.copy(txt, flabels/{sub}/{txt.name})这里有个容易忽略的细节如果同一只行李箱的多张照片来自不同角度随机划分可能让同一个缺陷同时出现在训练集和验证集里导致验证指标虚高。正式做法是按行李箱编号前缀分组整个组进训练或验证。对这份 650 张的数据集如果你发现文件名里有明显的编号规律建议手动按前缀拆分否则随机划分也能接受但要清楚它的指标是有水分的。3.4 yolo 训练自己的数据集从 data.yaml 到跑起来训练前要把数据集描述文件写好。无论你用的是 yolov5 还是 yolov8data.yaml 的结构基本一致。path: ./dataset # 数据集根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 nc: 2 # 类别数和标题里的 2 类对应 names: # 类别名顺序就是 txt 里的 id 映射 0: scratch 1: dentnames的顺序必须和 3.1 的class_names完全一致否则模型训练时学到的 id 意义就错了。这一点值得反复核对我见过不止一次因为 names 顺序和标签不一致训练日志里 loss 正常下降但预测结果两类完全对调的翻车现场。环境配好后yolov5 和 yolov8 的训练命令差别不大。如果用的是 yolov5python train.py --data dataset.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100如果用的是 yolov8yolo detect train datadataset.yaml modelyolov8s.pt imgsz640 batch16 epochs100参数上给几条实操建议。--img 640是速度和精度的平衡点如果你的缺陷是细长划痕、裂纹这类小目标把 imgsz 提到 1280 往往比调任何模型结构都见效。--batch在显存允许的范围里尽量大16 是起步值。--epochs100 对 650 张足够训练到 60 轮以后关注验证集是否还在下降不下降就早点停。4. 数据与训练常见问题排查现象、原因、解决4.1 图片缩放后所有框都错位现象训练出来的模型 loss 能降但预测框整体偏移明明框在轮子上模型框在箱子中央。原因图片预处理时做过 resize但标签没跟着变。这个问题在 VOC 转 YOLO 时最容易触发因为 xml 里存的是绝对像素坐标图片从 1920 缩到 640坐标还是按 1920 算的归一化后自然全错。解决转换脚本里取图宽高时动态用 PIL 或 cv2 读图片真实尺寸不要写死。from PIL import Image img_w, img_h Image.open(img_path).size另一个常见来源是数据增强。如果你用了 albumentations、imgaug 这类增强库必须确认 transform 里带了 bbox_params否则增强输出的是新图标签还是旧坐标。用手写 resize 加增强的组合时先跑通一张图的可视化检查再批量跑。4.2 标签里混入 0 坐标和越界坐标训练 loss 出现 NaN现象训练到某个 epochloss 突然变成 nan或者验证集 mAP 一直是 0。原因txt 里存在宽或高为 0 的框或者归一化坐标超出边界导致 loss 计算异常。这通常来自标注工具误操作比如标注时只点了一个点没拉出框或者转 VOC 到 YOLO 时除以了错误的图宽。解决把 2.4 的检查脚本对整个数据集跑一遍把越界标签和空标签直接删掉。python check_labels.py bad_labels.txt # 人工确认 bad_labels.txt 里的文件确认后移动而不是删除注意处理方式不要直接 rm先移到一个 backup 目录。因为有些越界框只是一个小数点精度问题原图里框本身是对的补标比删标签成本高得多。移动备份比删除稳妥这是数据清洗里最保守但最安全的习惯。4.3 类别号与类别名错位训练出来两类缺陷互认现象验证集 mAP 不低但可视化检测结果时scratch 的框全部标成 dent反之亦然。原因一个项目里出现了两份类别定义。比如转换脚本里的 class_names 是[scratch, dent]但 data.yaml 的 names 写成了[dent, scratch]。txt 里的 0 在第一份定义里是 scratch在训练配置里却对应 dent。解决写一个一眼就能看懂的映射校验把第一个样本的 txt 和 xml 对应打印出来。# 打印第一张图的 YOLO 标签和 VOC 标签做交叉比对 txt open(labels/train/0001.txt).read().strip() xml_root ET.parse(annotations/0001.xml).getroot() print(YOLO:, txt) print(VOC :, [(o.find(name).text) for o in xml_root.findall(object)])如果 YOLO 里第一行是0 0.5 0.5 0.1 0.1VOC 里第一个 object 的 name 是 scratch那么 0 必须对应 scratch。这个检查花 10 秒能省掉一次白训 100 个 epoch 的返工。类别少的时候尤其要查因为两类互认的错觉在指标上不太明显。4.4 小目标缺陷漏检loss 降不下去现象像表面划痕、细裂纹这类小目标训练完的模型在测试图上几乎检测不到或者只在部分图上命中。原因输入分辨率不够是最大因素。640 分辨率下一个 20 像素宽的划痕只占图的 3%特征在多层下采样后基本丢光。其次是增强策略误伤比如马赛克增强把多个小目标拼接后缺陷被切掉一半相当于标签和图像内容错位。解决先做对照实验把 imgsz 提到 1280其他参数不动看这个小目标类别的 recall 有没有明显变化。多数时候这一步就有改善。如果显存带不动 1280退一步用 960 加测试时增强。另一个我在行李箱场景里验证有效的做法是对小目标类别做复制粘贴增强把标注框内的小图块随机复制到同一张图的其他位置相当于变相增加了该类的样本数。这个策略在 yolo 的增强配置里对应 copy_paste 参数但对超大图有显存压力可以只在训练的后面 30 个 epoch 打开或者单独做离线增强。4.5 验证集指标好看但产线现场翻车现象val mAP0.5 跑到 0.9拿到现场的真实照片上测试漏检率却高得没法用。原因数据分布不一致。最典型的是现场机位角度、光照和数据集不一样另一个是现场出现的缺陷类型偏向数据集的少数类或盲区。验证集是从同一批数据里划分的分布接近所以指标虚高这本质上是验证集没有代表性。解决抽 50 张现场照片手工标注后单独建一个 test-set不参与训练只做评估。用这个外部测试集来确认模型的真实精度而不是拿数据集的 val 说事。如果现场测试集掉点严重把这个测试集的照片合入训练集重新训练通常一两个来回就有明显改善。置信度阈值也可以顺手调见下一章。5. 把 650 张用出 2000 张的效果验证、增强与阈值收尾5.1 用 yolo 测试图片做单图验证先看输出再看指标训练结束后别急着看 mAP先拿几张没见过的现场图跑一遍预测肉眼确认框的位置和类别对不对。命令很简单yolov8 用yolo predict modelruns/detect/train/weights/best.pt sourcetest_img.jpg conf0.25yolov5 用python detect.py --weights runs/train/exp/weights/best.pt --source test_img.jpg --conf-thres 0.25单图测试的意义在于发现指标的盲区。mAP 是一个加权汇总值两类缺陷中如果一类特别强、一类特别弱汇总值可能还行但弱的那类就是现场翻车的根源。单图逐个类别看才能找出短板在哪类、置信度卡在哪个区间。5.2 低成本增强组合比加模型更实在650 张要扩数量优先做亮度、对比度和轻微模糊这三类增强它们最贴近产线光照波动。实现上直接改数据增强配置里的 hsv_h、hsv_s、hsv_v 参数yolov8 的默认配置里已经带了。值得注意的是一开始不要动翻转增强如果行李箱上的缺陷有明确方位含义比如拉链区域和提手区域的缺陷水平翻转就把空间关系破坏了模型反而学乱。如果现场能拍到视频抽帧是比增强更有效的补数方式。从一段 5 分钟的产线视频里抽 100 帧用训练好的模型做预标注再人工修正得到的难样本质量远高于把现有图翻来翻去。用 labelimg 补标 50 张困难样本比加 500 张增强图对精度提升更明显。5.3 置信度阈值调整是最后一道关卡训练完成后把测试集所有预测结果的置信度分布导出来看一眼。漏检和误检通常分布在不同的置信度区间漏检的框置信度普遍在 0.2 到 0.5误检的假阳性框可能也在 0.4 左右。如果两者重叠严重调阈值收益有限需要回到数据增强或补标如果分布是分开的把 conf-thres 调到两者的边界值即可这一步能明显改善现场体验。我习惯在训练结束后不着急看 mAP而是把漏检和误检的图挑出来看它们卡在哪个置信度区间、类别混淆发生在哪里。这个习惯帮我省下过很多次无意义的换模型重训数据处理到位阈值的最后调整才有意义。希望帮到你。本文还有配套的精品资源点击获取
返回列表