ARTICLE DETAIL

资讯详情

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

农作物病害数据集:4800张图37类样本,YOLOv8目标检测实战

农作物病害数据集:4800张图37类样本,YOLOv8目标检测实战 简介这份农作物病害数据集面向从事农业AI、目标检测与图像分类的开发者与科研人员覆盖10种作物的健康样本及27类病害样本其中24类附带病害程度分析可用于病害识别、健康检测与监测类项目。资源包共2000个文件以1997张jpg图像为主体另含2个json标注文件和1个txt说明压缩包约940.87MB图像场景多样、分布均匀可直接投入训练与验证。数据集已按训练集、验证集、测试集三部分组织免去收集、挑选与标注环节适合作为病害检测的模板数据快速开展工程化实验。目前已有541人学习下载读者可据此搭建检测模型、评估算法鲁棒性并借助标注文件完成类别与病害程度的联合分析显著缩短数据准备周期。1. 农作物病害数据集4800 张图、37 类样本拿到手就能开训上周有个做智慧农业的哥们儿找我说他接了个病害监测的活儿模型结构都搭好了卡在数据上——自己扛着相机下田拍了三天回来一筛能用的不到两百张标注还得从头来。这不是个例。农作物病害识别这个方向公开数据要么类别少要么场景单一要么标注质量参差真正能直接扔进训练管线的并不多。这份农作物病害数据集就是冲着这个痛点来的4800 张实拍图训练集、验证集、测试集三份切好覆盖 10 种作物的健康样本和 27 种病害样本其中 24 种还带病害程度分级。它适合两类人——一类是想快速验证检测模型效果的算法工程师另一类是做农业物联网项目、需要现成数据撑起 demo 的开发者。不用再花两周时间收集、清洗、标注解压就能用。2. 拆开数据集看结构三份切分与 37 类标签怎么对应2.1 目录布局与文件组织拿到压缩包解压后根目录下是一个 README.txt 和四个文件夹。README 里写明了数据来源、采集设备和标注规范建议先花五分钟过一遍后面调参时心里有数。四个文件夹分别对应训练集、验证集和测试集还有一个存放标注文件的目录。图片格式统一为 JPG文件名保留了原始采集编号比如 IMG_20180623_181218.jpg 这种方便追溯。训练集和验证集共享同一批类别标签测试集则独立存放确保评估时不会出现数据泄漏。这种切分方式在工程上很常见训练集用来拟合验证集用来调超参测试集只在最后跑一次看真实泛化能力。提示解压后先别急着改文件名或移动目录标注文件里的路径是相对路径动了结构就得同步改标注容易翻车。2.2 类别体系10 种作物、27 种病害、24 种带程度分级数据集的核心价值在于它的类别设计。10 种作物覆盖了常见的大田和经济作物健康样本和病害样本混在一起模型要学的不是“有没有病”而是“哪种病、到什么程度”。27 种病害里有 24 种做了程度分析比如早期、中期、晚期剩下 3 种因为发病特征太接近没做细分。这种粒度在实际项目里很有用——农业场景下打药时机往往取决于病害程度不是简单的二分类能解决的。测试集的图片全部来自这 10 种作物的健康或病害样本没有引入新类别。这意味着你训完模型在测试集上跑出来的指标可以直接作为项目验收的参考。2.3 标注格式与目标检测的适配数据集标签里带了“目标检测”这个关键词说明标注不只是图片级分类还包含了边界框。常见做法是每张图对应一个 XML 或 JSON 文件里面记录了病害区域的坐标和类别。如果你用的是 YOLO 系列需要把坐标归一化到 0~1 之间如果用 Faster R-CNN 或 DETR保持绝对坐标就行。下面这段脚本是我平时用来快速检查标注完整性的跑一遍能看出有没有漏标或坐标越界import os import xml.etree.ElementTree as ET def check_annotation(img_dir, ann_dir): 遍历图片和标注检查一一对应关系和坐标合法性 img_files {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} ann_files {os.path.splitext(f)[0] for f in os.listdir(ann_dir) if f.endswith(.xml)} # 找出缺失标注的图片 missing img_files - ann_files if missing: print(f缺失标注的图片: {len(missing)} 张示例: {list(missing)[:3]}) # 检查坐标是否越界 for ann in ann_files: tree ET.parse(os.path.join(ann_dir, ann .xml)) for obj in tree.findall(object): bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) if xmin 0 or ymin 0 or xmax 4096 or ymax 4096: print(f坐标异常: {ann}, ({xmin},{ymin},{xmax},{ymax})) check_annotation(./train/images, ./train/annotations)这段代码做了两件事先比对图片和标注文件名找出漏标的再逐个解析 XML检查边界框坐标有没有负数或超出图片尺寸的。参数很简单传入图片目录和标注目录就行。跑完如果输出为空说明标注质量过关可以进入下一步。3. 从零跑通训练管线以 YOLOv8 为例的完整流程3.1 环境准备与数据格式转换假设你用的是 YOLOv8第一步是把 XML 标注转成 YOLO 格式的 txt。每行五个值类别索引、中心点 x、中心点 y、宽度、高度全部归一化到 0~1。下面这个转换脚本我用了很多次直接改路径就能跑import os import xml.etree.ElementTree as ET # 类别映射表根据你的实际类别修改 class_map {healthy: 0, disease_early: 1, disease_mid: 2, disease_late: 3} def xml_to_yolo(xml_path, output_dir, img_w1920, img_h1080): 将单个 XML 转为 YOLO txt 格式 tree ET.parse(xml_path) root tree.getroot() # 如果 XML 里有 size 节点优先用实际尺寸 size root.find(size) if size is not None: img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 归一化并计算中心点、宽高 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) # 写入同名 txt base os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(output_dir, base .txt), w) as f: f.write(\n.join(lines)) # 批量转换 for xml_file in os.listdir(./train/annotations): if xml_file.endswith(.xml): xml_to_yolo(os.path.join(./train/annotations, xml_file), ./train/labels)关键参数是 class_map你得根据 README 里的类别列表自己填。img_w 和 img_h 优先从 XML 的 size 节点读读不到才用默认值。转换完检查一下 txt 文件数量是否和图片数量一致少一个都可能在训练时报错。3.2 训练配置与超参设置YOLOv8 的配置文件里data.yaml 需要指定训练集、验证集路径和类别数。下面是一个模板path: ./dataset train: train/images val: val/images test: test/images nc: 37 names: [healthy_crop1, disease_crop1_early, ...] # 按实际类别顺序填nc 是类别总数37 类对应 10 种作物的健康和病害组合。names 列表的顺序必须和 class_map 里的索引一致否则模型学出来的类别会错位。启动训练的命令yolo detect train datadata.yaml modelyolov8m.pt epochs100 imgsz640 batch16 lr00.01epochs 设 100 是保守值实际看验证集 loss 曲线如果 50 轮就平了可以早停。imgsz 用 640 是 YOLO 的默认输入尺寸如果你的图片分辨率很高病害区域又小可以提到 1280但显存占用会翻倍。batch 根据显卡调整16 是 8G 显存的安全值。lr0 是初始学习率0.01 适合从预训练权重微调如果从头训可以降到 0.001。3.3 训练过程监控与指标解读训练启动后终端会实时打印每轮的 box_loss、cls_loss 和 mAP。box_loss 衡量边界框回归误差cls_loss 衡量分类误差mAP50 是 IoU 阈值 0.5 时的平均精度。重点关注验证集的 mAP50如果训练集涨但验证集不涨说明过拟合了可以加数据增强或减模型容量。常见做法是每 10 轮存一个 checkpoint训练完挑验证集 mAP 最高的那个权重。YOLOv8 默认存在 runs/detect/train/weights/ 下best.pt 就是最优权重。注意如果 mAP 一直卡在 0.2 以下先别怀疑模型回头检查标注转换有没有把类别索引搞错或者图片路径是不是写成了绝对路径导致读不到图。4. 避坑与排查标注、路径、显存、过拟合的五个血泪教训4.1 现象训练报错“No labels found”原因YOLO 读标注时默认找和图片同名的 txt如果你的 txt 放在单独目录但 data.yaml 里没配 labels 路径它就找不到。解决在 data.yaml 里显式加上labels: train/labels和val/labels或者把 txt 和图片放同一目录。4.2 现象mAP 波动大每轮差 0.1 以上原因验证集太小或类别分布不均某些类别样本太少模型学不稳。解决检查验证集里每个类别的样本数如果某类少于 20 张考虑合并到训练集或者用分层采样重新切分。4.3 现象训练到一半显存溢出原因batch 设太大或者 imgsz 提太高加上 YOLOv8 的多尺度训练会动态改输入尺寸。解决先把 batch 降到 8如果还炸就降 imgsz 到 512或者开 AMP 混合精度训练加ampTrue参数。4.4 现象测试集指标远低于验证集原因测试集和验证集的采集场景不同比如光照、背景差异大模型没学到域不变特征。解决在训练时加 RandAugment 或 Mosaic 增强提升模型对场景变化的鲁棒性。另外确认测试集没有在训练中被间接用到。4.5 现象某些病害类别始终检测不到原因这些类别的样本在训练集里太少或者标注框太小模型感受野覆盖不到。解决对稀有类别做过采样或者用 Copy-Paste 增强把病害区域贴到其他图片上增加样本多样性。5. 进阶技巧用测试集反推模型短板与数据增强策略训练跑通只是第一步真正拉开差距的是怎么用测试集做误差分析。我一般会写个脚本把测试集里预测错的图片单独拎出来按类别分组看看是漏检多还是误检多。漏检多说明模型对这类病害的特征没学好可以针对性加样本误检多说明类间差异小需要更细粒度的特征。下面这段代码用来统计每个类别的漏检和误检数量import json from collections import defaultdict def analyze_errors(pred_json, gt_json): 对比预测和真值统计每类的漏检和误检 with open(pred_json) as f: preds json.load(f) with open(gt_json) as f: gts json.load(f) # 按图片 ID 分组 pred_by_img defaultdict(list) gt_by_img defaultdict(list) for p in preds: pred_by_img[p[image_id]].append(p) for g in gts: gt_by_img[g[image_id]].append(g) stats defaultdict(lambda: {miss: 0, false: 0}) for img_id in gt_by_img: gt_cats {g[category_id] for g in gt_by_img[img_id]} pred_cats {p[category_id] for p in pred_by_img.get(img_id, [])} # 漏检真值有但预测没有 for c in gt_cats - pred_cats: stats[c][miss] 1 # 误检预测有但真值没有 for c in pred_cats - gt_cats: stats[c][false] 1 for cat, s in sorted(stats.items()): print(f类别 {cat}: 漏检 {s[miss]}, 误检 {s[false]}) analyze_errors(./preds.json, ./test_gt.json)跑完看哪些类别的漏检或误检突出再决定是补数据还是改模型。如果某个病害的漏检率超过 30%我会优先用 Copy-Paste 把这类病害区域抠出来贴到健康样本上生成一批合成图加入训练集。这招在小样本病害检测里屡试不爽但注意粘贴时边缘要羽化不然模型会学到不自然的边界。从那以后我每次拿到新数据集都强制先跑一遍标注检查和类别分布统计确认没有长尾问题再开训。希望帮到你。本文还有配套的精品资源点击获取
返回列表