简介:菲律宾水稻褐飞虱成虫目标检测数据集是一份面向农业视觉与植保智能化应用的YOLO格式标注数据,帮助研究人员与开发者构建褐飞虱成虫的自动识别模型。数据采集自菲律宾真实稻田,覆盖水稻不同生长阶段,包含1458张现场图片,其中训练941张、验证304张、测试213张,每张均配有边界框标注与类别标签,可直接用于目标检测模型的训练和评估。资源包共2000个文件,以1458个txt标注文件、540个jpg图片为主,另附1个yaml配置和1个docx说明文档,整体约56.36MB,结构清晰、即下即用。该数据集主要面向智能农业监测系统开发、精准植保决策支持、农业算法研究及农技教学场景,可辅助完成虫害实时监测、农药喷洒定位及田间诊断培训。目前已有197人学习浏览,适合需要标准农田环境样本进行病虫害检测算法验证的开发者与农业科研人员。
1. 先搞清一个现实问题:为什么这份褐飞虱数据集值得拿来就用
做水稻虫害监测的人都知道,褐飞虱成虫体长只有几毫米,趴在稻株中下部,颜色和枯叶几乎分不开,人工田间调查得一株株拨开看,一天下来也查不了几亩地。要是有一套目标检测模型,无人机或田间监控拍回来的照片就能自动给出虫害位置,问题是模型训练最缺的不是算法,而是带标注的图片。菲律宾水稻褐飞虱成虫目标检测数据集,1,458 张在真实稻田里拍摄的现场图片,统一转成 YOLO 格式,只标褐飞虱成虫一个类别,下载解压就能喂给 YOLO 训练。它还按训练 941 张、验证 304 张、测试 213 张分好了集,连划分的功夫都省了。适合做农业 AI、植保自动化和毕业设计的读者,也适合想验证 YOLO 在单类小目标数据上效果的人。这份数据集省掉的,是最枯燥的采集、筛选和画框环节。
2. 拆包看货:数据集内部结构、标签格式与 data.yaml 改造
2.1 从文件名读信息:一份典型 Roboflow 导出数据
先别急着训练,拿到 zip 先解压看货。这份数据集的图片文件名长这样:
01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004.jpg 00337_jpg.rf.b6074241442c5aecf427945e27b2bd68.jpg 00250_jpg.rf.6f8c6fa251c73765730d7d6ecfdb3e00.jpg这个命名规则能读出好几层信息:01758是原始采集图片的编号;_jpg表示原始图片被统一转成了 JPG 格式;rf是 Roboflow 平台的导出标记;第 4 段一长串十六进制哈希是 Roboflow 给每张图生成的唯一 ID,作用是防止不同来源的图片重名。凡是你看到这种文件名,基本可以判断这份数据是从 Roboflow 导出的,导出时大概率已经带好了 train / valid / test 三组划分和对应的标签文件。
解压之后的目录组织,Roboflow 常见有两种形式:一种是分成 train / valid / test 三个子目录,每个子目录下再分 images 和 labels;另一种是所有图片平铺在一个目录里,靠 train.txt / valid.txt / test.txt 三个文本清单标记分组。拿到 zip 先跑一遍结构检查:
unzip 菲律宾水稻褐飞虱成虫目标检测数据集.zip -d bph_dataset cd bph_dataset # 看顶层目录结构 find . -maxdepth 2 -type d | sort # 统计图片数量,确认和简介里的 941/304/213 对得上 find . -type f -name "*.jpg" | wc -l # 抽样看标签文件内容,确认坐标格式 find . -type f -name "*.txt" -exec head -3 {} \; | head -30这段命令做的事情是解压、看目录层级、统计图片总量、抽查标签内容。注意最后一条会把 labels 目录下所有 txt 的头三行都打出来,如果输出里混进了 README 之类无关文件,可以用find . -path "*/labels/*.txt"限定到标签目录再看。训练集 941 张图对应的标签文件数量应该也是 941 张左右,数量对不上就说明文件名有错配,后面第 5 章会专门处理。
注意:文件名里的 hash 是每张图的唯一标识,后续如果自行重新划分数据集,要整段保留这个 ID,只取前面的编号会导致不同图片重名冲突。
2.2 标签格式与类别定义:单类别数据集里的隐藏脏数据
YOLO 的标签文件是纯文本,跟图片同名同路径,只是扩展名从 .jpg 变成 .txt。每行的格式是:
类别ID 中心点x 中心点y 框宽 框高其中四个坐标值都除以图片宽高做了归一化,范围在 0 到 1 之间。这份数据集只标了褐飞虱成虫一个类别,按道理所有 txt 里的第一列都应该是 0。但实际拿到手的数据不一定干净——Roboflow 的导出界面里如果选了“包含所有类别”,可能把原始标注里的其它类别也一起带出来;或者某些标注工具导出的 class id 从 1 开始而不是从 0 开始。这些脏数据如果不检查,训练时会直接报 IndexError,或者模型把类别 1 当成不存在的东西。
我拿到数据集第一件事是写一段小脚本,统计训练和验证标签里每一类出现多少次:
import os from collections import defaultdict def scan_label_dir(label_dir): counts = defaultdict(int) total_boxes = 0 for fn in os.listdir(label_dir): if not fn.endswith(".txt"): continue with open(os.path.join(label_dir, fn), 'r', encoding='utf-8') as f: for line in f: parts = line.strip().split() if len(parts) < 5: print(f"警告: {fn} 中存在损坏行: {line.strip()}") continue counts[parts[0]] += 1 total_boxes += 1 print(f"目录 {label_dir} 的标签框总数: {total_boxes}") for cls_id, num in sorted(counts.items()): print(f" 类别ID {cls_id}: {num} 个框") return counts scan_label_dir("bph_dataset/train/labels") scan_label_dir("bph_dataset/valid/labels")这段脚本一石二鸟:既统计类别分布,又检查有没有行数不足 5 列的损坏标签。如果输出里只有类别 0,一切正常;如果出现 1 甚至 2,说明导出时把其它类别带进来了,需要对标签文件做批量修正,后面第 5 章有对应的处理脚本。框的宽度、高度、长宽比分布也可以顺手算一算,后面调小目标参数用得上。
2.3 data.yaml 改造:用绝对路径管理数据集边界
Ultralytics 的 YOLO 系列训练时,会读一个 YAML 配置来定位数据。Roboflow 导出的 data.yaml 往往长这样:
train: ../train/images val: ../valid/images nc: 1 names: ['brown planthopper - adult']问题在于:这个配置文件里的相对路径是从 YAML 文件所在目录出发的,如果你把 train 目录挪了位,或者直接把 YAML 放到项目根目录下,训练时会因为找不到图片而报错,而且报错信息常常很隐晦,只说某个目录不存在。我一般会把 data.yaml 重写一遍,用绝对路径:
# 把 path 改成实际解压目录,train/val/test 相对 path 来写 path: /home/user/datasets/bph_dataset train: train/images val: valid/images test: test/images nc: 1 names: ['brown planthopper - adult']后一种写法的好处是 path 一次定义好,train / val / test 相对 path 来写,后面训练命令里只要用data=.../data.yaml指过去一次,换机器只需要改一行 path。两个值得注意的习惯:test目录训练时用不到,但写上可以在训练后用同一条 YAML 直接做推理评估;names里的类名含空格和连字符,在大多数 Linux 脚本里没有问题,但如果后续要用类名拼接输出路径或做 Web 接口,建议改成短一点的标识,比如bph_adult。最后把 YAML 里 Roboflow 自带的下载字段删掉,那些是平台专用标记,Ultralytics 不认识也不读取,留着无害但清理掉更干净。
3. 把模型跑起来:ultralytics 环境配置与训练命令
3.1 环境搭建:CPU 与 GPU 两条安装路径
YOLO 的官方实现现在统一收敛在 ultralytics 包里,安装完就自带 YOLOv5 到 YOLO11 的一系列模型权重。先建一个干净的 conda 环境,避免和已有的 TensorFlow 或其它框架冲突:
conda create -n yolo python=3.10 -y conda activate yolo pip install ultralytics显卡用户可以接着装对应 CUDA 版本的 PyTorch,注意版本要匹配。以 CUDA 11.8 为例:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118装完验证一下:
python -c "import torch; print(torch.cuda.is_available())"输出 True 说明 GPU 可用;输出 False 要么是没装 GPU 版 PyTorch,要么是驱动和 CUDA 版本不匹配。这种环境问题跟数据集本身没关系,但很多人都卡在这一步。我建议先把 CPU 版跑通流程,再考虑 GPU 提速,至少能确认数据集本身没问题。CPU 跑 941 张图的 YOLOv8n,一百个 epoch 可能要几个小时,能接受;GPU 通常十分钟级别,差别很大。
3.2 训练命令与关键参数:从 nano 到 small 的取舍
环境就绪后用一条命令开训:
yolo detect train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0拆开看每个参数的含义:data指向第 2 章改好的 YAML;model用预训练权重,加载的是 COCO 上预训练过的权重,对褐飞虱这种小目标能提供一个不错的初始特征,比从头训练收敛快很多;epochs=100是单类小目标的常用起点,这类任务往往不到 30 轮就能达到不错的 mAP,多跑几轮可以确认曲线稳定;imgsz=640是默认输入尺寸,后面第 4 章会单独讨论该不该加大;batch=16依赖显存大小,24GB 显存跑 YOLOv8n 可以开 32 甚至 64,8GB 显存用 16 或 8 更稳。device=0指定用第一张显卡,没有 GPU 就改成device=cpu。
如果你的显卡够用,把 nano 换成 small:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| model | yolov8n.pt / yolov8s.pt | nano 跑通链路,small 涨点 |
| epochs | 100 | 单类小目标通常 50 轮前收敛 |
| imgsz | 640 起步,小目标可试 896/1280 | 分辨率对褐飞虱影响最大 |
| batch | 16(8GB 以下用 8/4) | 由显存容量决定 |
| device | 0 或 cpu | 多卡时写 0,1 |
Ultralytics 包现在可以加载 yolov8n.pt 或 yolov11n.pt 等权重,文件格式完全兼容,想对比新版 YOLO11 效果,直接把 model 参数替换即可,数据 YAML 不用改。
3.3 训练结果文件怎么读
训练结束后,输出目录runs/detect/train里有一堆产物,核心是weights/best.pt和weights/last.pt,以及results.csv。这个 CSV 记录了每个 epoch 的指标变化,我一般用 tail 看最后几行:
tail -5 runs/detect/train/results.csv结果里每列的含义大致是这样:
| CSV 列名 | 含义 | 对单类小目标任务的参考 |
|---|---|---|
| train/box_loss | 边界框回归损失 | 稳定下降即可,不必追求极小 |
| train/cls_loss | 类别损失 | 单类应很低,持续不降查标签 |
| metrics/precision(B) | 精确率 | 高代表误报少 |
| metrics/recall(B) | 召回率 | 低代表漏检多,比 precision 更值得关注 |
| metrics/mAP50(B) | IoU 阈值 0.5 的平均精度 | 单类任务的主指标 |
| metrics/mAP50-95(B) | 多阈值平均精度 | 小目标下明显低于 mAP50,属正常现象 |
这里要特别说明类别损失:单类数据集每张图只有一种目标,类别维度上几乎不需要判别,cls_loss 会很低。如果训练日志里 cls_loss 居高不下,反而说明标签文件里的 class id 有问题。另外 YOLOv8 默认开启自动数据增强(Mosaic、随机仿射等),在 941 张图的小数据集上能有效防过拟合,不要在训练阶段把增强随便关掉。
4. 单类小目标调参:褐飞虱为什么不能照搬通用流程
4.1 小目标检测的难点:几毫米的虫子在图上只有几个像素
褐飞虱成虫体长在 3 到 5 毫米,拍稻田的相机如果挂在无人机上离植株一两米远,一只虫子在图里往往只占 20×20 到 40×40 像素;在 YOLO 的 640×640 输入分辨率下,这属于公认的小目标。YOLO 模型对小目标的漏检率天然偏高,因为下采样过程中小目标的特征被层层压缩,最后只剩几个神经元在对应。这就是目标检测里最玄学的地方:不是模型不对,而是输入分辨率已经把它的能力上限框死了。
我一般会先从标签框的尺寸分布入手,看看大部分标注框到底多大:
import os import numpy as np sizes = [] for split in ["train", "valid"]: label_dir = f"bph_dataset/{split}/labels" for fn in os.listdir(label_dir): if not fn.endswith(".txt"): continue with open(os.path.join(label_dir, fn)) as f: for line in f: parts = line.strip().split() if len(parts) < 5: continue w = float(parts[3]) h = float(parts[4]) sizes.append((w, h, split)) sizes = np.array(sizes) print("框宽均值:", sizes[:, 0].mean().round(3), "框高均值:", sizes[:, 1].mean().round(3)) print("95% 分位宽度:", np.percentile(sizes[:, 0], 95).round(3)) print("95% 分位高度:", np.percentile(sizes[:, 1], 95).round(3))如果多数框的宽高都小于 0.05,也就是在 640 分辨率下约 32 像素,那这份数据就是典型的小目标分布,建议把 imgsz 提高到 896 甚至 1280 再训练,代价是显存和训练时长明显上升。另一种做法是推理阶段用切片工具,把大图切成小块分别检测再合并,适合部署;训练阶段通常直接加大 imgsz 更省事。
4.2 用验证脚本和 conf 阈值压误检
训练完先别急着下结论,跑一遍验证集推理,把每个框的置信度逼出来看:
yolo predict model=runs/detect/train/weights/best.pt source=data/valid/images conf=0.25 save_txt=True这个命令会在跑完每张验证图后,把预测结果以 YOLO 格式写到 runs/detect/predict/labels。这里的关键是 conf 参数——默认的 0.25 对褐飞虱这种细长小虫经常偏高,一小部分真虫会被当噪声滤掉。我的习惯是先用 conf=0.05 跑一遍,人工抽看 10 到 20 张图,数一下误报和漏报的比例,再一点点把阈值升回去。
单类检测有个容易被忽略的事实:没有其它类别做干扰时,模型倾向把长得像虫子的碎叶影、稻秆关节都报成阳性。这种误报不会显著拉低 mAP,因为在验证集上这些位置本来就没有真值框,精度指标会很好看;但部署到田间会变成灾难。所以 mAP 达标只是底线,真正的验收要看“误报率”这个指标,后面第 6 章会专门讲。
提示:验证集推理时先 conf=0.05 再逐档上调,比一开始就按 0.25 更合适,能看清模型在低置信度下到底“看到”了什么。
4.3 训练图像尺寸与批次容量的取舍
加大 imgsz 的同时显存会线性上涨。以 YOLOv8n 为例,imgsz=640 配 batch=16 大约占 8GB 显存;imgsz=1280 时 batch 降到 4 可能刚好。优先保 imgsz,因为分辨率对小型目标的收益比 batch 大得多。如果显存实在吃紧,还有一个折中办法:训练时用 imgsz=768 或 896,推理时用更大的 imgsz。YOLO 推理时的 imgsz 可以和训练不一致,只要差距不太大都能正常工作。
数据增强方面也有一点要留意:小目标数据和常规目标不同,Mosaic 增强会把四张小图拼成一张,虫子的相对尺寸被进一步缩小;如果发现训练曲线在中后期反而掉点,可以尝试调整 Mosaic 概率或者关掉部分增强再训一轮对比。
5. 常见问题排查:五个踩坑记录,现象、原因、解法
这份数据集整体质量不错,但任何第三方导出的数据都有“出厂设置”意外的可能。下面五个坑我按现象、原因、解决展开。排查顺序有讲究:先验标签再验路径,最后才验模型;不然你可能花一晚上调参,最后发现是文件映射错了,白白浪费算力。
5.1 训练刚启动就报 IndexError,类别 ID 越界
现象:yolo detect train执行几秒钟后日志出现类似IndexError: index 1 is out of bounds for axis 0 with size 1,训练直接中断。
原因:标签 txt 里出现了类别 ID 1,而 data.yaml 里只声明了 1 个类别。出现这种情况通常是 Roboflow 导出时把原本的版本或未清理的历史类别一起导出了,或者标注软件里曾经创建过第二个类别,删除后 class id 没有重新编号。
解决:先用 2.2 节的统计脚本扫全部标签目录,确认哪些文件包含非 0 的 ID。如果只是个别文件,直接把它们整体改写成类别 0:
import os, glob for txt in glob.glob("bph_dataset/*/labels/*.txt"): lines = [] with open(txt) as f: for line in f: parts = line.strip().split() if len(parts) == 5: parts[0] = "0" lines.append(" ".join(parts)) with open(txt, "w") as f: f.write("\n".join(lines))这段 Python 把三个子集里所有标签的第一列统一写成 0,执行前先对目录做一次 tar 备份,万一后面发现其它问题还能回滚。改完重新训练基本就过了。
5.2 训练跑完 100 个 epoch,mAP 全程是 0
现象:loss 正常下降,训练过程无报错,但 results.csv 里的 precision、recall、mAP50 全是 0。模型看起来像在学,其实没在学。
原因:最常见是 data.yaml 里的 val 路径写错。比如实际目录叫 valid,YAML 里写了 val;Ultralytics 在验证阶段找不到标签,就把验证集当成纯背景图,导致 mAP 被清零。另外标签文件在传输过程中被改名或丢失也会导致同样现象。
解决:先确认验证集的图片和标签数量一致,再逐张检查是否独缺:
ls data/valid/images | wc -l ls data/valid/labels | wc -l for img in data/valid/images/*.jpg; do base=$(basename "$img" .jpg) [ -f "data/valid/labels/${base}.txt" ] || echo "缺少标签: $img" done | head -20如果只是个别文件缺失,直接从训练集复制补齐;缺得多就再对照 2.1 节的文件名规则重新检查,大概率是后缀名 .jpg 与 .jpeg 不一致导致匹配失败。
5.3 中文路径或特殊字符导致图片和标签名对不上
现象:训练日志反复出现WARNING: skipping ... (not found),但打开文件管理器,图片明明就在那个目录里;标签目录里也能看到对应的 txt。
原因:这是跨平台解压最常见的坑。Windows 自带解压工具对 zip 里的中文目录做了一套编码转换,macOS 则会把文件名中的特殊字符做 Unicode 规范化,两边改完之后,jpg 和 txt 文件名的实际字节不再一致,YOLO 按同名规则找标签自然找不到。
解决:彻底一点,把所有图片和标签都重命名为纯数字编号,去掉中文和 hash:
cd bph_dataset/train find . -type f -name "*.jpg" | while read -r f; do new=$(echo "$f" | grep -oE '[0-9]+' | head -1) [ -n "$new" ] && mv "$f" "$new.jpg" done cd bph_dataset/train/labels for f in *.txt; do new=$(echo "$f" | grep -oE '[0-9]+' | head -1) [ -n "$new" ] && mv "$f" "$new.txt" done这里默认编号在文件名里唯一,如果出现重名,说明原始图片编号有重复,需要再加上 hash 前 8 位做后缀区分。无论哪种方案,改完再跑一遍“图片数量 = 标签数量”的校验,确认无误再训练。
5.4 自己重分数据集,造成同图进训练又进验证
现象:用脚本按 70/15/15 重新划分后训练,mAP 比原始划分还要高,但模型换成自己新拍的图片去测,效果远低于验证集表现。
原因:划分逻辑没按完整文件 ID 去重。这份数据集的文件名里每张图都有一段唯一 hash,有些脚本会按编号前缀或文件名前几位分组,结果同一张原图被复制成两个文件,一个进了 train 一个进了 valid。验证集的“高分”实际来自数据泄漏,部署时自然露馅。
解决:重新划分前先对文件名全集做一次去重。核心是三个集合之间不允许出现任何交集:
import os, random img_dir = "all_images" imgs = os.listdir(img_dir) # 按完整文件名哈希去重,避免同一张图进多个集合 seen = set() unique = [] for name in imgs: h = name.split('_rf_')[-1].split('.')[0] if '_rf_' in name else name if h not in seen: seen.add(h) unique.append(name) random.seed(42) random.shuffle(unique) n = len(unique) train = unique[:int(n * 0.7)] val = unique[int(n * 0.7):int(n * 0.85)] test = unique[int(n * 0.85):]这套逻辑的重点是随机种子固定,同一个数据集在不同时间复现实验时结果可对比,不会出现“这次训练比上次好”但其实是划分变了。如果你的原始数据本来就是 Roboflow 分好的 train / valid / test,建议直接用原始划分,不要二次重分,省得引入不可控的重复。
5.5 标签框几乎覆盖整张图,模型变成“见稻就报”
现象:训练后可视化预测框,发现框的宽高接近 1,把整株稻甚至整张图都框进去。验证集上 mAP 居然不低,因为框都落在图片中心区域,跟真值的 IoU 误打误撞能过 0.5。
原因:标签文件里的坐标被错误地归一化成了整图范围。常见于标注工具导出坐标基准不同,或预处理时对尺寸信息误读。这种问题在验证集上可能被掩盖,只有可视化标注框时才能看出来。
解决:用脚本把所有宽高超过 0.5 的框挑出来,逐张定位到图片人工看:
import os threshold = 0.5 for split in ["train", "valid"]: label_dir = f"bph_dataset/{split}/labels" for fn in os.listdir(label_dir): with open(os.path.join(label_dir, fn)) as f: for line in f: parts = line.strip().split() if len(parts) >= 5 and float(parts[3]) > threshold: print(f"{split}/{fn} 宽度异常: {parts[3]}")如果异常框不多,直接把对应图片从数据集里剔除;如果大面积出现,说明整份标签的坐标基准错了,应回到原始标注文件重新转换,而不是靠训练去“学习”错误标注。我在处理这类第三方数据时,会先随机抽 20 张图可视化标注框,这一步能筛掉几乎全部坐标异常。
6. 进阶:从“能用”到“可信”——验证集上的漏检定位
模型第一次跑出 mAP 只能说明“在验证集上跟标注对得上”,部署到田间之前,我建议再做一轮漏检定位。做法是把验证集里“真值有框但模型一张都没给出”的图片全部挑出来,按漏检数量从高到低排,人工看前 10 张,基本能看出模型的盲区规律。
对比脚本也很简单,核心是比较预测标签和真值标签的框数量:
import os def compare(pred_dir, label_dir): misses = [] for fn in os.listdir(label_dir): base = fn[:-4] pred_path = os.path.join(pred_dir, base + ".txt") true_n = 0 with open(os.path.join(label_dir, fn)) as f: for line in f: if len(line.strip().split()) >= 5: true_n += 1 pred_n = 0 if os.path.exists(pred_path): with open(pred_path) as f: for line in f: if len(line.strip().split()) >= 5: pred_n += 1 if true_n > 0 and pred_n == 0: misses.append((fn, true_n, pred_n)) return sorted(misses, key=lambda x: x[1], reverse=True) misses = compare("runs/detect/predict/labels", "bph_dataset/valid/labels") for fn, true_n, pred_n in misses[:10]: print(f"{fn}: 真值 {true_n} 框,预测 0 框")这段脚本遍历验证集标签,对每张图片找对应的预测标签;只要真值有框而预测文件缺失或框数为 0,就算一次漏检。sorted 把漏检最多的图片排在最前面,输出的是文件名单,后续可以配合图片目录逐张查看。
人工看这批漏检图,通常很快会发现规律。我常用的对应关系是这样:
| 漏检形态 | 主要对策 |
|---|---|
| 暗光、低对比 | 增强 HSV 亮度和饱和度扰动 |
| 密集重叠 | 加大 Mosaic 概率,适当提高 epochs |
| 小尺寸虫体 | imgsz 提到 896 以上,必要时切片推理 |
| 叶片遮挡 | 增加旋转和翻转增强概率 |
每轮针对盲区调整增强策略后再训一轮,验证集的指标可能没有明显上涨,但换到新场景的图片上测试,漏检率会实打实下降,这就是从“能用”到“可信”的差距。
我从这个项目开始,把“漏检定位”固化成了训练流程的固定环节。每次拿到新的检测数据集,我的顺序是:先体检标签(类别 ID、文件匹配、框尺寸分布),再跑基线模型,然后做漏检定位,最后才谈置信度阈值和部署。这份菲律宾水稻褐飞虱数据集也一样,按这套流程走完,你得到的不只是一个训练好的 YOLO 模型,还有一份能解释清楚“哪里漏、为什么漏”的评估结论。希望帮到你。
本文还有配套的精品资源,点击获取