尧图精选

花生叶片病害检测数据集实战:从标注格式到YOLOv8训练落地

🕒 发布时间:2026/10/1 6:20:16 📁 来源:尧图网络
简介这份花生叶片病害检测数据集面向从事农业图像识别、深度学习目标检测的开发者与研究人员可用于训练和验证花生叶片病害的检测模型适合具备一定目标检测基础、需要真实标注数据开展实验或课程项目的读者。资源包共335个文件以166张jpg图像与166个xml标注文件为主xml采用Pascal VOC格式记录叶片病害位置与类别另附2个csv标签文件与1个txt说明便于直接接入常见检测框架进行训练与评估压缩包整体约7.73MB体积轻量、便于快速下载与本地部署。目前已有346人学习下载可作为小样本场景下的练手数据。读者可据此完成数据加载、标注解析、模型训练与指标对比等完整流程xml与csv的配套结构也方便划分训练集与测试集减少自行标注与整理的时间成本适合快速验证算法效果或搭建演示原型。1. 花生叶片病害检测数据集从标注格式到训练落地的完整拆解如果你正在做农业视觉方向的项目大概率绕不开一个现实问题公开的作物病害数据集要么类别太粗要么图像质量参差要么标注格式跟你的训练框架对不上。花生叶片病害检测数据集就是在这个缝隙里被频繁搜到的一类资源——它面向的是叶片级别的病害识别与定位任务通常包含健康叶片和几种常见叶部病害如褐斑、黑斑、锈病等的实拍图像配套边界框或分类标签。适合谁用做目标检测或图像分类的算法工程师、农业院校做课题的研究生、以及需要快速验证模型效果的开发者。它解决的不是“从零采集数据”的问题而是让你把精力放在模型结构和调参上而不是花几周时间去田里拍照、标注、清洗。接下来我会按“拿到手怎么用→怎么训→坑在哪”的顺序把这份资源的落地路径拆开讲。2. 数据集结构与标注格式先搞清楚你拿到的是什么2.1 目录组织与文件命名逻辑常见做法是这类数据集解压后会有几个固定目录images/放原始图片annotations/或labels/放标注文件可能还有一个classes.txt或data.yaml描述类别映射。图片格式以 JPG 为主分辨率不统一长边从 640 到 2000 像素都有。命名上通常是xxx_001.jpg这种带序号的形式序号和标注文件一一对应。我一般会先跑一遍统计脚本确认图片数量、标注数量、类别分布避免训练到一半才发现某个类别只有十几张图。import os from collections import Counter img_dir dataset/images label_dir dataset/labels imgs [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png))] labels [f for f in os.listdir(label_dir) if f.endswith(.txt)] print(f图片总数: {len(imgs)}) print(f标注文件数: {len(labels)}) # 统计每个类别的框数量 cls_counter Counter() for lb in labels: with open(os.path.join(label_dir, lb)) as f: for line in f: cls_id line.strip().split()[0] cls_counter[cls_id] 1 print(类别分布:, dict(cls_counter))这段脚本做三件事列出图片和标注文件数量、逐行读取 YOLO 格式标注、统计每个类别的实例数。参数上注意label_dir要和实际目录一致如果标注是 COCO JSON 格式需要先转成逐行 txt 再统计。类别分布直接决定你后面要不要做重采样或加权损失。2.2 YOLO 与 COCO 格式的互转这个数据集最常见的标注格式是 YOLO txt每行class_id x_center y_center width height归一化到 0~1但也有部分版本给的是 COCO JSON。如果你用的是 Detectron2 或 MMDetection就得转成 COCO如果用 Ultralytics 的 YOLO 系列txt 直接能用。转换的核心是坐标归一化和反归一化以及类别 id 的映射。import json import os def yolo_to_coco(img_dir, label_dir, classes, out_json): coco {images: [], annotations: [], categories: []} for i, name in enumerate(classes): coco[categories].append({id: i, name: name}) ann_id 1 for idx, img_name in enumerate(os.listdir(img_dir)): if not img_name.endswith((.jpg, .png)): continue # 读取图片尺寸这里假设用 PIL from PIL import Image w, h Image.open(os.path.join(img_dir, img_name)).size coco[images].append({id: idx, file_name: img_name, width: w, height: h}) lb_path os.path.join(label_dir, img_name.rsplit(., 1)[0] .txt) if not os.path.exists(lb_path): continue with open(lb_path) as f: for line in f: c, x, y, bw, bh map(float, line.strip().split()) x1 (x - bw / 2) * w y1 (y - bh / 2) * h aw bw * w ah bh * h coco[annotations].append({ id: ann_id, image_id: idx, category_id: int(c), bbox: [x1, y1, aw, ah], area: aw * ah, iscrowd: 0 }) ann_id 1 with open(out_json, w) as f: json.dump(coco, f) yolo_to_coco(dataset/images, dataset/labels, [healthy, leaf_spot, rust], coco.json)逻辑说明遍历图片目录用 PIL 读尺寸再把 YOLO 的中心点宽高转成 COCO 的左上角宽高。参数上classes列表的顺序必须和标注里的 class_id 一致否则类别全乱。area字段是 COCO 评估时计算 mAP 用的不能省。转换完建议用pycocotools加载一遍验证能读出来且类别数对得上才算过。2.3 类别不平衡与图像质量筛查农业数据集的老毛病是类别不平衡健康叶片可能占一半以上某种病害只有几十张。直接训会让模型偏向多数类。常见做法是先用imbalanced-learn看分布再决定是过采样、欠采样还是用 focal loss。另外图像质量要筛模糊、过曝、叶片只占画面一角且标注框极小的建议直接剔除或裁剪。我一般会设一个阈值比如框面积小于图像面积 1% 的样本先标记出来人工过一遍。提示筛查脚本不要直接删文件先输出一个待确认列表避免误删有效样本。3. 训练环境搭建与模型选型从 YOLOv8 到 EfficientNet 的取舍3.1 环境依赖与版本锁定这类视觉任务的环境坑主要集中在 CUDA、PyTorch 和 torchvision 的版本匹配上。如果你用 Ultralytics 的 YOLOv8官方推荐 Python 3.8、PyTorch 1.8。我一般会先建虚拟环境再按显卡驱动对应的 CUDA 版本装 PyTorch最后装 ultralytics。别小看这一步版本不对会出现CUDA error: no kernel image is available这种玄学报错。conda create -n peanut python3.9 -y conda activate peanut # 根据你的 CUDA 版本选择下面以 CUDA 11.8 为例 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pillow pycocotools参数说明--index-url指定 PyTorch 官方 wheel 源避免装到 CPU 版本。装完用python -c import torch; print(torch.cuda.is_available())验证返回 True 才算环境通了。如果返回 False先查驱动版本再查 CUDA 和 PyTorch 的对应关系表。3.2 检测模型与分类模型的选型理由这个数据集可以走两条路目标检测定位病斑位置和图像分类判断整张叶片属于哪类病害。检测用 YOLOv8n 或 YOLOv8s参数量小、训练快适合数据量几千张级别的场景分类用 EfficientNet-B0 或 ResNet50如果只是判断叶片健康与否分类模型收敛更快、对标注要求更低。我的建议是如果你的业务需要知道病斑在叶片上的具体位置选检测如果只需要给出病害类型选分类。两者可以结合先分类再检测但会增加推理耗时。from ultralytics import YOLO model YOLO(yolov8s.pt) # 加载预训练权重 results model.train( datadata.yaml, epochs100, imgsz640, batch16, lr00.01, patience20, device0 )逻辑说明data.yaml里要写清train、val路径和names类别名。imgsz640是常见输入尺寸如果你的图片分辨率普遍偏小可以降到 416 或 512 加速训练。patience20表示 20 轮验证指标不提升就早停防止过拟合。device0指定第一块 GPU多卡用device0,1。3.3 数据增强策略与参数设置农业图像的光照、角度、背景变化大增强策略要针对性地选。常见做法是随机翻转、随机旋转 ±15°、色彩抖动亮度、对比度、饱和度、Mosaic 拼接。但要注意Mosaic 对密集小目标有效如果病斑本身很小且分散Mosaic 可能把目标切得太碎。我一般会先开 Mosaic训到后期关掉让模型在真实分布上微调。# data.yaml 示例 train: dataset/images/train val: dataset/images/val nc: 3 names: [healthy, leaf_spot, rust] # 增强参数在 train 里传 # hsv_h0.015, hsv_s0.7, hsv_v0.4 # degrees15.0, translate0.1, scale0.5 # mosaic1.0, mixup0.0参数说明hsv_h控制色调抖动幅度农业图像里病斑颜色是关键特征这个值别设太大0.015 左右够了。degrees15是旋转角度上限再大可能把叶片转出画面。scale0.5允许缩放模拟不同拍摄距离。mixup在检测任务里一般关掉容易造成标签混淆。4. 训练过程监控与指标解读别只看 mAP4.1 损失曲线与验证指标的联动分析训练启动后Ultralytics 会在runs/detect/train/下生成results.csv和可视化曲线。重点看三个train/box_loss、val/box_loss、metrics/mAP50。如果 train loss 持续下降但 val loss 开始上升说明过拟合该早停或加正则。如果两者都降但 mAP 不涨可能是学习率太小或标注质量有问题。我一般会在第 10 轮、第 50 轮、第 100 轮各看一次别等到跑完才发现方向错了。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) df.columns df.columns.str.strip() plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(df[epoch], df[train/box_loss], labeltrain) plt.plot(df[epoch], df[val/box_loss], labelval) plt.legend() plt.title(Box Loss) plt.subplot(1, 2, 2) plt.plot(df[epoch], df[metrics/mAP50], labelmAP50) plt.plot(df[epoch], df[metrics/mAP50-95], labelmAP50-95) plt.legend() plt.title(mAP) plt.show()逻辑说明results.csv的列名可能带空格先 strip 一下。mAP50 是 IoU 阈值 0.5 时的平均精度mAP50-95 是多个 IoU 阈值的平均后者更严格。农业病害检测里mAP50 到 0.8 以上算不错mAP50-95 通常低不少别拿后者跟分类准确率比。4.2 混淆矩阵与误检分析训练完会生成confusion_matrix.png这个图比 mAP 更能暴露问题。常见情况是健康叶片被误判成某种病害或者两种病害互相混淆。原因通常是病斑特征在早期阶段相似或者标注时边界模糊。解决办法一是合并易混类别二是增加难例样本三是调整分类阈值。我一般会把混淆矩阵里误检最多的那对类别单独拎出来看几十张原图确认是标注问题还是模型问题。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datadata.yaml, conf0.25, iou0.5) print(metrics.confusion_matrix)参数说明conf0.25是置信度阈值低于这个值的预测不算。iou0.5是 NMS 的 IoU 阈值。这两个值会影响混淆矩阵的形态调参时要固定住再对比。4.3 学习率与 batch size 的调参经验学习率太大loss 震荡不收敛太小收敛慢还容易陷局部最优。YOLOv8 默认用余弦退火初始 lr0 设 0.01 对大多数数据集够用。如果 batch size 受显存限制只能设 8 或更小lr0 要相应降到 0.005 左右。我一般会先跑 10 个 epoch 的短训看 loss 下降趋势再决定要不要调。另外warmup_epochs设 3 左右让模型前几轮慢慢进入状态避免一开始就大步长把预训练权重带偏。注意改 batch size 后学习率不是线性缩放就完事最好重新跑一次 lr finder 或短训验证。5. 避坑与常见问题排查那些让我重跑过的坑5.1 标注文件与图片不匹配现象训练启动时报FileNotFoundError或某张图没有对应标注loss 直接 NaN。原因图片和标注文件名不一致或者标注文件里有空行、格式错乱。解决写脚本做一次全量校验逐对检查图片和标注是否存在、标注行数是否大于零、坐标是否在 0~1 之间。import os img_dir dataset/images lb_dir dataset/labels bad [] for img in os.listdir(img_dir): if not img.endswith((.jpg, .png)): continue lb os.path.join(lb_dir, img.rsplit(., 1)[0] .txt) if not os.path.exists(lb): bad.append((img, missing label)) continue with open(lb) as f: lines [l.strip() for l in f if l.strip()] if not lines: bad.append((img, empty label)) continue for line in lines: parts line.split() if len(parts) ! 5: bad.append((img, fbad format: {line})) break vals list(map(float, parts[1:])) if any(v 0 or v 1 for v in vals): bad.append((img, fout of range: {line})) break print(f问题样本数: {len(bad)}) for b in bad[:20]: print(b)逻辑说明逐张图检查标注是否存在、是否为空、每行是否 5 个字段、坐标是否归一化。输出前 20 条问题样本方便定位。这个脚本我每次拿到新数据集都会先跑一遍能省掉后面很多莫名其妙的报错。5.2 类别 id 从 0 还是 1 开始现象训练时提示Label class 3 exceeds nc3。原因YOLO 要求 class_id 从 0 开始但有些标注工具默认从 1 开始。解决全局扫描标注文件如果最小 class_id 是 1就统一减 1。别手动改写脚本批量处理。import os lb_dir dataset/labels for lb in os.listdir(lb_dir): if not lb.endswith(.txt): continue path os.path.join(lb_dir, lb) with open(path) as f: lines f.readlines() new_lines [] for line in lines: parts line.strip().split() if parts: parts[0] str(int(parts[0]) - 1) new_lines.append( .join(parts) \n) with open(path, w) as f: f.writelines(new_lines)参数说明这个脚本假设所有 class_id 都从 1 开始统一减 1。如果只有部分类别偏移需要先统计再决定。改完记得备份原始标注。5.3 图片尺寸过大导致显存溢出现象训练到一半报CUDA out of memory。原因图片分辨率太高或者 batch size 设太大。解决先把图片统一缩放到长边 640 或 1280再训练。Ultralytics 的imgsz参数会在数据加载时缩放但如果原图是 4000×3000缩放本身也耗显存。我一般会预处理一遍把图片存成 1280 长边的副本训练时再缩到 640。from PIL import Image import os src dataset/images dst dataset/images_resized os.makedirs(dst, exist_okTrue) for img_name in os.listdir(src): if not img_name.endswith((.jpg, .png)): continue img Image.open(os.path.join(src, img_name)) w, h img.size scale 1280 / max(w, h) if scale 1: img img.resize((int(w * scale), int(h * scale)), Image.BILINEAR) img.save(os.path.join(dst, img_name))逻辑说明按长边 1280 等比缩放小于 1280 的保持原样。Image.BILINEAR是双线性插值速度和质量平衡。缩放后标注坐标不用改因为 YOLO 用的是归一化坐标。5.4 验证集指标虚高现象mAP 很高但实际推理效果差。原因训练集和验证集有重复图片或者验证集太小、分布不均衡。解决划分数据时按图片级别随机分别按标注行分。验证集至少占 15%且每个类别都要有样本。我一般会用sklearn的train_test_split按类别分层抽样。from sklearn.model_selection import train_test_split import os all_imgs [f for f in os.listdir(dataset/images) if f.endswith((.jpg, .png))] # 假设每张图的主类别已知这里用文件名前缀模拟 labels [f.split(_)[0] for f in all_imgs] train, val train_test_split(all_imgs, test_size0.2, stratifylabels, random_state42) print(f训练集: {len(train)}, 验证集: {len(val)})参数说明stratifylabels保证每个类别在训练和验证里的比例一致。random_state42固定随机种子方便复现。分完后把文件移动到对应目录再更新data.yaml路径。5.5 推理时置信度阈值设错现象模型把背景也框出来或者漏掉小病斑。原因conf阈值默认 0.25对农业图像可能偏低或偏高。解决在验证集上跑一遍不同 conf 值的 PR 曲线选 F1 最高的点。我一般会在 0.1 到 0.5 之间扫步长 0.05。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) for conf in [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5]: metrics model.val(datadata.yaml, confconf, iou0.5) print(fconf{conf}, mAP50{metrics.box.map50:.4f}, F1{metrics.box.f1:.4f})逻辑说明metrics.box.f1是 F1 分数综合了精确率和召回率。选 F1 最高的 conf 作为推理阈值。注意这个扫描要在验证集上做别用训练集。6. 进阶技巧用切片推理和 TTA 把 mAP 再抬一截如果你已经把基础模型训到 mAP50 0.85 左右想再往上走可以试两个技巧切片推理SAHI和测试时增强TTA。切片推理的思路是把大图切成重叠的小块分别检测再把结果合并对密集小目标特别有效。花生叶片上的病斑往往只占几十个像素整图推理容易漏切片后每个病斑在局部图里相对更大模型更容易捕捉。SAHI 库可以直接接 YOLO 模型几行代码就能跑。from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/detect/train/weights/best.pt, confidence_threshold0.3, devicecuda:0 ) result get_sliced_prediction( test.jpg, detection_model, slice_height512, slice_width512, overlap_height_ratio0.2, overlap_width_ratio0.2 ) result.export_visuals(export_diroutput/)参数说明slice_height和slice_width是切片尺寸一般设成训练输入尺寸的 0.8 倍左右。overlap_height_ratio0.2表示相邻切片重叠 20%防止目标被切在边界上。切片推理的代价是推理时间成倍增加适合离线分析或对精度要求高的场景实时检测要权衡。TTA 则是推理时对同一张图做翻转、缩放等变换把多次预测结果融合。Ultralytics 的model.val(augmentTrue)就能开 TTA但训练时别开只用于最终评估或推理。我一般会在模型定稿后用 TTA 跑一遍验证集看 mAP 能涨多少如果涨幅小于 1 个点就不值得增加推理耗时。还有一个容易被忽略的点类别合并。如果两种病害在早期阶段视觉特征几乎一样模型很难分开强行分只会拉低整体指标。我一般会先看混淆矩阵如果 A 和 B 的互相误检率超过 30%就考虑合并成一个“早期病斑”类等数据量够了再细分。这个决策没有标准答案取决于你的业务能不能接受粗粒度分类。从那以后我每次拿到新的农业数据集都强制走一遍“统计→校验→可视化→短训→全训”的流程不跳过任何一步。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →