简介:面向计算机视觉入门与YOLO实战项目,这份罐装饮料识别数据集提供了多品牌商品的图像与标注资源,覆盖东鹏特饮、红牛、芬达、养乐多、可乐、雪碧、王老吉、AD钙奶、特仑苏、旺仔牛奶及薯片等常见商品,支持YOLOv11格式标注,可直接用于检测模型训练与效果验证,免去手动标注和格式转换的麻烦。压缩包整体约45.95MB,共2000个文件,包含321张jpg原图、1678个txt标注文件和1个yaml配置文件;其中txt为YOLO格式的框标注信息,yaml定义类别与数据集路径,结构简明。目前已有929人学习下载,适合课程设计、毕业设计或货架场景识别等实际项目。多品牌、多角度图像组合有助于提升模型泛化能力,标注细节也可供目标检测入门者作为练习素材,无论是快速验证算法还是准备小型演示项目,都能节省大量数据整理时间。
1. 罐装饮料识别:为什么一千多张标注图比一万张杂图更值得先试
做零售货架巡检、自助结算台或者库存盘点时,罐装饮料识别经常被当成“简单任务”提出来。真正上手才会发现:可乐、雪碧、芬达这类罐体在灯光下反光严重,红牛和东鹏特饮远看都是金罐,养乐多又是小瓶身——YOLOv11 跑通用目标检测很容易,要在一千多张带 YOLO 标记格式的图片上把手感调稳,坑比想象中多。这个 .zip 数据集恰好覆盖常见的薯片、东鹏特饮、红牛、芬达、养乐多、可乐、雪碧七类,适合用来验证检测管线、做算法选型,也适合刚接触目标检测的工程师拿一份真实标注数据跑通从解压、训练到部署的完整闭环。
2. 罐装饮料为什么难识别:外观混淆、小目标与 YOLOv11 的应对
2.1 七类目标的视觉边界:金罐、白罐、黄瓶和深色罐的区分度
先不急着写代码。目标检测数据集的质量问题,很多是从“类别之间到底靠什么区分”开始的。这七类在货架上各有各的麻烦。
可乐和雪碧是典型的深色罐和浅色罐。光照一变,深色罐身会直接糊成一片黑,浅色罐会过曝成白色;芬达罐体是橙黄色,但货架暖光灯一打,它和可乐、红牛之间的色块边界迅速收缩。真正要命的是红牛和东鹏特饮这一对:一个是蓝银色罐身配红牛标志,一个是金色罐身配东鹏标志,远看都是竖向金属罐,当目标宽度只有 100 像素以内时,颜色直方图的重叠度相当高,只靠颜色特征完全分不开。养乐多则是物理尺寸问题——它只有普通易拉罐三分之一高,在一张 640 分辨率的照片里往往只占 20 到 30 像素,正好落在小目标的尴尬区间。薯片袋又完全不同,长宽比明显,塑料包装在旋转、褶皱下有大量形变。
这说明任务难点不是“有没有目标”,而是“类别判别”和“小目标召回”。如果直接拿通用场景训练的权重测这份数据,最集中的两类错误是:红牛被标成东鹏特饮,以及养乐多整瓶漏检。这两个结论会反复出现在后面的混淆矩阵和验证日志里,提前知道能少走很多弯路。
2.2 YOLOv11 的骨架和检测头:小目标召回和类别判别靠什么
针对这种小样本 SKU 识别任务选 YOLOv11,主要不是因为它比前代“更强”,而是它在给定数据规模下的收敛速度、部署成本和调试路径最合适。YOLOv11 延续了 YOLO 系列的 anchor-free 检测头设计,输出端直接预测每个位置的目标中心和四条框边距离,省掉了预先匹配 anchor 的过程。一千多张图、七类目标,anchor 都收敛得不够稳定,anchor-free 天然少了一个需要人工干预的参数。
网络结构上,YOLOv11 使用多尺度特征融合,检测头分别接在深层、中层和浅层特征图上。对罐装饮料识别真正有用的是浅层特征图,它保留了更多纹理和边界信息,是小尺寸罐体被召回的主要通道。训练时把输入分辨率订到 640,画面角落里一个养乐多罐只有 20 像素时,实际上主要靠浅层特征图对应位置的单元去响应。这也是为什么后面会反复强调:调小目标的第一手段是抬高输入分辨率,而不是盲目加深网络。
YOLOv11 还有一个适合这个场景的特点:模型体积按 n/s/m/l/x 分档。这个数据集只有一千多张、七个类,用 n 或 s 档先跑基线完全足够。几百兆的 x 档在这种小数据上容易过拟合,而且推理速度只适合 GPU 服务端。真实货架巡检如果最终要落到嵌入式设备或 Jetson Nano 这类平台上,前期用 n 档调通管线,比一上来就上大模型省时间得多,这也是常被忽略的选型问题。
2.3 一千张图到底够不够:迁移学习、增强策略和类别平衡
先把结论写出来:一千多张图对七个 SKU 的检测任务,在预训练权重加持下是够用的。原因有三点:YOLOv11 自带的 COCO 预训练权重让模型已经学会了边缘、颜色块、物体形状这些通用视觉特征,迁移到罐装饮料只需要在顶层微调类别判别;罐装饮料结构高度规整,形变远小于行人、车辆这类通用目标;这份数据本质上是近封闭场景,背景大概率是货架、冰柜或桌面,分布比开放世界集中得多。
但“够用”有前提,前提就是增强策略要做对。常见做法是在训练时开启数据增强:Mosaic 把四张图拼成一张,等于免费扩大了小目标样本数量;HSV 扰动能缓解罐体反光带来的颜色抖动;随机翻转对薯片袋这类非对称目标要谨慎,翻转后文字方向反转,模型会学到错误的纹理特征。我一般会在第一批训练时把所有增强全开,等验证集 mAP 稳定后,再关闭翻转重训一版对比效果。
类别不平衡也需要单独看。东鹏特饮、可乐这类整箱货架图出现频率高,养乐多如果只在收银台附近出现,样本量可能差一个数量级。一个最简单的检查方式是用下面命令统计每个标签文件第一个数字的出现次数:
find drink_dataset/labels -name "*.txt" -exec awk '{print $1}' {} \; | sort | uniq -c运行后如果发现哪一类的计数小于 50,就优先补那类的图片。这一步不能省,因为训练 loss 曲线不会告诉你“哪一类没学好”,只有每类 AP 和混淆矩阵会暴露问题。
3. 解压 zip、校验标签和写 data.yaml:把数据集喂给 YOLOv11 前的三步
3.1 解压与目录约定:images 和 labels 的命名必须一一对应
拿到 zip 后先别急着训练。第一步是解压,然后确认目录结构。YOLOv11 的数据加载器默认在根目录下找 images 和 labels 两个兄弟目录,labels 下每个 txt 文件名必须和 images 下对应图片的 basename 一致,不含扩展名。所以解压后先看 zip 里是不是套了一层内层目录,否则后面的 train 路径会写错。
unzip drink_dataset.zip -d drink_dataset find drink_dataset -maxdepth 3 -type d常见结构有两种:一种是images/和labels/平级;另一种是多套一个train/或val/目录。如果只有一套图片,也不需要急着手工切分,YOLOv11 的 train 命令支持在读取时做比例划分,后面会提到。先保证 labels 里的 txt 数量和 images 里的图片数量一致,数量对不上时,下面的校验脚本就该上场了。
3.2 标签完整性校验:用一段 Python 找出缺失、越界和错类
标签格式是每行五个数字:class_id、x_center、y_center、width、height,全部是归一化小数,class_id 从 0 开始,七类就是 0 到 6。这就是“支持 yolov11 格式的标记”的含义——不是某种 YOLOv11 私有格式,而是 Ultralytics 直接能读的 txt 标记。题目里的类别顺序按“薯片、东鹏特饮、红牛、芬达、养乐多、可乐、雪碧”对应 ID 0 到 6,但拿到手后永远不要假设标注工具的导出顺序和这个一致,必须验证。
from pathlib import Path def validate(img_dir, lbl_dir, num_classes=7): problems = [] img_files = [p for p in Path(img_dir).glob("*.*") if p.suffix.lower() in (".jpg", ".jpeg", ".png", ".bmp")] for img in img_files: lbl = Path(lbl_dir) / (img.stem + ".txt") if not lbl.exists(): problems.append(f"missing label: {img.name}") continue for line in lbl.read_text().strip().splitlines(): parts = line.split() if len(parts) != 5: problems.append(f"bad line: {lbl.name}: {line}") continue cid = int(parts[0]) if not (0 <= cid < num_classes): problems.append(f"class id out of range: {lbl.name}: {cid}") x, y, w, h = map(float, parts[1:]) if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): problems.append(f"bbox abnormal: {lbl.name}: {line}") print(f"{len(img_files)} images, {len(problems)} problems") for p in problems[:20]: print(p) validate("drink_dataset/images", "drink_dataset/labels")这段脚本做的事很简单:找出没有对应标签的图片、行数不等于 5 的标签、class id 越界、以及边界框坐标异常。我实际跑这类数据集时遇到过两种翻车:一种是标注工具导出空 txt,文件存在但 0 字节;另一种是把坐标从像素值直接写进 txt,忘了归一化。这两种情况训练都能跑起来,但收敛结果会让人误以为模型不行。出现问题时回到标注阶段修复,不要带着问题训练。注意脚本对图片扩展名做了大小写兼容,对应了后面避坑章里要讲的.JPG问题。
3.3 写 data.yaml:类别顺序直接决定模型输出
YOLOv11 训练时读的是一个 data.yaml,而不是目录名。它告诉框架三件事:数据根目录在哪、训练验证图片在哪、七个类别名字按什么顺序排列。names 顺序写错是灾难性的——模型训练正常、推理正常,但输出标签和真实物体错位,比如把红牛框标成了芬达。更麻烦的是训练时标签同时错位,损失函数前期正常下降,属于典型的“看起来成功实则全错”。
path: drink_dataset train: images val: images names: 0: chips 1: dongpeng_te 2: redbull 3: fanta 4: yakult 5: cola 6: sprite写完后先用上一步的校验统计一遍实际出现的 class id,再和 names 列表核对。我的习惯是运行awk统计命令看最大 id 用到了几,如果 id 范围不是 0 到 6,比如出现 7 或 8,说明要么有错误标注,要么这套数据的类别定义和你手上的清单不一致,先回去查,不要继续训练。val 暂时指向 images 目录允许,但正式验证一定要留出独立验证集,否则 mAP 会虚高。
4. 训练与推理实操:用 YOLOv11 把罐装饮料识别跑通的最小闭环
4.1 环境准备:Ultralytics 与 PyTorch 的版本先对齐
训练 YOLOv11 用的是 Ultralytics 统一封装的目标检测框架,命令行工具叫yolo,Python 类名是YOLO。环境上最容易出问题的是 PyTorch 安装方式:CPU 版 torch 会把训练速度拖到无法接受,GPU 版又要匹配本机 CUDA 版本。我一般先在命令行里做一次环境检查,再决定怎么装。
python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))" pip install ultralytics如果第一行输出torch.cuda.is_available()为 False,说明装的是 CPU 版 PyTorch,需要重装对应 CUDA 版本的 wheel。PyTorch 2.x 搭配 CUDA 11.8 或 12.1 的组合被验证得比较多。pip install ultralytics会同时拉齐依赖,但不会自动帮你换 torch 版本,所以顺序很重要:先确认 torch 能用 GPU,再装 ultralytics。
4.2 训练命令与五个必调参数:从预训练权重开始做迁移学习
环境就绪后,训练命令非常短。关键是搞清楚每个参数在这份数据集上应该怎么设,下面是我在这个项目里的初始配置。
yolo train \ data=data.yaml \ model=yolo11n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ cache=True \ device=0 \ project=runs/drink \ name=v11n_baseline| 参数 | 取值 | 在这个数据集上的理由 |
|---|---|---|
| model | yolo11n.pt | 一千多张图先跑小模型,速度快,过拟合风险低 |
| epochs | 150 | 迁移学习不需要 300 轮,验证 loss 超过 100 轮不降就提前停 |
| imgsz | 640 | 兼顾速度与精度,后续为小目标可提到 960 |
| batch | 16 | 显存不够降到 8,但不要低于 4,否则 BN 统计会不稳定 |
| cache | True | 一千多张图能塞进内存,训练快很多 |
model=yolo11n.pt会自动下载预训练权重,如果本地网络受限就手动下载后指定路径。训练时重点看两个曲线:train/loss在下降,val/loss没有在中段拐头向上。如果val/loss从某个 epoch 开始持续上升,是典型过拟合信号,优先降 epochs 或加强数据增强,而不是立刻换大模型。七类目标会自动覆盖 YOLOv11 的输出头类别数,不需要手工改模型结构。
4.3 推理验证:保存带框结果、导出 ONNX 的常用参数
训练完,“yolov11 保存推理结果”和高频操作是 predict 子命令加save=True,它会把画好框的图片输出到runs/detect/predict目录,这是离线测试数据集最直观的做法。如果目标场景要上嵌入式设备,下一步导出 ONNX,把模型转成 half,推理速度会有明显提升。
yolo predict model=runs/drink/v11n_baseline/weights/best.pt \ source=test_imgs save=True conf=0.25 yolo export model=runs/drink/v11n_baseline/weights/best.pt \ format=onnx imgsz=640 opset=12conf=0.25是这类饮品检测的安全门槛:类别少、区分度尚可,0.25 能压住一部分误检。如果测试集上大量误检,上调到 0.4;如果漏检严重,降到 0.1 观察。导出 ONNX 时 imgsz 必须和训练时一致,否则小目标框的位置会偏移。opset=12 是兼容性比较好的设置,打算用 TensorRT 的话可以再单独导出对应格式。
5. 踩坑记录:罐装饮料识别从标注到推理的 5 个高频问题
5.1 罐体反光把半个框标成了背景,mAP 卡在 0.5 上不去
现象是:训练结束后精确率还行,召回率始终上不去,放大验证集发现金属罐的框都只有真实罐体的一半。
原因是标注阶段在原始大图上没问题,一旦图片经过缩放或手机拍摄时高光过曝,罐身高光区域接近纯白,肉眼觉得像背景,标注框就沿着亮面边界画了。解决这类问题的常见做法是开启更强的 HSV 增强,并针对反光样本做“过曝模拟”,随机提升图片亮度,让模型学会忽略高光区域。血泪经验是:货架上拍的逆光图宁可淘汰,也不要硬塞进训练集,模型对反光的注意力会冲掉其他样本学到的特征。
5.2 红牛和东鹏特饮互相误检:类别判别不过关的典型样本
现象是:验证集上红牛的框被标成东鹏特饮,置信度还挺高。
原因有两层:视觉上两者都是金属竖罐、颜色接近;数据上两类样本数量往往比例失衡,模型把“金色罐体”当成了东鹏特征。先去看类别统计命令的输出,如果红牛只有几十个标注框,优先补拍。解决方法是除了补样本,把输入分辨率从 640 提升到 960,让罐身文字区域产生足够纹理特征;再观察混淆矩阵,如果两个类别长期互扰,可以在后处理加一条规则:同一个位置同时被两个类别以接近的置信度命中时,按货架先验保留其中一个。这条规则不复杂,但往往是这类 SKU 项目从 85% mAP 提到 92% 的关键一步。
5.3 zip 解压后图片和标签错位:后缀大小写和嵌套目录的连锁坑
现象是:训练日志里图片数量正常,但 loss 很大,打开验证图片发现框画在错误位置。
原因是标注包压缩时套了多层目录,或者在 Windows 下解压后图片后缀变成.JPG,YOLOv11 在 Linux 下按*.jpg匹配时漏掉大部分文件;另一种情况是图片和标签本来在 zip 的不同目录层级,解压后没有对齐。解决方法是先运行find drink_dataset -maxdepth 3 -type d看层级,再用脚本统一后缀。文件名里有空格和中文是另一个隐性坑,建议统一改成beverage_00001.jpg这类命名。YOLOv11 能处理中文路径,但到 ONNX 导出和部署阶段中文路径会带来不必要的麻烦。
5.4 训练到一半 loss 跳成 NaN:学习率和坏标签的双重嫌疑
现象是:前几十个 epoch 正常,某个 epoch 开始 loss 变成 NaN 并且不再恢复。
原因排查优先做两件事。一是学习率设置太高,迁移学习在较小数据集上,1e-2 级别会把损失推到爆炸;二是数据里有 0 面积或负值标签框,这些坏框在损失函数里产生无穷值。解决方法是先别慌,把学习率降到lr0=0.0005量级重跑;同时用 3.2 的校验脚本对全部标签做一遍体检,特别关注那些 0 字节空 txt。空 txt 会被 YOLOv11 跳过,造成实际训练图片比预期少几百张,loss 曲线也会因此抖动。batch 太小的情况下 BN 统计不稳定,训练后期同样容易发疯,建议 batch 至少 8。
5.5 货架远端和冰柜深处的小罐体漏检:小目标优化的三个切入口
现象是:贴近镜头的罐子全部正常,图片深处或货架高层的罐体几乎一个都检不出来。
原因是这些目标在 640 分辨率下只有 15 到 25 像素,经过检测头几次下采样后信息被压缩殆尽。解决优先级:第一,把 imgsz 从 640 提高到 960 或 1280,这一步对小目标最有效,改动也最小;第二,训练时保留 Mosaic 增强,它会把小目标拼贴到大图上,等价于增加小目标出现频率;第三,仍然不行就做切片推理,把一张大图切成 2x2 或 3x3 的小块分别推理,再把坐标映射回原图,最后用 NMS 合并重叠框。注意切片推理的速度是未切片的 3 到 5 倍,验证时不要和 batch 推理的速度混为一谈。
6. 再进一步:用混淆矩阵和切片推理验证你的真实数据分布
模型训练完,我不会立刻看总 mAP,而是先跑一次带输出结果的验证。
yolo val model=runs/drink/v11n_baseline/weights/best.pt \ data=data.yaml plot=True save_json=Trueplot=True会在验证输出目录生成混淆矩阵和一批带预测框的验证图片。我会对照混淆矩阵看两点:主对角线是否够亮,红牛与东鹏特饮的混淆块是否对称。如果不对称,说明某个类别样本量明显偏少,这是补数据的方向,不是调参的方向。
下一步做切片推理验证。下面这个脚本把验证图切成小块分别推理,再合并坐标,用来对比不切片版本的小目标召回率提升情况。
from ultralytics import YOLO import torch import torchvision model = YOLO("runs/drink/v11n_baseline/weights/best.pt") def slice_infer(img, patch_size=640, overlap=0.1): boxes, scores = [], [] h, w = img.shape[:2] step = int(patch_size * (1 - overlap)) for y0 in range(0, h, step): for x0 in range(0, w, step): patch = img[y0:y0 + patch_size, x0:x0 + patch_size] res = model(patch, conf=0.25, verbose=False)[0] for bbox, score in zip(res.boxes.xyxy.tolist(), res.boxes.conf.tolist()): x1, y1, x2, y2 = bbox boxes.append([x0 + x1, y0 + y1, x0 + x2, y0 + y2]) scores.append(score) keep = torchvision.ops.nms(torch.tensor(boxes), torch.tensor(scores), 0.5) return [boxes[i] for i in keep.tolist()]这是一个临时验证用的简化思路,实际工程可以用现成的切片推理库,但自己写几十行反而更可控。overlap 默认 0.1,如果目标恰好卡在 patch 边界被截断,把 overlap 加到 0.3 即可,代价是多算一些重复区域。
最后提醒一个小习惯:数据集图片统一改成纯数字前缀文件名。zip 解压、Docker 挂载、ONNX 部署三个环节都会顺很多,这是我从多个饮料识别项目里踩出来的教训。希望帮到你。
本文还有配套的精品资源,点击获取