
简介面向行李箱生产质检与工业视觉检测实战场景这份数据集提供650张已标注的真实行李箱图像支持damaged破损与good_condition完好两类目标识别适合用于YOLO系列模型训练、VOC格式迁移学习及缺陷检测算法效果验证。压缩包共1952个文件整体25.11MB其中jpg原图、xml标注与txt标签各650份并额外包含2个txt说明文件三种文件一一对应目录按JPEGImages、Annotations、labels分设可直接接入主流检测框架。标注统一采用矩形框完整规范共936个目标框damaged类199个、good_condition类737个图像清晰且未做增强便于直接训练和验证数据划分。目前已有126人学习下载。该数据集不附带训练权重或精度保证但标注规范、格式齐全可迅速用于构建行李箱外观缺陷检测系统的训练、验证与评估流程也适合作为毕业设计或工业视觉项目的练手数据。此外两类样本数量差异明显可用于练习类别不平衡条件下的检测调优。1. 行李箱缺陷检测数据集650张2类到底够不够用做行李箱出厂质检的工程师拿到这个数据集标题第一反应通常是“650张能不能跑”。真实情况是这个体量做工业级召回率不够但足够把目标检测这条链路完整跑通——VOC标注转成YOLO训练格式、训练收敛、验证、错误分析每一步都能在这个包上复现。它适合正在做yolo入门学习的人练手也适合箱包厂做缺陷检测可行性验证先证明缺陷可检再攒数据上产线。我见过太多人在第一步就翻车数据集解压出来直接丢给yolo训练mAP稀烂然后回头骂数据集垃圾。其实650张2类小数据集有自己一套用法从格式转换到数据增强参数都要跟着体量变。下面按实际操作顺序把这个流程讲清楚。2. 拆开这个zipVOC与YOLO双格式的目录结构和标注细节2.1 数据集包的标准目录布局与第一轮核查标题里“YOLOVOC格式”意味着同一批标注给了两种载体解压后通常按这种结构组织luggage_defect/ ├── JPEGImages/ # 原始图像jpg ├── Annotations/ # VOC格式标注xml ├── labels/ # YOLO格式标注txt ├── classes.txt # 类别清单每行一个类名 ├── train.txt # 训练集图像路径列表部分包会额外给 ├── val.txt # 验证集路径列表 └── data.yaml # yolo直接可读的配置部分包会带拿到包先别急着改目录写一行命令核对JPEGImages和Annotations的文件数是否一致。两类缺陷的命名习惯通常是英文标签比如scratch/dent这类也有的数据集直接叫defect_1/defect_2。先打开classes.txt或者随便翻一个xml里的 以包内实际标签为准。后面所有脚本里的类别列表都必须跟这里的类名严格一致大小写都不能错这是整套流程里最容易被埋的雷。目录结构看似简单但yolo对路径特别敏感分错一层后面训练就会静默跳过数据。2.2 同一张图两种标注VOC XML与YOLO txt的格式差异VOC格式是标注工具和公开数据集的通用交换格式信息全人也能看懂。随便抽一个Annotations下的xml结构是这样的annotation folderJPEGImages/folder filenameimg_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namescratch/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin120/xmin ymin180/ymin xmax340/xmax ymax260/ymax /bndbox /object /annotationxml里存的是像素坐标xmin/ymin/xmax/ymax理解成本低但yolo训练不吃这个格式它读的是归一化坐标的txt。同一张图在labels下是这么一行0 0.1198 0.2037 0.1146 0.0370五个字段按顺序分别是类别id、归一化中心x、归一化中心y、归一化宽、归一化高。类别id从0开始按classes.txt顺序排不是xml里的字符串。两组格式的换算关系就四个公式目标字段换算公式中心x(xmin xmax) / 2 / width中心y(ymin ymax) / 2 / height宽w(xmax - xmin) / width高h(ymax - ymin) / height这张表是转换脚本的核心下一章的脚本就是照这个公式写的。现实中你基本不会手动算但理解这个映射能让你一眼看出txt里的坐标是否合理行李箱缺陷的框占图比例不大w/h一般远小于1如果txt里出现大于1的值多半是转换时除以错了分母。另外行李箱的两类缺陷在框形态上有明显差别——划痕是细长条框的w/h很大凹陷是区域型缺陷框接近正方形。拿到txt后可以按这个经验粗筛一遍框形明显不合理的标注值得回原图确认。2.3 动手前的质量检查三个脚本动作650张是小数据每一张的标注都值钱。我习惯在转换前做三步静态核查省下来这一步后面训练会浪费更多时间。第一步统计每类缺陷的框数确认类别分布是否平衡。代码import os import xml.etree.ElementTree as ET counts {} for xml_file in os.listdir(Annotations): root ET.parse(os.path.join(Annotations, xml_file)).getroot() for obj in root.findall(object): name obj.find(name).text counts[name] counts.get(name, 0) 1 print(counts)这段只统计 的文本不做坐标校验用途是看类别分布。如果某一类框数只有几十个后面训练就要考虑类别不平衡靠权重或增强来拉而不是让模型硬学。第二步检查图像尺寸和标注是否越界。标注人员画框时容易把框拖到图像外行李箱的包边、阴影区域尤其容易出现这种情况。下面代码在转换前把越界xml揪出来for xml_file in os.listdir(Annotations): root ET.parse(os.path.join(Annotations, xml_file)).getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) for obj in root.findall(object): box obj.find(bndbox) xmin int(box.find(xmin).text) xmax int(box.find(xmax).text) if xmin 0 or xmax w: print(xml_file, x方向越界, xmin, xmax) # y方向同理如果输出为空说明框都在图内有记录就先决定是改xml还是转换时做裁剪不建议直接丢掉越界框那是浪费标注。第三步检查同名的jpg和xml是否齐全。解压后有的包会缺xml有的会多出没标注的图直接影响后面的文件名匹配ls JPEGImages | sed s/.jpg// | sort images.txt ls Annotations | sed s/.xml// | sort anns.txt diff images.txt anns.txtdiff输出为空就是文件齐全不为空就把差异文件列出来缺失的那几张要么补标注要么从训练集剔除。这三步做完数据集才算真正能进训练流程。3. 把VOC转成YOLO训练格式转换脚本与四个边界参数3.1 为什么这个转换绕不开标题同时给了VOC和YOLO两种格式看起来像“都准备好了”实际使用中你大概率还是要自己再转一次。一个原因是yolo版本不同对数据目录的要求会变另一个原因是650张这个体量经常是从别的项目里拷贝来的标注格式混杂。常见的做法是写一个不依赖图形界面的批量转换脚本一次跑完、结果可控。labelImg虽然有“另存为YOLO格式”的功能但批量处理和坐标越界控制还是脚本更可靠。我自己的习惯是宁可写几十行python不去手工点几百个xml——转换这件事重复做一次就是浪费半小时。3.2 转换脚本读XML、归一化、写txtimport os import xml.etree.ElementTree as ET def voc_to_yolo(xml_dirAnnotations, out_dirlabels, class_namesNone): if class_names is None: raise ValueError(必须传class_names) class_to_id {name: i for i, name in enumerate(class_names)} os.makedirs(out_dir, exist_okTrue) for xml_file in sorted(os.listdir(xml_dir)): if not xml_file.endswith(.xml): continue stem os.path.splitext(xml_file)[0] tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() size root.find(size) if size is None: print(f{xml_file} 缺少size节点跳过) continue width float(size.find(width).text) height float(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_to_id: print(f{xml_file} 含未注册类别 {name}忽略该目标) continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height # 越界保护裁剪到[0,1]同时打印警告供人工回查 if not (0.0 x_center 1.0 and 0.0 box_w 1.0): print(f{xml_file} {name} 坐标异常: fx_center{x_center:.4f}, w{box_w:.4f}) x_center min(max(x_center, 0.0), 1.0) box_w min(max(box_w, 0.0), 1.0) lines.append( f{class_to_id[name]} {x_center:.6f} {y_center:.6f} f{box_w:.6f} {box_h:.6f} ) if lines: with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines) \n) else: print(f{xml_file} 没有有效目标未生成txt建议剔除该图) voc_to_yolo(class_names[scratch, dent])逻辑说明第一层循环遍历Annotations下每个xml取 里的宽和高再遍历参数说明class_names的顺序就是txt里类别id的编码顺序必须和classes.txt一致。我这里的示例是[scratch, dent]两个类实操时替换成你包内实际的类名。这个列表后面还要写进data.yaml脚本、classes.txt、data.yaml三处要完全对齐。width和height取自xml的 节点不要自己读图因为有些图像的EXIF旋转会影响读图尺寸用xml里的值更稳定。3.3 转换脚本的四个边界参数与对应的坑脚本里最值得调的是下面四个点都是这类小数据集上的常见翻车来源边界点坑的现象建议处理越界裁剪txt坐标出现大于1的值裁剪到0-1并打印警告不裁剪yolo会报错或静默丢框类别映射两类缺陷混淆id错位以classes.txt为唯一事实脚本传参覆盖默认列表size缺失部分xml缺 节点跳过该文件并提示剔除对应图空目标文件标注全部被类别过滤掉不写txt在划分train/val之前剔除该图越界裁剪是最容易被忽略的。yolo训练时如果遇到txt坐标落在[0,1]之外有的版本直接报错有的静默过滤导致某张图等于没标注而你不一定能察觉。所以我的原则是转换时做一次clip同时把发生clip的文件名记下来训练前抽查几个clip过的图人工确认是标错还是真的越界。这比事后在训练日志里猜原因快得多。3.4 配套工作划分train/val并生成data.yaml转换完txt别忘了划分数据集。650张做工业检测常见做法是8:2划分约520张train、130张val。划分时按“文件名加随机种子”取固定比例import os import random random.seed(42) images sorted(os.listdir(JPEGImages)) random.shuffle(images) split int(len(images) * 0.8) train_list, val_list images[:split], images[split:] with open(train.txt, w) as f: f.write(\n.join(fJPEGImages/{name} for name in train_list)) # val.txt 同理random.seed(42)必须固定否则每次运行划分结果不同训练结果不可比。最后写data.yaml包括路径、类别数、类别名三类信息下一章训练直接用path: ./luggage_defect train: images/train val: images/val nc: 2 names: [scratch, dent]到这里格式转换、目录划分、训练配置三件事全部准备完成。进入训练前把labels、train.txt、val.txt和JPEGImages的路径都确认一遍路径错一个字符yolo会在训练时静默跳过数据表现就是loss一直不降。4. 用YOLOv8在这650张图上训练模型选型、损失函数与增强参数4.1 650张2类该选哪个模型这个体量不需要yolov8m或yolov8l参数量大在小数据集上只会加速过拟合。我一般推荐yolov8n起步理由是nano是参数量最小的一档在650张图上用coco预训练权重微调收敛速度明显快于随机初始化。如果nano的mAP能达到预期完全没有必要上更大模型。有人会问能不能用yolo v10或者yolo 26这类新结构说实话在这个数据量下检测头越复杂越难训换个更重的head不如先把数据流程跑通。模型和预训练权重由yolo命令自动拉取首次运行会下载yolov8n.pt到缓存目录。650张图训100轮普通显卡上大概十几分钟到几十分钟几乎不需要人工干预。4.2 yolov8训练自己的数据集先把目录结构与data.yaml固定上一章生成的data.yaml直接复用。训练前把最终目录固定成yolo习惯的布局方便后面换设备或换同事机器也能跑luggage_defect/ ├── images/ │ ├── train/ # 520张jpg │ └── val/ # 130张jpg ├── labels/ │ ├── train/ # 与train图像同名的txt │ └── val/ # 与val图像同名的txt └── data.yaml注意labels目录也必须按train/val拆开yolo默认找“与图片同级labels文件夹下同名txt”目录没配对时训练每张图都会跳过loss不动。这一步的操作是把train.txt里列出的jpg拷到images/train其余拷到images/val再把labels下对应的txt分到labels/train和labels/val。最后逐目录比对一下images/train每张jpg都必须在labels/train有同名txt多一个少一个都不行。这套目录结构不复杂但它是训练能否跑起来的默认前提。4.3 训练命令与yolo损失函数的三条曲线执行训练的命令如下yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ projectruns \ nameluggage_defect \ seed42参数说明modelyolov8n.pt加载coco预训练权重开始微调比从零训练收敛快很多epochs100对650张小数据来说已经够再长容易过拟合imgsz640兼顾检测速度和缺陷框在图像里的像素占比batch16按显存调显存不够就降batch不要改imgszimgsz对最终精度影响更大patience15连续15轮val指标不涨就早停防止白跑seed42固定随机种子保证两次训练结果可比。训练时不要只盯total loss。yolo损失函数由三部分构成box_loss负责定位CIoU变体、cls_loss负责分类BCE、dfl_loss负责边界框分布预测。在650张小数据集上更值得看的是cls_loss和val集的mAP50曲线如果cls_loss一直在降而mAP50不动说明模型在背训练集而不是学泛化特征如果三条loss都降但某个类别的ap一直是0基本是标注问题回到第2章的静态检查去排查。训练结束后用yolo detect val把val集指标打出来看per-class的ap而不是只盯总mAP。4.4 数据增强参数小数据集的关键取舍yolo默认的增强策略是按大数据集设计的mosaic随机拼接四张图、水平翻转、HSV扰动全开。对650张行李箱图mosaic频繁把四张图的缺陷拼到一起制造大量“合成样本”模型很容易学偏。这里需要做两个改动在训练命令里追加参数yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ mosaic0.5 \ hsv_h0.01 \ hsv_s0.5 \ hsv_v0.3 \ flipud0.0 \ scale0.2参数说明mosaic降到0.5而不是直接关掉保留少量拼接让模型见识组合场景flipud0.0是关键行李箱的纹理方向有物理意义上下翻转会把竖向划痕翻成横向这类硬增强在工业缺陷场景里经常帮倒忙hsv_h/s/v保留轻微扰动行李箱的皮纹、金属件、包装膜颜色差异大轻微HSV增强能让模型对光照更鲁棒scale0.2限制随机缩放的幅度避免小缺陷被缩得看不清。这一整节的本质是小数据集没有一套默认参数通吃的灵药。yolo的默认增强是给coco这种万级数据量设计的用在小数据集必须手动调。每调一个参数就跑一轮训练对比mAP50把有提升的参数组合保留下来。这种方式虽然慢但结果可解释比拍脑袋换模型靠谱。5. 避坑行李箱缺陷检测数据集最常见的4个翻车点下面四条是我在这个体量的小数据集上反复遇到过的问题每条按现象、原因、解决三部分写。前两条出在数据准备后两条出在模型训练都跟650张这个规模强相关换个大规模数据集这些坑可能不明显但小数据下每一个都会放大。5.1 现象训练loss正常下降但mAP一直为0原因这种“loss降、ap零”的组合九成是数据没配对。最常见的是labels/train的txt里第一列类别id超过了data.yaml的nc范围其次是txt与jpg文件名不完全同名比如jpg叫img_001.jpgtxt叫img_001多了个后缀或空行yolo扫描时找不到对应txt整张图被当成无目标背景图还有一类是xml里类名是“Scratch”而classes.txt里是“scratch”转换脚本因大小写不一致把目标全过滤掉了。解决先跑一次yolo detect val打印每类的ap如果只有某一类为0去看这类txt里是否还有对应坐标。通用做法是写个快速校验脚本对每张jpg断言存在同名txt并检查txt里最大的类别id是否小于nc。把这两项跑通再回头看训练配置。不要上来就调损失函数权重先在数据配对层面排除这是这类问题最高频的根源。5.2 现象验证集mAP很高换一批实拍图全漏检原因650张基本来自同一批产线、同一光照、同一个拍摄角度背景和行李箱摆放位置高度相似。模型学到的其实是“这个产线上的行李箱长这样”而不是“行李箱上的缺陷长这样”。如果验证集和训练集同批次mAP会被高估这是小数据集最容易出现“假阳性幸福”的地方。解决换真实场景图测试时先把nms_conf从默认值提高到0.3观察缺陷强度排序。如果置信度整体偏低说明模型特征来自背景而不是缺陷。另一个常用做法是按拍摄时间或批次划分数据集而不是随机划分让验证集与训练集来自不同批次。这比任何调参都更能暴露真实水平也直接决定这个方向值不值得继续投入数据采集。5.3 现象训练到一半loss变成NaN原因最常见的来源是xml里某个bndbox坐标等于0转换后box_w或box_h为0yolo计算损失时除零产生NaN。其次是学习率设置太高backbone还没收敛时梯度爆炸这在batch很小的情况下尤其容易出现。解决先查txt里是否存在接近0的宽高转换脚本里可以加一个判断if box_w 0 or box_h 0: continue然后检查学习率。yolov8默认lr00.01如果用的batch偏小建议降到0.001再试。遇到NaN先回退到上一个epoch保存的权重把lr降一半重train不要从头再来——重头跑一次100轮的成本远高于从checkpoint恢复。5.4 现象torch报错“image not found”或读图尺寸全为0原因解压工具把文件名里的空格或中文转义弄坏了jpeg文件本身没问题但yolo按路径读不到。行李箱缺陷数据集的原始图像经常按“批次-序号”命名文件名带空格或特殊字符的情况不少在Linux下路径解析直接失败。解决解压后统一重命名为纯数字序号比如img_0001.jpg这种目录名也做一遍检查。这一步属于数据预处理的一次性成本换任何训练框架都受益避免后面每次跑训练都卡在文件路径上。这四个坑里其实有一半可以在训练前用脚本挡掉我现在的习惯是转换完先跑一遍校验再进训练省下的时间远大于写校验脚本的时间。6. 把验证做扎实交叉验证与难样本分析6.1 用K折交叉验证拿稳定mAP650张一次随机划分结果波动可能很大。我一般的做法是先在固定seed的划分下调参最后用5折交叉验证出一个置信度更高的mAP。做法是写一个python脚本把650张文件名分成5组每次用4组train、1组val训练取5轮的val mAP均值。K折的代价是训练时间乘5但能看出模型对数据划分的敏感程度——如果5折结果方差很大说明模型没有真实学到缺陷特征问题在数据质量而不是参数。6.2 抽检预测框与缺陷区域的重合度mAP只告诉你整体分数不会告诉你漏检的是哪种缺陷。把val集预测结果保存成图片按置信度升序看最低的20张大概率能定位问题类型。用yolo一行命令就能出结果yolo predict \ modelruns/detect/luggage_defect/weights/best.pt \ sourceimages/val \ save_txtTrue \ save_confTrue \ saveTruesaveTrue后每张预测图带框保存到runs/detect/predict人工快速浏览特别关注“看起来像缺陷但没框出来”的区域。我自己的习惯是每一轮调参后保留上一轮结果对比同一张图新旧版本的框差异比盯loss曲线直观得多。这个数据集的价值不在于650张的数量而在于它是一个完整的“从标注到验证”的训练闭环。如果你刚接触yolo入门训练把这一套流程跑通后换个更大的工业缺陷数据集只是换路径和类名的事。踩坑是正常的我当年在这类小数据集上翻过车的地方比上面写的还要多最后养成的习惯就是数据准备阶段多花一小时做静态检查训练阶段就能少耗一晚上。希望帮到你。本文还有配套的精品资源点击获取