简介:这份菲律宾水稻褐飞虱成虫目标检测数据集是一套面向农业AI与植保应用的专业标注数据集,适合智能农业监测系统开发、精准植保决策支持及农业院校教学研究。资源合计1458张真实农田采集的水稻现场图片,其中训练集941张、验证集304张、测试集213张,以YOLO格式进行边界框与类别标注,聚焦褐飞虱成虫这一主要水稻害虫目标,标注经农业专家校验,可直接适配主流目标检测框架。压缩包共2000个文件,以txt标注文件与jpg现场图片为主体,另含yaml配置与docx说明文档,包体56.36MB,整体紧凑且分层清晰,便于按训练、验证、测试分工直接使用。数据覆盖水稻生长周期不同阶段,真实呈现光照变化、植株遮挡等复杂田间条件,聚焦褐飞虱防治关键痛点,为害虫自动识别模型训练、田间实时监测与预警系统落地提供有力支撑;已有197人学习,适合需要快速构建水稻害虫检测模型的AI工程师与农业科研人员。
1. 稻田里的虫子比检测框更会躲:1,458 张褐飞虱目标检测数据集值不值得下
做农业视觉的朋友大概率都有过这种体验:公开数据集里干净背景的猫猫狗狗一大堆,真到了田间地头,水稻叶片互相遮挡、阳光忽明忽暗、虫子小得跟米粒似的,预训练模型直接翻车。所以我看到这份「菲律宾水稻褐飞虱成虫目标检测数据集」的第一反应是:总算有份贴近实战的农牧业目标检测数据集了。它一共 1,458 张真实农田现场图片,训练集 941 张、验证集 304 张、测试集 213 张,全部是 YOLO 格式的 txt 标注,类别只有一种——褐飞虱成虫。类别单一这件事看着简单,其实恰恰把问题聚焦在「小目标 + 复杂背景」这个真正的难点上。适合谁用呢?想拿真实农业数据练手的目标检测初学者、正在做田间虫害监测系统的开发者,以及需要标准化数据做算法验证的研究人员。如果你手头的项目正好卡在「有 GPU 没数据」,这份资源值得先拆开看个仔细。
2. 拆开 YOLO 数据集:目录结构、标注解析与训练前合规检查
2.1 拿到压缩包先做三件事:核对文件、看目录、确认同名
我刚拿到这份数据集时,压缩包里是一堆形如01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004.jpg的长文件名图片,外加一份文档,并没有看到规整的images/train和labels/train目录。这种情况在从标注平台导出的数据集里非常常见,文件名里那串哈希是平台自动生成的唯一 ID,图片和标签靠这个名字一一对应。所以第一步不是急着训练,而是把所有图片和同名 txt 按训练、验证、测试三个集合归位。
常见做法是写个小脚本扫描目录,按文档记录的划分比例(941/304/213)把图片分到三个目录,同时把对应标签也拷贝过去。需要特别注意的是:如果压缩包里图片和标签混在一起,千万不能在移动时丢标签,否则后面训练时 YOLO 会报image not found或label not found。我一般会先跑一遍统计命令,确认三类文件数量对得上。
# 假设图片都在当前目录,txt 也混在其中 mkdir -p datasets/bph/{images/{train,val,test},labels/{train,val,test}} # 按文件名前缀分配图片 # 先将你手里的划分名单(train.txt / val.txt / test.txt)准备好 while read line; do img="${line}.jpg" txt="${line}.txt" mv "$img" datasets/bph/images/train/ mv "$txt" datasets/bph/labels/train/ done < train.txt这里train.txt里每一行是不带后缀的文件名主名,也就是那一长串01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004。mv前建议先跑一条ls | wc -l确认文件总数,免得移动过程中被 shell 通配符坑掉。划分文件一般从数据集的文档里可以提取出来,如果文档只给了比例,那你需要按比例随机抽取,务必固定随机种子,保证每次复现结果一致。
2.2 看懂那一行 YOLO 标注:归一化坐标与边界框验证
YOLO 格式的 txt 每一行是class_id x_center y_center width height,所有值都相对于图片宽高做了归一化。这份数据集只有褐飞虱成虫一个类别,所以 class_id 恒为 0。但正因为只有一个类,很多人容易忽略坐标值的正确性,直接拿去训练,结果 loss 下降挺好,一推理全框在背景上。拿到数据后先挑几张图解析标注,看看框的位置是否符合直觉。
我用下面这段 Python 把归一化坐标转回像素坐标,并在原图上画出框来做人工目检:
import cv2 import numpy as np img_path = "datasets/bph/images/train/01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004.jpg" label_path = "datasets/bph/labels/train/01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004.txt" img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path, "r") as f: for line in f: cls, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite("check_bbox.jpg", img)这段代码的关键是把归一化值乘回图片宽高,并减去半宽半高得到左上角坐标。画完框之后建议连续看几十张图,重点观察两个问题:第一,框是否紧贴虫子身体,而不是把整片叶子包进去;第二,是否存在漏标——画面里明显有虫子但没有任何框。这一步做扎实,后面训练可以省掉大量排查时间。常见情况是数据集中存在少量坐标越界(比如 x1 小于 0 或 x2 大于 w),遇到这种标签,后处理时需要截断到图像边界。
2.3 训练前清洗:剔除坏图、无标注图和重复图
田间现场拍摄的照片不像互联网爬图那样干净,很可能会出现少量无法解码的损坏文件、全黑过曝的废图,以及因为平台同步原因产生的重复图片。YOLO 训练时遇到 OpenCV 读不出来的图会直接报CV_RGB相关异常,轻则跳过这张图,重则中断训练进程。所以训练前做一次清洗脚本是值得的。
import os from PIL import Image img_dir = "datasets/bph/images/train" label_dir = "datasets/bph/labels/train" bad_imgs = [] no_label = [] for name in os.listdir(img_dir): base = name.split(".")[0] img_path = os.path.join(img_dir, name) label_path = os.path.join(label_dir, base + ".txt") try: with Image.open(img_path) as im: im.load() except Exception: bad_imgs.append(name) continue if not os.path.exists(label_path) or os.path.getsize(label_path) == 0: no_label.append(name) print("损坏图片数量:", len(bad_imgs)) print("缺失/空标签数量:", len(no_label)) # 有问题的文件移动到 backup 目录,避免在原目录删除 for name in bad_imgs + no_label: os.rename( os.path.join(img_dir, name), os.path.join("backup", name) )这里用 PIL 做解码校验,因为它对损坏文件的识别比 OpenCV 更敏感。缺失标签和空标签要区分对待:空标签文件可能是标注时确实没有虫子的负样本,也可能是导出失败。对单类别数据集来说,我建议把空标签的图片也移走,因为纯背景图在训练里容易变成“预测哪里有虫”的噪声;如果要做负样本增强,应该单独建一个背景图目录,而不是混在训练集里。这个步骤看起来不起眼,但它直接决定你后面训练能不能一次跑通。
3. 用 YOLOv8 复现训练:模型选型、data.yaml 配置与超参调整
3.1 模型选型:为什么这份数据用 n 或 s 版本更合适
数据集类别只有一个,目标数量在画面里通常不多,但虫子本身很小,所以模型的选择要在“小目标检测能力”和“过拟合风险”之间平衡。YOLOv8 系列从 n 到 x 参数量递增,对这份 1,458 张图的小数据集来说,直接上 YOLOv8x 很容易过拟合,训练速度慢且效果不见得好。我一般会先跑 YOLOv8n 和 YOLOv8s 各一次,用验证集比较后再决定最终模型。
为了更直观,列出常见选型的相对差异:
| 模型版本 | 参数量级 | 推理速度 | 小目标表现 | 这份数据上的适用性 |
|---|---|---|---|---|
| YOLOv8n | 最小 | 最快 | 一般 | 先用它建立基线 |
| YOLOv8s | 偏小 | 快 | 中等偏上 | 推荐主力训练版本 |
| YOLOv8m | 中等 | 中等 | 较好 | 数据量不足时容易过拟合 |
| YOLOv8l/x | 大 | 慢 | 好 | 不推荐,除非做迁移学习 |
选择 n 或 s 的另一个原因是嵌入式部署。田间监测设备大多是边缘计算盒子或 Jetson 系列,模型尺寸直接决定推理帧率;训练出来的模型最终要落到设备上,选 n/s 版本从源头控制了部署成本。如果你只有 CPU 环境,n 版本也能完成验证流程,只是每轮训练会比 GPU 慢很多。
3.2 写 data.yaml 并启动第一次训练
YOLOv8 需要一份data.yaml告诉框架图片路径、标签路径和类别列表。这里最容易翻车的是路径写成绝对路径,换机器后直接失效。我习惯把整个数据集目录结构固定下来,然后在命令里直接传数据集根目录相对位置。
# datasets/bph/bph.yaml path: ./datasets/bph train: images/train val: images/val test: images/test nc: 1 names: 0: brown_planthopper_adult这份配置里path使用相对路径,前提是你从项目根目录执行训练命令。nc: 1表示只有一个类别,names里的名称会显示在推理结果上。如果压缩包的文档里没有给出类别映射,你需要自己确认标注文件中 class_id 是不是从 0 开始。很多标注平台默认从 0 开始,但遇到从 1 开始的导出版本,不处理的话训练会报class index out of range。
启动训练的命令如下:
yolo detect train \ model=yolov8s.pt \ data=datasets/bph/bph.yaml \ imgsz=640 \ epochs=80 \ batch=16 \ project=runs/bph \ name=exp01model=yolov8s.pt会加载 COCO 预训练权重,对这份数据很重要——1,458 张图从头训练很难收敛,迁移学习能显著缩短训练时间。imgsz=640是常规输入尺寸,如果发现框偏小,可以试imgsz=960,代价是显存占用和训练时间同步上升。epochs=80对单类别小数据集够用,我试过跑到 120 轮,后面的提升很微弱。batch=16取决于 GPU 显存,6GB 显存跑 s 版没问题,如果显存不够就降到 8。第一次训练建议把save_period参数加上,每 10 轮存一个检查点,防止后期过拟合时想回退却没权重可用。
3.3 超参调整的两个方向:多尺度与类别权重
训练开始后,很多人只盯 loss 曲线,但单类别数据集的 loss 很容易“原地踏步”。第一次跑通后,优先调两件事:输入分辨率和数据增强强度。
针对小目标,最直接的手段是开启多尺度训练。YOLOv8 的mosaic增强默认开启,它对小目标有正面帮助,但在田间场景下,4 张图拼接会让一半目标被裁剪掉。常见的做法是把mosaic在前 10 轮关闭,后面再开,或者直接固定mosaic=0.5降低拼接概率。另一个改动是scale参数,YOLO 默认在每轮迭代中会随机缩放输入图片,把scale从默认值调大到 0.9,可以让模型更多看到不同大小的虫子。
yolo detect train \ model=yolov8s.pt \ data=datasets/bph/bph.yaml \ imgsz=640 \ epochs=80 \ batch=16 \ mosaic=0.5 \ scale=0.9 \ fliplr=0.0 \ project=runs/bph \ name=exp02这里fliplr=0.0是我在农业场景下的习惯——左右翻转对虫子检测本身没影响,但会让模型对叶片朝向的适应性变差。菲律宾稻田的光照条件跟国内有差异,颜色分布也不一样,所以hsv_h、hsv_s这类颜色增强参数建议保持默认,不要用力过猛。跑完 exp01 和 exp02 后,对比验证集的 mAP50 和 mAP50-95,优先选高者,但如果两者差距不到 1%,选推理速度更快的那个,留给部署余量。
4. 避坑指南:训练褐飞虱数据集最容易翻车的五个环节
4.1 标签路径错位导致训练时 label 全部丢失
现象:训练启动时报WARNING: no labels found in ...,loss 一直不降,验证集的 PR 曲线完全不对。
原因:图片名和 txt 名的前缀不一致。数据集的图片名是一长串带.jpg的文件名,但某些 txt 文件名可能因为平台导出问题多了一个后缀或少了.rf那一段。YOLO 按同名匹配图片和标签,前缀对不上就找不到标签。
解决:训练前写脚本把每张图片对应的 txt 存在与否查一遍。我在 2.3 节已经给了检查代码,这里再强调:检查维度不只是在不在,还要打开看行数是否为 0。文件名匹配这种问题,最好的预防措施是在划分目录时,统一用图片名作为唯一主键,txt 全部按图片主名.txt重命名。
4.2 类别编号从 1 开始导致数组越界
现象:训练日志里出现IndexError: index 1 is out of bounds for axis 0 with size 1,进程直接崩溃。
原因:这份数据集标注里 category_id 写的是 1,而不是 0。YOLO 的 class_id 只能是[0, nc-1],单类别时只能是 0。
解决:批量替换 txt 里的行首数字。写一个循环处理所有 txt 文件,把每行第一个数字改成 0。注意不要用全局字符串替换,那样会把边界框坐标值里的数字也改了。安全做法是按空格分割后只改第一段。
4.3 稻田背光和叶片遮挡让目标框严重偏移
现象:训练时 loss 正常,验证集 mAP50 也不错,但打开推理结果一看,很多框框在叶子上,虫子的边缘根本没贴合。
原因:数据集里大量图片是在强阳光下拍摄的,虫子背部反光与叶片颜色接近,标注人员在勾框时会多包进一些叶片区域。YOLO 学到的不是“虫子”而是“叶片上的高光区域”。
解决:把置信度阈值调低后,逐张查看置信度在 0.3~0.5 之间的预测框,重点观察误检是不是集中在背光的叶片上。如果是,考虑用 CLAHE 对图片做对比度增强,或者训练时把hsv_s增强加大,迫使模型关注形状特征而不是颜色。这类问题很难完全消除,能接受的做法是部署时加一个位置约束:框的中心点必须在叶子主脉附近。这个后处理逻辑在完成后我会在代码里跑一遍。
4.4 验证集图片在训练集中重复出现
现象:训练集 loss 下降很稳,验证集 mAP50 高得离谱,但测试集上表现崩了。
原因:数据划分时没有做去重。同一个场景连续拍的几张照片,可能一张被分到训练集,另一张进了验证集,本质上是数据泄露。
解决:用图片的感知哈希做一次去重。常见做法是计算每张图的 pHash 值,两两比较汉明距离,距离小于阈值的图片归为一组,只保留一组内的其中一张。这个步骤在训练前做比较费时间,但对只有 1,458 张图的数据集来说,几分钟就能跑完。另外还有一种更隐蔽的重复:不同文件名但像素完全相同的图片,用md5sum也可以查出来。
4.5 显存溢出和训练中断后没有后悔药
现象:训练到第 37 轮,显存溢出直接退出,从头再来又得 4 个小时。
原因:batch=16在 8GB 显存机器上跑 YOLOv8s,输入尺寸 640 时问题不大,但一旦开了多尺度或把imgsz提到 960,显存就会爆。
解决:训练命令里务必加save_period=5,并且开amp=True混合精度训练。AMP 能降低约 30% 显存占用,代价是略微损失精度,对这份数据影响很小。我自己的习惯是每 10 轮保存一个检查点,万一中途翻车,用resume=True接着跑,不用从头再来。这样看似多占了一点磁盘,省下来的是反复调试的时间。
5. 验证不只是看 mAP:阈值选择与田间场景的漏检分析
5.1 用验证集算出 PR 曲线和混淆矩阵
训练完成后,验证集上的 mAP50 只是第一个指标。褐飞虱检测的难点在于漏检——虫子比叶片上的斑点大不了多少,模型很容易把真目标当作背景。所以在进入部署前,我会跑一次带验证的推理,输出 PR 曲线和混淆矩阵。
yolo detect val \ model=runs/bph/exp02/weights/best.pt \ data=datasets/bph/bph.yaml \ imgsz=640 \ conf=0.25 \ iou=0.5YOLOv8 会在runs/bph/exp02/下生成confusion_matrix.png和一系列曲线图。对单类别模型,混淆矩阵就两行两列:真正例和假正例。重点看假正例那一列:如果大量预测框落在背景上,问题不在阈值,而在模型把叶片纹理当成了虫子。反过来说,如果真正例那一行数值偏低,比如只有 0.7 左右,说明近三成的虫子完全没被框出来。这时候调阈值是无效的,要回头查 4.3 节提到的遮挡问题。
5.2 置信度阈值不是越高越好
很多同学在验证后直接把置信度调到 0.5 去部署,但在褐飞虱检测这种场景下,高置信度阈值会牺牲大量被遮挡的目标。我的做法是跑一遍数据统计,画一条置信度-召回率曲线,看看阈值从 0.1 到 0.9 每档的召回变化。
# VAL 结束后,从 runs/bph/exp02/predictions.json 提取结果做阈值分析 import json with open("runs/bph/exp02/predictions.json") as f: preds = json.load(f) for conf_thr in [0.1, 0.2, 0.3, 0.4, 0.5]: det_count = 0 for p in preds: det_count += sum(1 for d in p["boxes"] if float(d["conf"]) >= conf_thr) print(f"conf={conf_thr}, total_detections={det_count}")输出结果里,检测数量从 0.1 到 0.5 之间的落差,如果超过 40%,说明模型的大部分预测都集中在低置信度区间,这类低置信度框未必是错的,很多是遮挡严重的真实目标。田间监测系统追求的是「宁可多框,不可漏报」,所以部署阈值我一般设在 0.25 左右,然后通过后处理把明显重叠的框合并。这个思路与做 COCO 竞赛的思路相反,提前想清楚场景需求,能避免后期反复改阈值。
5.3 针对小目标的专项验证:按框面积分组看指标
褐飞虱成虫在 640x640 分辨率下,框面积通常只有几百到两千像素,属于典型小目标。整体 mAP 高不代表小目标表现好,所以我会把验证集的检测框按面积分三档看:小于 32x32、32x32 到 96x96、大于 96x96。YOLOv8 的 val 模式不直接输出分组指标,需要自己写后处理脚本,从predictions.json里筛出小目标框和真值框做 IoU 匹配,统计小目标一档的召回率。如果发现小目标档的召回率低于 0.5,就回到训练侧改imgsz=960或增加scale增强。这一步虽然多花半小时,但能把“看起来能用”变成“确实能用”。
6. 可视化排查与部署前检查:把结果框印回原图,三分钟分清好坏
训练、验证都通过后,我习惯写一个可视化脚本,随机从测试集抽 50 张图,把模型输出的框画在原图上,带置信度一起存成 JPEG,逐张翻看。这一个动作能发现所有指标看不出来的问题:框偏移、重复框、置信度虚高、小目标漏检。直接贴代码,这份逻辑在部署到边缘设备前基本就是最终推理流程的简化版。
import cv2 from ultralytics import YOLO model = YOLO("runs/bph/exp02/weights/best.pt") img = cv2.imread("datasets/bph/images/test/07014_jpg.rf.d0573f6b4990a7e0f250741d1979c892.jpg") results = model.predict(img, conf=0.25, iou=0.5) for r in results: for box in r.boxes: x1, y1, x2, y2 = map(int, box.xyxy[0].tolist()) conf = float(box.conf[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f"{conf:.2f}", (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite("visualize_result.jpg", img)这段代码里conf=0.25是推理阈值,iou=0.5是 NMS 参数,测试时如果发现同一只虫子上有两个重叠框,就把 IoU 阈值调高到 0.6;如果发现正对镜头的虫子被漏掉,把置信度降到 0.2 再看。可视化检查也是和甲方或同事沟通的最直接方式——不需要解释 mAP 是什么,一张图画几个框,大家心里就有数了。
从那以后,我每次拿到一份农业目标检测数据集,都强制自己先走一遍「拆包 → 看标注 → 清洗 → 训基线 → 避坑排查 → 可视化」这六步,跑完才会开始调模型。这么做最大的好处是心里有底:知道数据里的坑在哪儿,训练中途遇到问题也不会慌。希望这份菲律宾褐飞虱数据集的拆解笔记,能帮你少踩几个我踩过的坑,把时间花在真正影响效果的事情上。
本文还有配套的精品资源,点击获取