ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

智能零售柜商品检测:5000张图与YOLO11三平台训练实战

智能零售柜商品检测:5000张图与YOLO11三平台训练实战 简介面向智能零售柜商品检测场景的目标检测数据集资源适合从事新零售视觉算法、无人零售柜商品识别项目的开发者与研究者使用也可作为通用零售场景目标检测数据的补充。数据集采集自真实智能零售柜监控画面涵盖罐装饮料、袋装零食等常见商品标注类别达113类采用labelimg标注质量较高并同步提供VOC(xml)、COCO(json)、YOLO(txt)三种主流格式可直接投入YOLO等算法训练。资源包共1个PDF文件大小约5.77MB内含数据集基本情况介绍与获取方式说明。随附YOLO11一键训练脚本支持GPU(GPUs)、CPU及Mac(M芯片)多平台方案并给出博主训练结果日志供参考。目前已有413人学习适合希望快速搭建零售商品检测基线、验证模型效果的读者参考使用。1. 智能零售柜商品检测5000 张图、三种标签格式和一套能跑通的三平台脚本智能零售柜的商品检测说白了就是让摄像头在货柜里认出「这层第几个位置放的是可乐还是矿泉水」。这件事的难点不在模型本身而在数据商品 SKU 多、包装相似、遮挡严重、光照随柜门开合剧烈变化公开数据集基本覆盖不到你自己的货柜。所以当我看到「5000 张图 VOC/COCO/YOLO 三种格式标签 支持 GPU/CPU/Mac 三平台 YOLO11 一键训练脚本」这个组合时第一反应是——它把零售柜落地里最耗时的两件事标注格式转换、环境适配都提前做掉了。这篇笔记就按我实际复现的思路把数据集怎么用、三种格式怎么选、YOLO11 脚本怎么改、三平台各自会翻什么车一条条讲清楚。适合正在做零售柜、无人货柜、智能货架商品识别的同学也适合刚入门 YOLO 想找一个真实场景练手的人。2. 零售柜商品检测数据集5000 张图到底够不够、怎么分2.1 先搞清楚 5000 张图在零售柜场景里意味着什么很多人拿到「5000 张」第一反应是少。放在 COCO 那种 80 类通用检测里确实少但零售柜是封闭场景 固定机位 有限类别这个量级是能出活的。关键在于类别数和每类样本数。假设你的货柜有 50 个 SKU5000 张图平均每类 100 张如果拍摄时每个商品在不同层、不同角度、不同光照下都出现过那这个分布是健康的但如果 5000 张里有 3000 张只拍了饮料区零食区每类只有二三十张那训练出来必然是偏科。我一般会先做一次类别分布统计而不是急着开训。零售柜场景里长尾 SKU比如某个冷门口味的酸奶和相似包装同一品牌不同规格是最容易掉点的两类。前者靠数据增强补后者靠更高的输入分辨率补。划分比例上零售柜我习惯用 8:1:1而不是通用的 7:2:1。原因是验证集和测试集要尽量覆盖「同一商品的不同摆放姿态」如果验证集里某个 SKU 从没出现过那指标就没有参考意义。划分时按商品类别分层抽样保证每个 split 里每个 SKU 都有样本。2.2 三种标签格式的取舍VOC、COCO、YOLO 各自适合谁数据集同时给了 VOC、COCO、YOLO 三种格式这不是凑数是因为三种格式对应三种不同的工作流。我把它们的差异整理成一张表方便你对号入座。格式文件结构坐标表示典型用途转换成本VOC每图一个 XML左上角 右下角绝对像素老项目、LabelImg 原生输出、需要保留图片尺寸信息转 YOLO 需归一化COCO单个 JSON[x, y, w, h] 绝对像素多任务检测分割关键点、需要官方评测脚本转 YOLO 需重算归一化YOLO每图一个 txt中心点 宽高归一化到 0~1Ultralytics 系列直接训练无需转换如果你用的是 Ultralytics 的 YOLO11直接用 YOLO 格式最省事因为它的 dataloader 就是按这个格式读的。VOC 和 COCO 的价值在于VOC 方便你用 LabelImg 继续补标COCO 方便你把这份数据和其他 COCO 格式的数据集合并或者跑一些基于 COCO API 的评测。这里有个容易忽略的点三种格式的类别顺序必须一致。VOC 的 XML 里类别是字符串COCO 的 JSON 里是 category_idYOLO 的 txt 里是 class_index。如果三者映射不一致你混着用就会得到「模型把可乐认成矿泉水」这种玄学结果。我一般会先写一个脚本把三种格式的类别名和索引对齐生成一份classes.txt作为唯一真相来源。2.3 用脚本校验三种格式是否对齐下面这段脚本做两件事读取 YOLO 格式的classes.txt然后抽查 VOC 和 COCO 里的类别名是否和它一致。这是开训前必做的一步能省掉后面几小时的排查。import os import json import xml.etree.ElementTree as ET # 1. 读取 YOLO 格式的类别表作为基准 def load_yolo_classes(classes_path): with open(classes_path, r, encodingutf-8) as f: # 每行一个类别名行号即 class_index return [line.strip() for line in f if line.strip()] # 2. 抽查 VOC 的 XML收集出现过的类别名 def collect_voc_classes(voc_dir, sample_n50): names set() xml_files [f for f in os.listdir(voc_dir) if f.endswith(.xml)][:sample_n] for xf in xml_files: tree ET.parse(os.path.join(voc_dir, xf)) for obj in tree.findall(object): names.add(obj.find(name).text) return names # 3. 抽查 COCO 的 JSON收集 categories 里的 name def collect_coco_classes(json_path): with open(json_path, r, encodingutf-8) as f: data json.load(f) return {c[name] for c in data[categories]} if __name__ __main__: yolo_classes load_yolo_classes(classes.txt) yolo_set set(yolo_classes) voc_set collect_voc_classes(VOC/Annotations) coco_set collect_coco_classes(COCO/annotations.json) print(YOLO 类别数:, len(yolo_set)) print(VOC 多出的类别:, voc_set - yolo_set) print(COCO 多出的类别:, coco_set - yolo_set) print(VOC 缺失的类别:, yolo_set - voc_set) print(COCO 缺失的类别:, yolo_set - coco_set)逻辑说明load_yolo_classes把classes.txt读成列表索引就是 YOLO 训练时用的 class id。collect_voc_classes只抽查前 50 个 XML因为全量解析在几千张图上会慢抽查足够发现类别名拼写不一致的问题。collect_coco_classes直接读 JSON 的 categories 字段。参数说明sample_n控制抽查数量数据量大时可以调到 100如果 VOC 的 XML 分散在多个子目录需要把voc_dir改成递归遍历。跑完如果三个集合的差集都是空说明格式对齐没问题可以进入下一步。3. YOLO11 一键训练脚本三平台怎么跑、参数怎么设3.1 一键脚本到底「一键」在哪哪些地方必须自己改所谓一键训练脚本通常封装了这几件事检查环境、生成data.yaml、调用ultralytics的YOLO().train()、训练完自动跑验证。它省掉的是重复敲命令但不省掉你对数据路径和类别数的确认。我见过太多人直接跑脚本结果data.yaml里的nc还是模板里的 80训练完发现模型输出 80 类白跑。零售柜场景下data.yaml至少要改三个地方path指向数据集根目录train/val指向图片文件夹names换成你自己的 SKU 列表。下面是一个针对零售柜的data.yaml模板# 零售柜商品检测 data.yaml path: /data/retail_cabinet # 数据集根目录 train: images/train # 训练集图片相对 path val: images/val # 验证集图片 test: images/test # 测试集可选 # 类别数必须和 names 长度一致 nc: 50 # 类别名顺序必须和 YOLO 标签里的 class_index 对应 names: 0: cola_330 1: cola_500 2: water_pure 3: water_mineral # ... 其余 SKU注意names的顺序。YOLO 标签里的class_index是数字训练时模型学的就是这个数字对应的类别。如果names顺序和标注时不一致模型会把「可乐」学成「矿泉水」而且 loss 还会正常下降这就是最坑的玄学问题。3.2 GPU、CPU、Mac 三平台的启动命令差异三平台的核心差异在设备指定和依赖安装。Ultralytics 的 YOLO11 用device参数控制GPU 填0或0,1CPU 填cpuMac 的 M 系列芯片填mps。GPU 平台NVIDIA的启动命令# 确认 CUDA 可用后再启动 python -c import torch; print(torch.cuda.is_available()) # 单卡训练batch 根据显存调 yolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs100 \ imgsz640 \ batch32 \ device0 \ workers8 \ projectruns/retail \ nameexp_gpuCPU 平台# CPU 训练慢建议用小模型 小 imgsz yolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs50 \ imgsz416 \ batch8 \ devicecpu \ workers4 \ projectruns/retail \ nameexp_cpuMacApple Silicon平台# MPS 后端PyTorch 需 2.x 以上 yolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs80 \ imgsz640 \ batch16 \ devicemps \ workers4 \ projectruns/retail \ nameexp_mac参数说明imgsz是输入分辨率零售柜商品包装上的文字和 logo 是区分相似 SKU 的关键所以 GPU 上我一般用 640CPU 上降到 416 换速度。batch在 GPU 上受显存限制8G 显存跑yolo11n 640 大概能到 16~32Mac 的 MPS 对 batch 比较敏感16 是相对稳的值。workers是 dataloader 线程数Windows 上如果报错就设成 0。3.3 训练过程中该盯哪些指标哪些指标是噪音零售柜场景下我重点看三个指标metrics/mAP50-95、每类的mAP、以及train/box_loss和val/box_loss的差距。mAP50-95是综合指标但零售柜里更实用的是每类 AP。因为你的业务可能对某些高价值 SKU比如贵价酒要求召回率极高而对低价商品容忍度高。Ultralytics 训练完会在runs/retail/exp/下生成results.csv里面每列对应一个指标可以写脚本按类提取。train/box_loss和val/box_loss的差距是判断过拟合的直接信号。如果 train 一路降、val 先降后升说明模型开始背训练集了。零售柜数据量不大过拟合很常见对策是加dropout、用更强的增强mosaic、mixup、或者直接减模型规模。至于precision和recall的波动前 20 个 epoch 基本是噪音不用太在意。YOLO 的 warmup 阶段指标抖动是正常的盯住 50 epoch 之后的趋势就行。4. 避坑与排查零售柜商品检测里最容易翻车的 5 个点4.1 现象训练 loss 正常下降但验证时模型把相似商品全认成一类原因这是零售柜最典型的坑根源通常是标注时类别名不统一。比如「可乐330」在有些 XML 里写成「cola_330」有些写成「Cola330」转成 YOLO 后变成了两个不同的 class_index但视觉上它们是同一个东西。模型学到的是「这个外观对应两个标签」于是输出混乱。解决回到 2.3 的校验脚本把 VOC、COCO、YOLO 三份类别名做一次全量比对统一成一份classes.txt。如果已经标完了写脚本批量重命名 XML 里的name字段再重新转 YOLO 格式。这个坑我踩过返工了两天才发现是拼写问题。4.2 现象GPU 上训练正常换到 Mac 上 loss 变成 NaN原因MPS 后端对某些算子的支持还不完整尤其是 YOLO11 里用到的某些归一化和激活函数。另外Mac 上如果 PyTorch 版本和 Ultralytics 版本不匹配也会出现数值不稳定。解决先把 PyTorch 升到 2.2 以上Ultralytics 升到最新。如果还 NaN把amp自动混合精度关掉命令里加ampFalse。MPS 上 AMP 支持不完善关掉后数值会稳很多代价是速度慢一点。另外imgsz在 MPS 上建议用 640 的整数倍非标准尺寸容易触发算子回退。4.3 现象CPU 训练跑了一天mAP 还是个位数原因CPU 训练慢是预期内的但 mAP 个位数通常不是速度问题而是数据加载路径错了。常见情况是data.yaml里的path用了相对路径而脚本的工作目录和你以为的不一样导致 dataloader 读不到图训练时用的是空标签。解决在训练脚本开头加一行打印确认数据集路径和图片数量。Ultralytics 训练时会输出train: Scanning ... images如果这个数字是 0 或者远小于预期就是路径问题。把path改成绝对路径最稳。另外 CPU 上workers设太大反而会拖慢因为进程切换开销高设 4 就够。4.4 现象模型在测试集上表现很好但部署到真实货柜后频繁漏检原因数据集里的图片和真实货柜的成像条件不一致。常见差异包括数据集是正面平拍真实场景是斜视数据集光照均匀真实场景有柜门反光和阴影数据集背景干净真实场景有货架边框和价签干扰。解决在数据增强里加入perspective透视变换、hsv_h/hsv_s/hsv_v色调饱和度亮度扰动、random_erasing随机遮挡。如果已经有真实场景的图哪怕只有几百张也加进训练集做微调效果比纯增强好。零售柜场景我一般会把hsv_v调到 0.5 以上模拟柜门开合的光照变化。4.5 现象训练完导出 ONNX 或 TensorRT 后检测结果和 PyTorch 不一致原因导出时的输入尺寸、归一化方式、NMS 参数和训练时不一致。YOLO11 导出 ONNX 默认会嵌入 NMS但如果你的部署环境自己做了 NMS就会双重 NMS导致框变少。解决导出时明确指定imgsz和half。GPU 部署用halfTrueFP16CPU 部署用halfFalse。如果部署端自己做 NMS导出时加nmsFalse。另外导出后一定要用同一张图分别跑 PyTorch 和 ONNX对比输出的框坐标差异在 1e-3 以内才算对齐。5. 把 5000 张图用出 5 万张的效果零售柜场景的增强与验证技巧数据量固定的时候能拉开差距的就是增强策略和验证方法。零售柜商品检测有一个其他场景没有的便利商品的位置在货柜里是相对固定的。这意味着你可以利用「位置先验」来做后处理也可以用它来设计更有针对性的增强。先说增强。Ultralytics 默认的增强里mosaic和mixup对零售柜是有用的因为它们能模拟商品被遮挡和堆叠的情况。但mosaic有个副作用它会把四张图拼在一起导致商品出现在图像边缘而真实货柜里商品不会出现在画面边缘。所以我会把mosaic的概率从默认的 1.0 降到 0.5mixup保持 0.1 左右。另外零售柜的摄像头是固定的所以degrees旋转不要开太大5 度以内就够开大了反而引入不真实的姿态。再说验证。常规的 mAP 不能完全反映业务效果我一般会额外做两件事。第一件是按货柜层做分组验证把测试集按「上层/中层/下层」分组分别算 mAP。因为不同层的拍摄角度和光照差异很大分组后能看出模型在哪一层弱。第二件是混淆矩阵分析Ultralytics 验证时会生成confusion_matrix.png重点看哪些 SKU 之间互相误判。如果发现「A 品牌矿泉水」和「B 品牌矿泉水」混淆严重说明模型没学到包装上的区分性特征对策是提高imgsz或者对这些类做针对性补标。下面这段脚本用来从results.csv里提取每类 AP并按层分组统计。假设你的测试集图片文件名里带了层信息比如layer1_xxx.jpg。import pandas as pd import re # 读取 Ultralytics 训练输出的 results.csv df pd.read_csv(runs/retail/exp_gpu/results.csv) # 列名通常带空格先 strip df.columns [c.strip() for c in df.columns] # 取最后一个 epoch 的指标 last df.iloc[-1] print(最终 mAP50-95:, last[metrics/mAP50-95(B)]) # 按层分组假设验证结果里有每图的预测这里用文件名模拟 # 实际项目中可以用 val 的 predictions.json 做更细的分析 def group_by_layer(image_names): groups {layer1: [], layer2: [], layer3: []} for name in image_names: m re.search(rlayer(\d), name) if m: groups[flayer{m.group(1)}].append(name) return groups # 这里只演示分组逻辑真实 AP 需要从验证输出里取 sample_names [layer1_001.jpg, layer2_003.jpg, layer3_007.jpg] print(分组结果:, {k: len(v) for k, v in group_by_layer(sample_names).items()})逻辑说明results.csv是 Ultralytics 每个 epoch 写一次的指标表最后一行的metrics/mAP50-95(B)就是最终指标。分组统计需要你从验证阶段保存的预测结果里提取这里用文件名演示分组逻辑实际项目中可以把val的save_jsonTrue打开得到每图的预测再按层聚合。参数说明results.csv的列名在不同 Ultralytics 版本里略有差异用strip()去掉空格能避免 KeyError。如果你的图片命名没有层信息可以在数据准备阶段加一个layer字段到文件名或单独的 csv 里。最后说一个我自己的习惯每次训练完我会挑 20 张验证集里置信度在 0.3~0.5 之间的图人工看一遍。这些是模型的「犹豫样本」最能暴露问题。如果这 20 张里有一半是误检说明阈值设低了如果有一半是漏检说明模型对这类商品的特征学得不够。这个习惯帮我省掉了很多次盲目调参的时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表