简介:本资源为茶叶病害目标检测数据集,面向从事农业图像识别、深度学习模型训练的学生与算法工程师,可用于分类与检测任务的模型训练、验证及竞赛练习。数据集采用Pascal VOC与YOLO双格式标注,包含883张jpg图片及对应的883个xml和883个txt标注文件,压缩包内共约2000个文件,整体大小200.5MB,另附有少量说明文本,方便直接接入主流检测框架。标注覆盖8个类别,包括炭疽病、藻斑病、鸟眼斑病、褐斑病、灰斑病、健康叶片、红叶斑病与白斑病,总框数884个,每张图片仅含单片叶子,适合单目标检测场景。目前已有887人学习下载,读者可获得开箱即用的标注数据、清晰的类别分布以及规范的labelImg标注结果,能有效减少数据采集与清洗成本,快速投入模型迭代与效果对比实验。
1. 茶叶病害数据集 VOC+YOLO 格式 883 张 8 类别:一份能直接开训的检测数据该怎么用
茶园里真正难的不是把模型跑起来,而是拿到一批标注干净、类别对得上、格式不用来回折腾的图。这份「茶叶病害数据集 VOC+YOLO 格式 883 张 8 类别」正好卡在这个痛点上:883 张图、8 个病害类别、同时给了 VOC 的 XML 和 YOLO 的 txt 两套标注。对做农业视觉、智慧种植、边缘端病害巡检的人来说,它省掉的不是下载时间,而是格式转换和类别对齐这两段最容易翻车的活。这篇文章不聊虚的,就按一线做法把这份数据集从「拿到手」到「跑出第一版 mAP」讲透:目录怎么摆、YOLO 的 txt 怎么读、8 类怎么配、训练参数怎么设、小样本下哪些坑必踩。适合刚入门 yolo 目标检测的新手,也适合手里有模型但缺农业场景数据的熟手。
2. 先搞懂 VOC 与 YOLO 两套标注到底差在哪
2.1 VOC 的 XML 与 YOLO 的 txt:坐标体系完全不同
VOC 格式的标注是一个图对应一个 XML 文件,里面用<object>记录每个目标,坐标是绝对像素值,写成xmin, ymin, xmax, ymax。YOLO 格式则是一个图对应一个 txt,每行一个目标,格式是类别索引 中心x 中心y 宽 高,而且这四个值全部是相对整图宽高的归一化值,范围 0 到 1。这两套东西看着只是数字不同,实际差在三个地方:一是坐标系原点,VOC 用左上角绝对像素,YOLO 用归一化中心点;二是类别表达,VOC 写类别名字符串,YOLO 写从 0 开始的整数索引;三是文件组织,VOC 的 XML 通常和图片分开放,YOLO 的 txt 一般和图片同名同目录或镜像目录。
正因为差在这三处,很多人拿到「VOC+YOLO 双格式」的数据集反而懵:到底用哪套?我的建议是训练直接用 YOLO 那套 txt,VOC 的 XML 留着做校验和回溯。原因很直接,主流 yolo 训练框架读的就是 YOLO txt,你再去转一道纯属给自己加戏。但 XML 别删,后面排查「某个框是不是标歪了」时,XML 里的绝对像素比归一化值直观得多。
2.2 883 张 8 类别意味着什么:小样本多类别的现实约束
883 张图分 8 个类别,平均下来每类不到 110 张,这是典型的小样本多类别场景。它带来的直接后果是:类别不均衡几乎必然存在,某些病害样本可能只有几十张,而健康叶片或常见病害可能上百张。第二个后果是,模型很容易在样本多的类上过拟合,在样本少的类上直接躺平。第三个后果是,验证集如果随机切,很可能某个类在验证集里只有个位数样本,mAP 波动大到没法看。
所以拿到这份数据,第一件事不是急着训,而是先统计每个类的框数量和图片数量。常见做法是写个脚本遍历 labels 目录,把每个类别的实例数打出来。如果发现某类少于 30 个实例,就要提前想好对策:要么用数据增强重点补这类,要么在损失里给这类加权,要么接受它在初期 mAP 偏低、靠后续迭代慢慢拉。别指望 883 张能训出工业级精度,它的合理定位是「快速验证 pipeline + 农业场景预训练起点」。
2.3 为什么双格式数据集更适合做格式转换的练手样本
很多人学 yolo 卡在第一步:自己的数据是 VOC,不知道怎么转 YOLO。这份数据集两套都给,恰好可以拿来反向验证你的转换脚本对不对。做法是:拿它自带的 XML 跑一遍你自己的 VOC 转 YOLO 脚本,再和它自带的 txt 逐行对比。如果坐标对得上、类别索引对得上,说明你的脚本没问题,以后处理自己的数据就放心了。如果对不上,差异点就是你脚本的 bug 所在。这比空看教程有用得多,因为你有标准答案可以比对。
下面是一个最小可用的 VOC 转 YOLO 脚本,逻辑就是读 XML、算归一化中心点和宽高、按类别映射写 txt:
import os import xml.etree.ElementTree as ET # 类别名到索引的映射,顺序必须和训练时的 data.yaml 一致 classes = ["class0", "class1", "class2", "class3", "class4", "class5", "class6", "class7"] class_map = {name: i for i, name in enumerate(classes)} def convert(xml_path, txt_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() 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 = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(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}") with open(txt_path, "w") as f: f.write("\n".join(lines))这段代码里img_w和img_h必须传真实图片尺寸,不能写死 640,因为归一化用的是原图尺寸。class_map的顺序是命门,它决定了 txt 里第一列的数字,一旦和 data.yaml 里的 names 顺序不一致,训练时类别就全错位了,模型会把 A 病害认成 B 病害,而且 loss 还能正常下降,属于最隐蔽的翻车。:.6f保留六位是常见做法,够用且不会因为精度丢框。
3. 把 883 张数据喂进 YOLO:目录、配置与训练命令
3.1 目录结构怎么摆才不会在训练时报找不到标签
YOLO 训练框架对目录结构有约定,最常见的是 images 和 labels 平行,且内部再分 train 和 val。推荐这样摆:
dataset/ images/ train/ (约 700 张) val/ (约 183 张) labels/ train/ (与 train 图片同名的 txt) val/ (与 val 图片同名的 txt)关键点是 labels 下的 txt 必须和 images 下的图片同名,只换扩展名。比如images/train/leaf_001.jpg对应labels/train/leaf_001.txt。如果名字对不上,训练时框架会认为这张图没有标签,直接跳过或报空标签警告。另一个坑是图片和标签数量不一致,比如你复制图片时漏了几张,或者转换脚本中途报错少写了 txt,训练不会立刻崩,但会静默丢掉那些样本,你以为在训 883 张,实际可能只用了 800 张。所以切分完先跑一遍数量核对。
3.2 data.yaml 里 8 个类别的顺序为什么是命门
data.yaml 是 YOLO 训练的入口配置,核心就几行:
path: /abs/path/to/dataset train: images/train val: images/val nc: 8 names: 0: class0 1: class1 2: class2 3: class3 4: class4 5: class5 6: class6 7: class7nc是类别数,这里必须是 8。names的顺序必须和 txt 里第一列的数字严格对应。我一般会把真实的 8 个病害名字按字母或按样本量从多到少排好,然后这个顺序在转换脚本、data.yaml、后续推理的类别映射里三处保持一致。只要有一处顺序变了,模型输出的类别名就全乱。这个错误不会让训练报错,只会让结果看起来「精度还行但预测全是错的」,属于血泪经验级别的坑。path建议写绝对路径,相对路径在不同工作目录下启动训练时容易找不到。
3.3 一条能跑通的训练命令与关键参数
假设用的是常见的 YOLO 训练框架,一条基础命令大致是这样:
yolo detect train \ data=/abs/path/to/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/tea \ name=exp1参数逐个说:model选 nano 版是因为 883 张属于小样本,大模型参数量多,过拟合风险高,nano 或 small 起步更稳。epochs=100是起点不是终点,配合patience=20做早停,验证指标 20 轮不涨就停,省时间。imgsz=640是通用默认,如果茶叶病害的斑点很小,可以试 800 或 960,但显存和速度要重新权衡。batch=16在单卡 8G 显存下一般能跑,爆显存就降到 8。lr0=0.01是常见初始学习率,小样本可以降到 0.005 更稳。project和name决定结果存哪,建议按实验编号命名,方便回溯。
训练启动后重点看三个东西:一是 loss 是否稳定下降,如果一开始就 nan,多半是学习率太大或标签有非法值(比如坐标超过 1);二是验证集的 mAP50 走势,小样本下前期波动正常,看趋势不看单点;三是每个类别的 AP,如果某类一直是 0,回去查这个类的标签是不是有问题。
3.4 训练前用三行代码做标签合法性体检
在正式训之前,我习惯先做一次标签体检,把坐标越界、空文件、类别越界这三类问题提前揪出来:
import os label_dir = "dataset/labels/train" for fn in os.listdir(label_dir): p = os.path.join(label_dir, fn) with open(p) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: print(f"字段数异常 {fn}:{i}") continue cls_id = int(parts[0]) vals = [float(x) for x in parts[1:]] if cls_id < 0 or cls_id > 7: print(f"类别越界 {fn}:{i} -> {cls_id}") if any(v < 0 or v > 1 for v in vals): print(f"坐标越界 {fn}:{i} -> {vals}")这段逻辑很直白:每行必须是 5 个字段,类别索引在 0 到 7 之间,四个坐标都在 0 到 1 之间。任何一条不满足,训练时要么报错要么静默学歪。坐标越界常见于转换时用了错误的图片尺寸,比如图片实际是 1280 宽但你按 640 归一化,算出来的值就会大于 1。空 txt 文件也要留意,它代表这张图没有目标,有些框架允许,有些会警告,最好确认一下是不是漏标。
4. 小样本 8 类别训练避坑:这些现象背后是什么
4.1 某个类别 AP 长期为 0:先查标签再查样本量
现象是训练几十轮后,7 个类别的 AP 都在涨,唯独某一类始终是 0。原因通常有两个:一是这个类的标签在 txt 里根本不存在,比如转换时类别名没匹配上被跳过了;二是这个类样本太少,比如只有十几张,模型还没学会就被早停截断。解决顺序是先统计这个类的实例数,如果为 0,回去查转换脚本的类别映射;如果只有个位数,先关掉早停多训几十轮,或者对这个类做针对性增强(旋转、裁剪、亮度扰动),再不行就在损失里给稀有类加权。别一上来就怀疑模型结构,小样本问题九成出在数据侧。
4.2 验证集 mAP 忽高忽低:切分方式比模型更影响结果
现象是同一份数据,换个随机种子重新切分,mAP 能差十几个点。原因是 883 张分 8 类,验证集本来就只有一百多张,某些类在验证集里可能只有两三张,一两个框预测对错就能让 AP 大幅跳动。解决办法是切分时做分层抽样,保证每个类在训练集和验证集里的比例接近,而不是纯随机切。如果某类总实例太少,考虑用交叉验证,把数据分成 K 折轮流做验证,最后看平均指标,比单次切分可信得多。这个坑不解决,你会一直在调参上浪费时间,其实问题在评估方式。
4.3 训练 loss 正常但预测框全偏:归一化尺寸用错了
现象是训练 loss 一路下降看着很健康,但推理时框的位置整体偏移或大小不对。原因多半是转换时归一化用的图片尺寸和实际不符,比如图片被预处理 resize 过,但标签还是按原图算的。另一种可能是训练时的 imgsz 和推理时的 imgsz 不一致,YOLO 内部会做 letterbox 缩放,如果两边缩放逻辑不同,框就会偏。解决方法是固定一套预处理流程:训练和推理用同一个 imgsz,标签归一化严格用原图宽高,推理后处理按同样的 letterbox 参数还原。这个坑的隐蔽性在于 loss 不报错,只有可视化预测结果才能发现。
4.4 显存爆了不一定是 batch 太大:先看 imgsz 和缓存
现象是 batch 设成 8 还是 OOM。原因可能是 imgsz 设太大,比如 1280 的输入,显存占用是 640 的四倍左右;也可能是开了数据缓存把整个数据集加载进内存或显存。解决顺序是先降 imgsz 到 640 试,再关掉 cache 相关选项,最后才降 batch。另外多尺度训练(如果开了)也会让显存峰值变高。小样本数据集本身不大,缓存带来的速度提升有限,关掉换稳定更划算。
4.5 类别名对不上导致预测结果全错位
现象是模型能出框,置信度也不低,但框上的类别名全是错的,比如把健康叶认成病斑。原因就是 data.yaml 的 names 顺序和 txt 里的类别索引不一致,或者推理时的类别映射表和训练时不同。这个坑最坑的地方在于训练指标看起来正常,因为模型学的是一套自洽的索引,只是你解读时用错了名字。解决办法是训练前把 data.yaml 的 names、转换脚本的 class_map、推理代码的类别列表三处打印出来逐行比对,确认完全一致再开训。我一般会把这份映射存成一个单独的 json,训练和推理都读它,从源头杜绝不一致。
5. 把 883 张用到极致:增强策略与效果验证的具体技巧
小样本数据集的胜负手不在模型,在你怎么把 883 张用出 2000 张的效果。我的习惯是先做一轮基线训练,不加任何增强,记录每个类的 AP,找出最弱的那两三个类,然后只对这些类做针对性增强,而不是全量乱加。茶叶病害的形态特点是斑点、变色、边缘枯萎,所以几何增强里旋转和缩放最有用,因为叶片在自然拍摄中角度和距离本来就多变;颜色增强里亮度和对比度扰动要适度,因为病害本身靠颜色区分,扰动太猛会把病斑特征抹掉。翻转可以用,但垂直翻转要谨慎,因为叶片正反面纹理不同,翻过来可能变成不真实的样本。
具体做法是在训练配置里开增强参数,常见的有degrees(旋转角度)、scale(缩放比例)、hsv_h/hsv_s/hsv_v(色调饱和度亮度扰动)、fliplr/flipud(水平垂直翻转)。我的起点是degrees=15、scale=0.5、hsv_v=0.4、fliplr=0.5、flipud=0.0,先跑一轮看弱类 AP 有没有起来,再微调。增强不是越多越好,加过头会让模型学到不真实的模式,验证集指标反而掉。
验证增强有没有用,不能只看总 mAP,要看弱类的 AP 变化和混淆矩阵。如果弱类 AP 涨了但强类掉了,说明增强伤到了强类特征,要收一收。如果所有类都涨,说明增强方向对。另一个验证手段是拿几张真实茶园照片做推理,肉眼看框和类别对不对,这比数字更直接。我一般会留 10 到 20 张完全不参与训练的图做最终目测,它们不进入任何评估指标,只用来做「人眼验收」。
还有一个容易被忽略的技巧:把 VOC 的 XML 利用起来做标签一致性抽查。随机抽 20 张图,把 XML 里的绝对坐标画成框,再把 YOLO txt 还原成绝对坐标画一遍,两套框叠在一起看是否重合。重合说明转换无损,不重合说明某一步算错了。这个检查花不了十分钟,但能避免你带着错误标签训一整天。
最后说投入判断:这份数据集的价值不在于它能训出多高的精度,而在于它让你用最低成本跑通「农业病害检测」的完整链路——格式转换、类别配置、小样本训练、增强调优、结果验证。跑通之后,你可以把这套流程平移到自己的茶园数据上,那才是真正产生价值的地方。我自己做农业视觉项目时,习惯是先拿一份小样本公开数据把 pipeline 跑顺,再换自己的数据,这样出问题时能快速定位是数据问题还是代码问题。希望帮到你。
本文还有配套的精品资源,点击获取