芒果害虫检测数据集实战:VOC与YOLO双格式标注解析与YOLOv8训练
简介本资源为芒果害虫检测数据集面向从事农业虫害识别、目标检测算法训练与课程实践的研究者及学生可解决芒果种植场景下多类别害虫图像样本不足的问题。数据集同时提供Pascal VOC与YOLO两种标注格式包含jpg图片及一一对应的xml、txt标注文件标注类别共10类涵盖Weevil、beetle、grasshopper、mango_hopper、mango_mealybug、moth、sawfly、slug、stem_borer、wasp等常见芒果害虫。压缩包为7z格式共约2000个文件以1999个xml标注文件和1个说明txt为主整体约191.04MB目录结构清晰便于直接接入检测框架进行训练与验证。目前已有223人学习下载适合需要快速构建虫害识别模型、开展对比实验或完成课程设计的读者参考使用。1. 芒果害虫检测数据集3575 张双格式标注10 类虫害一次说清做农业视觉检测的同行大概都有过这种经历模型结构调了一周最后卡在数据上——要么类别不齐要么标注格式对不上训练脚本。这次拆的这份芒果害虫检测数据集就是冲着这个痛点来的。它包含 3575 张 jpg 图片每张图都配了 Pascal VOC 格式的 xml 和 YOLO 格式的 txt标注类别 10 类覆盖象甲、甲虫、蝗虫、芒果叶蝉、芒果粉蚧、蛾、叶蜂、蛞蝓、茎螟、黄蜂这些芒果园里常见的害虫。换句话说拿到手就能直接喂给 YOLO 系列训练也能用 VOC 工具链做二次清洗。适合谁做果园虫情监测的算法同学、想练手目标检测的在校生、以及需要快速搭一个农业检测原型的工程师。下面从格式、目录、转换、训练、避坑一路拆到验证技巧尽量把能抄的作业都写出来。2. 双格式标注拆解VOC 与 YOLO 到底怎么对应2.1 为什么同一批图要存两份标注先讲清楚一个容易混淆的点这份数据集里 xml 和 txt 是同一批标注的两种表达不是两套独立标注。VOC 的 xml 用绝对像素坐标记录xmin/ymin/xmax/ymax而 YOLO 的 txt 用归一化后的class_id x_center y_center width height数值都在 0 到 1 之间。之所以两份都留着是因为不同训练框架吃的东西不一样YOLOv5/v8 官方仓库默认读 txt而很多老一些的检测代码、可视化脚本、标注复核工具仍然认 VOC。你如果只留一份换框架时就得自己写转换反而多一道出错环节。从项目正文能看到文件命名规律比如firc_mangopets_74.xml、firc_mangopets_1816.xml前缀统一是firc_mangopets_后面跟数字编号。这个编号就是图片和标注的对应键——firc_mangopets_74.jpg对应firc_mangopets_74.xml和firc_mangopets_74.txt。实操里最怕的就是图片和标注对不上号所以拿到数据集第一件事不是急着训练而是先做一次配对校验。2.2 目录结构与文件清单核对解压后典型的结构是这样不同打包方式可能略有差异以实际为准mango_pests/ ├── 使用前必读.txt ├── JPEGImages/ # 3575 张 jpg │ ├── firc_mangopets_74.jpg │ └── ... ├── Annotations/ # 3575 个 VOC xml │ ├── firc_mangopets_74.xml │ └── ... └── labels/ # 3575 个 YOLO txt ├── firc_mangopets_74.txt └── ...先用一条命令核对三份文件数量是否一致这是最省事的体检# 分别统计 jpg / xml / txt 数量三个数字必须相等 find . -name *.jpg | wc -l find . -name *.xml | wc -l find . -name *.txt | wc -l如果三个数字都是 3575说明文件没丢。要是某个数字对不上八成是解压时文件名编码出问题或者压缩包本身有损坏重新解压一次通常能解决。这一步花不了一分钟但能挡掉后面一堆莫名其妙的报错。2.3 类别名与 class_id 的映射关系10 个类别名是英文的Weevil、beetle、grasshopper、mango_hopper、mango_mealybug、moth、sawfly、slug、stem_borer、wasp。YOLO 的 txt 里存的是数字 id所以你必须先确定 id 到名字的映射顺序。常见做法是建一个classes.txt一行一个类别名行号从 0 开始就是 class_idWeevil beetle grasshopper mango_hopper mango_mealybug moth sawfly slug stem_borer wasp这里有个血泪经验映射顺序一旦定了就不能改。我见过有人训练时用一套顺序推理时又按字母序重排结果模型把beetle认成Weevil排查了半天才发现是类别表错位。建议把classes.txt和数据一起版本管理训练、验证、部署全程共用同一份。2.4 用脚本验证标注合法性在正式训练前写个小脚本扫一遍所有 txt检查有没有越界坐标、空标注、类别 id 超范围的情况。这类脏数据不查出来训练时 loss 会莫名其妙地抖。import os # 类别总数和 classes.txt 行数保持一致 NUM_CLASSES 10 label_dir labels bad_files [] for name in os.listdir(label_dir): if not name.endswith(.txt): continue path os.path.join(label_dir, name) with open(path) as f: lines [l.strip() for l in f if l.strip()] # 空标注文件图片里没有目标YOLO 允许但值得记录 if not lines: bad_files.append((name, empty)) continue for line in lines: parts line.split() # YOLO 每行必须是 5 个字段 if len(parts) ! 5: bad_files.append((name, field_count)) break cid int(parts[0]) coords [float(x) for x in parts[1:]] # 类别 id 越界 或 归一化坐标不在 [0,1] if cid 0 or cid NUM_CLASSES or any(c 0 or c 1 for c in coords): bad_files.append((name, range)) break print(f可疑文件数: {len(bad_files)}) for f in bad_files[:20]: print(f)逻辑说明逐行读每个 txt先判字段数是否为 5再判类别 id 是否落在[0, NUM_CLASSES)最后判四个归一化坐标是否都在 0 到 1 之间。参数上NUM_CLASSES必须和你的类别表一致写错这个数会把正常文件误判。跑完如果可疑文件是 0就可以放心进入下一步如果有先人工看几张判断是标注错误还是脚本判据太严。3. 从 VOC 到 YOLO转换脚本与坐标换算细节3.1 VOC 与 YOLO 坐标的换算公式虽然这份数据集已经给了 YOLO txt但你还是得懂换算因为一旦你要自己增补标注、或者拿 VOC 工具改完再转回来就得手动算。VOC 给的是左上角(xmin, ymin)和右下角(xmax, ymax)图片宽W、高H。YOLO 要的是中心点和宽高全部归一化x_center (xmin xmax) / 2 / W y_center (ymin ymax) / 2 / H width (xmax - xmin) / W height (ymax - ymin) / H反过来从 YOLO 还原 VOCxmin (x_center - width / 2) * W ymin (y_center - height / 2) * H xmax (x_center width / 2) * W ymax (y_center height / 2) * H坑就在W和H上必须用当前这张图的真实尺寸不能拿一个固定值套所有图。芒果害虫数据集里图片尺寸不一定统一用错尺寸会导致框整体偏移。3.2 批量转换脚本VOC 转 YOLO下面这个脚本把 Annotations 里的 xml 批量转成 YOLO txt同时生成classes.txt。它用xml.etree解析不依赖额外库。import os import xml.etree.ElementTree as ET from PIL import Image # 类别顺序决定 class_id务必和训练时一致 CLASSES [Weevil, beetle, grasshopper, mango_hopper, mango_mealybug, moth, sawfly, slug, stem_borer, wasp] cls_to_id {c: i for i, c in enumerate(CLASSES)} xml_dir Annotations img_dir JPEGImages out_dir labels_from_voc os.makedirs(out_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue stem os.path.splitext(xml_name)[0] tree ET.parse(os.path.join(xml_dir, xml_name)) root tree.getroot() # 用图片真实尺寸做归一化缺图就跳过并记录 img_path os.path.join(img_dir, stem .jpg) if not os.path.exists(img_path): print(缺图:, stem) continue W, H Image.open(img_path).size lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in cls_to_id: print(未知类别:, name, in, xml_name) continue 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) xc (xmin xmax) / 2 / W yc (ymin ymax) / 2 / H bw (xmax - xmin) / W bh (ymax - ymin) / H # 坐标裁剪到 [0,1]防止个别越界标注污染训练 xc, yc min(max(xc, 0), 1), min(max(yc, 0), 1) bw, bh min(max(bw, 0), 1), min(max(bh, 0), 1) lines.append(f{cls_to_id[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines))逻辑说明先按CLASSES建 id 映射遍历每个 xml用对应 jpg 的真实宽高做归一化再按公式算中心点和宽高。参数上CLASSES顺序就是 class_id 顺序改它等于改标签含义坐标裁剪那两行是保险防止个别标注框超出图片边界导致训练异常。跑完把labels_from_voc和数据集自带的labels对比一下如果内容基本一致说明自带 txt 是可信的。3.3 划分训练集与验证集YOLO 训练需要train.txt和val.txt两个列表文件每行是图片路径。按 8:2 划分import os import random img_dir JPEGImages imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.seed(42) # 固定种子保证每次划分一致 random.shuffle(imgs) split int(len(imgs) * 0.8) train, val imgs[:split], imgs[split:] for name, subset in [(train.txt, train), (val.txt, val)]: with open(name, w) as f: for im in subset: f.write(os.path.join(img_dir, im) \n) print(train:, len(train), val:, len(val))逻辑说明random.seed(42)是关键固定种子后每次跑划分结果都一样方便复现实验。参数上0.8 是训练比例数据量 3575 张时验证集约 715 张够评估用。如果某类样本特别少建议改成按类别分层抽样避免验证集里缺类。4. 训练落地YOLOv8 配置与参数怎么设4.1 数据集 yaml 配置YOLOv8 用一份 yaml 描述数据路径和类别。在项目根目录建mango.yamlpath: /abs/path/to/mango_pests # 数据集根目录写绝对路径最稳 train: train.txt val: val.txt nc: 10 names: 0: Weevil 1: beetle 2: grasshopper 3: mango_hopper 4: mango_mealybug 5: moth 6: sawfly 7: slug 8: stem_borer 9: wasp参数说明path用绝对路径能避免相对路径在不同工作目录下失效nc必须等于类别数 10names的键值顺序必须和 txt 里的 class_id 严格对应。这三处任意一处错位训练都不会报错但模型学出来的类别是乱的属于最隐蔽的坑。4.2 启动训练与关键参数yolo detect train \ datamango.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/mango \ nameexp1逐项说modelyolov8n.pt是 nano 版预训练权重3575 张图这个量级用 n 或 s 版就够上大模型容易过拟合imgsz640是常见输入尺寸显存吃紧可以降到 512batch16按显存调8G 显存跑 640 一般能到 16lr00.01是初始学习率微调预训练模型时这个值比较稳patience20表示 20 轮没提升就早停省时间。如果训练中 loss 一直不降先别怀疑模型回头查类别映射和标注质量。4.3 训练过程该盯哪些指标训练日志里重点看三个box_loss、cls_loss、mAP50。box_loss管定位cls_loss管分类两者都应该整体下降。mAP50是 IoU 阈值 0.5 下的平均精度是判断模型好坏的直接指标。如果box_loss降但mAP50不涨通常是标注框质量差如果cls_loss居高不下多半是类别不平衡或映射错位。3575 张 10 类平均每类 350 张左右属于中等偏少训练时建议开数据增强YOLOv8 默认已开 mosaic、翻转等。4.4 推理与结果核对训练完用验证集跑一次推理肉眼核对几张yolo detect predict \ modelruns/mango/exp1/weights/best.pt \ sourceJPEGImages \ conf0.25 \ saveTrueconf0.25是置信度阈值低于它的框不显示。核对时重点看两类错误把mango_hopper和grasshopper搞混这两类形态接近是这份数据集的难点以及小目标漏检mango_mealybug通常很小。如果混淆严重可以考虑在类别名上做更细的区分或者补充这两类的样本。5. 避坑与排查标注、映射、训练里最容易翻车的地方5.1 图片与标注对不上号现象训练时提示找不到某张图的标注或者 loss 异常高。原因jpg、xml、txt 三者文件名前缀不一致常见于解压时文件名被截断或编码转换。解决跑一遍配对校验用集合运算找出缺失项import os stems lambda d, ext: {os.path.splitext(f)[0] for f in os.listdir(d) if f.endswith(ext)} img stems(JPEGImages, .jpg) xml stems(Annotations, .xml) txt stems(labels, .txt) print(缺xml:, img - xml) print(缺txt:, img - txt) print(多余xml:, xml - img)三个差集都为空才算干净。5.2 类别 id 与名字错位现象模型能检测到目标但类别名全是错的或者某类永远不出现。原因classes.txt、yaml 里的names、txt 里的 id 三者顺序不一致。解决以 txt 里的 id 为准反查每个 id 对应的名字确认三处一致。最稳的做法是只维护一份classes.txtyaml 和转换脚本都从它读。5.3 空标注文件被误删现象某些图片明明有虫模型却学不会。原因这些图的 txt 是空的标注时漏标有人清理数据时把空 txt 当垃圾删了导致图片没有对应标注而被跳过。解决空 txt 要保留它表示这张图无目标是负样本对降低误检有用。删了反而让模型没见过背景。5.4 坐标越界导致训练崩溃现象训练几轮后 loss 变成 nan。原因个别标注框的归一化坐标超出 [0,1]比如xmax大于图片宽度。解决用 2.4 的校验脚本扫一遍或者转换时统一做坐标裁剪3.2 脚本里已经加了。越界框不裁剪梯度会爆。5.5 验证集类别缺失现象mAP50波动大某些类评估结果为空。原因随机划分时某类样本全落进训练集验证集里一个都没有。解决改用分层抽样保证每个类别在验证集里都有一定数量。样本少的类尤其要注意必要时对这类做过采样。6. 进阶技巧用混淆矩阵定位难分类类别训练跑通只是第一步真正决定模型能不能上线的是对错误的分析。YOLOv8 训练结束会自动生成混淆矩阵confusion_matrix.png这张图比单看 mAP 有用得多。矩阵对角线是分对的非对角线就是混淆。拿这份芒果害虫数据集来说我一般会重点看mango_hopper和grasshopper那一格——这两类都是跳跃类昆虫形态接近混淆概率高。如果这一格数值明显说明模型没学到区分特征可以考虑三个方向一是补充这两类的难例样本二是检查标注时有没有把两者标混三是适当提高输入分辨率让小差异更容易被看到。另一个实用技巧是按类别统计漏检和误检。YOLOv8 的验证输出里每个类都有 precision 和 recall把 recall 低的类挑出来单独看。mango_mealybug这类小目标如果 recall 偏低通常是输入尺寸不够把imgsz从 640 提到 800 往往有改善代价是显存和速度。下面这段代码可以快速把每个类的指标拉出来排序# 读取 YOLOv8 验证结果按 recall 升序排列优先处理差类 import pandas as pd # results.csv 在训练输出目录下列名含 metrics/recall(B) 等 df pd.read_csv(runs/mango/exp1/results.csv) df.columns [c.strip() for c in df.columns] # 取最后一轮各类指标需结合 val 输出的 per-class 结果此处示意排序思路 print(df.tail(1).T.sort_values(bydf.tail(1).index[-1]))逻辑说明这段是示意如何从训练日志里定位薄弱类别实际 per-class 指标建议直接看验证时打印的表格。参数上重点盯 recall 而不是 precision——农业检测里漏检一只虫的代价通常比误报高宁可多报也别漏报。还有一个容易被忽略的点验证集和训练集的分布要一致。如果训练集里某类都是近距离大目标验证集里却是远景小目标mAP 会虚低。划分数据时按拍摄场景分层比纯随机更靠谱。我现在的习惯是每次拿到新数据集先花十分钟跑配对校验和标注合法性检查再花五分钟看一眼类别分布确认没问题才开训。这个流程帮我挡掉过好几次训练半天发现数据是坏的的翻车。从那以后我每次开训前都强制走一遍这套检查希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →