☰
棉花病害检测数据集实战:YOLO标注格式解析与训练调优
2026/9/26 6:30:57 网站建设 项目流程

简介:这份棉花植物病害图像目标检测数据集面向从事农业视觉检测、YOLO 系列模型训练与改进的开发者及学生,提供约 4,600 张已标注图像及对应标签,覆盖枯萎病、卷曲、灰霉、健康、叶斑病等 6 个类别,可直接用于目标检测模型的训练、验证与测试。资源包共 2000 个文件,以 1999 个 txt 标注文件和 1 个 show.py 可视化脚本为主,压缩包约 156.57MB,并已按训练集、验证集、测试集完成划分,运行 show 脚本即可快速查看标注效果。目前已有 47 人学习下载。对于希望复现或改进 YOLOv5 检测流程的读者,该数据集能省去繁琐的标注与划分工作,便于直接投入实验、对比不同网络结构在棉花病害识别上的表现,也可作为农业病害检测课题的基线数据使用。

1. 棉花病害检测数据集:4,600 张标注图能跑出什么名堂

棉花叶片上那些黄褐斑、轮纹斑、枯斑,肉眼判断全凭经验,不同的人看同一片叶子可能给出不同结论。我去年帮一个做农业信息化的团队处理过一批田间巡检图,他们最初想用分类模型做病害识别,结果发现一张图里同时出现健康叶片、病斑叶片和背景杂草,分类器直接懵了。后来换成目标检测思路,把每个病斑区域框出来再分类,准确率才稳住。这个标题说的就是这类场景:约 4,600 张棉花植物病害图像,已经按 YOLO 标注格式整理好,可以直接拿来做目标检测训练。适合谁?做农业 AI 落地的算法工程师、想用 YOLO 系列跑自己数据集的学生、以及需要快速验证病害检测可行性的产品团队。数据量不算大,但胜在标注格式统一,省掉了最耗时的清洗和转换环节。

2. YOLO 标注格式拆解与 4,600 张数据的实战用法

2.1 YOLO 标注格式到底长什么样

YOLO 格式的标注文件是每张图对应一个.txt,每行代表一个目标框,格式为:

<class_id> <x_center> <y_center> <width> <height>

所有坐标都是归一化到 0~1 之间的浮点数,x_center和y_center是框中心点相对于图像宽高的比例,width和height是框宽高相对于图像宽高的比例。这种格式的好处是跟图像尺寸解耦,训练时不管输入是 640 还是 1280,标注都不用改。但新手最容易翻车的地方也在这里:如果标注时用的是绝对像素坐标,直接喂进去训练,损失函数会直接爆炸,模型学到的全是错误位置。

我一般拿到一批标注数据后,先做三件事:检查类别 ID 是否从 0 开始连续、检查坐标是否都在 0~1 之间、检查是否有空文件或只有一行但格式错误的文件。这三步能筛掉八成以上的脏数据问题。

2.2 用 Python 快速验证标注文件是否规范

拿到 4,600 张图对应的标注文件后,别急着开训。先跑一段校验脚本,把有问题的文件挑出来。下面这段代码我用了很多次,直接改路径就能跑:

import os import glob # 数据集根目录,下面应该有 images 和 labels 两个子目录 dataset_root = "/path/to/cotton_dataset" label_dir = os.path.join(dataset_root, "labels") image_dir = os.path.join(dataset_root, "images") # 类别数,根据你的 data.yaml 里的 nc 来改 num_classes = 5 # 假设有 5 类病害 bad_files = [] for label_path in glob.glob(os.path.join(label_dir, "*.txt")): with open(label_path, "r") as f: lines = f.readlines() if len(lines) == 0: bad_files.append((label_path, "空标注文件")) continue for line_idx, line in enumerate(lines): parts = line.strip().split() if len(parts) != 5: bad_files.append((label_path, f"第{line_idx+1}行字段数不对: {len(parts)}")) continue cls_id = int(parts[0]) coords = [float(x) for x in parts[1:]] if cls_id < 0 or cls_id >= num_classes: bad_files.append((label_path, f"第{line_idx+1}行类别ID越界: {cls_id}")) for c in coords: if c < 0 or c > 1: bad_files.append((label_path, f"第{line_idx+1}行坐标越界: {c}")) break print(f"共检查 {len(glob.glob(os.path.join(label_dir, '*.txt')))} 个标注文件") print(f"发现 {len(bad_files)} 个问题文件") for fp, reason in bad_files[:20]: print(f"{fp}: {reason}")

这段脚本的逻辑很直白:遍历所有.txt标注文件,逐行检查字段数、类别 ID 范围和坐标范围。参数num_classes必须跟你的data.yaml里nc保持一致,否则类别 ID 校验会误报。跑完之后如果bad_files为空,说明标注格式基本没问题,可以进入下一步。如果有问题文件,根据打印出的原因逐个人工修复,或者直接把这些文件从训练集里剔除。

2.3 划分训练集、验证集和测试集

4,600 张图不算多,划分比例我一般用 7:2:1,也就是训练集 3,220 张、验证集 920 张、测试集 460 张。划分时要注意类别均衡,不能随机分完之后某一类病害在验证集里一张都没有。下面这个脚本按类别分层抽样:

import os import random import shutil from collections import defaultdict random.seed(42) # 固定随机种子,保证可复现 dataset_root = "/path/to/cotton_dataset" image_dir = os.path.join(dataset_root, "images") label_dir = os.path.join(dataset_root, "labels") output_root = "/path/to/cotton_split" # 统计每个类别出现在哪些图上 cls_to_images = defaultdict(list) for label_file in os.listdir(label_dir): if not label_file.endswith(".txt"): continue img_name = label_file.replace(".txt", ".jpg") with open(os.path.join(label_dir, label_file), "r") as f: classes = set(int(line.split()[0]) for line in f if line.strip()) for c in classes: cls_to_images[c].append(img_name) # 按类别分层划分 split_ratio = {"train": 0.7, "val": 0.2, "test": 0.1} for split in split_ratio: os.makedirs(os.path.join(output_root, split, "images"), exist_ok=True) os.makedirs(os.path.join(output_root, split, "labels"), exist_ok=True) assigned = set() for cls_id, img_list in cls_to_images.items(): random.shuffle(img_list) n = len(img_list) n_train = int(n * split_ratio["train"]) n_val = int(n * split_ratio["val"]) for i, img_name in enumerate(img_list): if img_name in assigned: continue if i < n_train: split = "train" elif i < n_train + n_val: split = "val" else: split = "test" assigned.add(img_name) src_img = os.path.join(image_dir, img_name) src_label = os.path.join(label_dir, img_name.replace(".jpg", ".txt")) dst_img = os.path.join(output_root, split, "images", img_name) dst_label = os.path.join(output_root, split, "labels", img_name.replace(".jpg", ".txt")) if os.path.exists(src_img): shutil.copy(src_img, dst_img) if os.path.exists(src_label): shutil.copy(src_label, dst_label) print("划分完成")

这里的关键参数是random.seed(42),固定种子后每次划分结果一致,方便复现实验。split_ratio可以根据你的数据量调整,如果某类病害样本特别少,可以适当提高验证集比例,保证评估指标稳定。划分完成后,检查一下每个 split 下的图片数量和标注文件数量是否一致,不一致说明有遗漏。

2.4 生成 data.yaml 并启动 YOLO 训练

YOLO 系列训练需要一份data.yaml描述数据集路径和类别信息。假设你用的是 YOLOv8 或 YOLOv11,格式如下:

path: /path/to/cotton_split train: train/images val: val/images test: test/images nc: 5 names: ['angular_leaf_spot', 'bacterial_blight', 'fusarium_wilt', 'healthy', 'verticillium_wilt']

nc是类别数,names按类别 ID 顺序排列。这里我用了五个常见棉花病害类别名,实际使用时替换成你数据集里的真实类别。启动训练的命令:

yolo detect train data=/path/to/cotton_split/data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 patience=20

参数说明:model=yolov8s.pt是预训练权重,小模型在 4,600 张图上够用;epochs=100配合patience=20早停,防止过拟合;imgsz=640是输入尺寸,如果病斑特别小可以提到 1024,但显存占用会翻倍;batch=16根据你的显卡调整,8G 显存跑 640 尺寸大概能到 16。训练过程中重点看mAP50和mAP50-95两个指标,如果mAP50在 30 个 epoch 后还在涨但验证集损失开始上升,说明过拟合了,需要加数据增强或减小模型。

3. 从 4,600 张图到可用模型:训练策略与参数调优

3.1 小数据集训练 YOLO 的增强策略怎么选

4,600 张图在目标检测任务里属于小数据集,不加增强直接训,模型大概率会记住训练集里的背景纹理,换一块田拍就崩。YOLO 内置的增强参数里,我一般会开这几个:hsv_h=0.015、hsv_s=0.7、hsv_v=0.4做颜色抖动,模拟不同光照和相机白平衡差异;degrees=10做小角度旋转,田间拍摄不可能永远水平;translate=0.1和scale=0.5做平移和缩放,让模型适应病斑在画面中的不同位置和大小;fliplr=0.5水平翻转,棉花叶片左右对称,翻转不改变病害特征;mosaic=1.0四图拼接,这是 YOLO 系列涨点最明显的增强,但要注意如果病斑本身很小,mosaic 后可能被缩得更小,反而有害。

我踩过的一个坑是开了mixup=0.5,结果模型把两类病害混在一起学,混淆矩阵里两类互相误检率飙升。后来查原因,mixup 对细粒度分类任务不友好,尤其是病斑形态差异不大的类别。所以如果你发现训练后混淆矩阵里某两类互相误判严重,先把 mixup 关掉试试。

3.2 置信度门限和 NMS 参数怎么调

训练完模型,推理时有两个参数直接决定你看到的结果:置信度门限conf和 NMS 的iou阈值。默认conf=0.25、iou=0.45,但在棉花病害检测里,这两个值需要根据你的业务场景调。

如果漏检比误检更致命(比如病害预警系统,宁可多报不可漏报),把conf降到 0.1~0.15,让更多低置信度的框进入 NMS,然后靠iou来抑制重叠框。如果误检太多导致人工复核成本高,把conf提到 0.4~0.5,只保留高置信度结果。iou阈值控制的是同一个目标被重复框出的抑制程度,病斑密集时把iou降到 0.3~0.4,避免相邻病斑被合并成一个框。

from ultralytics import YOLO model = YOLO("/path/to/best.pt") results = model.predict( source="/path/to/test_images", conf=0.15, # 降低门限,减少漏检 iou=0.35, # 降低 NMS 阈值,避免病斑粘连 imgsz=640, save=True, save_txt=True # 保存预测结果到 txt,方便后续分析 )

跑完预测后,重点看save_txt生成的标注文件和原图对比,挑出漏检和误检的案例,分析是标注问题还是模型问题。如果同一类病害在某个光照条件下集体漏检,说明训练集里这类样本太少,需要补数据。

3.3 用混淆矩阵定位类别混淆问题

YOLO 训练结束后会在runs/detect/train/下生成confusion_matrix.png。这个图是排查类别混淆的第一手资料。横轴是预测类别,纵轴是真实类别,对角线越深越好。如果发现angular_leaf_spot和bacterial_blight互相误判严重,先别急着改模型结构,去翻训练集里这两类的标注图,大概率是标注标准不统一——比如有的标注员把角斑病的早期症状标成了细菌性疫病。

我处理过的一个案例:混淆矩阵显示fusarium_wilt有 30% 被误判为verticillium_wilt。查了标注发现,这两类病害在叶片上的早期症状确实相似,标注员之间没有统一标准。解决办法是把这两类合并成一个大类“枯萎病”,或者重新制定标注规范,让标注员对照症状图谱逐张确认。数据质量的问题,模型结构再改也救不回来。

4. 棉花病害检测的避坑与排查清单

4.1 标注文件与图片不对应

现象:训练启动后报错No labels found或者Image not found,或者训练 loss 一直是 nan。

原因:图片目录和标注目录的文件名没有一一对应。常见情况是图片是.jpg但标注是.JPG.txt,或者图片名里有空格导致路径解析失败。

解决:跑一段脚本检查两个目录的文件名是否匹配,把不匹配的文件列出来。统一改成小写扩展名,去掉文件名里的空格和特殊字符。

4.2 类别 ID 不连续导致训练崩溃

现象:训练时 loss 突然变成 nan,或者模型只学到了一部分类别。

原因:标注文件里的类别 ID 不是从 0 开始连续编号,比如只有 0、2、4 三类,但data.yaml里写了nc=5。YOLO 在计算损失时会对不存在的类别索引报错。

解决:用脚本统计所有标注文件里出现的类别 ID,重新映射成 0 到 N-1 的连续整数,同时更新data.yaml里的names列表。

4.3 小目标病斑在 640 尺寸下消失

现象:训练指标看起来不错,但推理时早期小病斑全部漏检。

原因:YOLO 默认下采样 32 倍,640 输入下最小检测目标大约 8x8 像素。如果原始图像里病斑只有十几个像素,缩到 640 后基本就没了。

解决:把imgsz提到 1024 或 1280,同时用rect=True保持长宽比,避免拉伸变形。如果显存不够,改用 YOLOv8n 或 YOLOv11n 小模型,牺牲一点精度换输入尺寸。

4.4 验证集 mAP 虚高但实际部署效果差

现象:验证集mAP50到 0.85 以上,但拿到田间新拍的照片上跑,漏检误检一大堆。

原因:训练集和验证集来自同一批拍摄条件,光照、角度、背景高度相似。模型学到了背景特征而不是病害特征。

解决:划分数据集时按拍摄批次或田块划分,而不是随机划分。如果数据来自多个田块,留一个田块的数据完全不参与训练,只做测试。另外加强颜色抖动和随机裁剪,逼模型关注病斑本身。

4.5 训练中 BN 层崩溃

现象:训练到一半 loss 突然飙升,之后再也降不下来,验证集指标断崖式下跌。

原因:小数据集加上大 batch size,BN 层统计量估计不准,running mean 和 variance 跑偏。这是 YOLO 训练里比较玄学的问题,有时候换个随机种子就好了。

解决:把batch降到 8 或 4,或者改用AdamW优化器配合更小的学习率。如果还不行,在模型里把 BN 换成 GN(GroupNorm),但 YOLO 官方代码不直接支持,需要改模型定义。

5. 用 4,600 张图做迁移学习和模型微调的进阶技巧

如果你手头还有别的作物病害数据集,比如烟草病虫害或者番茄叶片病害,可以把棉花数据作为预训练的一部分,做多作物联合训练。具体做法是把多个数据集的data.yaml合并成一个,类别名加上作物前缀避免冲突,比如cotton_angular_leaf_spot、tobacco_mosaic_virus。这样训出来的模型对叶片病害的通用特征提取能力更强,单作物微调时收敛更快。

另一个技巧是用训练好的棉花病害模型做伪标签,去标注新的未标注田间图像。流程是:先用 4,600 张图训一个基础模型,用这个模型对新的未标注图做推理,把置信度高于 0.7 的预测框转成 YOLO 标注格式,人工复核一遍后加入训练集重新训练。我试过用这个方法把有效数据从 4,600 张扩到 8,000 张以上,mAP50能涨 5 到 8 个点。但要注意伪标签的置信度门限不能太低,否则错误标注会被模型自己学进去,越训越偏。

验证模型是否真的学到了病害特征,而不是记住了拍摄背景,我有个土办法:把验证集里的图片做一次直方图均衡化,改变整体亮度和对比度,再跑一遍推理。如果指标掉得不多,说明模型关注的是病斑形态;如果掉得很厉害,说明模型严重依赖颜色和光照,需要补更多不同光照条件下的数据。这个测试花不了几分钟,但能帮你提前发现部署时的翻车风险。

我自己现在拿到任何一批新标注数据,第一件事永远是跑校验脚本和可视化抽查,而不是直接开训。标注文件里一个坐标越界或者类别 ID 错位,可能让你白跑一整天 GPU。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询