土豆病害检测数据集实战:VOC转YOLO、标签校验与类别不平衡处理

发布时间:2026/10/28 10:35:05
土豆病害检测数据集实战:VOC转YOLO、标签校验与类别不平衡处理 简介这份文档面向农业计算机视觉研究与土豆病害识别开发者围绕8908张图像、5类土豆状态构建的VOC与YOLO双格式检测数据集展开说明可用于早期病害筛查、品质分级等目标检测模型训练场景。压缩包共1个docx文件约3.71MB以文字形式汇总数据集结构、类别定义、标注规范与使用注意事项便于在正式训练前评估数据是否匹配任务需求。文档列出正常土豆、损伤、缺陷、真菌性病害与萌芽五类合计30854个矩形标注框其中正常土豆9868框、真菌性病害7674框并交代jpg、xml、txt三类文件的对应关系及labelImg标注流程附图片预览与标注示例帮助读者核对类别分布、标注粒度与样本覆盖情况。已有68人学习适合需要快速了解该数据集构成、增强情况与适用边界的研究者和工程人员参考。1. 土豆病害检测数据集8908 张图、30854 个框先看清它的边界拿到一份农业视觉数据集我第一件事不是跑训练而是翻标注。这份土豆病害检测数据集标称 8908 张 jpg、8908 个 xml、8908 个 txtVOC 与 YOLO 双格式并存5 个类别合计 30854 个矩形框。数字看着漂亮但真正决定它能不能用的是类别定义和框的分布——这决定了你训出来的模型是能分病害还是只会认“这是个土豆”。它适合三类人做农业视觉课题需要现成交叉格式数据的研究者、想在 YOLO 系模型上快速验证小目标密集检测的工程师、以及要交付一个能跑通的病害识别 Demo 的开发者。不适合指望“下载即高精度”的人数据集本身明确不保证精度这一点必须提前摆到桌面上。2. 五个类别与框数分布先算清这份数据集的账2.1 类别定义决定任务难度先把五个类别摊开看。Damaged potato机械损伤、Defected potato外观缺陷、Diseased-fungal potato真菌性病害、Potato正常、Sprouted potato发芽。这五类里前三类属于“病害/损伤”第四类是负样本基准第五类是储藏期问题。注意这是一个目标检测任务每张图上可能同时出现多个类别的土豆模型输出的是框和类别不是整图分类标签。类别名中文含义框数占比Potato正常土豆986831.98%Diseased-fungal potato真菌性病害767424.87%Damaged potato机械损伤485015.72%Sprouted potato发芽450914.61%Defected potato外观缺陷395312.81%合计—30854100%平均每张图 3.46 个框说明多数图是多目标场景不是单目标抠图。这种密度对 YOLO 系列很友好但也意味着小目标比例可能不低——土豆在画面里占比小的时候框会非常密。选型理由如果你的任务只需要区分“正常/异常”五类合二为一即可直接改 data.yaml 的 nc2不用重标。如果要细分病害类型就必须接受 Defected potato 只有 3953 个框这个事实它是弱类后文会讲怎么补。2.2 单类框数与图片数的关系数据集没有给“每类出现在多少张图里”只有框数。这个差别很关键9868 个正常土豆框可能集中在 4000 张图上也可能散在 8000 张图上。你要自己统计方法在 3.2 节给脚本。我一般会先跑一遍分布如果某类图片覆盖率低于 15%训练时就要考虑过采样。提示数据集说明里提到“有增强”但没给增强前后的对照。拿到手先看图片尺寸是否统一、是否存在明显翻转/裁剪痕迹这直接影响你划分验证集的方式——如果增强图与原图混在一起随机划分会导致验证集泄漏。2.3 格式说明与不含内容VOC 侧给的是 jpgxmlYOLO 侧是 jpgtxt不包含分割路径的 txt 文件也没有 ImageSets/Main 那套划分文件。也就是说train/val 的切分要自己做别指望解压就有现成的 train.txt。标注工具是 labelImg规则是矩形框没有多边形、没有关键点。这一点要在项目立项时就写进假设否则后面写 dataloader 会因为找不到分割文件报错。3. 从 VOC 到 YOLO目录结构、转换校验与训练接入3.1 目录怎么摆先立规矩拿到压缩包解压后常见结构是图片一个目录、xml 一个目录、txt 一个目录。我一般会重排成标准 YOLO 结构避免训练脚本里到处写绝对路径# 建立标准结构名称按项目自定 mkdir -p potato/{images,labels} # 图片统一放 imagestxt 统一放 labels文件名 stem 必须一致 # 假设原始目录为 raw/jpg 与 raw/txt for f in raw/jpg/*.jpg; do base$(basename $f .jpg) cp $f potato/images/$base.jpg cp raw/txt/$base.txt potato/labels/$base.txt done逻辑说明YOLO 系训练器靠“图片路径替换 images 为 labels、后缀换 txt”来找标签stem 一旦对不上就静默丢样本这是最常见的坑。参数说明目录名 images/labels 是约定不用改basename去掉后缀保留 stem是保证对齐的关键。3.2 校验脚本先证明标签没坏在训练前跑一遍完整性检查比训到一半发现 loss 不降要省时间得多。下面这段扫描标签目录检查类别 id 范围、坐标归一化、空文件与孤儿标签import os from pathlib import Path img_dir Path(potato/images) lbl_dir Path(potato/labels) NC 5 # 类别数与 data.yaml 一致 bad_cls, bad_coord, empty, orphan [], [], [], [] for lbl in lbl_dir.glob(*.txt): stem lbl.stem if not (img_dir / f{stem}.jpg).exists(): orphan.append(stem) # 有标签没图片 continue lines lbl.read_text().strip().splitlines() if not lines: empty.append(stem) # 空标签可能是纯背景图 continue for ln in lines: p ln.split() if len(p) ! 5: bad_coord.append((stem, ln)); continue cid int(p[0]) if cid 0 or cid NC: bad_cls.append((stem, cid)) xs list(map(float, p[1:])) if any(v 0 or v 1 for v in xs): bad_coord.append((stem, ln)) print(orphan:, len(orphan), empty:, len(empty)) print(bad_cls:, len(bad_cls), bad_coord:, len(bad_coord))逻辑说明先查图片存在性再查每行字段数与类别 id 合法性最后查坐标是否落在 0~1。参数说明NC5必须和后面 data.yaml 的 nc 对齐空标签文件不是错误纯背景图在检测任务里合法但要统计数量占比过高说明数据里有大量无目标图。跑完你会得到几个数孤儿标签数、空标签数、越界坐标数。孤儿标签直接删或补图越界坐标通常是标注时拖出了边界可以裁剪到 [0,1] 而不是丢弃整张。3.3 data.yaml 与训练命令# potato.yaml path: /abs/path/potato train: images/train val: images/val names: 0: Damaged potato 1: Defected potato 2: Diseased-fungal potato 3: Potato 4: Sprouted potato注意类别 id 的映射顺序必须与 txt 里的数字严格一致。这份数据集只给了类别名列表没给 id 映射转换脚本按列表顺序 0~4 写入是最常见做法如果你手上的 txt 是别人转的务必用上一节脚本抽样打印几行对照 xml 里的 name 反查别默认它一定对。划分训练/验证集时按 8:2 或 7:3按图片 stem 划分绝不要按行划分否则同一张图的不同框会同时进训练和验证指标虚高。抽样命令# 打乱后切分stem 级别 ls potato/images/*.jpg | shuf --random-source(yes) all.txt head -n $(( $(wc -l all.txt) * 8 / 10 )) all.txt train.txt tail -n $(( $(wc -l all.txt) * 8 / 10 1 )) all.txt val.txt然后启动训练YOLOv8 系常见写法yolo detect train datapotato.yaml modelyolov8s.pt epochs100 imgsz640 batch16参数说明imgsz640是默认如果原图分辨率高且土豆在图中占比小可以上到 960 甚至 1280但显存要跟上batch按显存调24G 卡上 640 分辨率跑 s 模型 16 一般没问题。训练日志里重点看每类的 mAP50别只看总 mAP。4. 类别不平衡与增强陷阱这份数据集的三个暗坑4.1 弱类不是靠调学习率救的Defected potato 3953 个框对比正常类 9868比例约 1:2.5还不算极端失衡但在多类检测里足够让弱类召回上不去。我一般不会一上来就改 loss 权重而是先做两件事一是统计弱类在多少张图里出现二是看弱类的框尺寸分布。如果弱类框普遍偏小问题在分辨率而不在采样。补法上优先用按类过采样复制含弱类的图片进训练集同时复制对应标签文件名加后缀避免覆盖。不要对验证集做任何过采样否则指标不可信。import shutil, random from pathlib import Path lbl_dir Path(potato/labels) img_dir Path(potato/images) WEAK {1} # Defected potato 的类别 id targets [] for lbl in lbl_dir.glob(*.txt): for ln in lbl.read_text().splitlines(): if ln and int(ln.split()[0]) in WEAK: targets.append(lbl.stem); break for stem in targets: for i in range(2): # 复制两份按需调 shutil.copy(img_dir / f{stem}.jpg, img_dir / f{stem}_os{i}.jpg) shutil.copy(lbl_dir / f{stem}.txt, lbl_dir / f{stem}_os{i}.txt)逻辑说明按 stem 找出含弱类的图复制图片和标签stem 加后缀保证一一对应。参数说明WEAK集合按你实际的类别 id 填复制份数从 1~2 起步过量会让模型过拟合这批图。4.2 “有增强”是个黑匣子数据集说明写了“有增强”但没写增强方式。这带来两个风险一是增强图可能和原图高度相似划分时若随机打散验证集里会出现训练图的近邻指标虚高二是增强可能改变了目标形态比如过度旋转导致框失效但标签没重算。应对方式训练前抽样 50 张看图重点看是否有同一场景的近似重复如果有划分验证集时按“场景/来源”分组而不是纯随机。这一步没有脚本能替你做只能人眼看。注意不要因为数据集声称已增强就关掉训练时的在线增强。在线增强是每个 epoch 随机的和离线增强不冲突关掉反而容易过拟合。4.3 五个类别的语义重叠Damaged机械损伤和 Defected外观缺陷在像素层面经常难分标注者自己也未必一致。这两类混淆会直接拉低 mAP。我的做法是先训一版导出混淆矩阵如果这两类互错率超过 30%就考虑合并成一个“异常”类再训一版做对比而不是硬调阈值。yolo detect val modelruns/detect/train/weights/best.pt datapotato.yaml plotsTrue参数说明plotsTrue会输出混淆矩阵和 PR 曲线到 runs 目录重点看对角线之外的块。如果某一类的 PR 曲线在低召回区就掉下去说明该类样本不足或标注噪声大。5. 避坑与排查五条踩过的记录现象一训练时提示找不到标签loss 一直是 nan 或 0。原因图片和标签的 stem 不一致比如图片是1.jpg、标签是1.txt但中间多了空格或大小写差异训练器静默跳过。解决跑 3.2 的校验脚本把 orphan 列表打印出来逐个对批量修名用rename统一小写。现象二训练正常但验证 mAP 高得离谱接近 0.99。原因增强图与原图混在一起后随机划分验证集泄漏。解决按场景分组划分或先用图片哈希去重再切分。现象三Defected potato 的召回始终在 0.3 左右上不去。原因弱类样本少且框偏小640 分辨率下特征被压缩。解决先过采样补到 6000 框量级再把 imgsz 提到 960 试一版两者叠加通常能拉到 0.5 以上仍不行就检查该类标注是否有大量漏标。现象四转换后坐标越界训练报 “non-normalized coordinates”。原因labelImg 标注时框拖出图像边界xml 里 xmax 大于图宽。解决转换脚本里对坐标做 clamp 到 [0,1]而不是整图丢弃同时统计越界比例超过 5% 说明标注流程有问题。现象五模型在验证集上表现好换一批实拍图就崩。原因数据集场景单一同背景、同光照域偏移大。解决训练时开 mosaic、HSV 抖动、随机缩放验证时另留一组不同来源的图做域外测试别只信同分布验证集。6. 把五类合成两类的迁移技巧先跑通再细分很多人拿到这份数据集就想直接训五类结果卡在弱类上反复调参。我的习惯是先做一次类别坍缩实验把 Damaged、Defected、Diseased-fungal、Sprouted 合并为“异常”Potato 保留为“正常”得到一个二分类检测任务。这个版本几乎一定能跑出可用的基线用来验证数据管线、增强策略、分辨率设置是否正确。# 五类转二类0 正常1 异常 import shutil from pathlib import Path src_lbl, dst_lbl Path(potato/labels), Path(potato/labels_bin) dst_lbl.mkdir(exist_okTrue) for lbl in src_lbl.glob(*.txt): out [] for ln in lbl.read_text().splitlines(): if not ln: continue p ln.split() cid 0 if int(p[0]) 3 else 1 # 3 是 Potato out.append( .join([str(cid)] p[1:])) (dst_lbl / lbl.name).write_text(\n.join(out))逻辑说明只改类别 id坐标原样保留图片目录复用。参数说明3是正常类的 id按你实际映射改生成新标签目录后data.yaml 的nc: 2、names改成两项训练命令不变。基线跑通后记录下 mAP50 和每类召回再切回五类做对比。如果五类版本的异常类召回明显低于二类版本的异常召回说明细分带来的类间混淆是主要瓶颈这时候再去考虑合并语义相近的类或者补标注而不是盲目加 epoch。验证方法上我一般会固定三件事固定随机种子、固定验证集划分文件、固定 imgsz。每次只改一个变量把结果记在表格里避免“调了一堆参数不知道哪个起作用”。导出模型后拿十张完全没在数据集里出现过的实拍图做人工过一遍看漏检是集中在某一类还是均匀分布——集中说明该类样本不足均匀说明分辨率或增强不够。从那以后我每次拿到这类农业检测数据集都强制先跑一遍标签校验、再做一次类别坍缩基线两个数对上了才敢往五类上堆训练时长。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询