简介:面向棉花病害智能检测任务,这份已标注数据集提供约4,600张真实田间图像及对应YOLO标签,覆盖枯萎病、卷曲、灰霉、健康植株、叶斑病等6个类别,适用于目标检测算法训练、模型评估与农业视觉研究。资源共包含2,000个文件,其中1,999个txt标注文件与1个show.py可视化脚本,压缩包整体156.57MB,标注格式可直接接入YOLO系模型训练流程。数据集已按训练、验证、测试划分完毕,用户下载后无需额外整理,运行show.py即可快速预览标签与图像对应关系,便于核查标注质量。对于需要构建棉花病害检测基准或开展YOLO系列改进实验的开发者,这份数据能有效节省采集和标注时间,也可用于迁移学习与数据增强效果对比。目前已有47人浏览学习,属于轻量级专用数据集,适合个人项目或课程实践使用。
1. 棉花病害检测卡在哪:4,600 张已标注图像能直接打通哪些环节
做植保视觉的人都知道,棉花病害检测最耗时间的不是模型选型,而是数据本身。棉叶上的病斑常常只有十几个像素,角斑病和褐斑病在自然光下长得几乎没有差别,田间背景又乱,叶片互相遮挡,拿通用目标检测数据集训练出来的模型一到田里就翻车。这份棉花植物病害图像目标检测数据,约 4,600 张图像加标签,全部按 YOLO 标注格式整理完毕,类别、坐标、目录结构都已经对齐到能直接喂给训练脚本的状态。它解决的是从零攒数据、标注、清洗这一整段最磨人的流程,适合正在做棉田病害巡检、无人机航拍识别,或者想拿目标检测练手但不想在标注上耗两周的人。拿到手之后,有三件事必须自己做:数据校验、类别口径确认、训练参数调整。下面按这条路拆开讲。
2. 数据集结构解剖:YOLO 标注格式、目录划分与类别边界
2.1 YOLO 标注一个框到底存了什么
YOLO 标注格式的每一行是五个值:类别 ID、归一化中心点 x、归一化中心点 y、归一化宽、归一化高。所有坐标都除以了图像宽高,取值落在 0 到 1 之间,不关心图像实际是多大。这和 VOC 那种左上角、右下角的像素坐标完全是两套逻辑,很多第一次拿到 YOLO 数据集的人直接把 txt 里的数当像素用,画出来的框全偏。
我一般会先写一个解析脚本,把归一化坐标还原成像素框,叠加到原图上人工抽看。这步虽然简单,却是判断标注质量最快的手段。
import cv2 def parse_yolo_label(txt_path, img_path): img = cv2.imread(img_path) if img is None: raise FileNotFoundError(f"图像读取失败: {img_path}") h, w = img.shape[:2] boxes = [] with open(txt_path, "r", encoding="utf-8") as f: for line in f: parts = line.strip().split() if len(parts) != 5: # 非标准标签行直接跳过并留痕 print("非标准标签行:", line.strip()) continue cls, xc, yc, bw, bh = parts # 归一化坐标乘以图像宽高,得到像素坐标 xc, yc = float(xc) * w, float(yc) * h bw, bh = float(bw) * w, float(bh) * h x1 = int(xc - bw / 2) y1 = int(yc - bh / 2) x2 = int(xc + bw / 2) y2 = int(yc + bh / 2) boxes.append((int(cls), (x1, y1, x2, y2))) return boxes这段逻辑很直白,但有三处值得注意。第一,读取图像用的是 cv2,这里只取 shape 算宽高,BGR 通道无所谓。第二,归一化坐标严格来说是中心点加宽高,还原像素框时要除以 2 再加减,漏了这一步框会整体偏移。第三,非标准行不能静默跳过,最好打印出来,后面统一处理,不然训练时某张图标签数量对不上,排查起来很被动。
参数说明:txt_path 是每张图对应的同名标签文件,img_path 是原图路径;返回的 boxes 里每个元素是 (类别ID, (x1, y1, x2, y2)) 的像素坐标元组,可以直接交给 cv2.rectangle 画框。h、w 必须用原图实际尺寸,不要用缩略图或 EXIF 里的尺寸,否则坐标会整体错位。
2.2 目录结构与 data.yaml 的约定
拿到数据集的第一步不是急着训练,而是把目录结构摸清楚。YOLO 训练框架的默认约定是 images 和 labels 分开放,各自再拆 train、val 子集,标签文件名必须和图像文件名一一对应。
| 路径 | 内容 |
|---|---|
| images/train | 训练图像,常见格式 jpg / png |
| images/val | 验证图像,建议按拍摄场景独立抽取 |
| labels/train | 与 images/train 同名的 txt 标签 |
| labels/val | 与 images/val 同名的 txt 标签 |
| data.yaml | 数据集描述文件,含路径和类别清单 |
data.yaml 里最关键的是 names 字段,一行一个类别名,顺序就是类别 ID。训练框架按行号映射 0、1、2,如果 names 顺序和标注 txt 里的类别 ID 对不上,模型学到的类别和你想的完全不是一回事。拿到手先打开 data.yaml 看一眼 names 数量是否和标注里出现的最大类别 ID 对齐,例如标注里出现过 ID 3,names 至少要有 4 项。
训练框架一般要求 data.yaml 的 path 写数据集根目录,train、val 写相对路径。这个字段容易踩坑:有人把 path 写死成自己机器的绝对路径,换台机器训练直接报路径找不到。我习惯把 path 留空,train、val 写相对路径,代码里切到数据集根目录再启动训练,这样工程迁移性最好。
2.3 类别边界:病害种类与标注口径
这份数据的类别以棉花叶部常见病害为主,拿到手后首先要打开 data.yaml 逐个确认类别清单,而不是直接开训。棉花病害命名在不同资料里不完全统一,同一个病斑,有人按角斑病标,有人按褐斑病标,还有人干脆只标一个叶斑病。
这里的核心问题是标注口径。一份数据集里如果混用了标病斑和标病叶两种口径,训练出来的模型会非常不稳定。病斑口径下框子小、数量多,病叶口径下框子大、数量少,两种框混在一起,损失函数在大小目标之间来回摇摆。判断口径是否统一的办法很简单:按类别统计框宽高的分布,画出直方图。如果某个类别同时存在大量 0.05 以下的极小框和 0.4 以上的大框,说明口径混了,需要挑出样本重新定标准。
我处理过类似的数据,当时直接把两个相似病害合并成一个叶斑病类别,虽然类别粒度变粗了,但标注一致性立刻好转,mAP 反而从 0.52 涨到 0.71。如果下游任务只关心叶片有没有病、病害面积多大,合并相似类别是性价比很高的操作;只有明确要做精细化分级时,才值得保留细分类别。
3. 从 YOLO 格式到损失收敛:训练配置、参数选型与校验脚本
3.1 训练前的三道体检
标注数据直接开训是新手最常见的翻车点。4,600 张图不是小数目,但哪怕只有一张标签越界,训练都可能出现 loss 变成 NaN 或某个 batch 被静默跳过。我每次拿到数据集,第一件事是跑一个全量体检脚本,检查三类问题:标签越界、空标签、图像与标签名不匹配。
import os from pathlib import Path def check_dataset(img_dir, label_dir): problems = [] for lp in Path(label_dir).glob("*.txt"): stem = lp.stem # 检查同名图像是否存在 imgs = list(Path(img_dir).glob(stem + ".*")) if not imgs: problems.append((stem, "no_match_image")) continue lines = [ln.strip() for ln in lp.read_text(encoding="utf-8").splitlines() if ln.strip()] if not lines: problems.append((stem, "empty_label")) continue for line in lines: parts = line.split() if len(parts) != 5: problems.append((stem, "bad_field_count", line)) continue try: cls = int(parts[0]) xc, yc, bw, bh = [float(v) for v in parts[1:]] except ValueError: problems.append((stem, "non_numeric", line)) continue # 归一化坐标越界检查 if cls < 0 or xc <= 0 or yc <= 0 or bw <= 0 or bh <= 0: problems.append((stem, "non_positive", line)) if xc + bw / 2 > 1.0001 or yc + bh / 2 > 1.0001: problems.append((stem, "out_of_image", line)) return problems这段脚本覆盖了三道体检。第一道是文件名匹配,标签存在但图像缺失,训练到一半会报错退出;第二道是空标签文件,训练框架会静默跳过空白图,问题是它不提示,你根本不知道哪些图没参与训练;第三道是数值检查,中心点加半宽超过 1 意味着框冲出图像边界,这类标签在 anchor 匹配阶段会产生异常梯度,这是 NaN 损失的高发来源。
参数说明:img_dir 和 label_dir 分别传训练或验证的 images、labels 目录,脚本返回 problems 列表,每项是 (文件名, 问题类型, 标签行)。越界阈值留 0.0001 容差,因为个别标注工具浮点精度会导致边界刚好压在 1.0 多一点,这种可以容忍;超过 1.0001 的必须处理。常见做法是把越界框按图像边界裁剪,或者直接删掉那一行重新标注,后者更省事。
3.2 训练命令:参数怎么选
体检通过之后,我用 YOLOv8 训练这份数据。命令行方式最直接,数据和配置收敛在一个目录里,便于复现。
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ imgsz=640 \ batch=16 \ epochs=150 \ patience=30 \ lr0=0.01 \ project=runs/cotton \ name=exp1逐个说参数怎么定。imgsz 默认 640,对棉花病害这种小目标居多的场景偏小。我把病斑尺寸统计过一遍,大量目标集中在 20 到 50 像素,640 输入下这些目标在深层特征图只剩两三个特征点。显存够的话建议提到 1024,代价是训练时间约翻倍;4,600 张图属于中等规模,时间成本可以接受。
| 参数 | 建议值 | 说明 |
|---|---|---|
| imgsz | 640 / 1024 | 小目标多就上 1024,显存不足退 640 |
| batch | 8 / 16 | 1024 输入用 8,640 输入用 16 |
| epochs | 150 | 配合 patience 提前停止 |
| patience | 30 | 连续 30 轮验证指标不涨就停 |
| lr0 | 0.01 | 配合 SGD,小数据集更稳 |
batch 主要看显存,16 在消费级显卡上配合 640 输入是安全值,用 1024 输入需要降到 8。epochs 给 150 并开 patience=30,即连续 30 个 epoch 验证集指标没有提升就提前停,避免死等。lr0 保持 0.01 配 SGD;数据量不算大,AdamW 在小数据集上收敛快但容易过拟合,这类场景我更愿意用 SGD。
训练日志里重点盯三类指标:box_loss 是否持续下降、验证集 mAP50-95 是否在 30 个 epoch 内还在涨、以及每个类别的 AP 分布。如果某个类别 AP 明显低于其他类,大概率是该类别样本太少,往下看数据增强的处理办法。
注意:data.yaml 的 path 字段不要写死本地绝对路径,换机器训练会直接报路径错误,留空或写相对路径更稳妥。
3.3 类别不均衡与增强策略
4,600 张图分布在多个病害类别里,几乎必然存在不均衡。我一般先看训练后按类别统计的实例数量,最少类和最多类可能差到 5 倍以上。YOLOv8 训练阶段没有直接按类别加权的检测损失选项,常见做法是两条腿走路:一是在数据侧对少样本类别做重采样,二是靠增强倾斜。
增强方面,YOLOv8 默认开了 mosaic,这对植保场景是好事,可以把不同病害贴到一张图上,间接增加背景多样性。但要注意,棉花叶片的病害空间方向性不强,翻转增强可以放开;旋转增强我限制在 15 度以内,旋转太多会把叶片纹理扭曲到失真。颜色增强方面,hsv_v 适当调大,因为田间光照强度差异很大,训练时见过更多亮度区间,实拍时不容易过曝丢检。
少样本类别我习惯单独建一个 supplementary 列表,从原图做简单复制粘贴增强:把小病斑裁剪出来,按随机位置贴到健康叶片图上,重新生成标签。注意粘贴时避开原图已有目标区域,避免框重叠导致类别混叠。这个操作在数据层面比调损失权重直观,效果也更可控。
4. 训练与部署避坑:标签越界、小目标漏检与类别混淆的排查记录
下面是实际折腾过的几个典型坑,每个都按现象、原因、解决记录。这些坑和技术选型关系不大,全是被数据和使用方式逼出来的,血泪值很高。
4.1 现象:训练到第 24 个 epoch,box_loss 突然变成 NaN
原因:排查了很久,最后定位到验证集的一张图。它的标签文件里有一行 width=1.2,明显是标注工具导出时把像素坐标当作归一化坐标写进去了,或者图片被压缩后标签没同步更新。训练框架不会对单行错误标签报错,它就安静地躺在数据里,等模型算到它时梯度直接爆掉。损失曲线前面一路下降,谁能想到一枚坏标签能让整个训练前功尽弃。
解决:把第 3.1 节的体检脚本设成每次训练的前置步骤,全量扫一遍再开训。处理策略我定成两条:能确认原图尺寸的,按正确尺寸重新归一化;无法确认的一律删行并记录。删完行之后,统计每张图的标签框数,人工抽查 20 张,确认误删率。这套流程挡住的不只是 NaN,还包括训练看着正常但某类 AP 奇低的慢性病。
4.2 现象:验证集 mAP50 有 0.83,但田间实拍时小病斑漏检一大片
原因:把漏检图拉出来逐张看,漏掉的全是面积在 15 到 30 像素之间的早期病斑。两个因素叠加:输入分辨率 640 时,这些小目标在深层特征图上几乎只占几个特征点,特征表达先天不足;同时原始标注对小病斑本身也存在漏标,模型学到的正样本不完整。mAP 虚高是因为验证集里大目标占比大,小目标被平均掉了。
解决:第一,imgsz 提到 1024,小目标的像素占比直接翻倍,同类场景下 recall 普遍能涨 5 到 8 个点;第二,对验证集按目标面积区间分别统计 recall,单独盯着 32x32 以下这类指标,不再被总 mAP 迷惑;第三,推理阶段开 TTA,原图和水平翻转各推一次再融合结果,小目标 recall 再涨 3 个点左右,代价是单帧耗时翻倍,实时巡检要评估算力再决定。
4.3 现象:两种叶部病害互相污染,混淆矩阵一整片亮,单类 AP 都卡在 0.5 以下
原因:这两种病害在早期症状上是渐进连续的,不是非黑即白。更麻烦的是标注口径前后不一致:第一批标注员只框边界清晰的斑点,第二批把整片发黄区也框了进来。模型看到的同一类目标有两种形态分布,决策边界根本拉不开。
解决:先把两类样本的框宽高分布打出来对比,发现第二批框普遍大一圈。我把边界模糊的样本挑出来重新目视复核,最后决定合并成一个大类叶斑病。合完之后单类 AP 从 0.5 涨到 0.75 以上。业务上只关心有没有病、病害面积多大,完全够用;如果确实要细分,唯一靠谱的路是把边界样本单独拆成一类,再补标几百张,没有捷径。
4.4 现象:训练集指标很漂亮,一换到田间实拍,整体掉点接近一半
原因:典型的领域偏移。数据里有相当一部分是室内单叶拍摄,背景干净、光线均匀;田间实拍是整株冠层,逆光、叶片互相遮挡、背景有土壤和杂草。模型在室内图上学到的叶片边缘锐度特征,到田间完全失效。这种偏移从训练曲线里看不出来,只能靠单独的场景验证集暴露。
解决:先统计训练图和实拍图的亮度、对比度分布,确认偏移方向;然后把实拍样张按 3:1 混进训练集做微调,再用 copy-paste 增强把室内病斑裁下来贴到田间背景上,制造混合样本。真正的关键动作是单独划出一个 field_test 集,只用它做最终验收,训练过程中不碰它,防止模型反向过拟合到验收集。
提示:field_test 集只用来做最终验收,不要在训练调参时反复看它的指标,看一次就被它带偏一次。
4.5 现象:推理时预测框全部挤在图像边缘,置信度普遍低于 0.3
原因:这批图的标签坐标整体偏移。追查后发现,其中一批原图在入库时被批量压缩过一次,图像尺寸变了,标签里的归一化坐标却没有按新尺寸重新计算。归一化坐标理论上不随尺寸变化,但压缩工具改了长宽比,坐标就悬空了。
解决:用第 2.1 节的解析脚本,把所有疑似问题图的预测框画出来目视扫一遍,框全部贴边的批次直接定位。处理方式是把这批图按原始长宽比重新导出,或者直接剔除出训练集。从那以后,凡是经过批量图像处理的目录,我都会把处理前后的尺寸记录留档,标签坐标重新生成后必须再过一次体检脚本。
4.6 现象:训练正常收敛,但导出的 ONNX 在本地推理结果和训练时预测完全不同
原因:导出时没有固定 imgsz,onnxruntime 动态输入尺寸下,模型的缩放逻辑和训练时不一致;另一个常见原因是导出后 NMS 参数被重置,conf 和 iou 阈值回到了默认值,导致输出框数量和位置都对不上。
解决:导出命令显式写 imgsz,并在部署代码里重新声明 conf 和 iou;拿训练集里同一张图对比 PyTorch 推理和 ONNX 推理的输出,坐标偏差超过 2 像素就要检查预处理里的 letterbox padding。这个坑和数据集本身无关,但凡是把数据集导出部署都会碰到,值得记录。
5. 把 4,600 张用出 10,000 张的效果:数据清洗、难例挖掘与增量扩充
5.1 训练结果反哺数据:漏检样本就是金子
第一次训练跑完,不要急着部署。把训练好的模型放回到训练集和验证集上推理一遍,置信度阈值调低到 0.05,把所有漏检的框捞出来。这个操作叫难例挖掘。模型在训练集上漏掉的样本,要么标得太差,要么目标特征太隐蔽,这些恰恰是下一轮训练最该补的素材。
用命令行先把预测结果落盘:
yolo detect predict \ model=runs/cotton/weights/best.pt \ source=datasets/cotton/images/train \ save_txt=True \ conf=0.05 \ project=hard_examples \ name=train_mining参数说明:save_txt=True 会让每张图生成一个同名 txt,里面是预测框,格式和标签一致;conf=0.05 是为了把低置信度的弱响应也保留下来,正常推理用 0.25 到 0.3,但挖掘难例必须放宽,否则真阳性也被滤掉了。输出在 hard_examples/train_mining/labels 目录,按图像文件名可直接和原标签做比对。
拿到预测 txt 之后,写一段比对脚本:读取预测框和真实标签框,做 IoU 匹配,小于 0.1 的预测框视为模型压根没找到,对应图像挑出来人工复查。IoU 计算是最基础的几何运算:
def iou(box_a, box_b): # box_a 和 box_b 都是 (x1, y1, x2, y2) 的像素坐标 x1 = max(box_a[0], box_b[0]) y1 = max(box_a[1], box_b[1]) x2 = min(box_a[2], box_b[2]) y2 = min(box_a[3], box_b[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) area_a = (box_a[2] - box_a[0]) * (box_a[3] - box_a[1]) area_b = (box_b[2] - box_b[0]) * (box_b[3] - box_b[1]) return inter / (area_a + area_b - inter + 1e-6)加了 1e-6 是为了防止分母为 0,两个完全重合的空框不会触发除零。复查时重点看两件事:是标注漏了,还是目标本身极小看不清。前者补标,后者记录成难例,下一轮训练时单独加大复制粘贴增强次数。
5.2 标注一致性体检:重标和合并的决策
数据标注的一致性直接决定模型上限。我常用的检查维度有三个:框宽高分布、类别实例数分布、同图重复框数量。框宽高分布前面提过,这里说后两个。
类别实例数分布:按类别统计所有标签的行数,如果某类只有几十个实例,这个类别的 AP 基本是虚的,训练时会被其他大类压制。实例数少于 100 的类别,我的处理惯例是:要么找漏标的同类图像补标,把数量拉到 300 以上;要么直接合并进语义相近的大类。
同图重复框:两个标注员对同一张图的同一个病斑重复画了两个框,一个精确一个宽松,这样的重复框会让损失函数在同一个位置产生两次梯度,框的回归方向互相打架。检查方法是按图像统计框数和框面积占比,正常叶片病害每张 1 到 8 个框,如果出现单张 20 个框且面积都小于 0.01,大概率是重复标注,需要人工合并。
5.3 半自动增量扩充:模型预标加人工校对
数据不足时,最常见的增量方案是拿现成模型去预标新采集的田间图片。流程是:新照片丢给 best.pt 推理,只保留置信度 0.6 以上的框,生成初步标签,然后人工用标注工具逐张校对。这一步的重点不是省标注时间,而是保证新样本的标签口径和原数据集一致。
预标有一个明显的坑:模型会系统性地漏掉它在训练中学得差的类别。如果新图上恰好有这类病害,预标结果里它没出现,人工校对时容易顺手忽略,导致新数据不仅没补齐短板,反而强化了原有偏差。我的习惯是:预标之前先列出模型单类 AP 最低的三个类别,校对时要求人工专门留意这些类别,只要图上出现疑似特征就手动补框,不能依赖模型输出。每批增量数据加完后,重新跑一次训练,对比全类别 AP 变化,确认短板类别是否真的在涨。
6. 导出与田间验证:ONNX 推理、指标复盘与一个养成习惯
训练收敛后,我习惯把 best.pt 导出成 ONNX,再做一次独立的指标复盘,才真正把这个数据集的价值兑现到现场。导出命令:
yolo export model=runs/cotton/weights/best.pt format=onnx imgsz=640导出后用 onnxruntime 在 CPU 上跑 20 张实拍图,重点不是速度,而是确认导出后的输入尺寸和 NMS 输出和训练时一致。常见坑是导出后置信度阈值被重置,部署代码里要重新指定 conf 和 iou,不要沿用导出的默认值。
指标复盘我只盯三组数:验证集全类别 mAP50、单类 AP 最低值、conf=0.25 时的 precision/recall 配对。前两组决定模型能不能上线,最后一组决定现场阈值怎么设。如果 recall 明显低于 precision,现场要把阈值下调到 0.15 左右,宁可多框也不能漏,植保场景漏检的代价远高于误检。
配套的推理脚本、校验工具、训练配置,我都和数据集放在同一个目录下,命名带日期版本。这半年养成了一个固定习惯:每次拿到标注数据,不管对方说得多完整,都强制走一遍体检脚本、类别分布直方图、抽 30 张图目视复核这三步,过了才允许开训练。这个习惯帮我挡掉了至少三次本来要通宵排查的数据事故。希望这份数据能让你少走同样一段路,希望帮到你。
本文还有配套的精品资源,点击获取