简介这份棉花植物病害图像目标检测数据集面向从事农业视觉检测、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_idx1}行字段数不对: {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_idx1}行类别ID越界: {cls_id})) for c in coords: if c 0 or c 1: bad_files.append((label_path, f第{line_idx1}行坐标越界: {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_okTrue) os.makedirs(os.path.join(output_root, split, labels), exist_okTrue) 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 modelyolov8s.pt epochs100 imgsz640 batch16 patience20参数说明modelyolov8s.pt是预训练权重小模型在 4,600 张图上够用epochs100配合patience20早停防止过拟合imgsz640是输入尺寸如果病斑特别小可以提到 1024但显存占用会翻倍batch16根据你的显卡调整8G 显存跑 640 尺寸大概能到 16。训练过程中重点看mAP50和mAP50-95两个指标如果mAP50在 30 个 epoch 后还在涨但验证集损失开始上升说明过拟合了需要加数据增强或减小模型。3. 从 4,600 张图到可用模型训练策略与参数调优3.1 小数据集训练 YOLO 的增强策略怎么选4,600 张图在目标检测任务里属于小数据集不加增强直接训模型大概率会记住训练集里的背景纹理换一块田拍就崩。YOLO 内置的增强参数里我一般会开这几个hsv_h0.015、hsv_s0.7、hsv_v0.4做颜色抖动模拟不同光照和相机白平衡差异degrees10做小角度旋转田间拍摄不可能永远水平translate0.1和scale0.5做平移和缩放让模型适应病斑在画面中的不同位置和大小fliplr0.5水平翻转棉花叶片左右对称翻转不改变病害特征mosaic1.0四图拼接这是 YOLO 系列涨点最明显的增强但要注意如果病斑本身很小mosaic 后可能被缩得更小反而有害。我踩过的一个坑是开了mixup0.5结果模型把两类病害混在一起学混淆矩阵里两类互相误检率飙升。后来查原因mixup 对细粒度分类任务不友好尤其是病斑形态差异不大的类别。所以如果你发现训练后混淆矩阵里某两类互相误判严重先把 mixup 关掉试试。3.2 置信度门限和 NMS 参数怎么调训练完模型推理时有两个参数直接决定你看到的结果置信度门限conf和 NMS 的iou阈值。默认conf0.25、iou0.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, conf0.15, # 降低门限减少漏检 iou0.35, # 降低 NMS 阈值避免病斑粘连 imgsz640, saveTrue, save_txtTrue # 保存预测结果到 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里写了nc5。YOLO 在计算损失时会对不存在的类别索引报错。解决用脚本统计所有标注文件里出现的类别 ID重新映射成 0 到 N-1 的连续整数同时更新data.yaml里的names列表。4.3 小目标病斑在 640 尺寸下消失现象训练指标看起来不错但推理时早期小病斑全部漏检。原因YOLO 默认下采样 32 倍640 输入下最小检测目标大约 8x8 像素。如果原始图像里病斑只有十几个像素缩到 640 后基本就没了。解决把imgsz提到 1024 或 1280同时用rectTrue保持长宽比避免拉伸变形。如果显存不够改用 YOLOv8n 或 YOLOv11n 小模型牺牲一点精度换输入尺寸。4.4 验证集 mAP 虚高但实际部署效果差现象验证集mAP50到 0.85 以上但拿到田间新拍的照片上跑漏检误检一大堆。原因训练集和验证集来自同一批拍摄条件光照、角度、背景高度相似。模型学到了背景特征而不是病害特征。解决划分数据集时按拍摄批次或田块划分而不是随机划分。如果数据来自多个田块留一个田块的数据完全不参与训练只做测试。另外加强颜色抖动和随机裁剪逼模型关注病斑本身。4.5 训练中 BN 层崩溃现象训练到一半 loss 突然飙升之后再也降不下来验证集指标断崖式下跌。原因小数据集加上大 batch sizeBN 层统计量估计不准running mean 和 variance 跑偏。这是 YOLO 训练里比较玄学的问题有时候换个随机种子就好了。解决把batch降到 8 或 4或者改用AdamW优化器配合更小的学习率。如果还不行在模型里把 BN 换成 GNGroupNorm但 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。希望帮到你。本文还有配套的精品资源点击获取