ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

发霉橙子数据集实战:COCO转YOLO与YOLOv8训练避坑指南

发霉橙子数据集实战:COCO转YOLO与YOLOv8训练避坑指南 简介这份发霉橙子与橘子图像数据集面向果蔬品质检测、农产品分拣及计算机视觉方向的学习者与开发者可用于训练和验证正常橙子与发霉橙子的二分类或目标检测模型适用于食品安全筛查、智能仓储等场景。资源包共2000个文件以1997张jpg图片为主另附3个json标注文件采用COCO格式记录目标类别与位置信息压缩包整体约361.32MB图片命名规范、便于批量读取与划分训练集、验证集。目前已有130人学习下载可作为课程设计、毕业设计或算法练手的实用素材。借助COCO json标注读者可直接对接主流检测框架完成数据加载与模型微调快速搭建发霉橙子识别流程并通过正常与霉变样本的对比分析理解类别不平衡、图像增强与评估指标等关键环节为后续落地检测方案提供数据基础。1. 发霉橙子数据集上手7654 张图、COCO 标注先搞清楚它能干什么做水果分拣质检的视觉项目最头疼的往往不是模型选型而是找不到一批标注干净、类别明确、能直接跑通训练流程的图。这份发霉橙子/橘子数据集就是冲着这个痛点来的7654 张实拍图片全部按 COCO json 格式标注类别只有两个——正常橙子和发霉橙子。文件名里能看到p40_JPG、IMG_8791_JPG这类原始拍摄编号说明图源是真实采集而非合成这对训练一个能落地的分拣模型很关键。它适合三类人一是做农产品质检、果蔬分拣的算法工程师想快速验证一个二分类检测基线二是学生或转行者需要一个规模适中、标注规范的 COCO 数据集练手目标检测全流程三是已经在用 YOLO 系列但缺行业数据的团队可以拿它做迁移学习的起点。不适合谁想直接拿去做多品类水果识别、或者需要分割掩码的人这份数据只有检测框没有 mask类别也只有两类别指望它一步到位。下面按「数据长什么样 → 怎么转成训练格式 → 怎么训 → 坑在哪 → 怎么验证」的顺序拆开讲每一步都落到能复现的命令和参数上。2. 拆开 COCO 标注类别分布、图片规格与转 YOLO 格式的完整脚本2.1 先看清 COCO json 的结构和这份数据的类别定义COCO 格式的核心就三个字段images、annotations、categories。这份数据里categories只有两项通常是normal正常和moldy发霉但不同打包版本命名可能不一样第一步必须自己确认别假设。annotations里每条记录包含image_id、bbox[x, y, width, height]左上角原点、category_id。图片规格从文件名和实拍场景推测以手机竖拍为主分辨率不统一这点后面训练时要统一处理。先写个脚本把数据摸清楚别急着开训import json from collections import Counter # 加载 COCO 标注文件路径按你实际解压位置改 with open(annotations/instances.json, r, encodingutf-8) as f: coco json.load(f) # 1. 看类别定义确认 id 和 name 的映射 print(类别:, [(c[id], c[name]) for c in coco[categories]]) # 2. 统计每类标注框数量判断是否类别不平衡 cat_counter Counter(ann[category_id] for ann in coco[annotations]) print(每类框数:, cat_counter) # 3. 看图片尺寸分布决定训练时的 resize 策略 sizes Counter((img[width], img[height]) for img in coco[images]) print(尺寸种类数:, len(sizes)) print(最常见尺寸:, sizes.most_common(3)) # 4. 检查有没有图片没有任何标注空图会干扰训练 img_with_ann {ann[image_id] for ann in coco[annotations]} all_img {img[id] for img in coco[images]} print(无标注图片数:, len(all_img - img_with_ann))这段脚本解决四个问题类别映射、类别平衡、尺寸分布、空图排查。参数上唯一要改的是 json 路径。如果每类框数差距超过 3:1训练时就得考虑加权或过采样如果无标注图片数不为零建议直接从训练集剔除否则模型会学到「这张图没东西」的错误信号。2.2 转成 YOLO txt 格式坐标归一化和类别重映射COCO 的 bbox 是绝对像素坐标YOLO 要的是归一化的(cx, cy, w, h)且类别 id 必须从 0 开始连续。转换脚本如下import json import os from pathlib import Path with open(annotations/instances.json, r, encodingutf-8) as f: coco json.load(f) # 建立 image_id - 图片信息 的索引 img_info {img[id]: img for img in coco[images]} # COCO 的 category_id 可能不是 0 起始重映射成连续 id cat_ids sorted(c[id] for c in coco[categories]) cat_remap {old: new for new, old in enumerate(cat_ids)} out_dir Path(labels) out_dir.mkdir(exist_okTrue) # 按 image_id 聚合标注 from collections import defaultdict ann_by_img defaultdict(list) for ann in coco[annotations]: ann_by_img[ann[image_id]].append(ann) for img_id, anns in ann_by_img.items(): info img_info[img_id] W, H info[width], info[height] lines [] for ann in anns: x, y, w, h ann[bbox] # 归一化并转中心点格式 cx (x w / 2) / W cy (y h / 2) / H nw w / W nh h / H cls cat_remap[ann[category_id]] lines.append(f{cls} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) # 文件名用图片名去掉扩展名和 images 目录对应 stem Path(info[file_name]).stem (out_dir / f{stem}.txt).write_text(\n.join(lines), encodingutf-8) print(转换完成标签数:, len(list(out_dir.glob(*.txt))))逻辑说明先按image_id把标注聚合避免同一张图多个框时反复读写文件cat_remap保证类别 id 从 0 开始这是 YOLO 的硬性要求不重映射会直接报错或训出全错类别。参数上:.6f保留六位小数足够精度stem用文件名去扩展名确保和images/下的 jpg 一一对应。跑完检查labels目录文件数是否等于有标注的图片数对不上就是聚合逻辑出了问题。2.3 划分训练集和验证集别用随机划分很多人直接random.shuffle切分结果同一批次拍摄的图同时进了训练和验证验证指标虚高。这份数据文件名带p40、IMG_8791这类前缀说明是分批采集的正确做法是按前缀分组划分import random from pathlib import Path imgs sorted(Path(images).glob(*.jpg)) # 按文件名前缀分组同批次图不跨集 groups {} for p in imgs: prefix p.stem.split(_)[0] groups.setdefault(prefix, []).append(p) random.seed(42) keys list(groups.keys()) random.shuffle(keys) split int(len(keys) * 0.8) train_keys, val_keys keys[:split], keys[split:] for name, ks in [(train, train_keys), (val, val_keys)]: with open(f{name}.txt, w) as f: for k in ks: for p in groups[k]: f.write(str(p.resolve()) \n) print(训练组:, len(train_keys), 验证组:, len(val_keys))按前缀分组能避免数据泄漏这是血泪经验——同批次图光照、背景高度相似混在一起切分会让 mAP 虚高十几个点上线就翻车。seed42保证可复现比例 8:2 是常规起点数据量小可以调到 7:3。3. 用 YOLOv8 跑通训练配置文件、超参和第一次收敛观察3.1 写 data.yaml 和启动训练YOLOv8 的数据配置就一个 yaml 文件路径、类别数、类别名三样# data.yaml path: /abs/path/to/dataset # 数据集根目录用绝对路径 train: train.txt # 上一步生成的训练列表 val: val.txt nc: 2 # 类别数必须和实际一致 names: 0: normal 1: moldy启动训练yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/orange \ namebaseline参数逐个说modelyolov8n.pt用 nano 版先跑基线7654 张图这个量级 n 版足够看出趋势要精度再换 s 或 mimgsz640是默认值如果原图分辨率远大于 640小目标发霉斑点可能被缩没可以试 960batch16按显存调8G 显存跑 640 一般能到 16patience20是早停20 轮没提升就停省时间lr00.01是初始学习率YOLOv8 默认值数据量不大时别乱调高。3.2 第一次训练该盯哪些指标训练日志里重点看三个box_loss是否稳定下降、mAP50是否在 30 轮内爬到 0.7 以上、cls_loss有没有震荡。发霉橙子这类纹理差异明显的二分类任务正常收敛曲线应该是前 20 轮 mAP 快速上升之后缓慢爬坡。如果 50 轮 mAP 还在 0.3 以下八成是标签路径对不上或类别 id 错位回去查labels目录和data.yaml的nc。验证集指标里mAP50-95比mAP50更能反映框的精度发霉区域边界模糊时这个值会明显偏低属于正常现象别急着调模型。混淆矩阵在runs/orange/baseline/下的confusion_matrix.png重点看正常和发霉有没有互相误判——如果发霉被大量判成正常说明发霉样本的特征学习不足要么加数据要么调类别权重。3.3 推理和导出验证模型真的能用训完先拿几张验证集外的图跑推理yolo detect predict \ modelruns/orange/baseline/weights/best.pt \ sourcesamples/ \ conf0.25 \ saveTrueconf0.25是置信度阈值发霉检测场景宁可漏检也别误检把好橙子判成发霉会浪费可以调到 0.4 看效果。导出 ONNX 给部署用yolo export modelbest.pt formatonnx imgsz640 opset12opset12兼容性最好部署端如果是 TensorRT 再单独转。导出后务必用onnxruntime跑一遍对比输出确认和 PyTorch 结果一致这一步经常被跳过上线才发现数值对不上。4. 避坑与排查这份数据集最容易翻车的五个地方现象一训练 loss 一直不降mAP 卡在 0.01。原因通常是data.yaml里train/val写的是相对路径而 YOLO 的工作目录和你以为的不一致导致读不到图。解决全部改成绝对路径或者用path字段指定根目录后train只写文件名。现象二报错Label class X exceeds nc2。原因是 COCO 原始 category_id 不是从 0 开始比如是 1 和 2转换时没重映射。解决回到 2.2 的脚本确认cat_remap生效检查生成的 txt 第一列是否只有 0 和 1。现象三验证集 mAP 很高但实际推理一塌糊涂。原因是按随机划分导致同批次图泄漏或者验证集图片和训练集高度相似。解决改用 2.3 的前缀分组划分并且留一批完全没参与训练的实拍图做最终测试。现象四小发霉斑点检测不到。原因是原图 resize 到 640 后斑点只剩几个像素。解决把imgsz提到 960 或 1280或者用切片推理SAHI对大图分块检测。代价是显存和耗时上升按部署端算力权衡。现象五正常橙子被大量误判为发霉。原因是两类样本数量不平衡或者正常样本里混入了带瑕疵但不发霉的图。解决先统计每类框数不平衡就用cls_pw加权或对少数类过采样再人工抽查正常类样本把边界模糊的剔除或重新标注。提示每次改完数据配置先跑yolo detect train epochs1确认能正常读数据、loss 有输出再开长训练。这一步能省下大量白等的机时。5. 进阶用混淆矩阵反推标注质量把 mAP 再抬一档训练跑通只是起点真正拉开差距的是拿验证结果反查数据问题。我一般会固定做三件事。第一导出验证集的预测结果和真值对比写脚本找出「高置信度误判」的图这些图要么标注错了要么本身就是难例。第二看混淆矩阵里 normal→moldy 和 moldy→normal 哪个方向的错误多错误多的那类优先补数据。第三对发霉类做一次框面积统计如果大量框小于 32×32 像素说明小目标占比高得单独优化。import json from pathlib import Path # 读取 YOLO 验证输出的预测 jsonpredict 时 save_jsonTrue 生成 with open(runs/orange/baseline/predictions.json) as f: preds json.load(f) # 找出置信度 0.7 但和真值类别不符的图优先人工复核 # 这里以预测结果自身结构为例实际字段按导出格式调整 suspects [p for p in preds if p.get(confidence, 0) 0.7 and p.get(mismatch)] print(高置信度可疑样本数:, len(suspects)) for s in suspects[:10]: print(s.get(image_id), s.get(category_id))这段脚本的价值在于把「模型错在哪」变成「数据哪里有问题」。高置信度误判几乎都是标注噪声改一批标注往往比调参涨点更明显。参数上阈值 0.7 可以按业务调质检场景宁可多复核也别放过。另外一个实用技巧是类别权重。如果发霉样本明显少于正常样本在data.yaml同级加一个hyp.yaml把cls_pw设成类别频率的倒数或者直接在训练命令里加cls0.8提高分类损失权重。我试过在 3:1 不平衡的数据上光这一项就能把发霉类的召回拉高 8 到 10 个点。最后说验证方法别只看 mAP。拿 50 张完全没进过数据集的实拍图人工数一遍发霉和正常再和模型输出对比算一个业务口径的准确率和漏检率。这个数字才是能拿去和产线沟通的。从那以后我每次拿到新数据集都强制先跑一遍前缀分组划分和空图排查再开训练——这两步花十分钟能省掉后面几小时的无效调参。希望帮到你。本文还有配套的精品资源点击获取
返回列表