☰
YOLOv11货架商品识别实战:从环境配置到库存管理
2026/10/5 7:41:19 网站建设 项目流程

简介:面向零售行业智能化升级,这份PDF围绕YOLOv11目标检测算法,系统讲解货架商品识别与库存动态管理从理论到落地的完整方案,适合目标检测开发者、零售科技从业者及AI学习者参考。资源为单个PDF文件,共35页,大小约1.89MB,支持目录章节跳转及大纲快速定位,文字、图表显示完整,体量适中,适合系统学习或快速查阅。截至目前已有77人参与学习下载。文档从零售业务需求分析展开,依次介绍YOLO系列算法演进、YOLOv11网络结构与损失函数,并详细拆解数据采集与标注、模型训练与部署、库存实时监控与补货预测,以及系统集成和实验评估等关键环节。并提供实验评估与对比分析,验证方案有效性。除算法原理外,还涵盖数据标注、模型评估、部署优化、安全运维等实操要点,可帮助读者快速搭建可用的零售识别与库存管理原型。

1. 货架商品识别为什么值得用YOLOv11重做一遍

货架商品识别这个需求,听起来是「用目标检测模型数一数货架上有几瓶可乐」,真正做过的人才知道,难点全在后面:几十个SKU长得几乎一样、后排商品在画面里只有十几个像素、玻璃瓶反光、补货前后光照变化,这些都会让模型在测试集上好看、在门店里翻车。YOLOv11在零售场景的深度应用,就是把这套链路做实:用Ultralytics框架训练自己的货架数据集,把摄像头画面变成按SKU统计的可见库存信号,再接到缺货提醒和补货工单上。适合正在做门店数字化、货架巡检或自动盘点的团队,也适合想用YOLOv11跑通第一个真实项目的开发者。

2. YOLOv11的网络结构与零售场景选型:从为什么选它到最小可跑环境

2.1 货架识别为什么选YOLOv11而不是更重的检测方案

货架场景和通用目标检测有个很大的区别:它不需要识别「任意物体」,而是要稳定地识别「固定的那几十个SKU」。这意味着模型的泛化压力不大,真正吃紧的是三个指标——单帧推理延迟、小目标召回率、以及长时间运行的稳定性。单看精度上限,两阶段的Faster R-CNN在大目标上不输,但一到低峰期同时跑几十路摄像头,帧率就成问题;DETR这类基于Transformer的检测器在小目标上有独特优势,但部署依赖重,边端设备上的推理优化成本高。YOLOv11属于单阶段、anchor-free的检测头设计,配合C3k2这类高效卷积模块,在同样算力下把推理延迟压得很低,这也是大多数零售识别方案把它当底座的原因。

从网络结构上看,YOLOv11的backbone把特征提取做得更浅而宽,neck部分用SPPF完成多尺度融合,对小目标并不天然敏感,但anchor-free的解耦头在小目标回归上少了一道「先匹配anchor再修正」的弯路,调参负担也小。热词里经常有人搜「yolov11网络结构图」,落到零售场景,你不需要把每个模块背下来,只需要记住一条主线:输入图经过backbone提特征,neck做多尺度融合,检测头直接输出每个目标的类别和框。这条主线决定了后面所有参数调整的思路——小目标漏检,就去看neck的融合策略够不够;推理慢,就去看backbone的宽度是不是可以缩到n或s。

选型还有一个现实理由:生态。Ultralytics把训练、验证、导出、部署的入口统一成一条命令行,数据集格式固定为YOLO的txt标注,这对接货架识别的数据管线非常省事。零售团队的算法工程师往往还要兼做数据标注和系统对接,没有精力维护一套Faster R-CNN的两阶段训练脚本。对纯小白来说,先跑通一条最小链路比纠结网络结构图重要得多。

2.2 环境配置:一套适合0基础的Ultralytics最小命令

环境配置这一步,网上搜「yolov11(ultralytics)环境配置 适合0基础纯小白」能找到大量教程,但多数写得太绕。我这里给一套做过多次的干净流程。第一步用conda建独立环境,避免把系统Python搞乱:

conda create -n retail python=3.10 -y conda activate retail

然后装PyTorch。这一步最大的坑是pip默认装的torch可能不带CUDA,明明机器有显卡,训练时却跑在CPU上。常见做法是先装匹配本机CUDA版本的torch,再装ultralytics:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics

参数说明:cu121对应CUDA 12.1,如果你的显卡驱动新、CUDA是12.x,这套能用;老显卡可以换成cu118或cu113对应的index-url。装完先跑一条命令确认GPU可用:

python -c "import torch; print(torch.cuda.is_available())"

输出True就说明GPU链路通了,输出False则回头检查torch版本和NVIDIA驱动。这一步值得多花十分钟验证,否则训练到一半才发现用的是CPU,心态会崩。

提示:手里只有CPU机器也不用放弃,YOLOv11n在CPU上跑推理可以接受,训练只是慢;先用CPU把流程跑通,再找带GPU的环境切过去,数据、脚本、权重都是通用的。

2.3 权重文件下载与推理结果保存:先跑通再训练

权重文件这块,很多人搜「yolov11权重文件下载」,其实不用手动找。ultralytics在第一次调用模型时会自动拉取对应权重,也可以提前从发布页下载yolov11n.pt、yolov11s.pt放到weights/目录,训练和推理时指定路径即可。n是nano版,体积最小,适合先跑通流程;s是small版,精度更好,适合正式训练前的基线测试。零售场景起步阶段,我一般建议用s,数据量不大时s和m的差距不明显,但s的训练时间友好很多。

先跑通推理,再谈训练。用一张货架照片验证整个链路,同时把推理结果保存下来,这正好对应很多人搜的「yolov11保存推理结果」「yolov11预测后保存」:

from ultralytics import YOLO model = YOLO("weights/yolov11s.pt") results = model.predict(source="shelf_test.jpg", conf=0.4, save=False) r = results[0] r.save("output/shelf_vis.jpg") # 画框后的可视化图 r.save_txt("output/labels", save_conf=True) # 保存YOLO格式的txt标签 print(r.boxes.data) # [x1, y1, x2, y2, conf, cls]

说明:r.boxes.data里每一行是一个检测框,后两列分别是置信度和类别ID;save_txt保存的是归一化坐标,和训练数据同格式,后面做库存统计时直接解析它就是。这两行代码是整条数据管线的地基——训练时模型学习的是这种格式,推理时输出的也是这种格式,后面所有库存逻辑都从这个txt出发。

3. 货架数据集是效果上限:采集、标注与VOC转YOLO格式

3.1 数据采集的三种来源与SKU标注粒度

货架识别模型的效果上限由数据决定,这句话被说烂了,但落在零售场景里有具体含义:你不能只拍一个门店的一个货架。常见做法是三种来源混着来。第一种是监控视频抽帧,摄像头固定角度,按每秒一帧抽取,覆盖营业高峰、低峰、补货前后不同状态,这是主力数据;第二种是巡店时用手机或平板在货架正前方、略高于层板的位置拍摄,补充不同门店的灯光和陈列差异;第三种是商品渲染图合成,把单品图贴到背景上做数据增强,这种方法只能当补充,不能当主力,否则模型会对合成图的边缘纹理过拟合。

标注粒度是另一个容易搞错的地方。货架场景的类名不要用「可乐」这种商品分类名,要用SKU编码,比如coke_500ml、coke_330ml、sprite_500ml是三个不同的类,哪怕它们长得几乎一样。道理很简单:库存动态管理要的是SKU级别的数量变化,不是「这排是饮料」这种粗粒度结论。同一商品不同包装、不同容量,在模型眼里就是不同类,如果你把它们标成同一类,后续库存根本没法对账。

标注工具用labelme或CVAT都行,界面不是重点,重点是保存格式。labelme默认保存JSON,CVAT可以导出VOC或COCO,而YOLOv11训练需要的是每个图片同名的一个txt。这里就涉及转换脚本,也是下面要写的。标注量的底线建议是每个SKU至少300到500个实例,并且至少覆盖3家门店的光照条件。低于这个量,模型会在过拟合和漏检之间反复横跳,调参再勤都救不回来。

3.2 VOC转YOLO格式:转换脚本与四个边界坑

VOC的XML里存的是像素绝对坐标(xmin, ymin, xmax, ymax),YOLOv11需要的是归一化后的中心坐标和宽高(cx, cy, w, h),这个转换没有难度,全是细节。下面脚本是实际项目中改过的版本,可以直接拿去用:

import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, img_w, img_h, class_map, out_txt): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.iter("object"): name = obj.findtext("name").strip() if name not in class_map: continue cls_id = class_map[name] box = obj.find("bndbox") xmin = float(box.findtext("xmin")) ymin = float(box.findtext("ymin")) xmax = float(box.findtext("xmax")) ymax = float(box.findtext("ymax")) if xmax <= xmin or ymax <= ymin: continue cx = ((xmin + xmax) / 2) / img_w cy = ((ymin + ymax) / 2) / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h cx = min(max(cx, 0.0), 1.0) cy = min(max(cy, 0.0), 1.0) w = min(max(w, 0.0), 1.0) h = min(max(h, 0.0), 1.0) lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") Path(out_txt).parent.mkdir(parents=True, exist_ok=True) Path(out_txt).write_text("\n".join(lines), encoding="utf-8")

逻辑说明:遍历XML里每个object,按class_map把商品名映射成类别ID,取出bndbox的四个坐标,先过滤掉反向框(xmax小于等于xmin这类脏标注),再转成中心点加宽高的归一化格式。最后把所有目标的字符串按行写入txt。img_w和img_h来自对应图片的实际尺寸,批量转换时用cv2或PIL读取一次即可。

四个边界坑,都是实际翻过车的。第一,类别ID必须按固定顺序映射,常见做法是先扫一遍所有标注文件,收集全部类名,按字母排序生成classes.txt,再回填class_map。如果直接用dict的insert顺序,数据集一更新,ID就整体错位,训练出来是废的。第二,归一化越界要截断而不是抛弃,有些标注框超出图片边界,转换后cx或w会大于1,对于伸出画面的商品,截断到0到1之间还能用;只有w或h变成0或负数才应该跳过。第三,图片扩展名大小写不一致,门店采集的数据经常出现shelf_001.JPG配shelf_001.xml的情况,YOLO训练时按.txt的同名前缀找图片,大小写对不上就静默跳过,最后训练集少了一半数据,转换脚本里统一把小写后缀转成.jpg最稳妥。第四,工程路径不能有中文,Windows上中文路径会让cv2读图失败,表现为训练时loss正常但图片加载为0,规范做法是数据集根目录用英文或拼音,比如shelf_dataset/,SKU名称用英文编码,不要用中文商品名。

3.3 小目标优化与数据增强:让模型看见后排商品

货架场景最普遍的痛点是后排商品。货架纵深两三层,摄像头装在正前方,后排的商品在画面里经常只有十几二十个像素,模型的下采样倍数一大,特征直接被抹掉。针对「yolov11小目标优化」这个方向,主流且有效的做法有三个层次。

第一层是数据侧的切图训练(tiling)。把原图切成不重叠的图块,比如1080p的货架图切成一排三块,每块640x640,让小目标在切图后变成中等目标。代价是标注也要跟着切,训练时间翻倍,但对小目标召回率的提升非常直接。第二层是增强策略。Ultralytics默认开mosaic,把四张图拼在一起训练,这对整体鲁棒性有帮助;但货架场景我建议在训练最后10个epoch把mosaic关掉(close_mosaic=10),否则模型在mosaic的拼接背景下收敛得不够稳。光照增强也很关键,门店灯光会在不同时段变化,对训练图做随机亮度、对比度扰动,能显著减少上线后的光照敏感。

第三层是清洗验证集。小目标优化中最玄学的地方在于,你改了参数,mAP没动,但现场漏检确实少了。原因是货架场景的大目标数量多,把mAP撑住了,小目标AP的变化被稀释。所以优化前后都要单独统计小目标(比如面积小于32x32像素)的AP,用这个数判断改动有没有效,而不是盯着总mAP。

4. 训练自己的货架模型:命令、参数与三类评估指标

4.1 训练命令与关键参数说明

数据准备到YOLO格式后,训练自己的模型这件事,在Ultralytics框架下就是一条命令的事。首先把数据集配置写成yaml:

# dataset/shelf.yaml path: shelf_dataset train: images/train val: images/val nc: 12 names: ["coke_500ml", "coke_330ml", "sprite_500ml", ...]

path是数据集根目录,train和val是对图片文件夹的相对路径,nc是类别总数,names按顺序对应标注txt里的类别ID。这个yaml是训练命令的入口,路径写错会在启动时报错,最常见的坑是train路径写成了图片文件而不是文件夹。

然后跑训练:

yolo detect train model=yolov11s.pt \ data=dataset/shelf.yaml \ epochs=100 batch=16 imgsz=640 \ device=0 patience=20 close_mosaic=10

参数说明:model指定预训练权重,yolov11s.pt是从COCO训练好的基础模型上继续微调(fine-tune),比从零训练收敛快得多;epochs在货架场景建议100起步,数据量大(超过2万张)再加到150;batch大小受显存限制,16是8GB显存比较稳的值,显存小就降到8;imgsz默认640,小目标严重的场景可以考虑960,但显存占用接近翻倍;patience=20意思是验证集指标连续20个epoch不提升就早停,防止后面白烧电费;close_mosaic=10让最后10个epoch关闭mosaic增强,让模型在真实分布上稳定下来。

训练结束后,runs/detect/train/weights下会有best.pt和last.pt两个文件,best.pt是验证集表现最好的权重,上线和导出都拿它;last.pt是最后一个epoch的权重,如果数据有更新想接着训练,用它做起点。

4.2 货架场景下怎么看模型好坏:mAP之外的三个信号

训练结束后,Ultralytics会在runs/detect/train目录下输出一堆图表,很多人只看mAP50就宣布模型好了。货架场景里,这个结论经常靠不住,原因是大目标撑起了指标。我的做法是额外看三个信号。

第一,按SKU看mAP。运行验证命令时指定classes参数,把12个SKU的mAP分别打出来:

yolo detect val model=runs/detect/train/weights/best.pt \ data=dataset/shelf.yaml \ classes=[0,1,2,3,4,5,6,7,8,9,10,11]

输出里哪个类的AP明显低于整体平均,就去翻它的标注数据,基本都是实例数太少或框太小,补数据比调参有用。第二,真实货架空跑误检。找一张完全空货架或明显不属于任何SKU的商品照片,跑一遍推理,数一下出了多少假框。零售场景里误检比漏检更让运营崩溃——系统一天发10条不存在的缺货警报,这个项目就废了。误检多就调高conf阈值,通常货架场景用0.4到0.5之间。第三,F1-confidence曲线。这个工具能告诉你阈值设在多少时,精确率和召回率的平衡最好,同时给出对应的F1分数。零售场景不用追求F1最高点,因为漏检可以靠下一帧补,误检会直接触发补货工单,所以我一般会在F1最高点基础上再往上提0.05到0.1的阈值,宁漏勿错。

4.3 小目标进阶:切图推理、通道注意力与HCANet

如果数据层和训练参数都调过了,小目标AP还是上不去,才轮到进阶手段,顺序不要反。切图推理是第一个进阶手段。训练时做了tiling,推理时也要做,否则训练和推理的尺度不一致。常见做法是用sahi这类切片辅助库,把大图切成1280x1280的重叠块,分别推理后按原坐标合并结果,再按IOU做nms去重。代价是推理时间翻几倍,所以一般只在识别黄金时段的关键货架用。

第二个进阶手段是改网络结构。热词里经常出现的「yolov11 hcanet」,是有人在尝试把HCANet这类通道注意力模块接在YOLOv11的neck后面,增强小目标通道的特征响应。这类做法属于研究向的改造,效果通常需要在小目标AP上做严格对比验证,不是装上就变强。我的建议是:先把数据清洗和训练参数调到极限,再决定要不要动结构,否则你连提升到底是数据红利还是模块红利都分不清,后面没法维护。

还有个容易被忽略的点:YOLOv11已经是anchor-free设计,网上老教程里调anchor尺寸的步骤对v11完全不适用,别照搬v5时代的经验。这一点能帮你少走很多弯路。

5. YOLOv11货架识别避坑:五条把项目拖垮的实战记录

这五条不是从文档里抄来的,是货架识别项目里真实踩过的坑。每一条都按「现象、原因、解决」的顺序讲清楚,希望你能在项目前期就避开。

5.1 后排小商品全套漏检:监控画面里的视觉死角

现象:训练loss正常收敛,验证mAP也到了0.85,但把模型部署到门店后,后排的小瓶装饮料几乎全军覆没,前排大瓶装却一个没漏。原因:验证集里大目标占比太高,mAP被前排撑住,小目标的AP低到没法看;再加上后排商品在1080p画面里只有十几个像素,经过backbone的多次下采样后特征已经消失。这是监控视角货架识别的结构性问题,不是调参能单独解决的。解决:对训练数据做切图tiling,把后排区域放大后再训练;同时把验证集按目标面积分层统计,单独盯住小目标AP。部署时如果允许,尽量让摄像头离货架近一点或斜向下压一点,物理上拉大目标尺寸比任何算法优化都直接。

5.2 增量训练新SKU后老SKU失忆:灾难性遗忘

现象:门店新上了一个SKU,往训练集里加了500张图,用预训练权重又跑了100个epoch,结果新SKU识别很好,老SKU的识别率反而掉了10个百分点。原因:典型的灾难性遗忘。新SKU的样本在训练集里占比大,模型把权重往拟合新数据的方向推,老SKU的特征空间被挤压;如果学习率没调低,这种现象会更严重。解决:增量训练时不要把新数据单独拿出来训,而是新老数据按比例混合,比如老数据采样到和新数据持平;学习率降到正常训练的十分之一;冻结backbone只微调解耦头,能最大程度保住老SKU的特征。新SKU刚上线的前两周,还要人工复核它那一类的检测框,积累实际现场数据后再做第二次增量。

5.3 缺货检测被空位深处的商品骗过:位置映射比数量更重要

现象:系统判断某排面还有货,但店员过去一看是空位,只是空位后面露出了一瓶后排商品。连续几次误报后,运营对系统完全不信任。原因:库存判断只看检测框数量,没有把检测框的位置和货架层位对应起来。后排商品虽然被检测到,但它不在当前排面的空间位置,不应该被计入。解决:在检测结果上叠加一层货架位置映射,用归一化的y坐标把画面切分成货架层位,只统计目标层位内的检测框;同时引入front-face信号,即商品正面朝外才算有效,深位侧放的商品即使被检出也排除。这一步是识别算法走向库存管理的关键,代码逻辑在下一章给出。

5.4 光照一变识别率对不上:训练数据单调的代价

现象:同一排货架,上午识别率正常,下午门店开了暖色灯,漏检率翻倍;另一家门店天花板灯管反光,同一模型效果差很多。原因:训练数据大多来自同一时间段、同一门店的采集,光照分布太窄,模型对颜色的依赖超过了形状和纹理。这不是模型坏了,是数据分布覆盖不够。解决:采集时按早中晚三个时段抽帧,至少覆盖两种灯光环境;训练时把亮度、色温、曝光扰动加进去;推理侧对暗光或过曝画面做预处理,例如先做自适应直方图均衡再进模型。养成看「按门店×时段拆分识别率」的习惯,而不是只看全天平均,问题能早发现。

5.5 多路摄像头帧率扛不住:算力不足时的降级路线

现象:单路摄像头推理只要30毫秒,但接了20路之后服务直接卡死,检测结果堆积,库存统计越差越多。原因:每路都按最高帧率推理,GPU显存和算力被占满;或者压根没上GPU,拿CPU在硬扛。解决:先降需求再升硬件。库存动态管理不需要实时,营业高峰期每1到2分钟抽一帧足够,低峰期甚至可以5分钟一帧;模型用yolov11n或yolov11s这种小体量版本;再不够就把模型导出成TensorRT的engine格式,同样的卡通常能再腾出一倍吞吐。不要一上来就上最大模型,货架识别拼的是长时间稳定,不是单帧极限精度。

6. 库存动态管理落地:从检测框数量到补货工单的最后一公里

识别模型跑通只是第一步。库存动态管理要做的,是把每帧的检测框变成运营能用的决策信号。我的做法是:定时抓拍、模型推理、按货架层位过滤、按SKU统计可见数量、和基线对比、触发补货。核心是把「检测到目标」转换为「该排面还有多少前排有效库存」:

from collections import Counter def count_visible_stock(label_path, layer_map, min_conf=0.45): cnt = Counter() for line in open(label_path): parts = line.strip().split() if len(parts) < 6: continue cls_id = int(parts[0]) conf = float(parts[5]) cy = float(parts[2]) if conf < min_conf: continue for sku, (y_low, y_high) in layer_map.items(): if cls_id == sku and y_low <= cy <= y_high: cnt[sku] += 1 return cnt

layer_map把SKU和它所在的货架层位绑定,cy是归一化的检测框中心y坐标。这一步把上一章说的位置映射落到代码里,能挡掉很多后排商品误计。统计结果和基线库存对比,低于30%就生成补货任务。验证时先别追求全店准确率,挑一个高流转货架,跑两周,统计「系统预测缺货」和「人工盘点缺货」的符合率,能到八成以上,这个方向就值得继续投入。

这段路的血泪经验是:早期我把识别到的货架可见数量直接当全店库存,结果系统判断和后台库存永远对不上。后来想明白,摄像头只能看到排面,库存动态管理的价值不是取代WMS,而是告诉运营「哪个排面该补了」,把识别结果定位成可见库存和缺货信号,项目才真正落地。如果你也在这个方向上试错,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询