ARTICLE DETAIL

资讯详情

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

YOLO实战:1531张罐头瓶子数据集目标检测训练全流程

YOLO实战:1531张罐头瓶子数据集目标检测训练全流程 简介面向YOLO系列算法目标检测实战的罐头和瓶子数据集包含1531张带标签图像已划分训练集、验证集和测试集并附有data.yaml配置文件可直接适配yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等主流版本无需额外整理即可训练和验证。压缩包共2000个文件其中1073个XML文件与927个TXT文件分别对应VOC格式和YOLO格式标注方便在两种主流标注体系间切换和对照学习整体大小68.59MB。目前已有54人学习浏览适合目标检测入门者快速体验完整的数据准备与模型验证流程。数据集不仅提供标准标注还包含YOLO格式的类别索引、中心坐标及宽高比例说明且标签已按训练、验证、测试分类存放配合VOC格式可深入对比差异data.yaml使用户能直接启动训练两种标签文件分目录管理结构清晰是练习模型部署与参数调优的实用素材。1. 罐头和瓶子数据集1531张带标签图像到底够不够训练一个能用的YOLO模型做工业质检、商超货架识别、回收分拣这类项目时最头疼的不是模型结构而是手上没有带标签的数据。YOLO算法本身很成熟部署教程也遍地都是但真正卡住进度的地方是数据集的质量和数量。这份「罐头和瓶子数据集」一共1531张图像带YOLO格式的txt标签目标覆盖罐头、瓶子还有一个容易被忽略的子类——鲜奶。对这个规模的数据集很多人第一反应是“太少了肯定训练不出好东西”实际用下来恰恰相反单类目标、场景相对固定、标签干净的前提下1500张图足够把模型跑到可用的水平关键看你有没有把数据整理到位。这篇文章就围绕这套数据集从解压zip开始到标签格式校验、目录整理、训练配置再到踩坑记录和验证技巧按一条能落地的路径讲清楚。适合正在学YOLO训练、做毕设或者刚接手一个检测项目但不知道从哪里下手的初学者也适合想快速验证一个数据集能不能用的熟手。我不会讲太多原理层面的东西重点是你拿到zip之后每一步该做什么、命令怎么写、参数怎么调、翻车了怎么排查。2. 拆开zip看数据YOLO标签格式、类别分布与鲜奶子类的识别难点2.1 YOLO标注文件怎么读一行五个数字代表什么用labelimg之类的工具打标完成后每张图像会对应一个同名的txt文件这是YOLO系列的通用标注格式。拿这份数据集里一张标注了罐头和瓶子的图举例它的txt文件内容长这样0 0.482031 0.517881 0.246875 0.537037 1 0.769531 0.480556 0.153125 0.535185 2 0.295703 0.542824 0.161719 0.481481每一行代表一个目标框由5个数字组成。第一个数字是类别id从0开始数这张图里出现了3个类别的目标后面四个数字分别是归一化后的中心点x坐标、中心点y坐标、框宽度、框高度。所谓归一化就是把像素值除以图像的宽和高所有值都落在0到1之间。这就有个很容易忽略的细节坐标看的是像素相对比例不看绝对像素。假设原图宽度是1024某个框中心x方向在512像素处那么txt里记录的就是0.5。写代码解析标签时一定要记得把归一化坐标乘回原图宽高才能画出真正的框。反过来如果你要手动改标签也必须把像素坐标除以图像宽高否则训练时YOLO会把框的位置完全理解错。这个转换关系是后面所有数据清洗工作的基础建议先花几分钟把这份数据集随机抽几张跑一遍上面的解析逻辑确认数据和图像能对上再继续。2.2 1531张图里到底有多少个目标统计类别分布和边界情况拿到数据集之后我一般不会急着训练先写一个统计脚本搞清楚每个类别的目标数量、图像尺寸分布、有没有空标签文件。这一步能提前暴露很多问题比如某个类别样本极少、标签文件损坏、图像尺寸不统一等。下面这个脚本可以直接跑import os from collections import Counter from PIL import Image img_dir images # 图像目录 label_dir labels # 标签目录 class_counter Counter() # 统计每个类别的目标数量 img_sizes Counter() # 统计图像尺寸 empty_labels [] # 记录空标签 label_count 0 # 标签文件总数 for label_name in os.listdir(label_dir): if not label_name.endswith(.txt): continue label_path os.path.join(label_dir, label_name) img_name label_name.replace(.txt, .jpg) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): print(f图像缺失: {img_name}) continue with open(label_path, r, encodingutf-8) as f: lines [line.strip() for line in f.readlines() if line.strip()] if len(lines) 0: empty_labels.append(label_name) continue for line in lines: parts line.split() if len(parts) ! 5: print(f标签格式异常: {label_name} - {line}) continue class_id int(parts[0]) class_counter[class_id] 1 # 记录每张图的尺寸 img Image.open(img_path) img_sizes[img.size] 1 print(类别统计:, dict(class_counter)) print(图像尺寸分布:, dict(img_sizes)) print(空标签数量:, len(empty_labels))这个脚本做的事情很简单逐个读取标签文件检查图像是否存在统计每个类别的框数量、图像的尺寸分布同时找出空标签和格式异常的行。类别统计非常关键它能直观告诉你数据集的平衡程度。比如发现罐头类有2000多个框但鲜奶类只有300个框那你训练时就要给鲜奶类提高损失权重或者做针对性的数据增强否则模型天然会偏向多数的罐头类鲜奶的召回率会低得可怜。图像尺寸分布同样值得注意。如果1531张的尺寸五花八门训练时统一resize到640x640会产生不同程度的形变拉伸这会影响小目标的检测精度。看到这种情况可以按尺寸聚类一下确定一个合适的目标尺寸或者训练前先做letterbox预处理。2.3 鲜奶和瓶子容易混标注边界模糊带来的训练陷阱这套数据集的标题把「鲜奶」和「瓶子」并列说明标注方认为它们是不同类。但实际看一眼标注你会发现很多鲜奶产品本身就有瓶身或者盒身结构。鲜牛奶装在玻璃瓶里、塑料瓶里、纸盒里从外形上和「瓶子」高度相似。如果你的标注原则没定清楚模型就会学到一团浆糊的特征。常见做法是把标注原则定为只要目标物是鲜奶产品本身不管容器是瓶还是盒都标记为鲜奶类单独的、不属于鲜奶的空瓶才标记为瓶子类。这样每个类别内部的视觉一致性才够强。标注边界如果不够清晰训练出来的模型多半会在鲜奶和瓶子之间反复横跳推理时同一个目标这次判瓶子、下次判鲜奶。你可以在拿到数据集后随机抽100张图把txt标签和图像画在一起人工做一遍一致性抽检。如果发现标注原则不统一最稳妥的做法是重新分组把所有鲜奶类目标挑出来单独用一个类别id重新打标瓶子只保留非鲜奶的瓶类目标。这个前置的清洗工作约等于人工复核一遍标签1500多张图大概需要一个下午但带来的收益非常直观——mAP能提升好几个点。3. 把zip整理成可训练的YOLO目录解压、格式校验与划分训练集验证集3.1 YOLO官方约定的目录结构images和labels必须对应解压完zip之后里面的目录结构大概率不是YOLO直接能读的格式。常见的情况是图片在某个文件夹里标签在另一个文件夹里还可能嵌套多层子目录。YOLOv5和YOLOv8默认的数据集目录结构是这样的dataset/ ├── images/ │ ├── train/ │ │ ├── img001.jpg │ │ └── ... │ └── val/ │ ├── img101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── img001.txt │ │ └── ... │ └── val/ │ ├── img101.txt │ └── ... └── data.yamlimages和labels各自下面再分train和val两个子目录一一对应。文件名必须完全一致只是后缀不同训练脚本按文件名去找对应的标签。很多人会在这里翻车图片是.jpg、标签却是.JPG或者.png又或者文件名带了空格、中文这些都会导致训练时图像被读取但标签对应不上自动跳过最后loss异常或者目标数量统计为0。这一步没什么好取巧的老老实实写一个脚本把文件名统一后缀统一为小写图片复制到images/train标签复制到labels/train。如果文件名里有中文或者特殊字符建议直接用数字序号重命名。文件名里的中文在Windows下没有问题但放到Linux服务器上跑训练时容易遇到编码问题为了省事一步到位全改掉。3.2 划分训练集和验证集随机种子和按目录划分的区别1531张图不需要划分测试集留出训练集和验证集就够了划分比例我一般用9:1或者8:2。这里的关键是以图像为单位随机划分不是以文件夹为单位。如果原本的目录结构是一个文件夹放50张图你按文件夹划分训练集和验证集的特征分布就会失衡。下面的脚本按比例划分并把结果输出到YOLO需要的目录结构import os import random import shutil random.seed(42) # 固定随机种子保证结果可复现 img_dir 原始图片目录 label_dir 原始标签目录 output_dir dataset train_ratio 0.9 all_images [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .jpeg, .png))] random.shuffle(all_images) train_count int(len(all_images) * train_ratio) train_images all_images[:train_count] val_images all_images[train_count:] def copy_files(images, split): img_out os.path.join(output_dir, images, split) label_out os.path.join(output_dir, labels, split) os.makedirs(img_out, exist_okTrue) os.makedirs(label_out, exist_okTrue) for img_name in images: stem os.path.splitext(img_name)[0] # 找到同名标签文件不区分后缀大小写 label_name None for suffix in (.txt, .TXT): cand stem suffix if os.path.exists(os.path.join(label_dir, cand)): label_name cand break if label_name is None: print(f警告: {img_name} 没有对应标签跳过) continue shutil.copy(os.path.join(img_dir, img_name), os.path.join(img_out, img_name)) shutil.copy(os.path.join(label_dir, label_name), os.path.join(label_out, label_name)) copy_files(train_images, train) copy_files(val_images, val) print(f训练集: {len(train_images)} 张) print(f验证集: {len(val_images)} 张)这段脚本的重点是固定了random.seed(42)。深度学习训练里有太多随机因素了不固定种子两次训练的结果可能差很多会把调参变成玄学。固定随机种子至少保证数据划分是可复现的你改了一个超参数后重新训练对比结果时才分得清是改参数的效果还是数据切分的偶然。另外注意脚本里对标签后缀做了大小写兼容处理这看起来是件小事实际运行中遇到的概率一点都不低。3.3 针对罐头、瓶子和鲜奶的数据增强反光、遮挡与样本不均衡罐头和瓶子这类产品最典型的视觉干扰是反光。金属罐的表面对光线的反射会造成高光区域这部分区域的纹理完全丢失玻璃瓶身则会透出背景边缘和背景融为一体。如果直接拿原始数据训练模型很容易把高光当成目标的一部分推理时换了光照就漏检。所以划分好目录之后我建议做一轮针对性的离线增强。常见的做法是复制原始样本做三类变换亮度随机调整模拟光照变化、高斯噪声模拟摄像头传感器噪声、随机裁剪缩放模拟不同拍摄距离。需要说明的是做增强时要连标签一起变换不能只增强图片否则标签和图像内容对不上。翻转增强要慎重。罐头和瓶子不是完全对称的特别是带标签文字的瓶身左右翻转之后文字是反的。如果训练时用了水平翻转参数模型会同时见过正反两向的文本特征推理时对真实场景的文字方向就失去了判断依据。像这类带文字信息的目标我一般把翻转增强关掉或者翻完以后用OCR先检测一下文字是否正常再做取舍。对于鲜奶类别数量偏少的情况可以在增强时提高这个类别的采样概率。典型的做法是统计每个类别在训练集中的框数量给少的类别每次迭代多复制几份参与训练。YOLOv8本身支持按类别设置采样权重你也可以用离线的方式对含鲜奶目标的图片做3到5次增强扩充到训练集里。这个操作对鲜奶的召回率提升非常明显。4. 用YOLOv8训练罐头和瓶子识别模型环境准备、数据配置与参数调整4.1 data.yaml怎么写路径、类别名和类别id的顺序不能错进入训练之前先把数据配置文件写对。YOLOv8读的是data.yaml内容非常简单但写错一个地方训练就跑了半天发现loss不降。完整配置如下# data.yaml path: /absolute/path/to/dataset # 数据集根目录建议写绝对路径 train: images/train # 训练集图片目录相对于path val: images/val # 验证集图片目录相对于path names: 0: can # 罐头 1: bottle # 瓶子 2: milk # 鲜奶这里有两个最容易出错的点。第一个是path字段如果填相对路径YOLOv8会根据当前工作目录去拼接不同环境工作目录不一致就直接报错找不到图片。我通常直接写绝对路径一了百了。第二个是names字段的顺序类别id必须和txt标签里的第一个数字完全对应。这份数据集里标签文件第一行第一个数字如果是0那names里的第0项就必须是can不能把类别名词的顺序打乱了。如果标签里的id和names不匹配训练不会报错但验证时看到的mAP会莫名其妙地低。还有一个细节names列表在YOLOv8里是从0开始编号的如果你写names的时候漏了中间某一项比如只写了1和2没写0或者不小心把字典写成了从1开始前面所有标签为0的目标全部会被忽略。常见做法是训练前写一个小脚本把训练集所有标签里出现过的类别id打印出来和names对一遍确认完全一致再开工。4.2 训练命令与关键参数imgsz、epochs、batch的合理范围YOLOv8在Anaconda环境下的安装和训练一条命令就能跑起来。假设你的数据集已经整理成了上一章的结构训练命令长这样yolo detect train \ data/absolute/path/to/data.yaml \ modelyolov8n.pt \ epochs120 \ imgsz640 \ batch16 \ projectruns/detect \ namecan_bottle_milk \ workers4 \ seed42用yolov8n.pt而不是yolov8s.pt或更大的模型是有考虑的。这份数据集本身只有1531张图属于中小规模。模型越大过拟合风险越高。预训练权重yolov8n.pt是COCO数据集上训出来的虽然COCO里没有「鲜奶」这类细分类但底层的边缘、纹理、形状特征已经学到了迁移过来只需要在顶层做微调能让小数据集的收敛速度大幅加快。参数方面几个重点说一下。imgsz640是性价比最高的输入尺寸太大显存不够太小罐头上的小字标签会糊掉如果你的图像分辨率普遍很高比如都是3000x3000的工业相机图可以尝试imgsz960但显存占用会翻倍。batch16是8G显存环境下的常规值实际要看显存动态调整显存不够时优先降到8不要直接换小模型回避问题。epochs120在小数据集上略偏多一般60到80轮就已经收敛多出来的轮数主要用来观察验证集的mAP是不是过了峰值开始回落方便你判断什么时候应该提前停。训练过程中日志输出的loss数值会持续下降刚开始下降很快后面变平缓这是正常的。验证集的mAP每一轮都会更新等到验证集的mAP连续20轮不再上升说明已经收敛可以按CtrlC手动停掉不必等epochs跑完。4.3 中断恢复与显存不足训练到一半失败的应对方案训练到一半最常见的中断原因是显存不够。报错信息一般是CUDA out of memory。这个问题的解决思路是先降batch降到8还不行就降到4再不行就换更小的模型。还有一个容易忽略的点是workers数值workers是数据读取的进程数配置过大会占大量内存不过这块报错通常和CUDA无关是系统内存满了。如果训练中断了yolo命令提供了一个断点续训的参数。代码里写resumeTrue就行yolo detect train resumeTrue这条命令会读取runs/detect/你的项目名/weights/last.pt从上次中断的轮次继续训练不需要重新跑前面几十轮。使用resume前要确认中断前后代码版本是一致的如果中途换过YOLOv8的版本权重文件可能不兼容报错的话建议直接基于最后的权重重新训练yolo detect train \ data... \ modelruns/detect/你的项目名/weights/last.pt \ epochs120 \ imgsz640 \ batch16基于last.pt继续训练和resume的差别是前者会从第一轮重新开始记录训练历史但权重是继承下来的后者会完整恢复训练曲线。如果只是断了一次用resume省事如果折腾过环境或者改过配置干脆从头训。5. 常见问题与避坑标签错位、鲜奶漏检、结果随机性排查5.1 某个类别标签全部为空训练时mAP始终为0现象训练日志正常输出loss也下降但训练结束时某个类别比如鲜奶的mAP一直是0其他类别正常。原因标签文件里类别id和data.yaml中的names列表顺序对不上。例如标签里milk类是2但data.yaml的names只写了两个类别milk被视为超出范围的目标训练时被直接忽略。解决用2.2节的统计脚本重新统计所有标签里的类别id。关注打印结果中的class_counter字典对照data.yaml里的names确保两边编号完全对齐。如果标签里的id有空缺比如只有0和2没有1说明数据整理时丢掉了一部分类别建议检查解压后的原始标签目录找出所有txt文件逐行过一遍找出缺失的类别是从哪个环节丢的。5.2 鲜奶包装反光导致漏检增强方向调错现象验证集上罐头和瓶子的检出效果不错鲜奶类别的召回率偏低尤其是有反光的鲜奶盒或者半透明包装的鲜奶瓶。原因增强参数配置里加了大量的随机旋转和透视变换本来是想提升鲁棒性但这类瓶罐目标的识别高度依赖边缘和结构特点。过度旋转后目标外观变化过大模型学到的是各种奇怪角度的共性特征反而削弱了对鲜奶盒、鲜奶瓶这种特定长宽比目标的表征。解决把旋转角度限制在±10度以内去掉透视变换增加亮度对比度扰动。训练参数里对应的是YOLOv8的增广参数hsv_h、hsv_s、hsv_v这些可以适当调高亮度和饱和度的随机范围去模拟不同光源下反光程度的变化。如果还是漏检对含鲜奶目标的图像单独做离线增强每种变换做3份把鲜奶在训练集中的占比提起来。5.3 zip解压后中文路径导致读取失败现象全部按流程走完了启动训练时提示找不到图片文件或者验证集图片读取数量为0。原因Zip压缩包内可能嵌套了带中文的目录名。解压到Windows系统时中文路径在PyTorch的DataLoader下偶发无法正常读取尤其是文件路径里同时包含中文字符和空格时报错信息还不太直观。解决把数据集放在一个纯英文路径下所有目录名都用英文小写图片和标签的文件名只用数字和字母组合。文件名较长的建议改短。这一步虽然看起来是小事但能省掉无数排查时间。路径问题不会在你跑通一次训练后就消失后续推理、导出模型时还会反复踩到最好一开始就彻底规避。5.4 两次训练结果差异很大无从判断哪个配置好现象用同一份数据集、同样的参数分两次训练mAP差了2到3个点怀疑是代码有bug或者模型写死了随机行为。原因YOLO训练涉及大量随机性数据加载顺序、增强参数采样、权重初始化、dropout等。我一般会固定数据划分的随机种子通常设为42。同时训练时加seed42参数把训练过程也固定下来。如果你需要横向对比不同的参数组合记得每次都固定同一个seed这样每次实验之间唯一的变量就是你要调的那个参数。解决每次实验在命令行里显式加上seed42或者写一个shell脚本把所有训练命令统一管理日志按日期命名留存。最后看结果时对比的不是单个mAP而是多次运行的平均值和方差。如果方差大于1个点说明数据集本身波动就大单次实验的最好成绩没有参考意义。5.5 数据集中存在标签边界超过1.0的情况现象训练日志没有异常但用验证集评估时个别的检测框位置整体偏移特别是位于图像边缘的目标。画出框来一看框跑到了图像外面。原因打标工具在标边缘目标时框的四边可能超出了图像边界保存的归一化坐标就会出现小于0或者大于1的情况。YOLO训练时会自动忽略越界的框但你的数据统计和图框可视化脚本没有做越界判断导致你看到一堆诡异的框在图像外面。解决写脚本对全部标签做一次越界检查把小于0和大于1的值强制截断到0到1之间。越界量不太大的情况下截断基本不影响标注质量比如框的右边界是1.05截断成1.0只是让框贴到图像边缘目标本身没有损失。越界超过0.3的说明打标时误差很大建议人工复核一下对应图像确认标签是否可保留。def clamp_label(line, img_w, img_h): parts line.split() cls int(parts[0]) cx, cy, w, h map(float, parts[1:]) cx max(0, min(1, cx)) cy max(0, min(1, cy)) w max(0, min(1, w)) h max(0, min(1, h)) return f{cls} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}这段代码按行读取标签对越界值做截断。注意这里截断的是中心点、宽和高不是直接对左上角和右下角做裁剪因为YOLO格式存的本来就是中心点和宽高写的时候不要混淆概念。6. 验证模型效果从mAP指标到单张推理的边界测试训练结束后模型结果在runs/detect/can_bottle_milk/weights/best.pt。这个best.pt是验证集mAP最高的权重也是你后续做推理和部署要用的文件。很多人习惯用last.pt就是最后一轮保存的权重实际上遇到验证集mAP先升后降的情况最后一轮的模型往往是过拟合的效果反而不如中间某轮的best.pt。所以为保险起见推理时只用best.pt不要用last.pt。验证模型效果不能只看总mAP要分开看每个类别的precision和recall。YOLOv8在验证完会输出每个类别的详细指标你需要关注的是鲜奶类的召回率。这个指标决定生产环境中鲜奶目标被漏检的比例比mAP更实际。如果召回率不理想参考5.2节的方向增强或者把置信度阈值调低一档从0.25调整到0.15用误检换漏检。单张推理的指令如下yolo detect predict \ modelruns/detect/can_bottle_milk/weights/best.pt \ sourcetest_images/milk_bottle.jpg \ conf0.25 \ saveTrue跑通推理之后我建议做一轮更细的边界测试挑几张含反光、模糊、遮挡、多个目标堆叠的图片逐一观察模型的检测结果。这一步能发现量化指标看不出的问题比如类别置信度在0.3左右徘徊的目标、两个目标靠太近被合并成一个框、小目标完全没被检出。这些现象汇总起来就是你下一步数据补充的方向。如果检测框位置比实际目标偏大或者偏小很多可以看训练时imgsz和原始图像分辨率的差异。原始图像如果是720p你强行缩到320训练框的精度肯定会打折。这个只能通过调高imgsz重新训练解决没有别的捷径。而如果框的位置准确但类别判断总是犹豫说明类别间的特征区分度不够方向还是在标签清洗和数据增强上再训练多少轮都白搭。我自己的习惯是验证完模型后把误检和漏检的图片单独建一个文件夹按错误类型重命名分类保存。下次再迭代数据集时这些图片就是最有价值的样本直接告诉你要补什么类型的数据。这套工作流用的顺了下次换任何数据集都能快速跑通训练少走很多弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表