简介:本资源为可口可乐与百事可乐标志目标检测数据集,面向从事目标检测算法练习、品牌识别或零售场景分析的学生与开发者,可用于训练和验证两类别检测模型。数据集共2223张jpg图片,每张均配有对应的VOC格式xml标注与YOLO格式txt标注,标注类别为CocaCola和Pepsi,其中CocaCola框数2268、Pepsi框数2398,总框数4666,采用labelImg工具按矩形框规则完成标注。压缩包内文件总数约2000个,以xml标注文件为主,另含说明文件,整体大小120.84MB,目录结构简洁,便于直接接入常见检测框架。目前已有347人学习下载,适合需要快速获取双类别标注数据、验证模型训练流程或进行迁移学习实验的读者参考使用。
1. 可口可乐与百事可乐标志检测:2220 张双标签数据集到底能做什么
快消品货架审核、自动售货柜库存盘点、广告植入统计,这些场景里最常被问到的一个问题就是:画面里到底有没有可口可乐或百事可乐的标志,在哪个位置。要回答这个问题,绕不开目标检测数据集。手上这份「可口可乐与百事可乐标志检测数据集2220张2个标签VOC+YOLO格式」就是为这类任务准备的:2220 张图像,2 个类别标签,同时提供 VOC 与 YOLO 两套标注格式。它解决的不是「识别饮料瓶」这种粗粒度问题,而是把品牌标志当作独立目标框出来,属于典型的细粒度小目标检测。适合谁用?想跑通 yolov8目标检测数据集处理流程的新手,想验证品牌 logo 检测方案的算法工程师,以及需要快速搭一个货架识别原型的开发者。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲。
2. 双标签 VOC+YOLO 数据集的结构与选型逻辑
2.1 2220 张、2 个标签意味着什么
先把数字翻译成工程语言。2220 张图像、2 个类别,平均每类样本量在千级,属于中小规模数据集。这个量级决定了它不适合从零训练一个大 backbone,而是更适合迁移学习:加载 yolo预训练模型下载得到的权重,冻结主干、微调检测头。2 个标签的好处是类别极度聚焦,模型不需要在几十个类之间分配容量,收敛快、混淆矩阵干净,很适合用来验证「小目标 + 相似外观」这个难点。
需要提前有心理预期的是类别不平衡。可口可乐和百事可乐在真实图像里的出现频次往往不对等,如果某一类只有几百张,训练时该类召回会明显偏低。动手前先统计每类实例数,别等训练完看混淆矩阵才发现某一类几乎没学到。
2.2 VOC 与 YOLO 两套格式的差异与转换
VOC 格式用 XML 描述,每个目标一个<object>节点,坐标是绝对像素值xmin/ymin/xmax/ymax;YOLO 格式用 txt,每行class cx cy w h,坐标是归一化到 0~1 的相对值。两者不是简单缩放关系,还涉及类别名到类别索引的映射。常见做法是保留一份类别映射表,转换时严格按表来,避免索引错位。
| 维度 | VOC | YOLO |
|---|---|---|
| 文件形式 | 每图一个 XML | 每图一个 txt |
| 坐标 | 绝对像素 xmin,ymin,xmax,ymax | 归一化 cx,cy,w,h |
| 类别 | 字符串 name | 整数索引 |
| 目录 | Annotations / JPEGImages | images / labels |
提示:转换前先备份原始标注。VOC 转 YOLO 是不可逆的有损操作(浮点归一化会丢精度),一旦覆盖原始 XML,后悔药没有。
2.3 目录组织与最小校验脚本
拿到数据集先别急着训练,跑一遍完整性校验,确认每张图都有对应标注、坐标不越界、类别名统一。下面这段脚本做三件事:统计图像与标注配对情况、检查 VOC 坐标是否越界、输出每类实例数。
import os, glob import xml.etree.ElementTree as ET from collections import Counter IMG_DIR = "JPEGImages" ANN_DIR = "Annotations" imgs = {os.path.splitext(os.path.basename(p))[0] for p in glob.glob(f"{IMG_DIR}/*.jpg")} xmls = {os.path.splitext(os.path.basename(p))[0] for p in glob.glob(f"{ANN_DIR}/*.xml")} # 1. 配对检查:找出有图无标注、有标注无图 print("有图无标注:", imgs - xmls) print("有标注无图:", xmls - imgs) cls_counter = Counter() bad_box = 0 for x in glob.glob(f"{ANN_DIR}/*.xml"): root = ET.parse(x).getroot() size = root.find("size") W, H = int(size.find("width").text), int(size.find("height").text) for obj in root.findall("object"): cls_counter[obj.find("name").text] += 1 b = obj.find("bndbox") x1, y1 = int(b.find("xmin").text), int(b.find("ymin").text) x2, y2 = int(b.find("xmax").text), int(b.find("ymax").text) # 2. 越界检查:坐标必须落在图像范围内且左上小于右下 if x1 < 0 or y1 < 0 or x2 > W or y2 > H or x1 >= x2 or y1 >= y2: bad_box += 1 print("每类实例数:", dict(cls_counter)) print("越界框数量:", bad_box)逻辑说明:先用集合差集找出配对缺失,这是数据集最常见的低级错误;再遍历 XML 检查坐标合法性。参数上IMG_DIR、ANN_DIR按实际目录名改,bad_box大于 0 就要人工回看,通常是标注时框拖出了边界。类别名如果出现「coca」「Coca」「cocacola」这类不一致写法,必须在转换前统一,否则会被当成多个类。
3. 从 VOC 转 YOLO 到跑通 yolov8 训练
3.1 VOC 转 YOLO 的转换脚本与四个边界坑
转换脚本本身不长,坑都在边界上。下面这段把 VOC 的 XML 批量转成 YOLO 的 txt,同时生成classes.txt。
import os, glob import xml.etree.ElementTree as ET CLASSES = ["coca_cola", "pepsi"] # 顺序即索引,务必与后续训练配置一致 cls2id = {c: i for i, c in enumerate(CLASSES)} ANN_DIR = "Annotations" OUT_DIR = "labels" os.makedirs(OUT_DIR, exist_ok=True) for x in glob.glob(f"{ANN_DIR}/*.xml"): root = ET.parse(x).getroot() size = root.find("size") W, H = int(size.find("width").text), int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in cls2id: continue # 未登记类别直接跳过,避免索引错位 b = obj.find("bndbox") x1, y1 = float(b.find("xmin").text), float(b.find("ymin").text) x2, y2 = float(b.find("xmax").text), float(b.find("ymax").text) # 裁剪到图像范围内,防止归一化后出现负值或大于1 x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(W, x2), min(H, y2) cx, cy = (x1 + x2) / 2 / W, (y1 + y2) / 2 / H w, h = (x2 - x1) / W, (y2 - y1) / H lines.append(f"{cls2id[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") stem = os.path.splitext(os.path.basename(x))[0] with open(f"{OUT_DIR}/{stem}.txt", "w") as f: f.write("\n".join(lines))四个边界坑:一是类别索引顺序,CLASSES列表顺序必须和训练时的data.yaml完全一致,错一位整个训练白跑;二是坐标裁剪,标注框偶尔会超出图像边界,不裁剪归一化后会出现负值,YOLO 会直接报错或静默丢弃;三是空标注文件,某张图没有目标时也要生成空 txt,否则 YOLO 会把它当负样本处理逻辑不一致;四是浮点精度,保留 6 位小数足够,截断太狠会让小目标框偏移。
3.2 data.yaml 与目录结构
YOLO 训练靠data.yaml找数据,路径写错是最常见的翻车点。推荐结构如下:
dataset/ images/ train/ val/ labels/ train/ val/ data.yamlpath: ./dataset train: images/train val: images/val nc: 2 names: 0: coca_cola 1: pepsinc是类别数,必须等于names的条目数;names的索引要和转换脚本里的cls2id对齐。path用相对路径时,训练命令要在dataset的上一级目录执行,否则会找不到文件。
3.3 用 yolov8 跑通第一次训练
环境配置建议用 conda 隔离,避免和系统里的其他 torch 版本打架。
conda create -n logo python=3.10 -y conda activate logo pip install ultralytics训练命令:
yolo detect train \ data=dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ name=coke_pepsi参数说明:model=yolov8n.pt用 nano 版预训练权重,2220 张的量级足够,想提精度再换yolov8s.pt;imgsz=640是标准输入,标志属于中小目标,不建议降到 416,会丢细节;batch=16按显存调,V100 上可以拉到 32 甚至 64;patience=20表示 20 轮无提升就早停,省时间。训练中重点看mAP50和每类的P/R,如果某一类召回长期为 0,回去查类别索引和该类样本数。
3.4 推理与结果验证
训练完用验证集跑一遍,确认模型真的学到了东西,而不是只记住了背景。
yolo detect predict \ model=runs/detect/coke_pepsi/weights/best.pt \ source=dataset/images/val \ conf=0.25 \ save=Trueconf=0.25是置信度阈值,标志检测建议先设低一点看召回,再根据误检情况往上调。验证时别只看 mAP 数字,把预测图翻一遍,重点看两类标志是否互相误判——这是本数据集最典型的失败模式,两个品牌配色和字体风格接近,模型容易混淆。
4. 训练中的避坑与排查清单
4.1 混淆矩阵总合不唯一
现象:跑完训练看混淆矩阵,行和列加起来对不上,或者某一类的总数和验证集实际实例数不一致。原因通常是验证集里存在被裁掉边界的框,或者转换时坐标越界被丢弃,导致实际参与评估的实例数少于标注数。解决:回到 2.3 的校验脚本,把越界框全部修掉再重新转换,确保验证集标注和图像严格对应。
4.2 训练中 BN 崩溃
现象:loss 突然变成 NaN,日志里出现 batch norm 相关的异常。原因多半是某个 batch 里有效目标太少,或者学习率过高导致梯度爆炸。解决:先把batch调大让每个 batch 有足够样本,再把初始学习率降到0.001甚至更低,同时开启amp=False排除混合精度干扰,定位是不是精度问题。
4.3 小目标召回上不去
现象:mAP 看着还行,但小尺寸标志经常漏检。原因:标志在整图里占比小,下采样后特征几乎消失。解决:把imgsz提到 800 或 960,或者开启多尺度训练scale=0.5,让模型见到不同尺寸的目标;数据层面可以对小目标图像做裁剪增强,人为放大标志占比。
4.4 两类互相误判
现象:可口可乐被识别成百事可乐,反之亦然。原因:两个品牌视觉特征接近,且训练样本里某一类偏少。解决:检查类别是否真的不平衡,必要时对少样本类做过采样;推理时适当提高conf阈值,宁可漏检也别错判,因为品牌错判在业务上比漏检更严重。
4.5 验证集指标虚高
现象:验证集 mAP 很高,换一批真实货架图就崩。原因:验证集和训练集来自同一批拍摄条件,分布太接近,模型过拟合了场景而不是目标。解决:手动留出一部分不同光照、不同角度的图做独立测试集,别用随机划分的验证集当最终结论。
5. 把这份数据集用出上限的几个技巧
想让 2220 张发挥更大价值,关键在数据增强和评估策略。增强上,颜色抖动要慎用——两个品牌的标志颜色本身就是判别特征,HSV 扰动过大会把红色调成橙色,反而制造噪声;更安全的是随机缩放、平移和轻微旋转,模拟货架拍摄的角度变化。 mosaic 增强对 YOLO 很有效,但小目标场景下四图拼接会让标志更小,建议把mosaic概率从默认 1.0 降到 0.5。
评估上,别只信 mAP。品牌检测业务里,误检和漏检的代价不对称,我会单独统计两个指标:一是每类在conf=0.5下的精确率,二是把conf从 0.1 扫到 0.9 画 P-R 曲线,找到业务能接受的平衡点。如果要做部署,导出 ONNX 后用onnxruntime跑一遍,确认推理结果和 PyTorch 一致,这一步能提前暴露算子不支持的问题。
还有一个容易被忽略的点:把类别名和索引固化到配置文件里,别散落在脚本各处。我吃过这个亏——转换脚本改了类别顺序,训练配置忘了同步,跑了一晚上发现两类标签整体错位,只能重来。现在我的习惯是类别映射只维护一份classes.txt,转换、训练、推理全部读它。希望帮到你。
本文还有配套的精品资源,点击获取