
简介本资源面向智能零售柜商品检测项目开发者与目标检测学习者提供真实零售柜监控场景采集的1000张高质量商品图片覆盖罐装饮料、袋装零食等常见品类标注标签包含113个商品类别可作为新零售场景通用商品检测数据的补充。资源包为1个PDF文件约5.79MB内附数据集基本情况介绍与获取方式并说明提供VOC、COCO、YOLO三种主流标注格式均采用labelimg标注可直接用于YOLO等算法训练。随资源附赠YOLO11一键训练脚本支持GPU、CPU及Mac芯片多平台训练方案并附博主训练结果日志供参考便于读者快速复现与调参。目前已有529人学习下载适合需要快速搭建商品检测基线、验证模型效果或补充零售场景数据的中高级开发者参考使用。1. 智能零售柜商品检测1000 张图、三种标签格式与 YOLO11 一键训练到底怎么落地智能零售柜的商品检测说白了就是让摄像头在货柜里认出「这一格放的是可乐还是矿泉水」。这件事的难点不在模型有多深而在数据能不能直接用、标签格式能不能对上、训练脚本能不能在 GPU、CPU、Mac 三端都跑起来。我见过太多团队卡在第一步拿到一批图标注是 VOC 的 XML训练代码却要 YOLO 的 txt中间转格式转出一堆坐标错位最后模型学了个寂寞。这个标题给出的方案很实在——1000 张零售柜商品图配 VOC、COCO、YOLO 三种格式标签再加一个支持 GPU、CPU、Mac 三平台的 YOLO11 一键训练脚本。它解决的不是「模型怎么设计」而是「数据到模型之间那条路怎么铺平」。适合谁做智能零售柜、无人货柜、冰柜商品识别的算法工程师和嵌入式部署同学尤其是手头有数据但被格式和平台折腾过的人。下面我按「数据长什么样 → 三种格式怎么转 → 三平台怎么训 → 坑在哪」的顺序把这条链路拆开讲。2. 零售柜商品数据集1000 张图里到底有什么为什么三种格式都要备2.1 零售柜场景的数据特点与标注难点零售柜商品检测和通用目标检测最大的区别在于商品排列密集、遮挡严重、同类商品外观差异小、光照反射强。1000 张图这个量级放在 COCO 上连零头都不够但在零售柜这种「背景固定、类别有限、目标位置相对规律」的场景里如果标注质量够高是能训出一个可用模型的。我一般会先看三个指标类别数、每类实例数、遮挡比例。零售柜常见类别在 20 到 80 之间每类至少要有 30 到 50 个实例否则模型对长尾类别基本没反应。标注难点集中在两处。第一是边界框贴边商品紧挨着摆放时框稍微大一点就框到隔壁商品小一点又切掉商品边缘YOLO 系列对框的回归比较敏感标注不一致会直接反映在验证集的 mAP 抖动上。第二是反光区域柜门玻璃反光会让商品局部过曝标注时容易把反光当成商品的一部分训练时模型学到的是反光纹理而不是商品特征。常见做法是标注时统一按「商品可见轮廓」画框反光区域不计入同时在数据增强里加随机亮度扰动来缓解。1000 张图按 8:1:1 切分训练集 800 张、验证集 100 张、测试集 100 张。如果类别多建议按类别分层抽样保证每个类别在验证集里都有实例。这个切分比例不是死的数据少的时候可以 7:2:1验证集大一点方便观察过拟合。2.2 VOC、COCO、YOLO 三种格式的字段对照三种格式本质上是同一份标注的不同表达方式理解字段对应关系转格式时就不会慌。VOC 是 XML一个图一个文件框用 xmin、ymin、xmax、ymax 表示绝对像素坐标。COCO 是一个大 JSONimages、annotations、categories 三个数组框用 [x, y, width, height] 表示绝对像素且 x、y 是左上角。YOLO 是一图一个 txt每行class_id cx cy w h全部是归一化到 0 到 1 的相对值cx、cy 是框中心。格式存储方式框表示坐标类型类别表示VOC每图一个 XMLxmin, ymin, xmax, ymax绝对像素name 字符串COCO单个 JSONx, y, width, height绝对像素category_id 整数YOLO每图一个 txtcx, cy, w, h归一化 0-1class_id 整数转换时最容易翻车的是 VOC 到 YOLO 的归一化cx (xmin xmax) / 2 / img_wcy (ymin ymax) / 2 / img_hw (xmax - xmin) / img_wh (ymax - ymin) / img_h。分母是图像宽高不是框的宽高这一点搞错框会整体缩到左上角。COCO 的 category_id 通常从 1 开始YOLO 的 class_id 从 0 开始转换时要减 1否则类别整体偏移一位训练时 loss 能降但预测全错。2.3 用脚本把 VOC 批量转成 YOLO 与 COCO下面这个脚本做三件事读 VOC XML生成 YOLO txt同时汇总成 COCO JSON。放在数据集根目录运行假设目录结构是images/和annotations/。import os import json import xml.etree.ElementTree as ET from PIL import Image # 类别列表顺序决定 class_id必须与训练时的 data.yaml 一致 CLASSES [cola, water, chips, milk, juice] class_to_id {c: i for i, c in enumerate(CLASSES)} def voc_to_yolo(xml_path, img_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() # 读取图像真实宽高不要用 XML 里的 size部分标注工具会写错 with Image.open(img_path) as im: img_w, img_h im.size lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_to_id: continue # 跳过未定义类别避免 class_id 越界 bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图像边界防止标注越界导致归一化后超出 0-1 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_to_id[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) def build_coco(image_dir, xml_dir, out_json): coco {images: [], annotations: [], categories: []} for i, c in enumerate(CLASSES): coco[categories].append({id: i 1, name: c}) ann_id 1 img_id 1 for fname in sorted(os.listdir(xml_dir)): if not fname.endswith(.xml): continue stem os.path.splitext(fname)[0] img_path os.path.join(image_dir, stem .jpg) if not os.path.exists(img_path): continue with Image.open(img_path) as im: w, h im.size coco[images].append({id: img_id, file_name: stem .jpg, width: w, height: h}) tree ET.parse(os.path.join(xml_dir, fname)) for obj in tree.getroot().findall(object): name obj.find(name).text.strip() if name not in class_to_id: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) coco[annotations].append({ id: ann_id, image_id: img_id, category_id: class_to_id[name] 1, # COCO 从 1 开始 bbox: [xmin, ymin, xmax - xmin, ymax - ymin], area: (xmax - xmin) * (ymax - ymin), iscrowd: 0 }) ann_id 1 img_id 1 with open(out_json, w) as f: json.dump(coco, f) if __name__ __main__: img_dir images xml_dir annotations yolo_dir labels os.makedirs(yolo_dir, exist_okTrue) for fname in os.listdir(xml_dir): if fname.endswith(.xml): stem os.path.splitext(fname)[0] voc_to_yolo( os.path.join(xml_dir, fname), os.path.join(img_dir, stem .jpg), os.path.join(yolo_dir, stem .txt) ) build_coco(img_dir, xml_dir, coco.json) print(done)逻辑说明voc_to_yolo逐图读 XML用 PIL 拿真实宽高做归一化并对框做边界裁剪避免越界。build_coco同时遍历 XML 和图像生成 COCO 的 images、annotations、categories 三段。参数上CLASSES的顺序就是 YOLO 的 class_id 顺序必须和训练配置里的 names 完全一致改一个顺序就要重新生成标签。class_to_id过滤掉未定义类别防止训练时出现越界索引。运行完检查labels/下 txt 行数是否和 XML 里 object 数一致不一致就说明有类别被过滤或框被裁没了。3. YOLO11 一键训练脚本GPU、CPU、Mac 三平台怎么跑通3.1 三平台环境差异与依赖安装GPU、CPU、Mac 三平台的核心差异在 PyTorch 的安装包和推理后端。NVIDIA GPU 走 CUDA需要装带 cu 版本的 torch纯 CPU 装 CPU 版 torch 即可Mac 走 MPSApple Silicon 上 torch 的 MPS 后端能加速但部分算子支持不全遇到不支持的算子会自动回退 CPU速度会掉。我一般先确认三件事Python 版本3.9 到 3.11 比较稳、torch 是否装对、ultralytics 版本是否支持 YOLO11。# NVIDIA GPU 平台CUDA 12.1 示例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics # 纯 CPU 平台 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics # Mac Apple Silicon pip install torch torchvision pip install ultralytics装完用一段代码验证后端是否可用别等训练跑起来才发现用的是 CPU。import torch print(torch:, torch.__version__) print(cuda available:, torch.cuda.is_available()) print(mps available:, torch.backends.mps.is_available()) if torch.cuda.is_available(): print(gpu:, torch.cuda.get_device_name(0))参数说明torch.cuda.is_available()为 True 才走 GPUtorch.backends.mps.is_available()为 True 才走 Mac 加速。如果两个都是 False训练会自动落到 CPU1000 张图在 CPU 上跑 YOLO11n 大概每轮几分钟到十几分钟能跑但慢。GPU 上同样数据每轮几秒到几十秒差距明显。3.2 data.yaml 与训练超参的对应关系YOLO11 训练靠一个 data.yaml 描述数据位置和类别路径写错是最常见的翻车点。下面是一个零售柜数据集的配置示例。path: /data/retail_cabinet train: images/train val: images/val test: images/test nc: 5 names: 0: cola 1: water 2: chips 3: milk 4: juicepath是数据集根目录train、val、test是相对 path 的路径。YOLO 会自动去找同名 labels 目录下的 txt所以images/train对应labels/train目录名必须成对。nc是类别数names的键必须从 0 连续到 nc-1中间断号会导致类别映射错乱。改类别时data.yaml 和生成标签时的 CLASSES 必须同步改只改一边就是血泪经验。训练超参里imgsz零售柜场景建议 640商品小的话可以上 960但显存和速度要权衡。batch在 GPU 上按显存给8G 显存跑 YOLO11n 640 大概能到 16 到 32。epochs1000 张图建议 100 到 300配合早停。lr0默认 0.01数据少可以降到 0.001 到 0.005避免早期震荡。3.3 一键训练脚本与三平台启动命令把训练参数写进一个脚本三平台共用靠自动检测后端来切换设备。import torch from ultralytics import YOLO def pick_device(): if torch.cuda.is_available(): return 0 # 第一块 NVIDIA GPU if torch.backends.mps.is_available(): return mps # Apple Silicon return cpu if __name__ __main__: device pick_device() print(using device:, device) model YOLO(yolo11n.pt) # 从预训练权重起步小数据集必备 model.train( datadata.yaml, epochs200, imgsz640, batch16 if device ! cpu else 4, devicedevice, workers4 if device ! cpu else 2, patience30, # 30 轮无提升就早停 projectruns/retail, nameyolo11n_retail, pretrainedTrue, optimizerauto, cos_lrTrue )逻辑说明pick_device按 CUDA、MPS、CPU 的优先级选设备保证同一份脚本三平台都能跑。YOLO(yolo11n.pt)加载预训练权重1000 张图从零训基本学不出东西预训练是必须的。batch在 CPU 上降到 4workers降到 2避免内存和进程数爆掉。patience30是早停验证集 30 轮不提升就停省时间。cos_lrTrue用余弦退火小数据集上比固定学习率稳。跑完在runs/retail/yolo11n_retail/weights/下拿 best.pt 做推理。三平台启动命令一致python train.py。GPU 上想指定某块卡改device0或device1。Mac 上如果 MPS 报算子不支持把 device 改成 cpu 先跑通再逐步试 MPS。4. 训练与推理避坑零售柜商品检测最常见的 5 个翻车点4.1 现象loss 一直降但 mAP 不动原因通常是标签类别错位或框归一化错误。VOC 转 YOLO 时 class_id 没减 1或者 cx、cy 分母用错模型学到的是错误映射loss 能降是因为它在拟合错误目标验证集自然不动。解决抽 5 张图用脚本把 YOLO txt 画回图上肉眼看框和类别对不对。画框代码用 PIL 的 ImageDraw把 cx、cy、w、h 反归一化后画矩形类别名标在框上一眼就能看出偏移。4.2 现象训练报 “no labels found”原因一般是目录结构不对。YOLO 找标签的规则是 images 路径里的images替换成labels后缀换成 txt。如果图像在images/train标签必须在labels/train且文件名不含后缀一致。常见错误是把标签放在labels/train/下但文件名带了_jpg之类后缀。解决写个检查脚本遍历 images 下所有图确认对应 labels 下同名 txt 存在且非空缺的列出来补。4.3 现象GPU 显存够但训练 OOM原因可能是 imgsz 或 batch 设太大也可能是 workers 太多导致数据加载进程占内存。零售柜图分辨率高的话640 的 imgsz 在 8G 显存上 batch 16 可能就顶了。解决先把 batch 降到 8再不行降 imgsz 到 512或者开ampTrue混合精度。workers 在 Windows 上设 0 或 2Linux 上可以 4 到 8Mac 上 2 比较稳。4.4 现象Mac 上 MPS 训练报算子不支持原因是部分算子在 MPS 后端没实现PyTorch 会抛错而不是自动回退。解决设环境变量PYTORCH_ENABLE_MPS_FALLBACK1让不支持的算子回退 CPU速度会慢但能跑完。如果还不行直接 devicecpuMac 上 CPU 训 1000 张图 YOLO11n 大概几小时能接受。4.5 现象验证集 mAP 高但实际柜子里认错原因是验证集和实际场景分布不一致。1000 张图如果都来自同一个柜子、同一光照验证集再高也只是过拟合这个柜子。解决切分数据时按柜子或按光照条件分层验证集里放不同柜子、不同时段的图。另外推理时把 conf 阈值调高到 0.5 以上零售柜场景宁可漏检也别错检错检会导致结算错误。5. 从 1000 张图到可用模型我常用的验证与迭代习惯训练跑完不是终点我一般会做三件事来确认模型能不能上柜。第一件是混淆矩阵YOLO 训练完会自动生成confusion_matrix.png重点看同类商品之间有没有互相混比如可乐和零度可乐混说明特征区分度不够要么加数据要么加类别。第二件是抽 20 张验证集图做推理把预测框和真实框叠一起看重点看小目标和遮挡目标的召回零售柜里被挡一半的商品是常态召回低就要在数据增强里加随机遮挡。第三件是拿手机拍几张实际柜子的图不标注直接推理看模型在真实光照和角度下的表现这一步最能暴露过拟合。迭代上1000 张图训出来的模型如果 mAP50 在 0.85 以上基本能上柜做辅助识别要到 0.95 以上通常得补数据到 3000 张以上或者用更大模型如 YOLO11s、YOLO11m。我自己的习惯是先用 YOLO11n 快速跑通链路确认数据和标签没问题再换大模型刷精度。换模型时 data.yaml 和标签都不用动只改YOLO(yolo11s.pt)这一行这是 YOLO 系列最省心的地方。还有一个容易忽略的点推理部署时的预处理要和训练一致。训练时 imgsz640推理时如果传原图不 resizeYOLO 内部会自己 letterbox但如果你在外面又做了一次 resize就会双重缩放导致框偏移。我一般推理直接用model.predict(source, imgsz640)不自己预处理。最后模型导出成 ONNX 或 TensorRT 上边缘设备时记得确认导出时的 opset 和动态轴设置零售柜边缘盒子算力有限TensorRT INT8 量化能提速但会掉点量化后一定要重新跑一遍验证集。希望帮到你。本文还有配套的精品资源点击获取