尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

智能零售柜商品检测实战:1000张图、三种标签格式与YOLO11三平台训练脚本

发布时间:2026/9/29 15:57:08

资讯中心
01
ARTICLE

智能零售柜商品检测实战:1000张图、三种标签格式与YOLO11三平台训练脚本

智能零售柜商品检测实战:1000张图、三种标签格式与YOLO11三平台训练脚本
简介这份资源面向智能零售柜商品检测方向的算法工程师与目标检测学习者提供真实零售柜监控场景下采集的1000张高质量商品图片覆盖罐装饮料、袋装零食等常见品类标注类别达113个可用于搭建商品识别项目也可作为新零售场景通用检测数据的补充。资源包为1个PDF文件约5.79MB内附数据集基本情况介绍与获取方式标签采用labelimg标注同时提供VOC、COCO、YOLO三种格式可直接投入YOLO等算法训练。随包附赠YOLO11一键训练脚本支持GPU、CPU及MacM芯片多平台方案并给出博主训练结果日志供对照参考。目前已有529人学习适合希望快速验证零售商品检测效果、减少数据准备成本的读者参考使用。1. 智能零售柜商品检测1000 张图、三种标签格式和一套能跑通三平台的 YOLO11 脚本智能零售柜的商品检测说白了就是让摄像头在货柜里认出「这一格放的是可乐还是矿泉水」。它和通用目标检测最大的区别在于商品密集摆放、遮挡严重、同类商品外观高度相似而且货柜场景的光照往往不均匀。你拿 COCO 预训练模型直接推理大概率会把一排饮料全认成 bottle。所以真正能落地的做法是拿一批贴合货柜视角的标注数据做一次针对性微调。这个标题给的正是这样一套组合1000 张商品图配 VOC、COCO、YOLO 三种格式标签外加一份能在 GPU、CPU、Mac 三平台一键启动的 YOLO11 训练脚本。它解决的是从「有数据」到「能训起来」之间那段最磨人的路——格式转换、环境适配、参数配置。适合谁做智能零售柜、无人货柜、自动售货机视觉模块的工程师以及想拿一个真实小数据集练手 YOLO 训练全流程的人。1000 张不算多但足够把 pipeline 跑通、把指标调到一个可用的水平。2. 三种标签格式到底怎么选VOC、COCO、YOLO 的差异与转换逻辑2.1 三种格式的结构差异与适用场景拿到数据集先别急着训先搞清楚三种标签格式各自长什么样不然转换的时候一定翻车。VOC 格式是每张图对应一个 XML 文件里面用object节点记录类别名和xmin/ymin/xmax/ymax的绝对像素坐标。COCO 格式是一个大的 JSON 文件所有图片的images、annotations、categories三个数组分开存bbox 是[x, y, width, height]的绝对坐标。YOLO 格式最简洁每张图对应一个 txt每行是class_id x_center y_center width height全部归一化到 0 到 1 之间。选哪个取决于你的训练框架。YOLO11 官方训练只吃 YOLO 格式所以最终一定要转成 YOLO。但 VOC 和 COCO 不是白给的——VOC 方便你用 labelImg 这类工具继续补标COCO 方便你接 mmdetection 或者做 COCO 指标评估。三种格式同时提供本质是让你在不同环节用不同格式不用自己写转换脚本。格式单文件结构坐标类型典型工具VOC每图一个 XML绝对像素 xmin/ymin/xmax/ymaxlabelImgCOCO单个 JSON绝对像素 x,y,w,hlabelme、mmdetectionYOLO每图一个 txt归一化中心点宽高YOLO 官方2.2 从 VOC 转 YOLO 的完整脚本与参数说明如果你手上只有 VOC或者想验证数据集里 VOC 和 YOLO 是否一致这段转换脚本可以直接抄。核心逻辑就三步读 XML 拿绝对坐标除以图片宽高做归一化按类别映射成 id 写 txt。import os import xml.etree.ElementTree as ET from PIL import Image # 类别列表顺序决定 class_id必须和训练时的 data.yaml 一致 CLASSES [cola, water, juice, snack, other] def voc_to_yolo(xml_path, img_dir, out_dir): tree ET.parse(xml_path) root tree.getroot() # 从 XML 里拿图片文件名和尺寸 filename root.find(filename).text img_path os.path.join(img_dir, filename) w, h Image.open(img_path).size lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in CLASSES: continue # 跳过未定义类别避免 id 越界 cls_id CLASSES.index(cls_name) 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) # 归一化并转成中心点宽高 x_c (xmin xmax) / 2.0 / w y_c (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {x_c:.6f} {y_c:.6f} {bw:.6f} {bh:.6f}) out_name os.path.splitext(filename)[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))逻辑说明CLASSES的顺序就是最终class_id的来源一旦训练开始就不能改否则标签全错位。归一化用图片真实宽高不是 XML 里写的size节点——有些标注工具的 size 字段是错的用 PIL 重新读一遍最稳。:.6f保留六位小数是 YOLO 官方推荐的精度太少会导致小目标框偏移。参数说明xml_path是单个 XML 路径批量处理时外面套一层os.listdir即可。img_dir和out_dir要分开别把 txt 写回图片目录否则 YOLO 扫描时会混乱。如果类别里有空格或中文建议先重命名成英文短名YOLO 的 data.yaml 对中文类别支持不稳定。2.3 COCO 转 YOLO 时最容易错的两个点COCO 转 YOLO 的坑比 VOC 多。第一个坑是 COCO 的bbox是[x, y, width, height]其中 x、y 是左上角绝对坐标不是中心点很多人直接拿它当中心点用框全偏了。第二个坑是category_id不连续——COCO 原始数据的 id 可能是 1、3、7、9但 YOLO 要求从 0 开始连续编号。转换时必须自己建一个category_id到0~N-1的映射表。import json with open(annotations.json) as f: coco json.load(f) # 建立 category_id 到连续 id 的映射 cat_map {c[id]: i for i, c in enumerate(coco[categories])} # 建立 image_id 到文件名的映射 img_map {img[id]: img[file_name] for img in coco[images]} # 建立 image_id 到宽高的映射用于归一化 size_map {img[id]: (img[width], img[height]) for img in coco[images]} for ann in coco[annotations]: img_id ann[image_id] w, h size_map[img_id] x, y, bw, bh ann[bbox] x_c (x bw / 2) / w y_c (y bh / 2) / h nw bw / w nh bh / h cls_id cat_map[ann[category_id]] line f{cls_id} {x_c:.6f} {y_c:.6f} {nw:.6f} {nh:.6f}\n txt_name img_map[img_id].replace(.jpg, .txt) with open(flabels/{txt_name}, a) as f: f.write(line)逻辑说明cat_map用enumerate保证 id 从 0 连续。size_map单独建是因为 COCO 的images数组里才有宽高annotations里没有。用a追加模式是因为一张图可能有多个标注不能每写一个就覆盖。参数说明如果 COCO 的categories里包含supercategory忽略它只取id和name。归一化后的值如果出现负数或大于 1说明原始 bbox 越界了这种标注要单独挑出来检查别直接喂给训练。3. YOLO11 一键训练脚本GPU、CPU、Mac 三平台怎么配3.1 三平台环境差异与依赖安装标题里说支持 GPU、CPU、Mac 三平台这不是噱头是真实痛点。GPU 服务器上你装torch要带 CUDA 版本Mac 上要用 MPS 后端纯 CPU 机器上只能老老实实跑devicecpu。三者装错一个脚本直接报错退出。先看依赖安装。GPU 平台最常见的是 NVIDIA 显卡装 PyTorch 时要指定 CUDA 版本# GPU 平台CUDA 12.1 示例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics # MacApple SiliconMPS 加速 pip install torch torchvision pip install ultralytics # 纯 CPU 平台 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics逻辑说明ultralytics是 YOLO11 的官方包装它会自动拉取匹配的依赖。GPU 平台的关键是--index-url指向 CUDA 版本的 wheel 源不指定的话 pip 默认装 CPU 版训练时torch.cuda.is_available()返回 False你会以为显卡坏了。Mac 的 MPS 不需要额外装 CUDAPyTorch 从 1.12 起内置支持。参数说明CUDA 版本要和显卡驱动匹配nvidia-smi右上角显示的 CUDA Version 是驱动支持的上限装的 torch CUDA 版本不能超过它。Mac 上如果torch.backends.mps.is_available()返回 False检查系统版本是否低于 macOS 12.3。3.2 一键训练脚本的完整写法与设备自动检测所谓「一键」核心是让脚本自己判断当前平台该用什么设备而不是让你手动改device参数。下面这段脚本把设备检测、路径配置、训练启动串在一起。import torch from ultralytics import YOLO def pick_device(): if torch.cuda.is_available(): return 0 # 第一块 GPU elif torch.backends.mps.is_available(): return mps # Apple Silicon else: return cpu if __name__ __main__: device pick_device() print(f使用设备: {device}) model YOLO(yolo11n.pt) # 用官方预训练权重做迁移学习 results model.train( datadata.yaml, # 数据集配置 epochs100, # 训练轮数 imgsz640, # 输入尺寸 batch16, # 批大小CPU/Mac 建议降到 4~8 devicedevice, # 自动选择的设备 workers4, # 数据加载线程 patience20, # 早停耐心值 projectruns/retail, # 输出目录 nameexp1, # 实验名 )逻辑说明pick_device按 CUDA、MPS、CPU 的优先级返回设备字符串。YOLO11 的train接口接受device参数传0表示第一块 GPU传mps走 Apple 加速传cpu走纯 CPU。yolo11n.pt是 nano 版本1000 张图的数据集用 nano 或 small 就够别一上来就上 large过拟合风险高且慢。参数说明batch在三平台上差异最大。GPU 显存 8GB 以上可以上 16Mac 统一内存建议 8纯 CPU 建议 4。workers在 Windows 上设太大会报共享内存错误设 0 或 2 更稳。patience20表示 20 轮指标不提升就停小数据集上能省不少时间。3.3 data.yaml 的写法与路径陷阱YOLO11 训练必须有一个data.yaml它告诉框架图片在哪、类别有几个、名字是什么。这个文件写错训练直接起不来。path: /home/user/retail_dataset # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 5 # 类别数 names: # 类别名顺序必须和 class_id 一致 0: cola 1: water 2: juice 3: snack 4: other逻辑说明path是根目录train和val是相对path的路径。YOLO 会自动把images替换成labels去找对应的 txt 标签所以图片和标签的目录结构必须镜像对称——images/train/xxx.jpg对应labels/train/xxx.txt。参数说明nc必须等于names的条目数多一个少一个都会报错。names用字典或列表都行但顺序就是class_id和转换脚本里的CLASSES必须完全一致。路径里不要有中文和空格YOLO 在部分平台上处理不了。4. 训练参数怎么调从 1000 张图里榨出可用指标4.1 小数据集的过拟合信号与应对1000 张图在目标检测里属于小数据集最大的风险是过拟合。典型信号是训练 loss 一路降验证 mAP 涨到某个点后开始跌或者震荡不升。这时候别急着加轮数先看数据增强够不够。YOLO11 默认开了 mosaic、mixup、HSV 抖动等增强。小数据集上我一般会把 mosaic 关掉最后 10 轮让模型在真实分布上收尾。参数是close_mosaic10。另外degrees旋转增强在货柜场景要慎用商品是正立摆放的旋转太多反而引入噪声设 0 或 5 就够。results model.train( datadata.yaml, epochs100, imgsz640, batch16, devicedevice, close_mosaic10, # 最后 10 轮关闭 mosaic degrees0, # 货柜场景不做旋转增强 hsv_h0.015, # 色调抖动模拟光照变化 hsv_s0.7, # 饱和度抖动 hsv_v0.4, # 亮度抖动 )逻辑说明close_mosaic让模型在训练末期见到无拼接的真实图像验证指标更可信。degrees0是因为货柜商品不会倒着放。HSV 三个参数模拟货柜灯光的色温和亮度波动这对零售场景很实用。参数说明hsv_h范围 0 到 10.015 是官方默认改动幅度小。hsv_s和hsv_v默认 0.7 和 0.4如果货柜光照特别稳定可以调低反之调高。这些参数没有绝对最优拿验证集 mAP 做网格搜索最靠谱。4.2 学习率与优化器的选择逻辑YOLO11 默认用 SGD初始学习率 0.01。小数据集上我试过 AdamW收敛更快但最终 mAP 有时不如 SGD 稳。如果你时间紧用 AdamW 把lr0设到 0.001 能快速看到效果如果追求最终指标SGD 配余弦退火更稳。results model.train( datadata.yaml, epochs100, optimizerSGD, # 或 AdamW lr00.01, # 初始学习率 lrf0.01, # 最终学习率 lr0 * lrf momentum0.937, # SGD 动量 weight_decay0.0005, # 权重衰减 warmup_epochs3, # 预热轮数 )逻辑说明lrf0.01表示学习率从 0.01 余弦降到 0.0001。warmup_epochs3让学习率从很小慢慢升到初始值避免一开始就大步长把预训练权重破坏掉。weight_decay是 L2 正则小数据集上可以适当加大到 0.001 抑制过拟合。参数说明momentum只对 SGD 有效AdamW 会忽略它。lr0如果设太大前几轮 loss 会飙到 nan这时候降一个数量级重来。warmup_epochs在 batch 很小时要加长否则预热不稳定。4.3 用验证集指标判断该不该继续训训练跑起来不是终点你得会看指标决定下一步。YOLO11 训练结束后会在runs/retail/exp1下生成results.csv和混淆矩阵图。重点看三个metrics/mAP50、metrics/mAP50-95、以及每个类别的 AP。如果某个类别 AP 特别低比如snack只有 0.3 而其他都 0.8 以上大概率是这类样本太少或者标注质量差。回去数一下snack的标注框数量少于 100 个的话考虑补标或者用过采样。如果 mAP50 高但 mAP50-95 低说明框的位置不够准检查标注框是否贴合商品边缘。混淆矩阵里如果cola和water互相混淆严重说明这两类外观太像要么合并成一类要么在数据里增加区分度大的样本。别硬调参数这是数据问题。5. 避坑与排查三平台训练时最常翻车的 5 个地方5.1 坑一GPU 平台报 CUDA out of memory现象训练刚启动就报RuntimeError: CUDA out of memory或者跑几轮后崩。原因batch设太大或者imgsz设太高。YOLO11n 在 640 尺寸下batch16 大约占 4GB 显存如果显卡只有 4GB 就爆了。另外workers太多也会占显存。解决先把batch降到 8 或 4再不行把imgsz降到 416。用nvidia-smi实时看显存占用留 500MB 余量。如果还不行开ampFalse关掉混合精度——混合精度省显存但偶尔会不稳定。5.2 坑二Mac 上 MPS 报错或速度极慢现象devicemps后报NotImplementedError或者训练速度比 CPU 还慢。原因PyTorch 的 MPS 后端对某些算子支持不全YOLO11 里个别操作会回退到 CPU导致频繁数据搬运。另外 macOS 版本太低也会导致 MPS 不可用。解决先确认torch.backends.mps.is_available()为 True。如果报算子不支持升级 PyTorch 到最新版。速度慢的话把workers设 0Mac 上多线程数据加载反而拖慢。实在不行就切devicecpuMac 的 CPU 跑 nano 模型也能接受。5.3 坑三CPU 平台训练慢到怀疑人生现象一轮训练要几十分钟100 轮跑一天。原因CPU 没有并行矩阵加速YOLO11 的卷积在 CPU 上就是慢。imgsz640和batch16在 CPU 上是不现实的。解决把imgsz降到 320batch降到 4epochs降到 50。用yolo11n.pt而不是更大的模型。如果只是验证 pipeline可以先用epochs5跑通确认没问题再上服务器用 GPU 正式训。5.4 坑四标签和图片不对应导致训练 loss 为 nan现象训练一开始 loss 就是 nan或者 mAP 一直是 0。原因labels目录和images目录结构不对应或者 txt 里的class_id超出了nc范围。也可能是归一化坐标算错出现了大于 1 或负值。解决写个检查脚本遍历所有 txt确认每行的第一个数字在0到nc-1之间后四个数字在0到1之间。再确认每张图都有对应的 txt文件名不含扩展名完全一致。import os def check_labels(label_dir, nc): for txt in os.listdir(label_dir): with open(os.path.join(label_dir, txt)) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: print(f{txt} 第{i}行字段数不对: {line}) continue cls_id int(parts[0]) coords [float(x) for x in parts[1:]] if cls_id 0 or cls_id nc: print(f{txt} 第{i}行 class_id 越界: {cls_id}) if any(c 0 or c 1 for c in coords): print(f{txt} 第{i}行坐标越界: {coords})逻辑说明这个脚本把最常见的三类标签错误一次性扫出来。字段数不对说明 txt 格式坏了class_id 越界说明类别映射错了坐标越界说明归一化算错了。参数说明label_dir传labels/train或labels/valnc传data.yaml里的类别数。跑一遍没输出就说明标签没问题。5.5 坑五训练完模型在货柜实拍上效果差现象验证集 mAP 0.85但拿手机拍货柜照片推理框全乱。原因数据集里的图片可能是特定角度、特定光照拍的模型过拟合到了那个分布。实拍时角度、距离、光照都变了模型没见过。解决在数据增强里加大scale、translate、perspective的幅度模拟不同拍摄条件。另外推理时把conf阈值调低到 0.25 试试有时候是阈值太高把正确框滤掉了。长期看还是要补拍实拍场景的图进训练集1000 张里如果全是棚拍图落地一定翻车。6. 把 1000 张图用到极致难例挖掘与增量补标的具体做法1000 张图训完第一版模型后别急着收工。真正拉开差距的是第二轮用第一版模型去推理未标注的货柜图把置信度低或者框错的样本挑出来补标再训第二版。这个流程叫难例挖掘是小数据集提指标最有效的手段。具体操作准备一批新的货柜实拍图不用标用训好的best.pt推理把conf设在 0.1 到 0.3 之间导出预测结果。然后人工过一遍把漏检的、框歪的、类别错的挑出来用 labelImg 补标成 VOC再用第 2 章的脚本转成 YOLO合并进原训练集。第二轮训练时把epochs设短一点30 到 50 轮就够因为模型已经收敛过一轮。这里有个血泪经验补标的时候一定要保持类别定义一致。我见过有人第一轮把「无糖可乐」和「普通可乐」分开标第二轮图省事合并成「可乐」结果class_id全乱训练直接崩。类别表一旦定下来中途改就是给自己找麻烦。验证方法上除了看 mAP我习惯再做一个「逐类召回率」检查。用验证集跑推理统计每个类别实际有多少个框、模型检出多少个召回率低于 0.7 的类别重点补样本。这个统计用 YOLO 的val模式加save_jsonTrue导出预测再和真值对比就能算出来。from ultralytics import YOLO model YOLO(runs/retail/exp1/weights/best.pt) metrics model.val(datadata.yaml, save_jsonTrue) # 输出每个类别的 P、R、mAP for i, name in enumerate(model.names.values()): print(f{name}: P{metrics.box.p[i]:.3f} R{metrics.box.r[i]:.3f} mAP50{metrics.box.ap50[i]:.3f})逻辑说明save_jsonTrue会导出 COCO 格式的预测结果方便你逐图对比。metrics.box.p、r、ap50是按类别索引的数组顺序和model.names一致。参数说明data.yaml要和训练时一致否则类别对不上。如果某个类别R很低但P很高说明模型太保守调低推理conf反过来P低R高说明误检多调高conf或者补负样本。最后说个习惯我每次训完模型都会把data.yaml、转换脚本、训练脚本、best.pt一起打包存档命名带上日期和数据集版本。智能零售柜的项目往往要迭代好几轮三个月后你根本记不清当时用的哪版标签。这个后悔药提前备好。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。