
简介这是一份面向目标检测与农业智能化应用的12种水果YOLO标注数据集适合算法开发者、农业科研人员及YOLO系列模型学习者使用。数据按YOLO格式组织包含训练集1556张、验证集151张、测试集434张图片覆盖苹果、香蕉、哈密瓜、无花果、葡萄、芒果、橙子、梨、菠萝、石榴、草莓、西瓜共12类常见水果JPEG原图与txt标注一一对应可直接用于YOLOv5/YOLOv8/YOLOv12等主流框架训练。资源包共2000个文件主要包括1998个txt标注文件、1个yaml配置文件和1个docx说明文档整体约113.11MByaml文件便于配置类别与路径docx文档则对数据集结构、应用场景做了详细说明。数据集兼顾单目标和成串多目标场景并包含易混淆水果样本有助于训练模型学习细粒度特征支撑水果自动分拣、果园成熟度检测与食品品质分级等实际任务。发布至今已有138人学习适合作为农业目标检测项目的基础训练数据。1. 2141 张图训 12 类水果真正要面对的不是数据量拿到这个数据集先别急着解压就跑yolo train。训练集 1556、验证集 151、测试集 434合计 2141 张 JPEG 图覆盖 Apple、Banana、Cantaloupe、Common_fig、Grape、Mango、Orange、Pear、Pineapple、Pomegranate、Strawberry、Watermelon 共 12 个类别。这个体量放在目标检测任务里属于典型的小样本场景。单看绝对数量不多但标注是统一的 YOLO 格式文件名保留了 Roboflow 导出的rf.后缀解压后类别编号、标注文件结构都比较规整意味着拿到手后直接对齐目录就能跑训练。这套数据更适合两类人一类是刚接触 YOLO 训练流程、需要一份结构干净的数据集来跑通整套 pipeline 的开发者另一类是做农业视觉项目但暂时没有条件自己采集标注的工程师拿来当基线数据验证方案可行。真正决定模型上限的是你怎么组织目录、清洗标注、调数据增强和设计验证策略这些才是下面要拆的内容。2. 目录重组、标注解析与类别映射训练前必须做对的事2.1 先别急着训把数据集目录对齐到 YOLO 的约定YOLO 系列v5/v8/v11/v12对数据集的目录要求其实高度一致图片和标注文件要分别放在images和labels文件夹下且同一张图的图片名和 txt 标注名必须完全一致只是扩展名不同。这个数据集里每个 txt 对应一张同名 jpg比如Orange_0413_jpg.rf.b948f8bd80b5a8008b60a3171d56ba35.txt对应的图片就是Orange_0413_jpg.rf.b948f8bd80b5a8008b60a3171d56ba35.jpg只是原始压缩包内目录结构可能还是培训集/验证集/测试集拆分状态。为了让 YOLO 的训练器直接读我一般会跑一个脚本把它重排成如下结构dataset/ ├── train/ │ ├── images/ # 1556 张 jpg │ └── labels/ # 1556 个 txt ├── valid/ │ ├── images/ # 151 张 jpg │ └── labels/ # 151 个 txt └── test/ ├── images/ # 434 张 jpg └── labels/ # 434 个 txt这里要注意一个细节很多下载下来的资源里test目录只有图片没有标注但这个数据集明确是测试集也带标注的所以可以直接拿测试集做最终评估。如果你解压后发现 test 没有 labels就老老实实把验证集拆一半出来当测试集不要硬凑。2.2 标注文件里到底写了什么先解析再训练YOLO 格式的标注每一行代表一个目标class_id x_center y_center width height前三个是归一化坐标取值在 0 到 1 之间。拿Orange_1564_jpg.rf.801c88b98888d4411bca9049f7eac57a.txt这类文件来说里面可能是5 0.432 0.511 0.234 0.318 5 0.710 0.656 0.188 0.245这表示两张目标类别编号都是 5对应 Orange。如果不做任何检查直接训练最怕遇到标注坐标小于 0 或大于 1 的情况YOLO 训练时要么报错要么学习到错误的回归目标。我习惯先跑一段 Python 验证标注合法性import os from pathlib import Path label_dir Path(dataset/train/labels) bad_files [] for txt in label_dir.glob(*.txt): with open(txt) as f: for line in f: parts line.strip().split() if len(parts) ! 5: bad_files.append((txt.name, 行格式错误)) continue try: cid, x, y, w, h float(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) except ValueError: bad_files.append((txt.name, 数值解析失败)) continue if not (0 x 1 and 0 y 1 and w 0 and h 1): bad_files.append((txt.name, f坐标越界 x{x} y{y} w{w} h{h})) if cid 0 or cid 12: bad_files.append((txt.name, f类别编号异常 cid{int(cid)})) if bad_files: for name, reason in bad_files[:20]: print(name, reason) else: print(全部标注文件格式合法共, len(list(label_dir.glob(*.txt))), 个文件)这段脚本做了三层检查行内是否恰好 5 个数值、坐标是否都在 0 到 1 之间、类别编号是否落在 0 到 11 区间内。坐标越界是最常见的问题多发生在人工标注后导出时如果测试集标注本来就缺了一部分跑完这个脚本也能发现端倪。正常情况下这个数据集应该全部通过但下载资源在传输、解压过程中偶尔会出现文件损坏提前跑一遍不亏。2.3 类别映射与 data.yaml错一个编号全盘皆输12 个类别的编号顺序决定了模型输出的含义。以摘要描述中的顺序为准类别映射为编号类别名编号类别名0Apple6Orange1Banana7Pear2Cantaloupe8Pineapple3Common_fig9Pomegranate4Grape10Strawberry5Mango11Watermelon这个顺序不能随意改动。YOLO 的数据集配置是一个 yaml 文件里面写清训练、验证路径和类别名称训练时会根据 yaml 里的names顺序生成类别索引。如果 yaml 里把 Banana 放在第一位而标注文件里 Banana 的编号是 1那模型学到的含义就错位了。对应这个数据集data.yaml应该写成path: E:/dataset/12fruits # 改成你自己的绝对路径 train: train/images val: valid/images test: test/images nc: 12 names: 0: Apple 1: Banana 2: Cantaloupe 3: Common_fig 4: Grape 5: Mango 6: Orange 7: Pear 8: Pineapple 9: Pomegranate 10: Strawberry 11: Watermelon这里path建议用绝对路径因为不同的训练框架对于相对路径的解析根节点不同YOLOv5 是相对于项目根目录YOLOv8/Ultralytics 是相对于 yaml 文件所在位置搞混了就会报dataset not found。如果你用 Linux 服务器训练注意把路径分隔符和大小写检查一遍。这一段配置错了后面所有的训练结果都没有意义。3. 训练 12 类水果检测模型从命令参数到指标解读3.1 用 YOLOv8 起一个可复现的基线这个数据集适合直接拿来训 YOLOv8n 或 YOLOv8s。选 v8 而不是 v5 的原因v8 的 anchor-free 检测头在处理葡萄这种密集小目标时比 v5 的 anchor-based 收敛更省心且 Ultralytics 的 API 对数据集格式要求更宽松。YOLOv12 这样的新结构也可以尝试但建议先用 v8 跑通流程、拿到基线 mAP 后再对比其他模型以免引入过多变量。训练命令只需要一条yolo detect train \ datafruits.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ projectruns/fruit_baseline \ namev8n_640如果你用的不是 Ultralytics 环境也可以用克隆版的 YOLOv5 仓库python train.py \ --data fruits.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --name fruit_v5s命令本身的逻辑区别不大data指向刚才写好的 yamlmodel或weights指定预训练权重imgsz决定训练分辨率batch要根据显卡显存调整。这个数据集图片尺寸和来源多样640 的分辨率是兼容性和训练速度的平衡点。显存小于 8G 就把 batch 降到 8分辨率降到 512否则容易 OOM。3.2 七个影响收敛的关键参数训练一次不会让所有参数都调到最优但以下参数在水果检测这个场景下值得优先关注参数推荐值影响imgsz640太小丢小目标太大显存不够batch168G 显存则 8影响 BatchNorm 统计和收敛稳定性epochs100~200数据量不大100 轮起步optimizerautov8 默认 AdamW小数据集上 AdamW 收敛比 SGD 快patience30早停耐心值防止过拟合后白跑augment默认开启 mosaic对小目标检测很重要但也要看数据分布lr00.01SGD/ 0.001AdamW初始学习率太大容易震荡数据增强的mosaic在 v8 默认是开启且概率为 1.0 的它把四张图拼成一张训练。对葡萄、草莓这种小目标密集的场景效果明显但在这套数据里有个隐患如果原图本身已经包含多个目标mosaic 拼接后目标尺寸进一步缩小模型初期容易学不到有效特征。我的习惯是先让mosaic1.0跑 50 轮看 loss 曲线如果 mAP 卡住不涨就把mosaic降到 0.5 再续训。3.3 评价指标mAP50 高不代表模型能上线训练结束后 Ultralytics 会输出一组指标训练集和验证集的结果会打印在终端里测试集的结果可以用下面命令单独评估yolo detect val \ datafruits.yaml \ modelruns/fruit_baseline/v8n_640/weights/best.pt \ splittest重点看四个数mAP50、mAP50-95、precision、recall。对水果检测来说mAP50达到 0.85 以上算可用基线mAP50-95通常在 0.6 到 0.75 之间取决于类别难度。但这里有一个常见误区mAP50高只说明框框大致画对了位置不代表能区分哈密瓜和香瓜这种长得很像的类别。mAP50-95对框的精确度要求更高更能反映小目标和重叠目标的检测能力。这时候还需要查看每类的 AP 分布比如用from ultralytics import YOLO model YOLO(runs/fruit_baseline/v8n_640/weights/best.pt) metrics model.val(datafruits.yaml, splittest) print(metrics.box.ap_class_index) print(metrics.box.ap)metrics.box.ap返回每个类别的 AP 值对应关系由ap_class_index给出。如果 Pomegranate 的 AP 明显低于其他类别而数据量又少那问题大概率出在样本不平衡上而不是模型能力。遇到这种情况先统计每个类别的图片数量再决定要补充数据还是调整权重。3.4 小目标与易混淆类别的增强策略Grape、Strawberry 这类目标在整张图里占比很小天然是检测难点。除了调大imgsz还有两个实用做法。第一个是在 yaml 或训练参数中开启自动数据增强策略YOLOv8 的augment参数组里可以单独控制yolo detect train \ datafruits.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ hsv_h0.015 \ hsv_s0.5 \ hsv_v0.4 \ degrees10 \ translate0.1 \ scale0.3 \ fliplr0.5这里scale0.3表示每张图随机缩放范围是 0.7 到 1.3 倍这能让模型在不同尺度下见到目标translate0.1让目标在图中位置有偏移fliplr0.5是水平翻转概率。degrees10在水果场景里要谨慎苹果橙子是圆的转一下没问题但香蕉大幅度旋转后语义可能变化对成熟度标注也不友好我一般不超过 15 度。第二个做法是针对难分类别做二次采样。如果训练时发现 Banana 和 Cantaloupe 的混淆严重香蕉弯曲形状和哈密瓜椭圆轮廓在某些角度下确实接近可以从训练集里提取这两类样本各复制一份放到额外目录中用mixup增强混合训练。这种针对性的数据重加权比盲目加数据量更高效。4. 损失不降、mAP 卡住、标注越界数据问题定位与修复4.1 先看训练日志再动网络结构训练跑完 100 轮但 mAP 一直不动很多人第一反应是换更大的模型。但在这个量级的数据集上换大模型通常会加重过拟合。正确做法是先看三样东西训练 loss 曲线、验证 loss 曲线、类别 AP 明细。loss 曲线不降有两种典型形态。第一种是box_loss快速下降但cls_loss居高不下说明模型能框出目标但认不出类别。这时大概率是易混淆类别在作怪需要回到第 3.4 节的类别采样策略。第二种是cls_loss也在降但验证集 mAP 纹丝不动通常是因为验证集存在标注错误模型已经学到正确特征但在错误标注上验证分数上不去。你在第 2.2 节跑过的校验脚本只能查越界和格式错误查不了语义错误比如一张草莓图被标成了 Apple。这种需要用模型预测结果反查验证集把 val 集的预测结果导出人工抽查置信度最高的错误预测看是不是数据标注本身错了。4.2 标注文件常见病空文件、重复目标和类别漂移Roboflow 导出的数据集有一个常见毛病某些图片没有目标标注文件是 0 字节。训练时 YOLO 会忽略空标注文件但如果你后续做的是迁移学习或者微调空的标注文件会影响损失计算。检查命令find dataset/train/labels -name *.txt -size 0 | wc -l如果超过 10 个空文件建议训练时直接过滤掉这些样本from pathlib import Path for split in [train, valid, test]: label_dir Path(fdataset/{split}/labels) img_dir Path(fdataset/{split}/images) empty [p.stem for p in label_dir.glob(*.txt) if p.stat().st_size 0] for name in empty: (label_dir / f{name}.txt).unlink() (img_dir / f{name}.jpg).unlink() print(split, 移除空标注, len(empty), 个)另一个问题是类别漂移。比如这个数据集里 Cantaloupe哈密瓜和 Watermelon西瓜在颜色纹理上接近Common_fig无花果和 Pomegranate石榴在切面视觉上容易混淆人工标注时难免有部分标签错位。这类错误无法靠脚本自动修复只能通过训练后的混淆矩阵定位。Ultralytics 在验证后会生成confusion_matrix.png重点看非对角线上的高值位置再回到对应图片确认标注是否错误。如果确认是标注错误修改 txt 里的类别编号比重新训练更省时间。4.3 从混淆矩阵反推采样策略假设验证结果里 Cantaloupe 被误判为 Watermelon 的比例是 8%说明模型在两者纹理特征上没学到位。除了检查标注正确性还有一个采样技巧把训练集里 Cantaloupe 的图片复制一份放进一个单独目录用标注脚本把 Watermelon 类别改成 Cantaloupe做显式负样本干扰。这种做法在细粒度分类里叫 hard negative mining在目标检测里同样适用。实际操作时注意不要让数据集中某一类比重失衡到其他类无法学习。一般控制重采样后最高类别样本数不超过最低类别的 5 倍。如果训练集统计后发现某个类别就 30 张图比如数据集中 Wintermelon 或 Fig 数量偏少可以考虑用预训练模型做伪标注扩充。拿最后训练好的模型去预测一批无标注的水果图命中高置信度的结果经过人工复核后加入训练集。这种半监督迭代在这个量级的数据集上往往能再提升 3~5 个点 mAP。5. 上线前的验证置信度阈值、易混淆后处理与模型导出5.1 用 F1 曲线选置信度阈值模型训练完后best.pt给出的默认置信度阈值是 0.25。但在实际工程中置信度阈值不能拍脑袋定。水果分拣场景中漏检一个坏果比多框一个背景更严重所以阈值要跟着业务损失走。Ultralytics 训练完成后会在结果目录生成F1_curve.png那是全局 F1 随阈值变化的曲线。我一般建议跑一个扫描脚本自己选阈值from ultralytics import YOLO model YOLO(best.pt) results model.val(datafruits.yaml, splittest, conf0.001, iou0.5) for conf in [0.1, 0.2, 0.25, 0.3, 0.4, 0.5]: r model.val(datafruits.yaml, splittest, confconf, iou0.5, verboseFalse) print(fconf{conf:.2f} precision{r.box.mp:.4f} recall{r.box.mr:.4f} mAP50{r.box.map50:.4f})这个脚本用很低的基础置信度跑一遍再输出不同阈值下的查准率和查全率。如果选 0.3 时 recall 从 0.88 掉到 0.82但 precision 提升明显而你的业务允许漏检一些目标那就选 0.3。没有绝对最优只有场景最合适。5.2 针对易混淆类别的类别级后处理对 Cantaloupe/Watermelon、Common_fig/Pomegranate 这类易混淆组合可以在推理层做一个简单的置信度惩罚当两个类别的最高置信度都低于 0.6 且差距小于 0.1 时强制输出类别 ID 较小的一方或者让模型返回一个模糊标签交由人工复核。这种逻辑用 Python 包装模型推理很容易实现from ultralytics import YOLO model YOLO(best.pt) confusable_pairs [(2, 11), (3, 9)] def infer_with_guard(img_path): results model(img_path, conf0.25, iou0.5)[0] output [] for box in results.boxes.data.tolist(): x1, y1, x2, y2, conf, cls box cls int(cls) for a, b in confusable_pairs: if cls in (a, b): other_conf 0 for box2 in results.boxes.data.tolist(): if int(box2[5]) (b if cls a else a): iou_val compute_iou(box[:4], box2[:4]) if iou_val 0.5: other_conf max(other_conf, box2[4]) if abs(conf - other_conf) 0.1 and max(conf, other_conf) 0.6: cls min(a, b) output.append((x1, y1, x2, y2, conf, cls)) return output这里compute_iou是标准的两个框交并比计算。这个 trick 不是必须的但在验证集上通常能提升 1 到 2 个点的 mAP50代价是推理耗时增加 10% 左右。适合部署在离线分拣工位而不是实时视频流。5.3 导出到 ONNX/TensorRT 与推理提速验证完精度后最后一步是把模型导出。YOLOv8 一条命令搞定yolo export modelbest.pt formatonnx opset12 dynamicTrue导出的 ONNX 可以在 CPU 上用 ONNXRuntime 跑也可以在 Jetson 上转 TensorRT。输入尺寸建议固定为训练时的 640×640动态尺寸在 TensorRT 上反而可能因为显存碎片导致延迟波动。整个数据集从解析到导出的链路走下来你会发现真正花时间的不是训练本身而是第 2 章的目录对齐和第 4 章的诊断修复。这份数据集的标注质量在公开农业数据里算中上水平前期把基础打牢后续换 YOLOv12 或做多模态增强时都能复用同一份数据管线。本文还有配套的精品资源点击获取