基于YOLOv8的智能结算系统实战:从数据标注到模型部署全流程

📅 2026/8/26 22:27:32
基于YOLOv8的智能结算系统实战:从数据标注到模型部署全流程
简介目标检测在零售场景的落地远不止是跑通一个模型商品识别与智能结算系统背后涉及数据采集、标注规范、模型选型、训练调参与工程化部署的完整链路。YOLOv8作为当前主流的目标检测算法凭借精度与速度的均衡表现常被用于构建货架商品识别与自动计价系统。但真实结算场景中商品外观相似、手部遮挡、类别不固定等问题都对模型提出了更高要求。本文从实际项目出发围绕YOLOv8的模型选型、数据集构建、数据增强策略、训练参数调优、ONNX导出及业务逻辑集成展开探讨如何将检测结果转化为订单闭环并解决相似商品误检与重复计价等工程难题。对于正在探索AI赋能零售、智能结算终端或无人货柜的技术团队这套方法论同样适用于门禁识别、安防检测等目标检测应用场景。 我去年在朋友的小超市里帮忙做了一套智能结算的原型系统就是拿 YOLOv8 识别货架商品、自动算总价的那种。当时在网上找了很多所谓“源码说明”下载下来发现不少都只是套壳 Demo跑通容易离真正能用差得远。这篇就按我实际趟出来的路子把整套系统从数据、训练到部署的关键节点拆开讲清楚。1. 为什么用 YOLOv8 做商品识别结算方案选型与项目边界确认1.1 结算场景难在“商品长得像”而不是“目标检测本身”拿目标检测做商品识别最容易被忽略的点是这不是一般的目标检测任务。停车场车牌识别、安全帽检测类别之间差异非常大但超市商品不一样比如可口可乐和百事可乐外观只有颜色深浅和商标的细微差别同类的不同口味包装也几乎一样。用 YOLOv8 做这类任务难点就两个类别间相似度高。模型很容易把“红色易拉罐”全识别成可口可乐忽略具体品牌细节。类别数多且不固定。超市 SKU 动辄几千就算小超市也有几百模型分类头的压力比常规视觉任务大得多。所以方案选型上你要提前想清楚是做一个“少类别高精度”的结算终端比如无人货柜、食堂收银台还是做一个“多类别全品类”的完整超市系统前者是 YOLOv8 完全能扛住的场景后者则建议用“YOLOv8 做检测 分类模型做二次细分类”的级联方案。1.2 YOLOv8 内部选型n / s / m / l / x 到底用哪个YOLOv8 官方给了 n / s / m / l / x 五个体积档位很多新手拿到项目直接无脑上 YOLOv8x结果训练一天一夜推理还卡成幻灯片。我实际对比下来结果如下表模型尺寸单张推理耗时CPU, 640px单张推理耗时GTX 1660Ti适用场景YOLOv8n6.3MB约 80ms约 6ms嵌入式、手机端、实时视频流YOLOv8s22.5MB约 150ms约 9ms普通 GPU 部署精度较高的选择YOLOv8m52MB约 300ms约 15ms离线批量识别精度要求高YOLOv8l89MB约 450ms约 22ms服务器端YOLOv8x168MB约 800ms约 32ms服务器端追求极致精度做结算系统我第一版就用 YOLOv8s。原因很简单结算场景根本不需要每秒 30 帧识别一般商品放在识别区 1-2 秒内出结果就行。精度比 YOLOv8n 高不少推理速度又足够支撑实际使用GTX 1660Ti 就能很轻松地跑起来不需要依赖昂贵的专业卡。如果做的是手机端或嵌入式设备上的结算模块那 YOLOv8n 量化是更合适的方向后面部署章节会细说。1.3 系统整体架构从摄像头到订单完成要拆成几层智能结算系统不是一个模型就能搞定的。我在源码里把整个项目拆成了 4 个相对独立的模块图像采集层负责从摄像头、视频文件或静态图片里拿图。实际项目里最常见的坑是摄像头型号杂、分辨率不一致所以我在这一层加了一个统一处理接口——所有输入先统一缩放再进入识别流程。检测识别层YOLOv8 模型。输入是图像输出是每个商品的类别、坐标框、置信度。业务映射层把识别结果映射到价格和库存。这一步在学术 Demo 里几乎没人做但实际落地非做不可。模型给出的是“class_id 7”你要通过这张映射表告诉系统7 农夫山泉 550ml单价 2 元库存减 1。交互展示层微信小程序 / Web 前端 / 实体终端屏幕用户看到识别结果、确认并完成支付。这个分层结构是我踩了“识别都对了但业务跑不通”的坑之后总结出来的。后面每个模块我都会结合源码展开讲。2. 数据才是整个系统的天花板商品数据集采集、标注与增强实操2.1 数据采集不是拿手机拍一遍完事很多项目死在数据上。商品检测数据集最让人头疼的问题是训练集里都是“完美商品”测试时一张真手拿着薯片就给识别错了。所以采集阶段就要人为增加难度。我的做法是背景要杂货架、桌面、购物筐、手拎着四种场景都至少拍 100 张。背景会影响模型学到的特征会让模型依赖物品轮廓的边缘信号。角度要够乱摄像头不能只在正上方俯拍。超市结算台常见机位是斜向 45 度到 60 度俯拍所以标注图里要掺一部分和实际机位一致的角度。俯拍和斜拍出来的视觉特征差异非常大。光线要自然不要用单反加补光灯拍出影棚效果真实摄像头就是普通环境光偏黄偏暗都正常。我在采集时特意挑了白天、傍晚、开灯、关灯四种情况。必须有“手介入”的样本结算场景里用户一定会把手伸进画面手部遮挡商品会造成大量漏检。训练集里一定要有一部分手持商品的照片否则到现场你就会看到模型检出率直线下降。2.2 标注具体操作类别一致性比框的细致程度更重要数据标注是这个项目里最耗时也最影响模型上限的环节。YOLOv8 的标注格式是每个图片对应一个 txt每行五个数字class_id center_x center_y width height这四个坐标都是归一化坐标范围 0-1由像素坐标/图片宽高得到。标注的时候用 LabelImg 就能搞定它导出时会自动生成这种格式。我总结出几条实测筛选标准推荐你注意不要抠得过细。标注框沿商品外轮廓即可不用精确到像素级。结算场景不允许实际上也没必要。同一商品不同品规要单独设类。纸巾这种商品小包装是 3 层 120 抽大包装是 5 层 200 抽模型可能看包装上的文字识别也可能看整体长宽比识别。如果你把它们归为一类后期价格就会乱套。标签不要用中文。源码里中文路径和中文类别名会带来很多莫名其妙的编码错误我用 clean_code 这种英文小写加下划线的命名方式后期省了很多事。2.3 数据增强用 mosaic 提高小商品检测精度YOLOv8 自带的数据增强已经很强了默认开启 mosaic、翻转、HSV 变换这些但有两个参数建议根据商品识别场景手动调整。第一个是 hsv_h / hsv_s / hsv_v 三个值。默认值分别是 0.015 / 0.7 / 0.4。对于商品识别颜色信息极其关键——可口可乐和百事可乐就是靠颜色区分的。如果 HSV 扰动太强模型会学到错误的颜色容差把两者混淆。我把这三个参数分别调小到 0.01 / 0.3 / 0.2红罐和蓝罐的区分就稳定了很多。第二个是 mosaic 的启用时机。mosaic 对提升小目标检测能力很有帮助但我训练 200 轮之后发现模型在真实数据上反而变差了。想了半天才弄明白mosaic 把四张图拼一起会让物体显示过小而结算场景的商品通常占画面很大面积。我的处理方式是前 100 轮开启 mosaic 提升特征提取能力后 50 轮关闭 mosaic 让模型回归到真实尺度的分布。3. 训练环节环境配置、参数调优和损失曲线判读3.1 环境配置最容易把人卡死的一步YOLOv8 最大的优势是用pip install ultralytics一步到位装官方库不需要像老版 YOLOv5 那样手动配一堆依赖。但有几个细节在配置时不处理好后面必踩坑。Python 版本官方要求 3.8但实测 3.10 和 3.11 兼容性最好3.12 某些依赖编译会有兼容问题。简单说不要图新直接装 3.10。PyTorch 版本关于 PyTorch 2.x 是否支持 YOLOv8 的问题目前官方库已经充分兼容 PyTorch 2.0 及 2.1 以上装最新稳定版即可。但如果显卡驱动是旧版本对应的 CUDA 版本不支持新版 PyTorch。我的建议是先查nvidia-smi里的 CUDA 版本再决定装哪个版本的 PyTorch。CPU 跑训练可行但要有心理准备。拿 YOLOv8s 训 100 张图的商品数据CPU 一轮要 5 分钟以上300 轮就是一天多。哪怕只有一张 GTX 1660Ti 6GB 显存也能把训练时间缩短到原来的零头。3.2 参数配置基于 GTX 1660Ti 的实测参考值GTX 1660Ti 是很多入门级玩家的第一块卡6GB 显存跑 YOLOv8sbatch size 开太大容易爆显存。实际的参数配置建议如下# train_config.yaml task: detect mode: train model: yolov8s.pt data: dataset/last_data.yaml epochs: 300 batch: 16 imgsz: 640 patience: 30 device: 0 workers: 4 optimizer: auto lr0: 0.01 lrf: 0.01 momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 3.0 hsv_h: 0.01 hsv_s: 0.3 hsv_v: 0.2 mosaic: 1.0几个参数的作用我解释一下batch: 166GB 显存下 YOLOv8s 640 输入batch 16 刚好能跑满但不爆显存。如果还出现 out of memory就把 imgsz 降到 480。patience: 30连续 30 轮验证集 mAP 没有提升就自动停止。我实际训练时大概在 250 轮左右触发了早停所以你不会白等 300 轮。lr0 / lrf最初学习率 0.01最终学习率 0.00010.01 * 0.01给模型后期一个精确收敛的空间。很多新手直接把学习率调到 0.001反而让模型收敛非常慢。3.3 画损失曲线图如何判断模型真的训好了YOLOv8 训练完会自动在runs/detect/train目录生成results.png里面包含了 box_loss / cls_loss / dfl_loss 和 mAP50 / mAP50-95 等曲线。很多人只看 mAP 最后一个点不够。我建议按这几个维度来读图box_loss定位损失也就是框标得准不准。如果曲线在震荡中缓慢下降最后趋于平缓没问题如果到后期还在大幅波动说明学习率太大或者数据质量有标签噪声需要调低 lr0。cls_loss分类损失就是对商品类别研判的对不对。如果训练集 cls_loss 降到很低但验证集还在高位基本可以断定过拟合了需要加数据增强或减小模型体积。mAP50-95综合指标。这个值反应的是不同 IoU 阈值下的平均精度。商品识别场景下 mAP50 重要mAP50-95 更值得关注——因为边框质量直接决定了结算时是否会误把相邻商品重叠框在一起。如果看到 mAP50 很高但 mAP50-95 很低说明分类正确率没问题但框的位置不稳定。这时候我去检查标注数据发现有小部分框把商品边缘切掉了重新修正后指标立刻上来了。4. 部署落地从 ONNX 导出到完整结算闭环的搭建思路4.1 模型导出不要去改模型加载逻辑训练完的模型是 .pt 文件在实际部署时直接用model YOLO(best.pt)加载也会在开发时运行没问题但在生产环境落地上我建议做一次格式转换。ONNX通用性最强跨平台跨语言适合做 Web API 服务或嵌入式设备的中间格式。TensorRTNVIDIA GPU 上推理最快但部署复杂度高需要和显卡型号绑定的编译过程。OpenVINO适合 CPU 推理掉速性能提升如果服务器没有 GPU这个值得试。我自己实际项目用的是 ONNX然后通过 ONNX Runtime 加载推理。代码很简单from ultralytics import YOLO # 加载训练好的模型 model YOLO(runs/detect/train/weights/best.pt) # 导出为 ONNX 格式 model.export(formatonnx, imgsz640, opset12, simplifyTrue)导出以后用onnxruntime库做推理import onnxruntime as ort import cv2 import numpy as np sess ort.InferenceSession(best.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider]) img cv2.imread(test.jpg) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized cv2.resize(img_rgb, (640, 640)) input_tensor np.expand_dims(resized.astype(np.float32) / 255.0, axis0) outputs sess.run(None, {images: input_tensor})需要注意ONNX 导出的模型对输入尺寸是固定的识别区域是 640导出是 640如果输入不一致会自动拉伸导致框的位置坐标需要按原始图比例换算回来。4.2 实际算力评估GTX 1660Ti 能不能支撑这套系统结论放在前面能而且绰绰有余。结算场景不像自动驾驶要求每帧毫秒级响应。用户把商品放到识别区后系统有 1 秒左右的时间完成检测就行。我实测在 GTX 1660Ti 上ONNX 导出后的 YOLOv8s 模型单帧推理约 9ms即使加上图像预处理、后处理、价格映射和 UI 刷新整体延迟控制在 80ms 以内。如果做的是实时视频流识别比如货架上的持续监控那就要考虑多线程处理了。关键洞见是不要每帧都跑模型而是做帧采样。比如每 5 帧抽一帧做检测即每秒约 6 次检测对结算场景已经足够了运算量直接降为原来的五分之一。4.3 业务闭环识别结果如何变成订单模型输出的是类别 ID 和置信度离“结算”还有一段距离。我在源码里设计了一个商品映射表# products.py PRODUCT_MAP { 0: {name: 可乐330ml, price: 3.0, stock: 100}, 1: {name: 雪碧330ml, price: 3.0, stock: 100}, 2: {name: 农夫山泉550ml, price: 2.0, stock: 200}, 3: {name: 乐事原味薯片, price: 7.5, stock: 50}, }识别流程说白了很简单模型识别结果 查表得到价格 统计数量和总价 输出前把置信度低于阈值的过滤掉。置信度阈值我推荐设到0.55-0.6之间。设低了误检商品会被算进订单里设高了真商品被漏掉用户会有抱怨。这个值不是玄学要用测试集来卡调一版阈值跑一批真实结算数据对比“多收”和“漏收”的比例取平衡点。还有个容易被忽略的问题同一个商品被连续两帧识别到要不要加两次需要结合业务逻辑判断走“并集”还是“最新一帧为准”。我采用“去重”策略——同一位置高 IoU 的检测框视为同一商品只在首次出现时加入订单后续帧只做位置跟踪不再重复计价。5. 实际落地时踩过的坑遮挡误检、重复计价和处理手部干扰5.1 手部遮挡和商品叠放严重拉低识别率的两大元凶结算台最真实的场景就是用户把商品放在识别区然后手还没来得及移开这时候模型识别率会掉到令人发指的地步。我统计过手部遮挡会导致识别率从 95% 降到 70% 左右。我的处理办法是加入一个“稳定期检测”逻辑检测到商品后不立即计价而是等待 300 毫秒如果同一商品连续三帧都被稳定的高置信度确认并且商品不在移动状态再计入订单。这本质上是用时间换空间——手迟早会移开识别区里的商品最终会达到一个相对稳定的状态。这个逻辑对用户体验影响很大。刚开始我做成“一旦识别到立即计价”结果用户手一伸订单里哗啦啦加了三个不存在的东西。后来改成稳定期检测误触率直线下降。5.2 相似商品误检可口可乐 vs 百事可乐这就是我之前强调的数据问题。我第一次只用了 200 张图训出来的小模型几乎每次都把百事可乐识别成可口可乐因为训练集里可口可乐图片数量是百事的 3 倍模型学到的类别先验严重偏向可口可乐。解决方法是三管齐下先配平数据可口可乐 300 张百事可乐也 300 张不再多退少补。再做难样本挖掘专门拍那种“包装已经扭曲、两个品牌看起来很像”的实物照片加进训练集。再加细分类网络如果说 YOLOv8 输出的置信度两个类别都在 0.5 左右徘徊说明模型拿不准这公平处理将图像区域 Crop 出来交给 EfficientNet 跑二分类单独判断是可口还是百事。最终准确率提升到 99.2%这个方案在真实项目中非常管用。5.3 模型持续迭代上线只是开始最后分享一个经验。你的模型永远不是一次训完就结束的。只要系统实际使用每天都会产生大量真实场景的图片和识别日志。定期从日志里把“置信度低于 0.5 但用户确实买了商品”的样本捞出来补充标注后继续训练。这是一种成本最低但见效最快的模型迭代方式。我的做法是跑一个简单的日志收集脚本把所有低置信度、用户取消订单的重试场景截图保存下来每周抽半天时间清洗、补标、增量训练一次。用不了几周模型对这家店的商品习惯就会越来越上道。这点我觉得是整个项目里最值得投入精力的环节前几轮训练练的是模型后面这步练的是整个系统对真实环境的适应能力。本文还有配套的精品资源点击获取