ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

芒果害虫检测数据集:VOC+YOLO双格式与YOLOv8训练避坑指南

芒果害虫检测数据集:VOC+YOLO双格式与YOLOv8训练避坑指南 简介芒果害虫检测数据集面向目标检测学习者与农业AI研究者提供3575张图像及对应的VOC与YOLO双格式标注包含10个常见农业害虫类别可有效弥补芒果园害虫检测中标注数据不足的缺口。类别具体涵盖象鼻虫、甲虫、蝗虫、芒果叶蝉、芒果粉蚧、蛾、锯蜂、蛞蝓、蛀茎虫、黄蜂等图像场景贴近果园实况适合训练和评估精细多类目标检测模型。压缩包内文件总数为2000个以1999个XML标注文件为主体并附有1个TXT说明文档整体体积约191.04MBXML与TXT标签文件共同提供目标框和类别信息数据组织规范且带有使用前说明便于快速接入YOLO、Faster R-CNN等主流检测框架免去格式转换与数据清洗的额外工作。目前已有224人学习使用对需要标准化害虫检测语料的研究者或模型开发者而言是一份可直接落地的数据资产可服务于虫害早期预警、精准施药等智慧农业场景。1. 芒果害虫检测数据集一份能直接开工的VOCYOLO双格式数据果园里装了几个虫情测报灯一周拍出几千张照片靠人蹲在电脑前一眼一眼找蓟马和果蝇两三个小时就眼花。这是很多植保团队想做自动识虫时面对的第一道坎——检测算法不缺缺的是带标签的芒果害虫数据。这份3575张、10个类别的数据集同时给了VOC和YOLO两种标注格式解压开就能直接喂给YOLOv8或Faster R-CNN跑训练省掉了最耗人力的数据整理环节。适合三类人正在做毕业设计的本科生接了果园智能监测项目的算法工程师以及想验证“虫情测报灯目标检测”方案是否值得投入的植保系统集成商。但拿到手之后先别急着跑train有一个细节得先确认否则后面全白做。2. 从7z到可训练目录文件结构、10个类别与两套标注格式2.1 压缩包内部长什么样目录树与文件清单解压之后典型的目录结构是这样MangoPest3575/ ├── VOC/ │ ├── Annotations/ # 3575个XML标注文件 │ ├── JPEGImages/ # 3575张原图JPG │ └── ImageSets/Main/ # train.txt / val.txt / trainval.txt └── YOLO/ ├── images/ # 与原图一一对应的JPG ├── labels/ # 每张图一个TXT类别归一化框坐标 └── classes.txt # 类别清单10个名称按索引排列这里有个容易看花眼的地方VOC目录和YOLO目录下各有一份图片。VOC那边的JPEGImages是给labelImg这类标注工具看的YOLO这边的images是给训练脚本直接读的两张图内容一样但文件名可能带不同前缀。用之前先核对一下两个目录的图片数量差一张都要查别指望“反正数据集打包好了”就跳过。压缩包装的是什么格式也影响你用什么工具解压。7z不是zipWindows自带的资源管理器解不了需要装7-Zip或者Bandizip。Linux上如果没装p7zipunzip命令也会直接报错先跑一句sudo apt install p7zip-full再7z x MangoPest3575.7z。这是整个流程里最不起眼但最常见的翻车点。文件清单不用记关键是理解这4类文件的职责XML存的是“框的绝对像素坐标类别名”txt存的是“归一化坐标类别编号”classes.txt定义编号和名称的映射关系ImageSets/Main下的txt定义训练集和验证集怎么切。搞清楚这四者的关系后面改格式、调训练集都不会慌。2.2 10个类别是谁易混淆项比想象中多这套数据集的10个类别具体名单以压缩包里的classes.txt为准。按芒果种植里最常见的情况一般会覆盖蓟马、蚜虫、叶蝉、果蝇、象甲、介壳虫、粉虱、天牛、螨类、毒蛾这十类。如果只按中文名去认容易在标注质量检查时踩坑——同一只虫不同虫态、不同龄期、不同拍摄角度外观差异极大。比如介壳虫的若虫和成虫一个是移动的小白点一个是固定在枝条上的褐色壳状物人眼都觉得不像同一类模型更分不清。常见类别拍摄特征最容易混淆的对象蓟马体型极小2mm上下常聚集花穗蚜虫蚜虫群集嫩梢和叶背黄绿或黑色蓟马叶蝉体长5mm左右翅膀半透明飞虱、蝽类若虫果蝇红眼明显多在果实表面其他小型蝇类介壳虫固定在枝干壳状或粉状枝干病斑、虫瘿毒蛾幼虫有毛簇体长较大尺蠖、其他蛾类幼虫这份表不是让读者背的而是检查标注时的对照表如果训练完发现A类大量被识别成B类先别怀疑模型结构回头查这两类在标注数据上到底有多大区分度。这类数据集里标注边界画得含糊的地方通常就是后期混淆矩阵里最脏的那几格。2.3 VOC与YOLO两套标注编号一致才是核心VOC的XML里bbox记录的是绝对像素坐标单位是像素坐标原点在图片左上角。YOLO的txt里记录的是归一化相对坐标所有数值都在0到1之间。两者唯一的桥梁是classes.txt里类别名和编号的对应关系这个对应一旦错位训练出来的模型就是灾难。看一段XML的结构annotation folderJPEGImages/folder filenamemango_thrips_001.jpg/filename object namethrips/name bndbox xmin142/xmin ymin85/ymin xmax158/xmax ymax97/ymax /bndbox /object /annotation这段XML里width和height在 节点里但标注文件经常不写全。用的时候要自己补上因为YOLO格式必须用图片宽高做归一化。我一般会先跑一段脚本把每张XML里的filename和对应图片实际文件名做个比对发现对不上就结束——不然后面转换脚本做映射时返回一堆“FileNotFoundError”看着像代码问题其实是数据问题。YOLO的txt每一行是class_id x_center y_center width height比如0 0.5050 0.3510 0.0450 0.0200表示第0类中心点在图片50.5%宽度、35.1%高度处框宽4.5%、高2%。蓟马这类小目标在3575张里很常见对应的数值就很小转换后变成0.0000也不是没可能。这也是下一个章节要重点处理的问题。3. 把VOC转成YOLO一份可复用脚本与四个边界坑3.1 为什么仍然需要自己转一次数据集明明给了VOC和YOLO两套格式为什么还要自己动手转三个原因。第一做消融实验时不少工具只认VOC比如mmdetection的默认配置或者只认YOLO格式那你需要的是能在两个方向来回切换的能力而不只是依赖别人给好的那一份。第二原始划分的train/val比例未必符合你的场景比如验证集只有200张样本太少你希望自己重新切。第三数据里几乎必然存在标注质量问题——越界框、空标注文件、损坏XML——别人的转换流程不会帮你过滤这些自己跑一遍转换等于做了一次全量体检。3.2 转换脚本核心逻辑与参数说明import os import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(voc_dir, yolo_dir, img_dir, classes_file): # 读取类别清单类别在classes.txt中的行号就是YOLO的class_id classes [c.strip() for c in classes_file.read_text().splitlines() if c.strip()] class_to_id {name: idx for idx, name in enumerate(classes)} xml_dir Path(voc_dir) / Annotations for xml_path in sorted(xml_dir.glob(*.xml)): tree ET.parse(xml_path) root tree.getroot() # 读取实际图片宽高XML里的size经常和真实图不一致 img_path Path(img_dir) / f{xml_path.stem}.jpg if not img_path.exists(): print(f[WARN] missing image: {img_path.name}) continue with open(img_path, rb) as f: from PIL import Image import io img Image.open(io.BytesIO(f.read())) w, h img.size out_lines [] for obj in root.findall(object): name obj.findtext(name) if name not in class_to_id: print(f[WARN] unknown label {name} in {xml_path.name}) continue class_id class_to_id[name] bndbox obj.find(bndbox) xmin float(bndbox.findtext(xmin)) ymin float(bndbox.findtext(ymin)) xmax float(bndbox.findtext(xmax)) ymax float(bndbox.findtext(ymax)) # 越界框防护这里只记录不做裁剪后面统一处理 if xmax xmin or ymax ymin: print(f[WARN] invalid bbox in {xml_path.name}: {name} {xmin},{ymin},{xmax},{ymax}) continue x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h # clamp到0~1防止坐标越界导致训练崩溃 x_center max(0.0, min(1.0, x_center)) y_center max(0.0, min(1.0, y_center)) box_w max(0.0, min(1.0, box_w)) box_h max(0.0, min(1.0, box_h)) out_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) # 写入labels目录文件名与图片同名 label_path Path(yolo_dir) / labels / f{xml_path.stem}.txt label_path.write_text(\n.join(out_lines) \n if out_lines else ) print(done) # 使用示例 convert_voc_to_yolo( voc_dirdata/MangoPest3575/VOC, yolo_dirdata/MangoPest3575/YOLO, img_dirdata/MangoPest3575/VOC/JPEGImages, classes_filePath(data/MangoPest3575/YOLO/classes.txt) )这段脚本的核心思路是不信任XML里记录的 直接用PIL读真实图片宽高。很多标注工具在图片旋转或裁剪之后XML里的width和height不会同步更新直接拿旧尺寸做归一化出来的框全偏移。另一个重点是越界防护把坐标裁剪到0~1之间防止训练时出现NaN。脚本里用了max(0.0, min(1.0, x))这种写法比if判断更紧凑含义完全一样。每个函数都加了打印方便追踪哪张图出了问题。3.3 四个边界坑从遇到的真实情况说起坑一边框坐标越界。XML里xmax大于图片宽度y的坐标出现负值这类在采集设备分辨率变化时特别常见。直接后果是YOLO训练loss先降后升验证集mAP忽高忽低。解决方式就是上面脚本里的clamp处理但要注意clamp之后框的位置已经变了如果越界特别严重比如xmax是宽度的2倍这个框本身就该丢弃而不是硬修。坑二全零或空标注文件。有的标签文件生成了但没有任何目标写入txt是0字节。训练时data loader读到空文件会直接跳过不会报错但你的“3575张有标签图”实际就少了样本。用一行命令检查find labels -name *.txt -empty查出来的文件全部删掉同时从训练列表里剔除对应图片。坑三类别编号错位。classes.txt的排序和XML里 写的是“scale”转换脚本必须走class_to_id映射而不是靠行号硬猜。肉眼比对前几行看不出来直接在转换后统计所有txt的class_id分布和安全类别清单做一遍对比最稳。坑四XML损坏或编码异常。从Windows机器上拷出来的XML常见GBK编码和UTF-8混用ET.parse直接抛ParseError。现象就是转换脚本跑到第100张忽然中断前面生成的txt又不删掉导致后面训练时图片和标签数量不一致。解决方式是在循环外加try...except把失败的XML文件名写进一个error.log全部跑完后统一处理而不是让脚本中途崩掉。4. 用YOLOv8跑通这个数据集3575张图的训练参数配置4.1 数据集yaml路径、类别数和类别名用Ultralytics YOLOv8训练的第一步是写一个数据集配置文件。这个yaml是训练脚本读取数据入口的唯一依据路径写错比模型结构选错更致命。# mango_pest.yaml path: /home/user/data/MangoPest3575/YOLO # 改成你解压后的实际绝对路径 train: images # 训练集图片目录相对path val: images # 验证集图片目录 # 类别顺序必须和classes.txt保持一致 names: 0: thrips 1: aphid 2: leafhopper 3: fruit_fly 4: weevil 5: scale 6: whitefly 7: longhorn_beetle 8: mite 9: tussock_moth这里有个细节很多人忽略Ultralytics的yaml里train和val可以指向同一个目录它会根据images目录下每张图是否有对应labels文件来自动判定可用样本再按比例切训练和验证。但如果数据集的ImageSets/Main里有现成的train.txt和val.txt更稳妥的做法是直接用那个划分因为原作者切分时可能考虑了同一株果树上多张照片的重复问题。路径建议写绝对路径相对路径在换机器跑的时候经常出幺蛾子。4.2 训练命令从imgsz和batch谈起yolo detect train \ datamango_pest.yaml \ modelyolov8s.pt \ imgsz640 \ batch16 \ epochs120 \ patience20 \ workers4 \ projectruns/mango_pest \ nameyolov8s_640选yolov8s而不是yolov8n是因为芒果害虫类别间相似度高蓟马和蚜虫的视觉差异极小n模型容量不够特征提不出来。3575张图撑不起yolov8l这种大模型的训练容易欠拟合变成过拟合得不偿失s是合理起点。imgsz640是默认值但芒果害虫里大量小目标后面会专门讲是否要调到960。batch16这个数值要看显存我用12GB的显卡跑yolov8s、640分辨率、batch16刚好吃满显存小的降到8就行。patience20的意思是连续20个epoch验证集mAP没有提升就自动停省时间。损失函数方面YOLOv8默认用的是结合了分类和回归的复合损失不需要手动改但训练时关注一下loss曲线里box_loss和cls_loss的比例变动。如果cls_loss下不去说明分类本身难优先查数据问题而不是调参。4.3 train/val划分按图随机还是按场景划分训练集和验证集的划分方式决定了你最后看到的mAP是真实水平还是虚假繁荣。最简单的做法是随机打乱后按8:2切但这样做有个隐患同一株果树的照片可能同时出现在训练集和验证集——因为背景类似、光线类似、甚至同一只虫出现在相邻照片里验证集跑出来的成绩会虚高。我一般会先把文件名按拍摄点位前缀分组同一个点位只进训练集或只进验证集。如果文件名没有明显的点位信息退一步用哈希取模做分组也比纯随机切要稳。检查划分是否合理的一个低成本方法训练完后随机抽10张验证集图片人工判断它们和训练集图片是否“看起来太像”。如果闭着眼都能猜出这张图在训练集里有同胞兄弟赶紧重切。5. 芒果害虫检测的常见避坑小目标漏检、相似虫态与样本长尾5.1 蓟马蚜虫这类小目标漏检上采样比堆模型更便宜现象训练完的模型对果蝇、毒蛾这类大目标检测得挺好但蓟马和蚜虫几乎全部漏检验证集上一张图里十几只蓟马模型一只都没框出来。原因蓟马在640分辨率下的尺寸只有10像素左右经过YOLOv8的多次下采样到检测头那一层特征图时目标只剩一两个像素特征已经丢了。这不是模型不努力是信息根本传不到检测头。解决第一优先级是把imgsz从640调到960甚至1280小目标在更大输入下保留更多像素。代价是显存占用翻倍batch要相应减半。第二优先级是开启切片推理常见做法是用SAHI库把原图切成小块分别检测再合并结果推理速度会下降但召回率提升明显。第三优先级才轮到换模型结构比如在YOLOv8基础上加P2检测头。对3575张这个数据量级加检测头容易过拟合不如先试前两招。5.2 易混淆类互相误检先查标注再调模型现象训练时总loss降得不错但验证集上蚜虫和蓟马互相乱认介壳虫有三分之一被识别成枝干病斑。原因这类情况通常是标注本身存在问题比如标注员把幼龄蓟马标成蚜虫或者把密集的介壳虫群体只框了外轮廓而没有细分个体。模型学到的边界是乱的自然分不清。解决从验证集里挑出所有预测框和真实框IoU大于0.3但类别不一致的样本逐一查看原图和标注框。这一步很花时间但能直接揪出几十个标错的框。把错误标注修正后重新训练mAP的提升往往比换个更强的backbone还明显。我见过最夸张的一次只改标注没改模型mAP0.5从0.63涨到0.78。5.3 长尾类别拉低整体mAP先看类别分布再谈增强现象训练完打印每个类别的AP发现9个类别都超过0.8只有一个类别AP不到0.3整体mAP被拖到0.7以下。原因数据集是10类共3575张但类别分布通常不平均。虫害暴发有季节性某种害虫在采集周期内只出现了30张图而另一种常见害虫占了800张。20:1的样本量差距模型自然偏向大类别。解决最有效的不是直接复制粘贴小类别样本做增强而是先写脚本统计每类的框数量看清差距到底多大。数据增强层面可以只对少数类做复制粘贴把该目标的框从原图抠出来随机贴到其他图上这在目标检测里被验证过有效。如果增强后还是不涨就考虑用带focal loss的变体让模型把注意力放在难分类的少样本类别上。但不要指望调loss能逆天改命样本量不足30张的类别再怎么调都是矮子里拔高个。5.4 背景误检花萼、枯枝被当成害虫现象模型在测试照片上频繁框出花萼、枯枝、果柄置信度还不低。原因训练集里的正样本都是在虫害暴发期拍的背景里有大量的花萼和枯枝标注框只框了虫体但模型把视觉上最显著的花萼纹理当成了和“害虫”强相关的特征。这类问题在迁移到真实果园场景时更严重因为真实环境下背景复杂度远超训练集。解决一是准备一批纯背景负样本图在yaml里加一个background类或使用Ultralytics的negatives机制。二是降低cls_loss权重、提高box_loss权重让模型更关注位置精度而不是背景纹理。三是在部署端对置信度阈值做校准不要把默认的0.25直接上生产先跑一批实地数据看分布再定。6. 从模型到现场验证指标取舍和一个使用习惯6.1 mAP0.5和mAP0.5:0.95该看哪个训练结束后终端会同时打印mAP0.5和mAP0.5:0.95前者只要求预测框和真实框的IoU超过0.5就算命中后者则从0.5到0.95每间隔0.05计算一次取平均。害虫检测的实际使用场景里框的位置偏一点不是致命问题只要框住了害虫主体就算有效所以mAP0.5是核心验收指标mAP0.5:0.95用来衡量框的定位精度。如果一个模型mAP0.5很高但0.5:0.95明显偏低说明框的位置飘部署时应该考虑缩小NMS的IoU阈值减少重叠框。6.2 混淆矩阵和单类AP表做验收只看总mAP不够我会把验证集上的混淆矩阵和每个类别的单类AP打印出来。混淆矩阵能一眼看出哪些类别在互相污染比如第0类蓟马有15%被识别成第1类蚜虫那就值得回头查一个标错案例。单类AP表可以确认长尾问题到底拖了谁的后腿。还有个实用技巧随机挑30张验证图片把模型预测结果叠加画到原图上人眼过一遍比看任何指标都能更快发现“模型学会了什么奇怪的东西”。6.3 部署前的最后一个习惯看bad case我习惯在训练收官前固定住模型用训练时完全不参与评估的一批图片做bad case分析找10个漏检和10个误检逐个写原因。写多了会发现规律漏检多数是小目标遮挡误检多数是背景纹理相似。这比泛泛地“调参再训练”更有方向感。这个数据集的价值在于给了你一个可控的起点但每个果园的品种、物候期、光照条件都不一样迁移部署时必须带着自己的数据做一轮微调。希望这个工作流能帮你少走几趟弯路也希望那个“先看标注再调模型”的习惯能帮你省下一周盲目调参的时间。本文还有配套的精品资源点击获取
返回列表