资讯动态

零售场景实例分割数据集处理全流程:从COCO到YOLOv8训练实践

发布时间:2026/8/26 8:36:42 来源:尧图企业网站定制
简介实例分割是计算机视觉中区分图像中每个独立对象并生成像素级掩码的关键技术尤其适合密集堆叠、相互遮挡的复杂场景。其原理基于深度卷积网络通过掩码分支为每个实例生成精确轮廓相较目标检测的矩形框能更可靠地支持商品计数、货架陈列分析与购物车行为识别等零售AI应用。在零售场景中模型常依赖COCO标注的数据集需要处理多类别、小目标与长尾分布等挑战。实际工程中将COCO格式转换为YOLOv8训练格式并合理划分数据是高效落地实例分割模型的重要环节。本文围绕货架与购物车实例分割数据集系统梳理从数据校验、标注解读、格式转换到模型训练评估的完整链路为零售视觉开发者提供可复用的实践路径。 上周我刚从网盘拉下来一个叫“货架与购物车实例分割数据集_20251122_165431.zip”的压缩包光看文件名就知道是零售场景下的实例分割数据时间戳说明是近期导出的版本。这类数据集在仓库盘点、无人结算、货架陈列分析项目里非常常见但很多人下载之后第一反应就是解压、扔进模型开训结果往往在数据格式、标注质量、类别映射上反复折腾。这篇文章就把我从拿到 zip 到训练出可用模型的全过程拆开讲清楚包括压缩包验证、解压避坑、COCO 标注读取、转 YOLOv8 分割格式、训练参数调整以及对这份数据集的评估思路。想拿零售场景做实例分割的开发者、算法工程师或者刚入门想找一份能练手的数据集的朋友应该都能从里面找到对应自己阶段的内容。1. 货架与购物车实例分割数据集解决的是零售场景里哪两个老难题开始处理这份数据集之前我习惯先想想它到底要解决什么业务问题否则后面做数据检查和模型调优时容易没有方向。零售视觉里有两个高频又互相独立的场景一个是货架一个是购物车。货架场景的难点在于商品极度密集同类商品经常紧挨着摆放中间只有几像素的缝隙而且同一种商品在不同货架层、不同光照条件下外观差异很大购物车场景的难点在于商品是随机堆叠的瓶子、盒子、软包装互相遮挡车体本身的金属网格还会和商品边缘混在一起。这两个需求同时出现在一份数据集里说明制作方应该是想做一个覆盖“陈列识别 购物行为理解”的统一模型或者是为一个完整的门店视觉系统准备基础训练材料。实例分割在这种场景里的价值和目标检测是完全不同的。目标检测给每个商品一个矩形框两个商品紧挨着时框与框之间出现大面积重叠后处理用 NMS 根本不解决问题因为正确检测的结果本来就该重叠。实例分割直接给每个商品、每辆购物车一个像素级掩码遇到紧挨着的两瓶可乐掩码边界能精确到瓶身轮廓模型明确知道这是两个实例而不是一个。对后续的货架陈列统计来说这个精确度意味着你能知道某个 SKU 实际占了几层货架、排面数量是多少对购物车结算场景来说掩码能帮助后续的体积估算或商品去重比单纯一个框要可靠得多。这份数据集我特意看了命名后缀里的时间戳20251122 说明是最近才整理的版本。从实操角度看新版本通常意味着标注规范更统一、类别定义更贴近真实门店需求。相比 COCO 这类通用数据集零售领域的实例分割数据非常稀缺尤其是货架加上购物车两个场景放在一起的能省去自己找数据、清洗数据的大量时间。不过话说回来拿来直接用和用得对是两码事接下来几节就是我处理这份数据时按顺序做的事。2. 压缩包先别急着解压目录清单、完整性校验与文件类型三连查很多人的习惯是从网盘下载完 zip 直接双击解压但我建议在 Linux 服务器上处理数据集时先花两分钟把压缩包本身检查一遍。网络下载过程中文件损坏、网盘自动改名、甚至是下载到了网页错误页这些问题都很常见等解压到一半再报错反而更难排查。我总结了一个三连查的习惯基本能挡住八成以上的压缩包问题。2.1 第一查用 unzip -l 看压缩包内部结构在解压前先用unzip -l查看压缩包内文件列表这一步能帮你判断目录组织是否合理也避免解压后文件散落一地或者把当前目录搞乱。unzip -l 货架与购物车实例分割数据集_20251122_165431.zip执行后会列出 zip 内所有文件的路径、原始大小、压缩后大小。我主要看三件事第一是否有一个顶层目录比如retail_seg/有的话解压出来会自动建好目录没有的话我需要自己新建防止文件直接铺满当前目录第二images 和 annotations 是否分开通常实例分割数据集会有一个 images 目录放原图另一个 annotations 目录放标注 JSON 或文本第三有没有 README、license、class_names.txt 这类辅助文件这类文件是理解数据集的关键很多人忽略不看后面踩坑了才回头找。2.2 第二查用 unzip -t 做完整性与一致性测试只查文件列表还不够zip 文件可能因为下载中断导致中央目录损坏或部分数据块缺失。用unzip -t可以对压缩包内所有文件做 CRC 校验比单纯看文件大小准确得多。unzip -t 货架与购物车实例分割数据集_20251122_165431.zip如果所有文件都提示OK说明压缩包完整。如果某个文件报crc mismatch或者bad CRC大概率是传输过程中数据损坏需要重新下载。如果直接报End-of-central-directory signature not found那基本可以断定文件不完整甚至根本不是 zip 文件。unzip -t这个检查步骤看起来简单但特别值得养成习惯。我之前遇到过一次数据集训练到一半突然发现某些图片掩码错位最后排查是因为压缩包某段数据损坏导致解压出来的图片和标注配对有问题。从那以后任何数据集我都要先跑一遍完整性校验磨刀不误砍柴工。2.3 第三查用 file 确认真实文件类型有时候下载回来的文件后缀是.zip但实际内容根本不是 zip 格式。最常见的是下载到了网页的 404 页面或者网盘提示“文件已过期”。这时用file命令一眼就能看出来file 货架与购物车实例分割数据集_20251122_165431.zip正常的 zip 会输出Zip archive data, at least v2.0 to extract。如果输出HTML document、gzip compressed data、ASCII text之类的结果就说明文件被替换过或者下载不完整。这种情况下不要继续解压直接回到下载源重新获取。尤其是你在浏览器里点了下载但跳转到了某个中间页浏览器保存下来的很可能是 HTML 而不是真正的压缩包。为了快速判断我把这三种情况整理成了简单对照表检查项命令正常结果异常结果目录清单unzip -l file.zip显示 zip 内文件列表报错或中文乱码完整性unzip -t file.zip所有文件 OKcrc mismatch / 中央目录找不到文件类型file file.zipZip archive dataHTML document / gzip compressed data这三连查做完才算真正准备好解压。3. 解压前后最容易翻车的地方编码、权限和原始标注备份确认压缩包没问题之后自然就是解压。但解压看起来是最简单的操作实际在数据和标注联动场景里暗坑不少。尤其是中文文件名、权限设置、以及标注文件被意外修改这三个问题我几乎每次换新数据集都会遇到。3.1 Linux 下的解压命令和目录规划先规划好数据目录再解压不要在当前目录里随手一搞。我的习惯是把数据集统一放到一个独立数据盘下用数据集名建一个目录然后把压缩包放进去再解压mkdir -p /data/datasets/retail_seg mv 货架与购物车实例分割数据集_20251122_165431.zip /data/datasets/retail_seg/ cd /data/datasets/retail_seg unzip 货架与购物车实例分割数据集_20251122_165431.zip这里有一个经常被忽略的点如果压缩包内部没有顶层目录解压后图片和标注文件会直接铺在retail_seg/下后期维护、移动、同步都很痛苦。我一般会在unzip -l阶段就确认这一点。如果确实没有顶层目录我习惯先建一个子目录然后再把 zip 移进去解压等价于手动补一层路径。这种细节前期花一分钟后期能省一小时。3.2 文件名编码问题中文乱码的应急处理网络上下载的数据集经常是中文文件名压缩时大概率是 GBK 编码。Linux 系统默认 UTF-8直接 unzip 会出现中文名乱码导致图片文件路径和标注里的 file_name 对不上。遇到这种情况时如果 unzip 版本较老可以尝试指定编码但各家发行版支持并不一致更稳妥的做法是直接用 Python 的 zipfile 模块解压Python 3.11 之后的版本对编码兼容好了很多import zipfile with zipfile.ZipFile(货架与购物车实例分割数据集_20251122_165431.zip, r) as zf: zf.extractall(/data/datasets/retail_seg/)用这个方案zip 内的文件名会按 zip 元数据里的实际编码还原中文目录也能正常创建。如果仍然乱码那就需要先解压再用convmv之类的工具批量转换文件名编码或者利用标注 json 里的 file_name 做一次重命名映射。这一块的处理顺序建议是先看 JSON 里存的 file_name 是什么规则再决定当前文件名要不要调整。3.3 权限问题解压后立刻设置好读写权限在服务器上解压数据集经常会遇到 root 解压后其他账号无法读取文件的情况。不要等到训练脚本报 Permission denied 再去处理。解压完成后我一般立即把属主和权限设置好chmod -R urwX /data/datasets/retail_seg chown -R $(whoami) /data/datasets/retail_seg另外如果后续还要修改标注文件、生成 label 缓存、写入训练日志目录的写权限也很重要。尤其要注意chmod -R urwX里的X它只给目录加执行权限不会给普通文件加执行权限这是比chmod -R 777更安全的设置。3.4 原始标注备份必须做的保底操作任何需要写回、转换、清洗的数据集我都强烈建议先对原始标注文件做一份只读备份。比如这份数据集的 annotations 目录里如果有instances_train.json就把整个 annotations 目录复制到一个backup文件夹里然后只读cp -r annotations backup_annotations_origin chmod -R a-w backup_annotations_origin为什么一定要做这个因为你在做格式转换、数据清洗时脚本一旦有 bug就可能直接覆盖原文件。训练结束后你发现结果不对想回头核对原始标注时原文件已经被改得面目全非那个教训非常深刻。保留只读副本之后转换脚本再怎么跑原始标注都能随时拿来对照排查问题会轻松很多。4. 读懂 COCO 标注json 字段、RLE 解码与可视化初筛解压出来之后数据的核心资产其实是标注文件。实例分割数据集的标注格式多种多样COCO 是最常见的一种。如果这份数据集用的是 COCO JSON 格式那我建议先别急着写转换脚本先花点时间把 json 结构和几个关键字段搞清楚。4.1 COCO json 的三段式结构COCO 格式的标注 json 一般由三大部分组成images、annotations、categories。images 数组每个元素包含 id、file_name、width、height 等字段描述一张图片的基本信息。annotations 数组每个元素描述一个实例包含 id、image_id、category_id、bbox、area、segmentation、iscrowd 等字段。categories 数组类别列表包含 id、name、supercategory。其中segmentation是最重要的字段也是实例分割与目标检测区别所在。COCO 的多边形标注通常是一个 list索引 0 是坐标数组按x1, y1, x2, y2, ...排列。iscrowd字段表示这个实例是否是密集人群或重叠对象如果是 1一般用 RLE 编码训练时通常选择忽略。很多人在这一步会犯一个低级错误字段里的bbox不是实例分割的标签它只是目标检测的标注。如果直接拿另一个检测模型的代码读这份 JSON忽略 segmentation那实例分割就名存实亡了。理解字段结构是后面所有工作的地基。4.2 用可视化脚本做第一轮质量筛查我拿到实例分割数据集的第一件事不是看统计数字也不是直接转格式而是先写一个可视化脚本把标注叠加到原图上。这一步能直观发现很多问题比如标注多边形是不是跑偏了、分类是不是错的、有没有漏标。import json import cv2 import numpy as np with open(annotations/instances_train.json, r) as f: data json.load(f) for img_info in data[images][:10]: img cv2.imread(fimages/{img_info[file_name]}) mask np.zeros((img_info[height], img_info[width]), dtypenp.uint8) for ann in data[annotations]: if ann[image_id] ! img_info[id]: continue if ann[iscrowd]: continue seg ann[segmentation] if isinstance(seg, dict): # RLE 格式需要用 pycocotools 解码 continue poly np.array(seg[0], dtypenp.int32).reshape(-1, 2) cv2.fillPoly(mask, [poly], 255) cv2.imshow(mask, mask) cv2.waitKey(0)这个脚本非常简略只适合做第一眼排查。真正的可视化检查应该把原图、掩码、类别名一起画出来这样你才能发现标注是否精确贴合商品边缘以及类别是否标错。零售场景里很容易出现“洗衣液和柔顺剂外观接近标注员把类别标反”的情况光看统计数字是发现不了的。4.3 处理 RLE 编码的 segmentation上面脚本里我直接跳过了 RLE 格式但实际数据集如果来自 COCO 官方或者某些大规模标注平台很多密集实例会用 RLE 存储。RLE 是 run-length encoding压缩效率高但不能直接用多边形坐标来理解。解码 RLE 需要借助 pycocotoolsfrom pycocotools import mask as mask_utils rle ann[segmentation] if isinstance(rle, dict): if counts in rle and isinstance(rle[counts], list): rle mask_utils.frPyObjects(rle, img_info[height], img_info[width]) binary_mask mask_utils.decode(rle) print(binary_mask.shape, binary_mask.dtype)解码出来是 HxW 的 uint8 mask之后再提取轮廓或直接转成 YOLO 多边形格式。这一块如果处理不当后续转换出来的 txt 文件可能是错误的模型在训练时就会产生奇怪的 loss。4.4 类别分布和实例面积统计可视化检查通过后就要做数据分布的量化统计了。这一步我通常用一段脚本打印类别实例数、每张图平均实例数、实例面积占比分布。统计完之后重点关注三个信号类别极不均衡比如某个类别 5000 个实例另一个类别只有 50 个。若直接训练少数类基本学不好需要考虑过采样或类别权重。小目标占比过高货架上的商品经常很小。如果大量实例面积占原图比例低于 1%模型会比较难收敛需要调大输入分辨率或专门做小目标增强。极端密集图片一张图里几十上百个实例这种图虽然有价值但训练时显存占用高也可能导致梯度波动。统计时可以单独标记。我自己统计这类数据时发现货架场景很容易出现某类畅销商品数量特别多、冷门 SKU 数量特别少的情况这是零售数据集的常见特点。如果目标只做几类核心商品那问题不大如果要做全量 SKU 识别就要认真处理长尾分布。5. 转成 YOLOv8 分割格式的核心逻辑和数据划分策略看完标注、确认数据质量能接受之后接下来最常用到的就是把它转成 YOLOv8 支持的格式因为 Ultralytics 的训练链路最顺。YOLOv8 实例分割的标注格式是每张图对应一个 txt 文件每行表示一个实例格式为class_id x1 y1 x2 y2 ... xn yn坐标统一归一化到 0-1 之间。5.1 COCO 转 YOLO 格式的转换逻辑核心逻辑非常简单遍历 annotations按 image_id 分组把每个实例的 segmentation 多边形坐标除以图片宽高得到归一化坐标然后写入以图片名命名的 txt 文件。但这里有三个容易出错的点我详细说一下。第一类别 id 映射。COCO 的 category id 不一定是连续的可能是 1、3、7 这样的稀疏编号YOLO 要求 class_id 从 0 开始连续排列。所以一定要先构建一个映射表把原 category id 映射到新 id。如果直接用原 id 写入 txt模型会把标签空间理解成有空洞的后果非常隐蔽训练时 loss 正常下降但测试结果对不上。第二坐标归一化。有的人会只对 x 除以宽、y 除以高但如果在写 txt 时把顺序搞反或者忽略了原始坐标是多边形那就会出现坐标错乱。第三RLE 转多边形。这部分需要先用 pycocotools 解码 RLE 得到 mask再用mask_utils.toPolygon或cv2.findContours提取轮廓再按 YOLO 格式写入。千万不要忽略这一步很多转换脚本遇到 RLE 就跳过导致部分图片没有对应 txt训练时该图直接变成无标签数据模型在那里偷偷学背景。我写了一个更完整的转换片段保留 RLE 处理逻辑import json import numpy as np from pathlib import Path from pycocotools import mask as mask_utils def convert_coco_json_to_yolo(coco_json_path, image_dir, label_dir): with open(coco_json_path, r) as f: coco json.load(f) cat_id_map {cat[id]: i for i, cat in enumerate(coco[categories])} img_id_map {img[id]: img for img in coco[images]} anns_by_img {} for ann in coco[annotations]: if ann[iscrowd]: continue anns_by_img.setdefault(ann[image_id], []).append(ann) for img_id, anns in anns_by_img.items(): img img_id_map[img_id] img_w, img_h img[width], img[height] lines [] for ann in anns: cat_id cat_id_map[ann[category_id]] seg ann[segmentation] polygon_xy None if isinstance(seg, dict): rle seg if counts in rle and isinstance(rle[counts], list): rle mask_utils.frPyObjects(rle, img_h, img_w) mask mask_utils.decode(rle) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: continue contour max(contours, keycv2.contourArea) polygon_xy contour.flatten().tolist() else: polygon_xy seg[0] if not polygon_xy: continue pts [f{polygon_xy[i] / img_w:.6f} {polygon_xy[i 1] / img_h:.6f} for i in range(0, len(polygon_xy), 2)] lines.append(f{cat_id} .join(pts)) if lines: out_txt Path(label_dir) / (Path(img[file_name]).stem .txt) out_txt.write_text(\n.join(lines), encodingutf-8)转换完成后最好用可视化脚本再随机抽几张图确认 txt 里的坐标和原图上的货架、购物车能对上。这一步多花十分钟能极大地避免后续训练时出现神秘的 loss 不降问题。5.2 数据划分不能只按文件名随机切零售数据集最大的特殊性在于场景相关性很强。同一组货架的连续照片、同一辆购物车的多张视角图如果只做简单随机划分训练集和验证集之间可能会有大量相似样本导致验证 mAP 虚高。我在做这类数据集时会先按场景标识分组再在组级别做划分。具体操作上如果文件名或目录名包含货架编号、门店编号、摄像头编号我建议先提取出来作为分组 key。比如文件名是shelf_12_001.jpgshelf_12就是一个组把同一个货架的所有图片放到同一个组然后按组随机划分到 train/val/test。# 示例按前缀分组再把组拆成 train/val/test python split_by_group.py \ --image_dir images \ --label_dir labels \ --group_pattern shelf_\d \ --ratio 0.8 0.1 0.1如果文件名里看不出场景分组也可以利用 EXIF 信息里的拍摄时间、相机位置来聚类。这个工作不复杂但对模型泛化能力影响很大尤其是零售场景想要跨门店部署的时候评估结果必须反映真实的跨场景泛化能力而不是在同一场景不同帧之间的记忆能力。5.3 配置 data.yaml 和训练命令划分完成后YOLOv8 需要一份 data.yamlpath: /data/datasets/retail_seg train: images/train val: images/val test: images/test names: 0: product 1: shopping_cart注意 data.yaml 里的 names 必须和 txt 里的 class_id 严格一致。然后就可以启动训练yolo segment train \ dataretail_seg.yaml \ modelyolov8n-seg.pt \ epoch100 \ imgsz640 \ batch16 \ device0第一次跑建议直接用 nano 版本先确认数据加载没问题、loss 能降下来再换大模型调精度。如果数据集里类别多、目标小imgsz可能需要提到 960 或 1280但相应会显著增加显存占用需要同步调小 batch。6. 训练中的几个真实状况loss 曲线、显存压力和类别失衡把配置跑通只是第一步训练过程中几乎一定会遇到几个状况。我把最常遇到的几个问题以及我的处理方式列出来这些都是实际踩过的。6.1 seg_loss 下降但 mAP 停滞先怀疑过拟合YOLOv8 训练结果页面里的 seg_loss 是分割分支的损失如果 seg_loss 在训练集上持续下降但验证集 mAP 不涨甚至下降最可能的问题是模型在训练集上过度拟合了。零售数据的模式往往比较固定同类商品外观一致、摆放位置也差不多如果训练集和验证集又有相似样本曲线表现会看起来不错但一旦换到新门店就会崩。遇到这种情况我先做三件事一是降低模型容量从 l 版换到 m 版看 mAP 是否更稳定二是加大数据增强尤其是随机翻转、颜色抖动、旋转帮助模型摆脱“背场景”的倾向三是检查验证集划分确认没有太多相似帧。这三步一般能解决大部分问题。6.2 显存溢出实例分割比检测更吃显存很多人以为用yolov8n-seg.pt就能轻松跑起来但实例分割模型比同量级的目标检测模型更占显存因为分割分支需要输出大尺寸的 mask logits。训练时如果报 CUDA out of memory我建议按这个顺序调整先减小 batch 到 8 或 4再看imgsz是不是 1280如果是就先降到 640。如果还不行考虑开启梯度累积或者用更小的模型。很多时候先把训练流程跑通更重要精度可以后面再调。我习惯第一次训练用小模型、小分辨率、小 batch确保从数据加载到 eval 全链路没有 bug然后再逐步加大。6.3 类别不均衡导致少数类掩码质量差零售数据集类别不均衡很常见训练结束后看每个类别的 mAP会发现热门商品类别的 mAP 可能到 0.85 以上冷门商品只有 0.4 左右。处理方法有几种一是给少数类提高 loss 权重YOLOv8 里可以用每个类的权重来控制但需要改配置二是对少数类做复制增强每轮训练时多采样几张三是在数据划分时对少数类做分层采样保证验证集中也有足够样本。我个人的习惯是先不调权重先把不均衡的分布记录清楚再用原始数据跑一轮 baseline。因为有时候模型对少数类效果差不只是数量少还可能是因为少数类本身外观和多数类接近加权重反而会让模型产生误检。先跑 baseline再根据混淆矩阵做针对性处理比盲目调权重靠谱。6.4 预测结果可视化mask 粘连和空洞问题训练结束后的评估阶段我会把验证集上的预测结果保存成图来检查。比较典型的问题有两类一是两件紧挨着的商品 mask 粘连在一起看起来像是同一个实例二是购物车金属网格区域的预测掩码有空洞或者把车体结构误检成商品。这类问题 mAP 指标不一定能完全反映但产品落地时非常影响体验。处理 mask 粘连可以在后处理里对掩码做小面积连通域剔除或者用形态学开运算分离细缝处理金属网格误检需要专门收集购物车空车状态作为负样本加入到数据集中。这两点都是比较实务的经验通用模型训练教程里很少提到但零售场景里非常重要。7. 我拿这份数据集实测后的一些总结与建议整条链路走下来我对这类零售实例分割数据集最大的感受是数据集本身的价值不取决于文件大小或图片数量而取决于标注质量和场景划分是否贴近真实业务。货架和购物车两个场景合在一起训练出的模型在门店视觉项目里确实能省去很多数据采集时间但前提是你愿意在数据检查、格式转换和划分策略上花功夫。7.1 把 zip 校验和备份流程固化到脚本里现在不管是从网盘、GitHub 还是其他渠道下载数据集我都会先把下载、完整性校验、备份标注这三个动作做成一个脚本每次拿到新数据集就自动跑一遍。这个习惯是从那次压缩包损坏导致训练数据错乱之后养成的。虽然多花两分钟但换来的是训练过程不会因为数据问题反复返工。unzip -t dataset.zip unzip dataset.zip -d dataset_dir cp -r dataset_dir/annotations dataset_dir/backup_annotations_origin chmod -R a-w dataset_dir/backup_annotations_origin7.2 验证集可视化这一步永远别跳很多人会依赖自动评估指标觉得 mAP 高就万事大吉。但实际在零售场景里mAP 高也可能存在掩码边界粗糙、错误合并、类别混淆等问题。我每轮训练结束后都会抽几十张验证集图片把预测掩码和 GT 叠在一起看这个可视化确认的过程虽然原始但效果比任何指标都直接。7.3 如果要做全量 SKU 识别建议再补一批冷门商品样本如果你要把这份数据集用在真正的门店项目里光靠数据集里的类别可能不够因为实际 SKU 数远大于数据集类别数。一种思路是把这份数据作为预训练基础再针对自己门店的畅销商品做增量标注。增量标注不需要做数千张每类几百个实例就能显著提升识别精度。另一个思路是借助大模型生成合成数据把商品渲染到不同的货架背景上也能有效扩充训练样本。总的来说处理一份实例分割数据集最忌讳的就是跳过数据检查直接开训。压缩包完整、标注明确、划分合理、转换正确这四个基础做好了训练效果自然水到渠成。希望这篇分享能给正在做零售视觉或者实例分割落地的朋友提供一些实际参考。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价