
简介这是一套面向YOLO系列算法的产品识别数据集专门聚焦可口可乐产品的目标检测场景适合需要训练自定义检测模型的开发者、研究人员及竞赛团队使用。压缩包共含2000个xml标签文件整体约143.6MB内置数据配置文件可无缝对接YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10等版本直接开展训练、验证与测试。标签采用规范的目标框格式记录类别索引以及归一化后的中心坐标、宽度和高度并按常规方式完成数据集划分使用者只需按需调整配置路径即可启动实验。相比从零标注这套资源能显著节省数据准备时间适合快速构建产品识别基线模型也适合在可口可乐等快消品识别场景下进行算法精度对比与迁移调优。目前已有95人浏览学习可兼顾入门实践与进阶实验需求。1. YOLO 产品识别数据集5166 张可口可乐图像能直接训练吗做目标检测最头疼的不是调参而是数据。YOLO 系列算法对数据格式要求严格网上找一圈要么是 VOC 格式要自己转要么标签质量稀碎。这个 YOLO 产品识别数据集5166 张可口可乐相关图像带完整标注同时给了 yolo 的 txt 和 voc 的 xml 两种标签格式解压就能喂给 yolov5、yolov8、yolov9、yolov10、yolo11 训练。对刚入门 YOLO 的开发者来说这是少见的可以直接跑通的图像标注数据集对做零售商品识别的工程师它又是一个现成的迁移学习底座。我把它拆了一遍整理出完整的数据结构解读、训练前校验脚本、yolov8 训练流程和几个容易翻车的细节下面按实际踩过的顺序写。2. 双标签格式的数据集结构yolo 的 txt 与 voc 的 xml 怎么共存2.1 目录划分训练集、验证集、测试集与标签文件夹这套数据集的目录组织是标准 YOLO 布局解压后你会看到类似下面的结构datasets/ ├── data.yaml ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── annotations/ ├── train/ ├── val/ └── test/images 放图像labels 放 yolo 格式的 txt 标签annotations 放 voc 格式的 xml 标签三个子目录下的 train/val/test 划分是一致的。这一点很重要训练的时候 YOLO 通过 data.yaml 指定图片路径然后自动去同名目录找对应标签文件任何一层文件名对不上都会报错。我一般拿到数据集先不看训练效果而是核对划分比例。这套数据 5166 张图常见的划分是 8:1:1 左右但具体比例你要自己数。方法很简单在终端里执行find images/train -name *.jpg -o -name *.png | wc -l find labels/train -name *.txt | wc -l第一个命令统计训练图片数第二个统计训练标签数。对比两个数字能快速发现有没有缺标签的孤儿图片。因为 images 和 labels 是按文件名对应的如果图片有 4000 张、txt 只有 3900 个多半是有图没标或标注漏导出训练时那 100 张会被 YOLO 静默跳过你根本察觉不到只会觉得 mAP 上不去。我习惯在训练前就把这一步走完避免后面白跑。2.2 data.yaml 配置文件该怎么读data.yaml 是这个数据集能否直接训练的关键它告诉 YOLO 三件事数据集根目录在哪、训练验证测试图片分别在哪个文件夹、类别有哪些。打开后结构大概长这样path: . train: images/train val: images/val test: images/test names: 0: coca_cola 1: sprite 2: fantapath 是数据集的根目录train/val/test 是相对 path 的图片文件夹路径names 是类别列表。标签 txt 里的 class id 就是这里的索引比如0对应coca_cola。注意 names 的内容以你解压后的实际文件为准我这份清单里可能不止可口可乐一个类文件名末尾带的部分类别名称就是提示。如果你要换机器训练最常见的坑就是 path 写死成了别人机器上的绝对路径。比如/home/user/datasets/coca这种换到你的电脑上必然找不到文件。所以拿到手第一件事把 path 改成相对路径.然后把 data.yaml 和 images、labels 放在同一个根目录下这样无论项目搬到哪都不会因为路径失效重跑。修改后可以执行一行命令验证路径是否可读python -c import yaml; cyaml.safe_load(open(data.yaml)); print(c[train], c[val])能打印出正确的 train 和 val 路径说明 YOLO 能顺着这个配置找到图片。2.3 文件名里的信息img_0167_634.xml 到底在说什么这套数据集的命名规律值得说一下因为从文件名能猜出很多信息。看项目里的文件名img_0167_634.xml、img_0167_641.xml结构是img_场景编号_图片编号。0167 应该是采集场景或货架编号634、641 是同一场景下不同视角或不同摆放位置的图像编号。同一场景多张图、每张图可能有多个目标框这是商品识别数据的典型形态。可口可乐产品识别和车辆检测不一样货架上的商品密集、尺寸小、互相遮挡同一瓶可乐在不同角度拍出来差异很大。文件名前缀相同的图往往来自同一批货架训练集和验证集划分时就要注意尽量按场景前缀切分而不是随机打散。我的习惯是训练前用脚本看一眼标签框的分布。比如统计单张图里目标数量最多的前几个文件from pathlib import Path label_dir Path(labels/train) boxes_per_image [] for txt in label_dir.glob(*.txt): count len([line for line in txt.read_text().splitlines() if line.strip()]) boxes_per_image.append((count, txt.stem)) boxes_per_image.sort(reverseTrue) for count, name in boxes_per_image[:10]: print(f{name}: {count} 个目标)这个脚本可以让你知道单张图最多标了多少个目标。如果出现一张图四五十个框的极端情况训练时小目标漏检率会偏高后面要针对性地调 imgsz 或数据增强参数。如果普遍只有一两个框说明数据集偏向简单场景测试时会高估模型能力。3. 训练前的标签体检用脚本把 5166 张图的标签全部过一遍3.1 统计目标数量与类别分布拿到标签文件后第一步不是训练而是统计。YOLO 的 txt 标签格式是class x_center y_center width height五个字段class 从 0 开始坐标都是相对于图像宽高的归一化比例值范围 0 到 1。这五列少一列、多一列、类型不对训练都会出问题。先做类别分布统计确认每个类别的样本量是否均衡。这里要注意txt 文件里的 class id 和 xml 里的类别名是对应的但对应关系要看 data.yaml 的 names 顺序。统计脚本很简单from pathlib import Path def count_classes(label_dir: Path): stats {} total_files 0 empty_files 0 for txt in label_dir.glob(*.txt): total_files 1 lines [line for line in txt.read_text().splitlines() if line.strip()] if not lines: empty_files 1 for line in lines: parts line.split() if len(parts) ! 5: print(f非法行: {txt.name} - {line}) continue cls int(parts[0]) stats[cls] stats.get(cls, 0) 1 return stats, total_files, empty_files stats, total_files, empty_files count_classes(Path(labels/train)) print(f标签文件总数: {total_files}) print(f空标签文件: {empty_files}) print(f类别分布: {stats})这段代码做了两件事统计每个类别出现了多少个目标框同时顺手检查每行是不是恰好五列。非法行直接打印出来方便定位问题文件。参数方面splitlines()会去掉换行符strip()去掉首尾空格避免空行被误判成标签。如果发现空标签文件很多训练时这些图片会被当成背景样本大量空标签会导致模型倾向预测无目标也就是漏检。3.2 校验边界框坐标是否合法归一化坐标最怕两类错误越界和宽高为零。越界指的是 x_center 或 y_center 大于 1 或者小于 0宽高为零说明标注框退化成了点。YOLO 训练遇到这两种框轻则损失震荡重则直接 NaN。这种问题靠肉眼看 5166 个标签根本不现实写个一次性校验脚本更靠谱from pathlib import Path def validate_boxes(label_dir: Path): bad_files [] total_boxes 0 for txt in label_dir.glob(*.txt): for line_no, line in enumerate(txt.read_text().splitlines(), 1): line line.strip() if not line: continue try: cls, xc, yc, w, h map(float, line.split()) except ValueError: bad_files.append((txt.name, line_no, 字段解析失败)) continue total_boxes 1 if not (0 xc 1 and 0 yc 1): bad_files.append((txt.name, line_no, f中心点越界: {xc}, {yc})) if not (0 w 1 and 0 h 1): bad_files.append((txt.name, line_no, f宽高异常: {w}, {h})) print(f总框数: {total_boxes}) print(f异常记录: {len(bad_files)}) for name, line_no, reason in bad_files[:20]: print(f {name}:{line_no} {reason}) validate_boxes(Path(labels/train))如果异常记录很多先别急着训练。yolo 格式的 w 和 h 是宽度和高度占图像宽高的比例正常的框一定满足0 w 1。出现宽高为 0 的情况通常是标注工具在导出时舍入了过小的框或者转换脚本里xmax - xmin计算时把坐标顺序写反了。这类脏数据不清理训练出来的模型边界框抖动会非常明显。3.3 voc 与 yolo 格式互转的脚本实现既然数据集同时给了 xml 和 txt很多时候你还是需要自己转格式。比如你想在某个只支持 VOC 格式的工具上做二次标注就需要把 yolo 的 txt 转回 xml。核心公式只有两组。VOC 的 bndbox 存的是绝对像素坐标xmin, ymin, xmax, ymax。转 yolo 时def voc_to_yolo(xml_path: str, img_w: int, img_h: int): import xml.etree.ElementTree as ET tree ET.parse(xml_path) root tree.getroot() objects [] for obj in root.findall(object): name obj.find(name).text 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) xc (xmin xmax) / 2 / img_w yc (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h objects.append((name, round(xc, 6), round(yc, 6), round(w, 6), round(h, 6))) return objectsimg_w 和 img_h 是图像的像素宽高必须从对应的 jpg/png 里读不能从 xml 里拿。有的 xml 会带 size 字段包含图像尺寸但不少标注工具导出时这个字段是空的。我一般直接用 PIL 读图from PIL import Image img Image.open(images/train/img_0167_634.jpg) w, h img.size转出来后按class xc yc w h的格式写进 txt一行一个目标。反方向 yolo 转 voc 就是把公式反过来xmin (xc - w/2) * img_wymin (yc - h/2) * img_h。容易踩的坑是四舍五入后xmin可能变成负数或者xmax超出图像宽度转换时要做一次 clamp 裁剪把坐标限制在[0, img_w]和[0, img_h]范围内。4. 用 yolov8 把数据集跑通训练、验证、测试一条线4.1 环境准备与目录落位现在的 YOLO 生态里yolov8 是最省心的一个ultralytics 把训练、验证、导出全封装成了命令行。环境安装就一条命令pip install ultralyticsultralytics 会连带装上 torch、torchvision、opencv-python 这些依赖。如果你有 GPU建议提前装好对应版本的 CUDA 版 torch不然默认装的 CPU 版训练速度会慢到怀疑人生。判断 torch 是否用了 GPU python -c import torch; print(torch.cuda.is_available())输出 True 才说明 GPU 可用。目录落位的时候把整个数据集文件夹放到一个干净的位置比如~/datasets/coca/确认 data.yaml 里的 path 改成.。之后在数据集根目录外执行训练命令YOLO 会自己根据 data.yaml 找数据。训练产物默认写到runs/detect/这是 ultralytics 的工作目录每次训练会生成新的 train 子目录不会覆盖之前的实验结果。4.2 训练参数怎么设epochs、imgsz、batch 的取舍训练命令不长但参数值的设定有讲究。先用小模型验证流程能跑通再用大模型提精度。我通常用 yolov8s 起步yolo train \ data~/datasets/coca/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ device0几个关键参数逐个说。modelyolov8s.pt是预训练权重在 COCO 上训练过的模型。用它做初始化收敛速度比随机初始化快得多这也是迁移学习能少跑几十个 epoch 的原因。epochs100对 5166 张图的中等规模数据集够用产品识别任务不算特别难多数情况 50 个 epoch 以内 loss 就平了。imgsz640是训练分辨率可口可乐瓶在货架场景里属于中小目标如果你的验证集里目标普遍偏小可以提到 960 试试代价是显存占用几乎翻倍。batch16看显存来12G 显存跑 640 分辨率、yolov8s 模型batch 16 是安全值。显存不够就降到 8梯度累积的效果类似。patience20是早停验证集指标连续 20 个 epoch 不提升就自动停止能避免无效的长时间训练。说一下 loss 怎么看。训练过程中终端会打印box_loss、cls_loss、dfl_loss三个值后面还有mAP50这种指标。box_loss 和 cls_loss 下降趋势正常就行不用追求每个 epoch 都降。真正做模型选择要看验证集 mAP不是看训练 loss 多低。很多初学者训练 loss 降到很低验证 mAP 反而一般这是过拟合信号说明模型在背训练集而不是学泛化特征。4.3 验证与测试看指标而不是看心情训练完自动会在runs/detect/train/下生成权重文件best.pt是验证集指标最好的那一个last.pt是最后一个 epoch 的。验证阶段用 best.ptyolo val \ modelruns/detect/train/weights/best.pt \ data~/datasets/coca/data.yaml验证结果会告诉你几个关键指标precision是查准率预测为正的样本里有多少是真的recall是查全率真实目标里有多少被找出来了mAP50是 IoU 阈值 0.5 下的平均精度mAP50-95是 COCO 风格的严格指标。产品识别场景里漏检一瓶可乐比误检一个空区域更影响业务所以我一般更看重 recall 和 mAP50mAP50-95 作为参考。测试阶段就是拿没训练过的图看实际效果yolo predict \ modelruns/detect/train/weights/best.pt \ source~/datasets/coca/images/test \ saveTrue \ conf0.25saveTrue会把标注了检测框的结果图存下来存到runs/detect/predict/下。conf0.25是置信度阈值低于这个值的预测框会被过滤掉。想要更严格的检测就调高到 0.4 或 0.5想找回更多漏检就调低。实际使用中这个阈值不用在训练时改推理时随时能调我一般跑完测试会多试几个 conf 值对比哪一档最符合现场需求。产品识别场景客户通常希望宁缺毋滥宁可少检不能乱检conf 设高一点更安全。5. 产品识别数据集标注避坑五个我实际踩过的坑5.1 标签内容上的三个坑第一个坑是类别 id 错位。现象是训练时 loss 下降正常但验证集 mAP 很低画出预测框一看类别标签全乱了。可口可乐标成雪碧雪碧标成芬达。原因基本都出在 VOC 转 YOLO 时类别名到 id 的映射顺序和 data.yaml 的 names 顺序不一致。XML 里存的是类别名字符串转成 txt 时必须经过一层映射字典这个字典的顺序跟 data.yaml 对不上id 就全偏了。解决方式是把映射关系写成单独的 label_map 字典转换脚本和 data.yaml 都从同一个字典生成从根上避免两处定义不一致。第二个坑是坐标越界被静默修正。现象是训练到一半 loss 突然跳高或者某些 batch 的 loss 明显高于其他 batch。原因是标签里存在 x_center 或 y_center 略大于 1 的框可能是标注时鼠标拖出了图像边缘转换脚本又没做 clamp。YOLO 遇到越界坐标不会报错而是直接参与损失计算极端值会拉偏梯度。解决方式就是 3.2 节那个校验脚本在训练前把所有标签过一遍越界的先裁剪再归一化。第三个坑是 xml 里的 bndbox 坐标四舍五入导致框退化。现象是某些目标检测框位置正确但尺寸偏小或偶尔出现宽高为 0 的野框。原因是 VOC 转 YOLO 时只保留了 6 位小数对于 1920×1080 的大图0.000001 的误差对应约 2 个像素多次转换累积下来小目标可能被舍入成无效框。解决方式是转换脚本里对 w 和 h 加一个下界保护小于 1e-6 的就丢弃并打印警告避免脏数据混进训练集。5.2 数据组织相关的两个坑第四个坑是图片编号撞车。现象是数据准备好后训练能跑但读取标签时经常出现WARNING: no labels found in ...日志里能看到不少图片没匹配到标签。原因是有多个采集场景不同场景里可能存在重名文件比如两个货架目录下都有img_0001_001.jpg平铺复制到同一目录时后复制的覆盖了先复制的标签文件和图片就失配了。解决方式是在数据整理阶段用脚本检查文件名唯一性发现有重复就统一按场景编号_原文件名重命名。这套数据集的文件名结构虽说是 img_0167_634 这种全局唯一的格式但你自己扩展数据或合并其他数据集时很容易碰到重名问题值得提前防一手。第五个坑是训练集和验证集划分泄漏。现象是训练 loss 和验证 loss 都很漂亮mAP 高到离谱但一到真实场景检测就稀烂。原因是划分数据集时用了随机切分同一场景相邻视角的图片一部分进了训练集、一部分进了验证集验证集相当于开卷考试评估结果虚高。解决方式是按场景分组切分同一个场景编号的所有图片必须落在同一个集合里。具体做法是统计文件名前缀根据前缀列表来划分 train/val/test而不是逐张图随机分配。这一步决定了你的指标有没有参考价值生产上线的决策不能建立在虚高的 mAP 上。6. 进阶用法把可口可乐识别模型调到能落地的程度数据本身能直接训练但距离部署到现场还有几步。先说迁移学习的细节。yolov8s.pt 是在 COCO 上预训练的COCO 里有 bottle 这个类别但不认识可口可乐的具体外观。训练时传freeze10可以冻结前 10 层 backbone让它先保留通用特征提取能力只训练后面的检测头对小数据集特别友好。等跑完一轮再用freeze0微调全部层精度还能再涨一点。然后是导出部署格式。训练完的 .pt 文件不能直接上生产环境导出成 onnx 是常见做法yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640 opset12导出后的 onnx 可以用 onnxruntime 或 TensorRT 推理速度比原生 PyTorch 快一个量级。导出时注意 imgsz 要和训练时一致不一致的话精度会有下降。最后是置信度阈值的调优技巧。很多场景的误检不是模型问题是阈值没调对。我习惯在验证集上跑一次完整的指标曲线找到 precision 和 recall 的交汇点那个位置通常是最优阈值。从那以后我每次拿到新数据集都会强制走一遍标签校验、按场景划分、训练、验证、导出 onnx 的完整流程这套动作做完心里才有底。希望帮到你。本文还有配套的精品资源点击获取