
简介数据集围绕行李箱外观缺陷检测任务构建适合目标检测模型训练、算法对比及工业质检场景验证。压缩包内图片清晰标注信息完整共包含2个类别damaged破损与good_condition良好箱面合计936个矩形标注框其中damaged框199个、good_condition框737个可直接用于训练缺陷定位与分类模型。文件共1953个主要由650张jpg图像、650个xml标注文件和YOLO格式txt文件构成可兼容YOLO与VOC训练流程资源整体打包为zip仅25.16MB下载与解压均很轻便。数据集未做图像增强分辨率清晰适合希望基于真实原始样本开展实验的初学者或工程人员。当前已有48人学习/下载可作为轻量级公开数据基准支撑行李箱缺陷检测相关课程设计、论文实验或原型验证。1. 行李箱缺陷检测数据集的真实价值小模型也可以快速起步拿到一个650张、2类的YOLOVOC格式行李箱缺陷检测数据集时别嫌它量小。缺陷检测在质检场景里最现实的问题是“缺陷样本本来就不多”真实工厂要攒几千张划痕、凹陷照片往往需要几条产线连续跑几周而一个650张的小数据集反而适合用来把漏检逻辑、训练链路和部署验证先跑通。这个数据集适合两类人一是刚学目标检测想拿自己的数据而不是COCO练手的开发者照着YOLOv8的标准流程能完整走一遍数据校验、预训练权重加载、训练和测试二是做行李箱/箱包质检方案评估的工程师用小样本验证打光、拍摄角度和模型选型再决定要不要投入产线。下面按我实际做过的质检项目顺序把这个数据集的每个使用环节和容易翻车的点都过一遍。2. 拆包后的第一件事YOLO标签和VOC标注到底差在哪数据包名字里同时写着YOLO和VOC这其实是最容易让人忽略的坑。YOLO格式是每个txt文件里存归一化坐标VOC格式是每个xml文件里存像素坐标两者虽然描述同一个目标但坐标基准完全不同。如果你不做一次字段级交叉校验就直接训练轻则某些图片的标签被静默跳过重则训练时loss忽高忽低最后模型在验证集上看起来不错一到真机就漏检。2.1 同一张行李箱照片在两种标注格式里的样子先看VOC的xml它把坐标存成了像素值annotation filenameIMG_0012.jpg/filename size width1920/width height1280/height /size object namescratch/name bndbox xmin612/xmin ymin780/ymin xmax1090/xmax ymax1180/ymax /bndbox /object /annotationxmin、ymin是左上角xmax、ymax是右下角单位是像素。这张图里scratch这个类别对应的就是缺缺陷框如果同一张图还有凹陷就会有第二个object节点。对应同一个目标的YOLO标签文件一行文本0 0.4432 0.7656 0.2489 0.3125第一个数字是类别ID0对应scratch1对应dent后四个数分别是归一化后的中心点x、中心点y、宽、高。计算方式很简单x_center (xmin xmax) / 2 / image_widthy_center (ymin ymax) / 2 / image_heightwidth (xmax - xmin) / image_widthheight (ymax - ymin) / image_heightYOLO系列从v5到v8标签文件格式基本保持同一套规则所以这个txt对YOLOv8、YOLOv5、YOLOX等主流检测框架都通用。需要特别注意的是如果xml里的目标宽度为0或者坐标越界转换后就会生成异常标签这类问题在650张的小数据集里可能只有几张但足以让训练不稳定。2.2 用Python把VOC转成YOLO格式的可靠脚本很多标注平台导出时只给VOC训练时又需要YOLO格式。即使这个zip里两种都给了我也会自己写脚本转一次因为同一批数据在两个文件夹之间不同步的情况太常见。下面这个脚本可以直接复用import xml.etree.ElementTree as ET import os # 类别顺序必须和训练时一致先确定好别中途乱改 CLASS_NAMES [scratch, dent] def convert_voc_to_yolo(xml_file, out_txt, img_w, img_h): tree ET.parse(xml_file) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in CLASS_NAMES: # 遇到没见过的类别宁可打印出来不要静默跳过 print(f[skip] {os.path.basename(xml_file)} - unknown class: {name}) continue cls_id CLASS_NAMES.index(name) 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) # 防御性检查非法框直接忽略 if xmax xmin or ymax ymin: print(f[warning] invalid bbox in {xml_file}: {xmin},{ymin},{xmax},{ymax}) continue if xmin 0 or ymin 0 or xmax img_w or ymax img_h: print(f[warning] bbox out of range in {xml_file}: {xmin},{ymin},{xmax},{ymax}) continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: with open(out_txt, w) as fp: fp.write(\n.join(lines))这段脚本有几个参数值得注意CLASS_NAMES的顺序直接决定类别ID如果你把dent写在scratch前面整个模型的预测结果就全反了img_w和img_h必须和真实图片分辨率一致很多标注平台导出xml时不会带上图片尺寸你读取时要二次核对。防御性检查不是多余的我遇到过某个数据集里一张图片只有1KB但xml里标注了1000多个长宽坐标那种数据进训练就是血泪。2.3 可视化矩形框把标注错误扼杀在训练前光看txt数值很难发现问题。我习惯把YOLO标签直接画到图上一张一张快速翻比任何统计脚本都直观。下面这个可视化脚本可以少写很多重复代码import cv2 import os CLASS_NAMES [scratch, dent] def draw_yolo_boxes(image_path, label_path): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: data line.strip().split() if len(data) ! 5: continue cls_id int(data[0]) xc, yc, bw, bh map(float, data[1:]) x1 int((xc - bw / 2) * w) x2 int((xc bw / 2) * w) y1 int((yc - bh / 2) * h) y2 int((yc bh / 2) * h) color (0, 0, 255) if cls_id 0 else (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, CLASS_NAMES[cls_id], (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imshow(check, img) cv2.waitKey(0) # 使用示例 draw_yolo_boxes(datasets/luggage650/images/train/IMG_0012.jpg, datasets/luggage650/labels/train/IMG_0012.txt)重点检查三种情况框是否明显偏离行李箱轮廓一个划痕是否被拆成了两三个互相重叠的框标签里是否存在两个类别ID混乱的目标。这种视觉检查花不了二十分钟但它能避免你浪费几小时在错误的标注上训练。3. 把650张行李箱图变成YOLOv8认得的训练集划分与增强拿到一个650张的小数据集最忌讳的就是把所有图片直接丢进去训练然后跑完看个mAP就说效果好。小数据集的训练集、验证集和测试集划分比大数据的划分更影响最终指标因为每一张图都可能改变几个百分点的分数。3.1 固定随机种子做train/val/test划分并同步复制标签我一般按8:1:1的比例划分并且固定随机种子。这样你做对比实验时每次改变的不是数据划分而只是模型或参数。import os import random import shutil random.seed(42) src_images datasets/luggage650/images src_labels datasets/luggage650/labels out_root datasets/luggage650_split all_images [f for f in os.listdir(src_images) if f.endswith((.jpg, .png))] all_images.sort() random.shuffle(all_images) n_total len(all_images) n_train int(n_total * 0.8) n_val int(n_total * 0.1) # 剩下的自然就是 test splits { train: all_images[:n_train], val: all_images[n_train:n_train n_val], test: all_images[n_train n_val:], } for split, img_list in splits.items(): img_out os.path.join(out_root, images, split) label_out os.path.join(out_root, labels, split) os.makedirs(img_out, exist_okTrue) os.makedirs(label_out, exist_okTrue) for img_name in img_list: shutil.copy(os.path.join(src_images, img_name), img_out) base_name os.path.splitext(img_name)[0] label_name base_name .txt label_path os.path.join(src_labels, label_name) if os.path.exists(label_path): shutil.copy(label_path, label_out) else: print(f[missing] {label_name})这段脚本有一个容易被忽略的点图片文件名和标签文件名必须严格一一对应。如果IMG_0012.jpg没有IMG_0012.txt训练时会报空标签警告一般在YOLOv8里该图会跳过或者当作背景图处理。小数据集只要缺三五个标签训练就会出问题所以最好把[missing]打印出来人工处理。3.2 增强参数不要全开小样本更看重适度650张小数据集的增强思路是让模型看到更多变化但不要破坏缺陷本身的形状。像行李箱这类刚性物体过度旋转增强会让划痕变形失去物理意义YOLOv8的fliplr可以开但degrees不建议超过15。我常用的增强参数值如下参数推荐范围说明hsv_h0.01-0.02色相变化行李箱颜色多样可以稍微放宽hsv_s0.5-0.8饱和度变化帮助适应不同塑胶材质反光hsv_v0.3-0.5亮度变化模拟不同产线打光degrees5-15旋转角度太大缺陷形态不自然translate0.1-0.2平移当前小数据集常见scale0.3-0.5缩放模拟不同拍摄距离fliplr0.5水平翻转行李箱对称可以开mosaic1.0或0.5小数据集开mosaic可以增加样本但缺陷微小目标会被缩小建议先试1.0再看效果注意这些参数是通过YOLOv8命令行传入的例如hsv_h0.015 hsv_s0.7 hsv_v0.4 degrees10 fliplr0.5。它们对最终效果的影响很大我建议把mosaic和scale先单独拉开实验因为行李箱表面上有些纹理本身就很像划痕增强过头会让模型把正常纹理也当成缺陷。3.3 写一个不会出错的dataset.yamlYOLOv8的配置文件和YOLOv5兼容直接写一个简单的yaml# datasets/luggage650/data.yaml path: /home/user/datasets/luggage650_split # 这个根目录建议写绝对路径 train: images/train val: images/val test: images/test nc: 2 names: 0: scratch 1: dentpath字段是根目录train和val是相对path的路径。我遇到过最典型的问题是把path写成相对路径然后在别的目录下跑yolo train系统拼接出来的路径不对训练报错。另一个坑是names的ID顺序必须和标签文件里的cls_id一致。标签文件里0代表scratchyaml里0就必须是scratch如果你把dent写在0位置训练不会报错但预测结果会互相换掉这种错误非常隐蔽。4. 用YOLOv8训练行李箱缺陷检测模型预训练权重、命令行参数与日志判断数据准备完成之后就到了整个流程里最容易产生成就感也最容易自我麻痹的阶段训练。YOLOv8的封装程度很高一条命令就能跑起来但命令里的参数怎么设定日志怎么判断才是真正决定这个模型能不能用的地方。4.1 环境安装与预训练权重选择先装ultralytics包这个动作是所有训练的前提pip install ultralyticsYOLOv8在首次运行时会自动下载预训练模型权重比如yolov8n.pt这就是常说的yolo预训练模型下载。如果你网络受限也可以手动把权重文件放到当前目录下。对于650张、2类的小数据集我强烈建议用yolov8n或者yolov8s而不是一上来就用yolov8x。原因是缺陷特征相对简单大模型在小数据上很容易过拟合训练时间还长收益微乎其微。4.2 训练命令epochs、batch、imgsz怎么定下面是我在这个数据集上会用到的命令cd /home/user yolo detect train \ modelyolov8n.pt \ datadatasets/luggage650_split/data.yaml \ epochs200 \ batch16 \ imgsz640 \ patience30 \ hsv_h0.015 hsv_s0.7 hsv_v0.4 \ degrees10 \ fliplr0.5 \ projectresults/luggage_experiment \ namerun1参数解释如下modelyolov8n.pt使用预训练权重初始化网络。650张小数据集的训练一定要加载预训练权重否则收敛很慢最终精度也会差不少。epochs200小数据集跑200轮足够配合patience做早停。batch16在8GB显存上这个值比较稳如果显存不够降到8。imgsz640行李箱是整体目标缺陷较细640分辨率是通用选择。你的图片如果是1920的大图建议先离线resize到1280或640不要直接用原尺寸训练否则显存和速度都会失控。patience3030轮内验证集指标不升就自动停止小数据集的训练一般到不了200轮可能在80-120轮就停了。注意project和name这两个参数如果你每次跑实验都换一个name结果会单独存文件方便后续做对比。如果不指定默认会写到当前目录的runs/detect/train里。4.3 判断训练收敛yolo损失函数、mAP50和混淆矩阵训练过程中终端会持续输出训练损失和验证指标。你要重点看三个损失box_loss边界框回归损失、cls_loss分类损失、dfl_loss分布焦点损失。当这三个损失在第30轮后还有明显波动说明学习率太大如果验证集指标五轮都不变也先别急着打断因为小数据集经常出现平台期然后突然涨一点。训练结束后验证集上会给出mAP50和mAP50-95。小数据集上mAP50达到80以上比较正常但更关键的是看混淆矩阵。用下面的代码读取from ultralytics import YOLO model YOLO(results/luggage_experiment/run1/weights/best.pt) metrics model.val(datadatasets/luggage650_split/data.yaml) # 打印混淆矩阵 print(metrics.confusion_matrix.matrix)混淆矩阵里最常见的坑是scratch和dent两个类别互相混淆或者大量真实目标被检测成背景。读到混淆矩阵后不要急着改网络结构先回去看对应图片的标注是不是有误。我在实际项目中曾经因为一个类别在标注时框得过大把行李箱滚轮也包进去了导致模型把滚轮误检成划痕。5. 行李箱缺陷检测中的避坑指南这5个问题实战里几乎一定会碰到训练不是跑通了就万事大吉。下面这几个问题我几乎每次用类似小数据集都会碰到每一个都是“现象→原因→解决”的固定套路。5.1 类别名对不上转换脚本静默丢标签现象训练时的cls_loss下降很快但验证集mAP50一直为零。翻训练日志看到每张图有标注数量极少有的图一个目标都没有。原因标注平台导出xml时类别名可能是scratch Scratch带上空格或大小写差异你脚本里的CLASS_NAMES没有匹配上转换时全部被跳过更隐蔽的是一个txt文件里只有4、5行标签正常最后一行类别ID超出ncYOLOv8读取时直接忽略整张图。解决转换脚本里遇到未知类别不要跳过要打印完整类别名和出现次数。训练前用一个小脚本扫描所有标签把类别ID超出0和1的文件挑出来import os label_dir datasets/luggage650_split/labels/train for f in os.listdir(label_dir): path os.path.join(label_dir, f) with open(path, r) as fp: for line in fp: cls_id line.split()[0] if int(cls_id) 1: print(f{f}: class_id {cls_id} out of range)这一步能省下后面排查的绝大部分时间。5.2 小样本过拟合训练loss下降验证loss回升现象训练到第40轮训练损失持续下降但验证损失在30轮后开始反弹precision和recall在验证集上震荡最终模型对没见过的图表现很差。原因650张数据太少了模型在后期记住了训练集中缺陷的具体纹理和背景颜色而不是学习“行李箱上异常纹路”这一抽象概念。解决第一个手段是加patience早停让模型停在验证集最好的位置第二个手段是减小模型尺寸从yolov8s换成yolov8n第三个手段是适度加大增强特别是scale和hsv_v让模型看到更多亮度变化。如果以上都不明显就需要人工筛选最像缺陷的难例补充标注到训练集里。5.3 长条划痕被拆成两段NMS把结果过滤掉现象置信度阈值调到0.1才能看到完整划痕默认0.25时只检测到划痕的一小段或者同一道划痕出现两个重合框。原因行李箱划痕长宽比常常大于5:1模型预测出的框会有多个候选重叠默认NMS的iou0.45会把两段框合并或者删除其中一个。另外长条目标的中心点定位本身就容易漂移。解决训练和推理时把iou阈值调高到0.7保留更多重叠候选框推理时降低conf到0.15用“宁可错检也不漏检”的策略然后在后处理里把高度重叠的长条框做加权合并。这个调整对长划痕特别有效。5.4 行李箱拉链、滚轮边缘被当成缺陷现象正常箱体的栅格纹理、拉链边缘在模型预测框里被标成scratch而且置信度还不低。原因缺陷分类本质上是在找异常纹理但正常纹理中也含有类似划痕的强边缘尤其拉链和滚轮在打光下会出现高反光细线。解决这类问题看混淆矩阵里背景列的数字就能判断。处理办法是收集几十张没有任何缺陷的“负样本”图片加入训练集并给它们一个空标签文件。训练时模型会碰到大量背景自然会把单纯的边缘特征权重降低。另外一个办法是后处理时按标注框位置过滤——行李箱滚轮通常在图像角落拉链一般在箱体中线附近可以根据先验规则去掉一部分误检。5.5 换一个行李箱型号召回率断崖式下降现象当前测试集上mAP50有0.85换到另一批不同颜色、不同纹理的行李箱实拍图上召回率从80%掉到30%却没有任何训练报错。原因数据集本身只有650张大概率只覆盖了少数几种箱体材质。棕色全PC箱和银色铝框箱在纹理细节上差异巨大模型从650张里学到的特征不够泛化。解决这是小数据集的本质限制只能靠数据补齐。常见做法是在产线打光固定的前提下有意识地加入不同材质、不同使用程度的样本做一次“分批次微调”——把新采集的真机照片做成一个小的验证集然后跑30个epoch的增量训练。关键是不要手动复制旧标签而是先让旧模型自动标注再人工修正一批不然微调会加剧过拟合。6. 验证和打包流程拿训练好的模型去碰一碰真机照片模型训练完成后先别急着导出部署一个值得养成的习惯是做一次真机照片验证。用训练和验证都见过的数据评估模型是自我感动拍几张没有被清洗过的现场照片立刻能看到模型的真实水平。from ultralytics import YOLO model YOLO(results/luggage_experiment/run1/weights/best.pt) results model.predict( sourcelive_photos/20240315_phone_capture.jpg, conf0.2, iou0.7, save_txtTrue, save_confTrue, ) print(results[0].boxes)conf0.2是故意调低一点先看模型能检出哪些目标如果0.2时误检太多再往高调到0.35。不要直接按默认0.25定结论因为真机照片的曝光和训练集差异很大一次阈值判断不够。验证没问题后导出为ONNX格式方便后续去掉PyTorch依赖、在工控机上跑推理yolo export modelresults/luggage_experiment/run1/weights/best.pt formatonnx imgsz640导出后用ONNXRuntime做一个最简单的批量推理脚本检查导出后的模型和PyTorch结果是否一致。如果你的产线用的是TensorRT或OpenVINO也可以在这个节点换上对应后端但导入流程和超参选择是相同的。最后一个小建议每次新到一个行李箱批次不要把新样本一股脑合并进旧数据重训。更稳妥的做法是把新样本收集成单独的验证集让旧模型先标一遍你再人工纠正几十张再利用这些难例做5-20个epoch的微调观察新验证集的召回率变化。我这里十次有八次会在这个环节发现模型在新批次上的缺陷形态和旧训练集差距很大暴露问题后再去决定是补采集还是改打光。这条流程走顺了650张的数据集也能帮你快速判断整个质检方案的可行性。希望我的这些踩坑记录能帮你少走一点弯路。本文还有配套的精品资源点击获取