☰
棉花病害检测数据集实战:YOLO标注格式解析与训练避坑指南
2026/9/26 8:04:43 网站建设 项目流程

简介:这份棉花植物病害图像目标检测数据集面向从事农业视觉检测、YOLO 系列模型训练与改进的开发者及学生,提供可直接投入训练的真实标注样本。数据覆盖枯萎病、卷曲、灰霉、健康叶片、叶斑病等 6 个类别,标签采用 YOLO 通用格式,并已预先完成训练集、验证集与测试集划分,省去自行清洗与切分的繁琐步骤。压缩包共约 2000 个文件,以 1999 个 txt 标注文件和 1 个 show.py 可视化脚本为主,整体约 156.57MB,运行脚本即可快速查看图像与标注框的对应效果。目前已有 47 人学习关注,适合作为目标检测课程设计、病害识别课题或模型对比实验的基础数据。借助该数据,读者可完成从数据加载、类别统计到模型训练与评估的完整流程,并配合作者主页的 YOLOv5 改进实战内容,进一步验证注意力机制、特征融合等改进策略在农业场景下的实际效果。

1. 棉花病害检测数据集:4,600 张已标注图像能省掉多少前期脏活

棉花叶片上的病斑,早期跟健康组织的色差极小,轮纹、褪绿、水渍状边缘混在一起,肉眼在田间巡检时经常要凑到 20 厘米内才敢下判断。这份资源就是冲着这个场景来的:约 4,600 张棉花植物病害图像,全部完成目标检测标注,直接给到 YOLO 格式的 txt 标签,拿来就能进训练管线。它解决的不是模型结构问题,而是目标检测里最耗人力的那一环——数据采集与标注。适合谁?做农业视觉的算法工程师、要快速验证病害检测方案的学生、以及手上有 YOLO 训练流程但缺行业数据的从业者。你不需要从零拍图、标框、清洗,拿到手先跑通 baseline,再决定要不要扩数据。下面按「数据长什么样 → 怎么接进 YOLO → 坑在哪 → 怎么验证」的顺序拆开讲。

2. YOLO 标注格式拆解:从目录结构到类别映射的落地检查

2.1 先看清一份 YOLO 检测数据集到底由什么组成

目标检测数据集不是「一堆图加一堆 txt」这么简单。YOLO 格式的核心约定是:每张图像对应一个同名 txt 文件,txt 里每一行代表一个目标框,格式为class_id x_center y_center width height,后四个值都是相对图像宽高的归一化浮点数,范围 0 到 1。这意味着标签本身不携带像素坐标,读取时必须结合原图尺寸还原。这份棉花病害数据约 4,600 张,按常见组织方式,目录里通常会有images/和labels/两个平行文件夹,再各自分train/、val/,也可能直接平铺。拿到手第一件事不是训练,是核对三件事:图像与标签是否一一对应、类别 id 是否从 0 连续编号、归一化值有没有越界。

我一般会先跑一段统计脚本,把类别分布和框的尺寸分布打出来。棉花病害里如果某类病斑特别小,框的宽高归一化后可能只有 0.02 到 0.05,这种小目标在默认 640 输入下经过多次下采样后特征几乎消失,是后面训练不收敛的高频原因。先看清分布,再决定输入尺寸和 anchor 策略,比盲目开训省时间。

2.2 用脚本核对图像与标签的配对关系

下面这段脚本做三件事:遍历图像目录、检查同名标签是否存在、统计每个类别的框数量。放在数据集根目录运行即可。

import os from collections import Counter from pathlib import Path IMG_EXTS = {".jpg", ".jpeg", ".png", ".bmp"} root = Path(".") # 数据集根目录,按实际路径改 img_dir = root / "images" lbl_dir = root / "labels" missing_label, empty_label = [], [] cls_counter = Counter() box_sizes = [] for img_path in img_dir.rglob("*"): if img_path.suffix.lower() not in IMG_EXTS: continue # 标签路径:把 images 替换成 labels,后缀换成 .txt rel = img_path.relative_to(img_dir).with_suffix(".txt") lbl_path = lbl_dir / rel if not lbl_path.exists(): missing_label.append(str(img_path)) continue lines = [l.strip() for l in lbl_path.read_text().splitlines() if l.strip()] if not lines: empty_label.append(str(img_path)) continue for line in lines: parts = line.split() if len(parts) != 5: print("格式异常:", lbl_path, line) continue cid = int(float(parts[0])) w, h = float(parts[3]), float(parts[4]) cls_counter[cid] += 1 box_sizes.append((w, h)) print("缺失标签数:", len(missing_label)) print("空标签数:", len(empty_label)) print("类别分布:", dict(sorted(cls_counter.items()))) if box_sizes: ws = sorted(s[0] for s in box_sizes) hs = sorted(s[1] for s in box_sizes) print("框宽中位数:", ws[len(ws)//2], "框高中位数:", hs[len(hs)//2]) print("最小框宽:", ws[0], "最小框高:", hs[0])

逻辑说明:rglob("*")递归遍历,兼容train/val子目录结构;标签路径用relative_to加with_suffix推导,避免手写字符串替换出错。参数说明:IMG_EXTS按你实际拿到的图像后缀调整,如果数据里混了.tif要补进去;cls_counter输出的是每个类别 id 的框总数,正常情况应该是连续编号且没有某个 id 为 0;box_sizes的中位数和最小值直接告诉你小目标占比,最小框宽低于 0.03 就要警惕。

2.3 类别名与 data.yaml 的对应关系

YOLO 训练不认类别名,只认 id,但评估和可视化需要名字。所以必须有一份data.yaml把 id 映射回病害名称。这份数据如果没附带 yaml,你得自己按标注时的类别顺序补。常见棉花病害类别大致是枯萎病、黄萎病、叶斑病、红腐病这类,但具体以数据实际标注为准,不要凭经验硬套。

# data.yaml path: ./cotton_disease # 数据集根目录 train: images/train val: images/val nc: 4 # 类别数,按实际改 names: 0: fusarium_wilt 1: verticillium_wilt 2: leaf_spot 3: red_rot

参数说明:nc必须等于 names 的条目数,写错会在训练启动时报维度不匹配;train和val是相对path的路径,别写成绝对路径,换机器会崩。如果数据只有 train 没有 val,常见做法是从 train 里按 8:2 切一份出来,切的时候要连图像带标签一起移动,不能只移图像。

3. 接进 YOLOv8 训练管线:环境、配置与首轮 baseline

3.1 环境配置与依赖版本选择

YOLOv8 走 ultralytics 包,环境比早期 YOLOv5 干净很多。Anaconda 建环境时 Python 选 3.9 或 3.10 最稳,3.11 以上偶尔会遇到某些依赖轮子没跟上。CUDA 版本跟你的显卡驱动匹配即可,不强制最新。装的时候一条命令搞定,但要注意它会把 torch 一起拉下来,如果你机器上已有特定 torch 版本,先确认会不会被覆盖。

conda create -n cotton_yolo python=3.10 -y conda activate cotton_yolo pip install ultralytics # 验证安装与 GPU 可见性 yolo checks

逻辑说明:yolo checks会打印 ultralytics 版本、torch 版本、CUDA 是否可用、显卡型号。这一步别跳过,很多「训练报错」其实是环境根本没认到 GPU,跑在 CPU 上慢到怀疑人生。参数说明:如果yolo checks显示 CUDA 不可用,先查驱动,再查 torch 是不是 CPU 版;pip install ultralytics默认装最新版,若你要复现某个特定版本行为,用pip install ultralytics==8.x.x锁版本。

3.2 首轮训练命令与关键参数

baseline 不要一上来就调参,先用默认配置跑通,确认数据管线没问题。下面这条命令是最小可用集。

yolo detect train \ data=./cotton_disease/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=runs/cotton \ name=baseline

逻辑说明:model=yolov8n.pt用 nano 版先跑通,速度快、显存占用低,适合验证数据;device=0指定第一块 GPU,多卡用device=0,1。参数说明:imgsz=640是默认输入尺寸,如果 2.2 节统计出小目标很多,可以提到 1024,但显存和速度要重新评估;batch=16在 8G 显存上跑 640 一般够,报 OOM 就降到 8 或 4;epochs=100对 4,600 张数据通常够看到趋势,但别拿它当最终结果,先看 loss 曲线和 mAP 有没有正常下降上升。

训练启动后重点盯三个输出:box_loss是否稳定下降、cls_loss是否发散、验证集 mAP50 是否在 20 个 epoch 后有起色。如果 box_loss 一直平,多半是标签归一化有问题;如果 cls_loss 震荡剧烈,可能是类别不平衡或学习率偏大。

3.3 从训练日志判断数据质量

YOLOv8 会在runs/cotton/baseline/下生成results.csv和混淆矩阵图。results.csv每行一个 epoch,列里有train/box_loss、metrics/mAP50、metrics/mAP50-95。我一般会先看 mAP50 的爬升曲线:正常数据在 30 到 50 epoch 应该能看到明显上升,如果 100 epoch 还在 0.1 以下,先别怀疑模型,回去查标签。混淆矩阵能告诉你哪两类在互相误判,棉花病害里叶斑和轮纹斑如果标注边界模糊,这两类混淆是常态,属于数据本身的问题,不是训练能救的。

提示:首轮训练不要开数据增强的激进配置,默认增强已经够用。等 baseline 跑通、确认标签无误后,再考虑加 mosaic、mixup 或调整 anchor。

4. 避坑与排查:这份数据最容易翻车的五个地方

4.1 标签归一化值越界或坐标系搞反

现象:训练启动不报错,但 box_loss 居高不下,预测框全部挤在图像中心或飞出边界。原因:标注工具导出时用了像素坐标没归一化,或者把x_center y_center写成了x_min y_min。YOLO 只认归一化中心点格式,像素坐标直接喂进去,数值大于 1,模型学到的就是错误分布。解决:跑 2.2 的脚本,加一段检查0 <= x <= 1的逻辑,越界的行打印出来,用脚本批量转换或退回标注工具重新导出。

4.2 图像与标签文件名大小写或后缀不一致

现象:脚本统计显示「缺失标签数」很高,但你去文件夹里看明明有对应 txt。原因:图像是.JPG大写,标签是.jpg.txt或.txt,或者文件名里有多余空格。Linux 下大小写敏感,Windows 下不敏感,跨平台搬运就会翻车。解决:统一后缀为小写,标签命名严格用「图像主名 + .txt」,不要带原后缀。批量重命名用脚本处理,别手动改。

4.3 类别 id 不连续或从 1 开始

现象:训练时提示Label class X exceeds nc,或者某一类永远学不出来。原因:标注时类别从 1 开始编号,而 YOLO 要求从 0 开始且连续;或者中间某个 id 没有样本,nc设成了最大 id 加一,导致空类占位。解决:统计实际出现的 id,重新映射成 0 到 n-1 连续编号,同步改data.yaml的nc和names。映射关系要记录,别改完自己都忘了哪个 id 对应哪种病。

4.4 训练集与验证集图像重复导致指标虚高

现象:验证集 mAP 高得离谱,部署到新图上效果断崖式下跌。原因:切分时随机打乱,同一张图或同一株棉花的连拍图同时进了 train 和 val,模型等于见过验证集。解决:按拍摄批次或植株 id 切分,同一来源的图只进一个集合。如果数据没有批次信息,至少做一次图像哈希去重,把重复图剔掉再切。

4.5 小目标病斑在默认输入下丢失

现象:大斑块检测正常,早期小病斑全部漏检。原因:640 输入下,归一化宽高小于 0.03 的框经过 backbone 下采样后特征图上的响应只剩一两个像素。解决:提高imgsz到 1024 或 1280,或者改用带 P2 小目标检测层的模型结构。提高输入尺寸会显著增加显存和推理耗时,部署前要权衡。

5. 验证与进阶:用混淆矩阵和置信度门限把模型调到能用

训练跑完不等于能用。我习惯在部署前强制走一遍验证流程,第一步是拿val集跑yolo detect val,看每个类别的 P、R、mAP50。棉花病害里如果某类召回率特别低,先别调模型,回去看这类样本量是不是太少。4,600 张里如果某类只有一两百框,属于长尾,常见做法是过采样或加类别权重,但更稳的是补数据。

第二步是调置信度门限。默认conf=0.25对农业检测偏保守,漏检多;调到 0.1 能捞回小病斑,但误检会上升。我的做法是画一条 P-R 曲线,找 F1 最大的点作为门限,而不是拍脑袋。下面这段脚本用验证集预测结果算不同门限下的 F1。

from ultralytics import YOLO import numpy as np model = YOLO("runs/cotton/baseline/weights/best.pt") results = model.val(data="./cotton_disease/data.yaml", conf=0.001, iou=0.6) # results.box 里含每个类别的 precision/recall 曲线数据 # 实际调门限时,遍历 conf 从 0.05 到 0.5,取 F1 最大点 for conf in np.arange(0.05, 0.55, 0.05): r = model.val(data="./cotton_disease/data.yaml", conf=float(conf), verbose=False) p, rec = r.box.mp, r.box.mr f1 = 2 * p * rec / (p + rec + 1e-9) print(f"conf={conf:.2f} P={p:.3f} R={rec:.3f} F1={f1:.3f}")

逻辑说明:conf=0.001先跑一次拿全量预测,再逐门限评估;r.box.mp和r.box.mr是 ultralytics 返回的平均精度和平均召回。参数说明:iou=0.6是 NMS 的 IoU 阈值,检测密集小目标时可以降到 0.5 减少框合并;门限最终值要结合业务,宁可误检不可漏检的场景就选召回高的点。

第三步是看混淆矩阵。runs/cotton/baseline/下会有confusion_matrix.png,横轴预测、纵轴真实。如果叶斑和轮纹斑互相误判严重,说明这两类在视觉上确实接近,要么合并类别,要么在数据里补充区分性强的样本。这一步没有捷径,只能对着矩阵一类一类看。

最后说个我自己的习惯:每次拿到新数据集,不管多急,都先跑一遍配对检查和类别统计,再切一小份 200 张的子集做 10 epoch 快速验证。子集能跑通、loss 正常下降,再上全量。这个习惯帮我挡掉过好几次标签格式错误和路径写错的问题,省下的返工时间远超那十分钟。希望这份棉花病害数据能帮你把前期脏活压缩掉,把精力留给模型和业务本身。

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

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

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

立即咨询