YOLOv11货架商品识别实战:从环境配置到库存管理
发布时间:2026/10/5 7:41:21 作者:尧图编辑部 阅读量:1,286

简介面向零售行业智能化升级这份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 python3.10 -y conda activate retail然后装PyTorch。这一步最大的坑是pip默认装的torch可能不带CUDA明明机器有显卡训练时却跑在CPU上。常见做法是先装匹配本机CUDA版本的torch再装ultralyticspip 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(sourceshelf_test.jpg, conf0.4, saveFalse) r results[0] r.save(output/shelf_vis.jpg) # 画框后的可视化图 r.save_txt(output/labels, save_confTrue) # 保存YOLO格式的txt标签 print(r.boxes.data) # [x1, y1, x2, y2, conf, cls]说明r.boxes.data里每一行是一个检测框后两列分别是置信度和类别IDsave_txt保存的是归一化坐标和训练数据同格式后面做库存统计时直接解析它就是。这两行代码是整条数据管线的地基——训练时模型学习的是这种格式推理时输出的也是这种格式后面所有库存逻辑都从这个txt出发。3. 货架数据集是效果上限采集、标注与VOC转YOLO格式3.1 数据采集的三种来源与SKU标注粒度货架识别模型的效果上限由数据决定这句话被说烂了但落在零售场景里有具体含义你不能只拍一个门店的一个货架。常见做法是三种来源混着来。第一种是监控视频抽帧摄像头固定角度按每秒一帧抽取覆盖营业高峰、低峰、补货前后不同状态这是主力数据第二种是巡店时用手机或平板在货架正前方、略高于层板的位置拍摄补充不同门店的灯光和陈列差异第三种是商品渲染图合成把单品图贴到背景上做数据增强这种方法只能当补充不能当主力否则模型会对合成图的边缘纹理过拟合。标注粒度是另一个容易搞错的地方。货架场景的类名不要用「可乐」这种商品分类名要用SKU编码比如coke_500ml、coke_330ml、sprite_500ml是三个不同的类哪怕它们长得几乎一样。道理很简单库存动态管理要的是SKU级别的数量变化不是「这排是饮料」这种粗粒度结论。同一商品不同包装、不同容量在模型眼里就是不同类如果你把它们标成同一类后续库存根本没法对账。标注工具用labelme或CVAT都行界面不是重点重点是保存格式。labelme默认保存JSONCVAT可以导出VOC或COCO而YOLOv11训练需要的是每个图片同名的一个txt。这里就涉及转换脚本也是下面要写的。标注量的底线建议是每个SKU至少300到500个实例并且至少覆盖3家门店的光照条件。低于这个量模型会在过拟合和漏检之间反复横跳调参再勤都救不回来。3.2 VOC转YOLO格式转换脚本与四个边界坑VOC的XML里存的是像素绝对坐标xmin, ymin, xmax, ymaxYOLOv11需要的是归一化后的中心坐标和宽高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(parentsTrue, exist_okTrue) Path(out_txt).write_text(\n.join(lines), encodingutf-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_mosaic10否则模型在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 modelyolov11s.pt \ datadataset/shelf.yaml \ epochs100 batch16 imgsz640 \ device0 patience20 close_mosaic10参数说明model指定预训练权重yolov11s.pt是从COCO训练好的基础模型上继续微调fine-tune比从零训练收敛快得多epochs在货架场景建议100起步数据量大超过2万张再加到150batch大小受显存限制16是8GB显存比较稳的值显存小就降到8imgsz默认640小目标严重的场景可以考虑960但显存占用接近翻倍patience20意思是验证集指标连续20个epoch不提升就早停防止后面白烧电费close_mosaic10让最后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 modelruns/detect/train/weights/best.pt \ datadataset/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_conf0.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 cntlayer_map把SKU和它所在的货架层位绑定cy是归一化的检测框中心y坐标。这一步把上一章说的位置映射落到代码里能挡掉很多后排商品误计。统计结果和基线库存对比低于30%就生成补货任务。验证时先别追求全店准确率挑一个高流转货架跑两周统计「系统预测缺货」和「人工盘点缺货」的符合率能到八成以上这个方向就值得继续投入。这段路的血泪经验是早期我把识别到的货架可见数量直接当全店库存结果系统判断和后台库存永远对不上。后来想明白摄像头只能看到排面库存动态管理的价值不是取代WMS而是告诉运营「哪个排面该补了」把识别结果定位成可见库存和缺货信号项目才真正落地。如果你也在这个方向上试错希望帮到你。本文还有配套的精品资源点击获取