简介面向智能零售柜商品检测场景的目标检测数据集配套说明文档主要服务于新零售算法开发者、相关专业学生及竞赛团队解决智能零售柜场景中商品检测训练数据获取与快速上手问题。资源以 PDF 形式提供共 1 个文件、大小 5.77MB内附数据集基本情况介绍与网盘获取方式该数据集包含 5000 张真实智能零售柜监控场景图片涵盖罐装饮料、袋装零食等常见商品标注类别多达 113 个。数据标注采用 LabelImg 完成并提供 VOC(XML)、COCO(JSON)、YOLO(TXT) 三种格式可直接用于 YOLO 等主流算法训练。同时文档介绍了配套的 YOLO11 一键训练脚本支持 GPU、CPU、Mac(M 芯片) 多平台运行并提供博主训练结果日志作为参考。目前已有 413 人学习浏览适合希望快速获得高质量商品检测数据并完成模型训练验证的中高级开发者也可作为新零售场景算法落地的参考数据储备。1. 智能零售柜商品检测这套数据集和脚本把你从最劝退的三件事里解放出来在智能零售柜项目里算法团队接到最多的需求不是训练一个模型而是在柜子里把商品认准。柜内摄像头位置固定、隔着玻璃、有反光有遮挡盒子里的饮料和零食还经常前后叠放公开数据集上的模型搬过来往往直接失灵。这个标题打包了三样东西5000张针对柜内商品检测的图片每张图都配好VOC、COCO、YOLO三种格式的标签文件外加一套能够在GPU、CPU、Mac三平台直接跑起来的YOLO11一键训练脚本。一句话说清楚它的价值不教你造轮子而是把找数据、转格式、配环境这三件最容易劝退的事先替你做完让你把精力花在调模型和上线。适合手里有零售柜业务但算法刚起步的团队也适合想完整跑一遍YOLO11训练到部署流程的开发者。2. 5000张图怎么设计才不白拍采集规范、类目划分与标注卡控数据集的照片数量只是表象真正决定模型上限的是采集时的场景覆盖和标注时的一致性。5000张图听起来不多但零售柜是受限场景SKU数量有限、摄像头位置固定、商品排列有一定规律只要采集设计到位这个量级足够把模型训到可用的程度。2.1 采集方案光照、角度、SKU差异怎么控制常见做法是分批次采集而不是一天拍完。第一批覆盖不同柜型、不同层板高度、不同摄像头安装角度第二批专门去覆盖补货前、补货中、补货后的状态差异第三批则针对同一个SKU的不同摆放姿态和数量组合。每批之间留出时间间隔这样能自然引入光照变化——早上的阳光、中午的灯光、夜间的补光灯对柜内玻璃反光和商品颜色都会产生实质影响。相机选型上要注意分辨率与视场角的匹配。柜内摄像头常见的是200万到500万像素如果图幅里一个饮料瓶只有十几个像素宽那后续再怎么调模型都救不回来。我一般会先拍几张测试图算一下最小目标在图像里的像素尺寸目标短边小于20像素的要么调整安装位置要么放弃这类极端场景。每个SKU的样本量不能平均分配。零售柜里的销量是长尾分布爆款商品占大多数冷门商品偶尔才出现。采集时按照爆款多拍、冷门保底的原则保证每个SKU至少有80到150个标注实例而不是机械地每类拍100张。另外一定要拍一批空柜图和只有背景的图这部分在训练时作为负样本能够显著降低空柜误报——零售柜的场景里空柜误报比漏检更让运营头疼。2.2 标注口径框怎么打、边界怎么切、极小目标怎么处理标注工具的选择直接影响效率。常见做法是先用LabelImg做VOC格式的初标再用X-AnyLabeling这类支持半自动辅助的工具做二次标注。工具本身不关键关键是标注口径要在动工前定死否则返工成本极高。框的边界以商品的可见轮廓为准。完整露出的商品按最小外接矩形标被遮挡的商品只标可见部分不凭想象把被挡住的部分补全对于夹在两件商品之间、可见面积小于整体20%的直接放弃不标。这里有个容易松动的细节边缘贴边的商品框偏移1到2个像素问题不大但如果每张图都习惯性地往里收一点最后框会整体偏小影响mAP计算。极小目标的处理需要单独定规则。柜内摄像头视角下底层的瓶装水经常因为透视关系变得很小这类目标如果完全放弃模型就学不到远处也有商品但如果硬标标注噪声又会很大。我的做法是短边小于12像素的目标直接跳过12到20像素的目标正常标注但这类目标在质检时会被重点抽检。质检环节至少要过两道关。第一道关是脚本自动检查统计每张图的标注框数量、框的宽高分布、有没有越界坐标第二道关是人眼抽检按5%到10%的比例随机抽图重点看遮挡和极小目标的框是否贴合。如果两道关通过率低就让标注员返工。2.3 类目设计与数据分布想要模型不飘先让分布别歪类目设计上最常见的错误是把品牌口味容量全拼进一个类目名导致类目数爆炸、每类样本量都不够。合理做法是先按货道上的摆放单位来定义类目比如某品牌可乐330ml罐装是一个类目500ml瓶装是另一个类目但不同口味的同容量包装可以合并因为模型看到的外观几乎一样。5000张图对应的类目数建议控制在15到30个之间。类目太少模型体现不出区分能力类目太多每类的平均样本量会被稀释。表格里的分布可以按这个思路设计类目类型建议占比说明爆款SKU40%单类样本量最大负责保证主体召回率常规SKU40%每类保持均衡覆盖不同摆放姿态冷门SKU15%样本量少但必须保留防止漏检负样本/空柜5%无目标图压制空柜误报训练时如果发现类别不均衡导致冷门类AP很低不要急着删数据优先试类目权重或者对冷门类做简单的mosaic增强后面避坑章会细讲。3. VOC/COCO/YOLO三种标签格式转换逻辑、目录组织与四个边界坑同一个数据集给出三种格式听起来是重复劳动实际上是在兼容不同的训练习惯和工具链。VOC适合配合LabelImg等工具做二次修正COCO适合用Detectron2或mmdetection跑对比实验YOLO是YOLO系列训练脚本直接吃的格式。多数情况下你只需要其中一种但保底给全可以避免后续工具链切换时重新标注。3.1 三种格式的组织形态和本质区别VOC格式是一张图对应一个XML文件XML里记录每个目标的name、pose、truncated、difficult和bndbox坐标COCO格式是把所有图片和目标汇总到一个JSON文件里通过images、annotations、categories三个数组建立关联YOLO格式则是每张图对应一个txt文件每行是class_id x_center y_center width height坐标做了归一化。三者本质上记录的是同一批框但组织方式和坐标表达不同。维度VOCCOCOYOLO文件组织JPEGImages Annotations ImageSets单JSON 图片目录images labels坐标表达xmin, ymin, xmax, ymax绝对像素bbox为x, y, w, h绝对像素x_center, y_center, w, h归一化0~1类别表达文件夹名或XML内namecategories数组的idtxt每行首列的class_id典型适用目标检测教学、标注工具mmdetection、Detectron2YOLO系列训练栈容易忽略的一个点是YOLO的归一化坐标是相对于原图宽高的如果训练时imgsz设置为640而原图是1280宽模型内部会先做resize标签跟着一起缩放不需要你手动改坐标。但COCO的bbox如果不经过resize就直接用就会和训练时的像素坐标对不上这一点在混用工具时经常翻车。3.2 转换脚本一条命令把VOC转成COCO和YOLO拿到一个VOC标注的数据集最常见的诉求是同时产出COCO和YOLO格式。下面这个脚本可以完成转换同时做了坐标越界检查和空标注检查。import os import xml.etree.ElementTree as ET import json from collections import defaultdict def voc_to_yolo_and_coco(xml_dir, img_dir, output_dir, class_list): xml_dir: VOC格式XML文件目录 img_dir: 图片目录 output_dir: 输出目录 class_list: 类别列表顺序即YOLO的class_id顺序 os.makedirs(f{output_dir}/images, exist_okTrue) os.makedirs(f{output_dir}/labels, exist_okTrue) class_to_id {name: idx for idx, name in enumerate(class_list)} coco_images, coco_annotations, coco_categories [], [], [] ann_id 0 for xml_name in sorted(os.listdir(xml_dir)): if not xml_name.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_name)) root tree.getroot() img_file root.find(filename).text width int(root.find(size/width).text) height int(root.find(size/height).text) img_id len(coco_images) coco_images.append({ id: img_id, file_name: img_file, width: width, height: height }) # 每个带标注的XML生成一个同名txt txt_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_to_id: continue bndbox obj.find(bndbox) xmin int(float(bndbox.find(xmin).text)) ymin int(float(bndbox.find(ymin).text)) xmax int(float(bndbox.find(xmax).text)) ymax int(float(bndbox.find(ymax).text)) # 越界修正坐标不能超出图片范围 xmin max(0, xmin); ymin max(0, ymin) xmax min(width, xmax); ymax min(height, ymax) if xmax xmin or ymax ymin: continue # YOLO归一化坐标 x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height txt_lines.append(f{class_to_id[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) # COCO标注 coco_annotations.append({ id: ann_id, image_id: img_id, category_id: class_to_id[name], bbox: [xmin, ymin, xmax - xmin, ymax - ymin], area: (xmax - xmin) * (ymax - ymin), iscrowd: 0 }) ann_id 1 txt_name os.path.splitext(xml_name)[0] .txt with open(f{output_dir}/labels/{txt_name}, w) as f: f.write(\n.join(txt_lines)) for cls in class_list: coco_categories.append({id: class_to_id[cls], name: cls}) with open(f{output_dir}/annotations.json, w) as f: json.dump({images: coco_images, annotations: coco_annotations, categories: coco_categories}, f, indent2) print(f处理完成共 {len(coco_images)} 张图{ann_id} 个标注框)脚本的逻辑分三段第一段解析XML读取图片名、尺寸和每个目标的坐标第二段对坐标做越界修正同时过滤掉宽高为负的脏数据然后分别生成YOLO格式的txt行和COCO格式的annotation记录第三段汇总写入annotations.json。参数说明class_list的顺序决定了YOLO格式里的class_id一定要和后续训练时data.yaml里的类别顺序保持一致否则会出现模型训完了输出标签全错位的诡异问题。另外代码里使用了sorted排序扫描XML文件保证多次运行生成的image_id稳定这对后续增量标注很关键。3.3 格式转换中常见的四个边界坑坐标越界是最常见的问题。标注工具偶尔会产出一个框超出图片边界YOLO的归一化坐标因此会大于1或小于0。这类脏数据不处理训练时loss会突然跳一个尖峰然后模型开始震荡。上面的脚本用clamp方式做了裁剪但如果越界特别严重的框裁剪后宽高变得很窄也不是好样本建议直接过滤。空标注文件会让训练中断。有些图确实没有目标但YOLO训练时读到空的txt文件会报no labels found。处理方式是在转换后加一个检查逻辑把所有空txt文件记录到exclude列表中在data.yaml里用exclude配置跳过这些图。浮点输出精度是隐蔽问题。YOLO的标签文件用float保存归一化坐标如果只保留3位小数对于4000像素宽的图误差会达到几个像素训练时影响不明显但转回VOC做人工校验时框会明显偏移。我在设计上保留6位小数就是这个原因。类别顺序漂移是最难排查的坑。很多人把VOC转YOLO后只改了数据路径没有重新生成data.yaml训练跑起来loss正常下降但预测结果却张冠李戴——名字是可乐框里是薯片。排查方式就是检查labels文件夹里txt文件第一行的数字范围并用脚本把class_id映射回类别名逐一核对。4. YOLO11一键训练脚本GPU/CPU/Mac三平台的环境准备与参数调优YOLO11的训练入口收敛在Ultralytics框架上官方API暴露得比较干净但一键训练真正要解决的是环境探测和参数默认值的问题。三个平台的坑完全不一样GPU踩的是CUDA和torch版本匹配Mac踩的是MPS后端兼容性CPU踩的是慢到让人怀疑人生。4.1 三平台环境差异CUDA、MPS、CPU fallback怎么选NVIDIA GPU平台第一步不是装ultralytics而是确认torch能不能调用显卡。很多人在Windows上装完torch发现CUDA不可用症结在于pip默认安装了CPU版本。常见做法是先去PyTorch官网用conda或者pip命令安装对应CUDA的torch再回来装ultralytics。如果你的显卡比较新报错requires device with capability (9, 0) but your GPU has capability (12, 0)说明torch版本太老不认识新卡升级torch即可不用怀疑显卡坏了。Mac平台的Apple Silicon芯片支持MPS加速torch可以直接利用但注意MPS后端的内存管理和CUDA不太一样batch size开得比同显存GPU小。检测是否可用的标准写法如下import torch def get_device(): if torch.cuda.is_available(): return cuda elif hasattr(torch.backends, mps) and torch.backends.mps.is_available(): return mps else: return cpu device get_device() print(f当前训练设备{device})这段代码的核心是探测顺序优先CUDA其次MPS最后CPU。hasattr(torch.backends, mps)是必要的因为老版本torch在非Mac平台上根本没有mps这个属性直接访问会报AttributeError。CPU平台跑YOLO11不是不能跑而是要做好心理准备。5000张图、640分辨率、100个epoch在主流CPU上大概需要几十个小时。建议只在环境验证或小数据调试时用CPU正式训练还是找一台GPU机器。4.2 一键训练脚本骨架一份配置同时管住数据和模型训练脚本的常见做法是拆成train.sh和train.py两个文件shell负责探测平台并设置并行参数Python文件负责加载数据和模型。核心逻辑是自动探测设备、自动设置worker数、自动选择预训练权重路径。#!/bin/bash # train.sh - 一键训练YOLO11 # 用法: ./train.sh set -e # 自动探测设备类型 if python -c import torch; torch.cuda.is_available() 2/dev/null | grep -q True; then DEVICEcuda elif python -c import torch; print(torch.backends.mps.is_available()) 2/dev/null | grep -q True; then DEVICEmps else DEVICEcpu fi echo 使用设备: $DEVICE # Mac与CPU平台建议减少worker数避免内存占满 if [ $DEVICE cpu ]; then WORKERS0 else WORKERS4 fi python train.py --device $DEVICE --workers $WORKERS# train.py import argparse from ultralytics import YOLO def main(): parser argparse.ArgumentParser() parser.add_argument(--device, defaultcuda) parser.add_argument(--workers, typeint, default4) parser.add_argument(--imgsz, typeint, default640) parser.add_argument(--epochs, typeint, default100) parser.add_argument(--batch, typeint, default16) args parser.parse_args() data_yaml data.yaml # 里面标注train/val路径和类别列表 model YOLO(yolo11n.pt) # n/s/m/l/x按显存和精度选择 model.train( datadata_yaml, epochsargs.epochs, imgszargs.imgsz, batchargs.batch, deviceargs.device, workersargs.workers, patience20, # 验证集20轮不提升就早停 lr00.01, # 初始学习率 weight_decay0.0005, projectruns/retail_cabinet, nameexp1, exist_okTrue, ) if __name__ __main__: main()这里有一个值得强调的参数patience早停。5000张图的训练集不算大如果在基础学习率下硬跑满100个epoch后半段大概率在过拟合。设patience20后验证集mAP连续20个epoch不提升就会自动停止省下来的时间可以做AB实验。workers参数在Mac上很敏感。MPS模式下dataloader开太多worker会导致内存吃紧甚至训练中途被杀进程。CPU平台则直接设0用主进程加载数据速度慢但稳定。batch参数需要根据显存调整16是一个在8GB显存下比较稳妥的起点显存不够就往下调到8或4。4.3 训练参数怎么调针对柜内商品的关键项imgsz优先设640而不是1280。柜内摄像头图片往往超过1000万像素但商品检测吃的是局部纹理640分辨率下饮料瓶的图案已经足够分辨。直接设1280会让显存占用翻倍训练时间拉长mAP提升却可能不到1个点。如果一定要提高小目标召回优先用YOLO11自带的tile切图推理而不是把整体分辨率拉高。预训练权重的选择上yolo11n.pt速度最快、显存需求最低适合先跑通流程yolo11m.pt或yolo11l.pt在精度上有明显提升但Mac上跑m系列基本会把内存吃满。建议先拿n系列确认数据集没问题再换m系列正式训。学习率参数在5000张图这种规模下不要乱动。lr00.01是Ultralytics的默认值配合默认的cosine衰减即可。如果你发现loss前期下降太慢先检查数据再检查标签——八成是类别id错位或者有脏标签九成不是学习率的问题。5. 智能零售柜场景的五大坑小目标、反光、遮挡与误检排查手记零售柜场景的检测难点和通用目标检测不太一样场景受限、目标种类固定、但拍摄条件很恶劣。这一章把实际调试中最常见的五类问题按现象、原因、解决的顺序拆开。5.1 小瓶饮料总是漏检置信度还在0.3以上徘徊现象底层的330ml罐装饮料经常漏检或者检出来了但置信度很低训练日志里该类别的AP明显低于其他类。原因小目标在YOLO11的深层特征图里已经丢失了空间细节。模型下采样32倍后一个30像素的小罐子只剩下不到一个特征点自然无法区分品牌。解决先在数据集层面确认这类目标的数量是否足够不够就补充采集。如果数量够了训练时开启YOLO11的增强参数特别是scale和translate让模型见过更多小尺寸的变体。仍然不行的换yolo11m或更大模型大模型的浅层特征图尺寸更大对小目标更友好。直接改模型结构加检测头的做法对于不熟悉源码的人来说得不偿失。5.2 玻璃反光导致同一个商品被框两次现象带弧形玻璃的柜门一处反光区域被模型识别成商品一个真实商品旁边多出一个高置信度的虚框。原因反光区域的颜色和纹理与真实商品的包装高度相似尤其是在光线角度变化时。模型学到的是这个颜色形状像商品而不是商品应该在这里。解决训练集里混入带反光的负样本图让模型见到更多反光但不含目标的情况。如果反光集中在特定位置比如柜门中部可以在预处理时对该区域做针对性数据增强。线上的兜底手段是加入NMS优化策略将IoU阈值调低一些让同一商品附近重复的框被压掉。5.3 遮挡严重时框只框住了露出来的半边现象瓶装水被前排零食挡住一半标注框只框住露出的部分训练时模型学到的是半个商品的特征预测结果框明显偏小。原因标注口径上只标可见部分的策略在遮挡角度小的时候是有效的但当遮挡占比超过30%时可见部分的框和完整商品的框差异会让模型无所适从。解决给这部分遮挡样本单独设置规则可见面积大于50%的正常标注可见面积在20%到50%之间的标注框稍微外扩10%到15%贴合商品的实际物理边界。这种做法虽然牺牲了一点标注一致性但模型学出来的框更贴近真实使用场景因为线上预测时每个商品都有一个物理边界。5.4 爆款类AP刷到0.95冷门类只有0.3现象训练结束后看每类AP柱状图销量靠前的几个类目接近满分冷门类目惨不忍睹。原因类别不均衡导致模型把大类的特征学得过于充分推理时倾向于把不确定的样本判给先验概率高的大类。解决优先尝试在data.yaml里给冷门类加weight让loss函数对冷门类的错误更敏感。其次对冷门类做mosaic增强复制但增强倍数控制在2到3倍过度复制反而会让模型过拟合到特定图像上。还有一个不太起眼但很有效的手段训练时把冷门类的图片重复采样一遍相当于变相增加epoch。5.5 训练到一半报OOM或者Mac上直接被杀进程现象GPU训练时显存溢出报CUDA out of memoryMac训练时进程突然消失没有任何报错信息CPU训练时内存占用吃满导致系统卡死。原因GPU显存溢出通常是batch设太大或者开了太大的imgszMac上被杀进程多数是dataloader的worker数太多内存被多个子进程瓜分CPU平台则是图片解码太频繁内存没有及时释放。解决GPU上把batch减半或者开启梯度累积YOLO11的train参数里有accumulate选项。Mac上把workers设成0并且关闭一切后台大程序。CPU平台把batch降到4以下同时检查数据集的图片格式JPEG比PNG解码快得多。一个通用的调试技巧先用小数据集比如500张跑通整个流程确认硬件没问题后再上完整5000张。6. 从训练到上柜指标怎么读、模型怎么导出、现场实测怎么安排训练结束后不要急着导出模型先看看验证集上到底谁拖了后腿。整体mAP只能说明平均还不错不能暴露某个SKU经常漏检的问题。我的习惯是把每类的AP列出来再统计一个空柜误报率——把空柜测试图跑一遍数一数出了多少假框。零售柜项目里空柜误报让补货系统误判库存比单类漏检更致命。确认指标合格后用下面这段代码导出ONNX格式这一步同时完成模型精简model YOLO(runs/retail_cabinet/exp1/weights/best.pt) model.export(formatonnx, imgsz640, halfTrue)halfTrue会将权重转为FP16模型体积缩小一半在支持FP16推理的边缘设备上速度更快。如果部署设备是纯CPU的ARM板子不要开half很多CPU的NPU对FP16支持不完整出现了掉精度再排回来很费时间。导出后建议用onnxruntime跑一遍同一张测试图对比和PyTorch输出的差异差异超过0.05就要怀疑导出配置有问题。现场实测的优先级排序第一把柜子放到阳光直射的位置测一遍反光场景第二模拟高峰期货架半空状态第三将商品故意摆乱、倒放看模型的鲁棒性。这三个场景各录10分钟视频用离线视频帧跑推理统计漏检和误检帧数比在测试集上刷分数更接近真实效果。按这些步骤完整跑一遍5000张图的数据集加上一键训练脚本可以在两到三天内让你拿到一个可评估的模型。把数据、配置、训练日志都留档尤其是每次实验的参数变化——这东西一旦开始改记忆完全不可靠翻车三天找不到原因是很正常的。我自己吃过这个亏现在每轮实验都固定存一份命令行参数和data.yaml快照调参时才有后悔药。希望帮到你。本文还有配套的精品资源点击获取