菲律宾水稻褐飞虱数据集详解与YOLO训练实战指南 简介菲律宾水稻褐飞虱成虫目标检测数据集是一份面向农业视觉与植保智能化应用的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, encodingutf-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 python3.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 datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0拆开看每个参数的含义data指向第 2 章改好的 YAMLmodel用预训练权重加载的是 COCO 上预训练过的权重对褐飞虱这种小目标能提供一个不错的初始特征比从头训练收敛快很多epochs100是单类小目标的常用起点这类任务往往不到 30 轮就能达到不错的 mAP多跑几轮可以确认曲线稳定imgsz640是默认输入尺寸后面第 4 章会单独讨论该不该加大batch16依赖显存大小24GB 显存跑 YOLOv8n 可以开 32 甚至 648GB 显存用 16 或 8 更稳。device0指定用第一张显卡没有 GPU 就改成devicecpu。如果你的显卡够用把 nano 换成 small参数推荐值说明modelyolov8n.pt / yolov8s.ptnano 跑通链路small 涨点epochs100单类小目标通常 50 轮前收敛imgsz640 起步小目标可试 896/1280分辨率对褐飞虱影响最大batch168GB 以下用 8/4由显存容量决定device0 或 cpu多卡时写 0,1Ultralytics 包现在可以加载 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 fbph_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 modelruns/detect/train/weights/best.pt sourcedata/valid/images conf0.25 save_txtTrue这个命令会在跑完每张验证图后把预测结果以 YOLO 格式写到 runs/detect/predict/labels。这里的关键是 conf 参数——默认的 0.25 对褐飞虱这种细长小虫经常偏高一小部分真虫会被当噪声滤掉。我的习惯是先用 conf0.05 跑一遍人工抽看 10 到 20 张图数一下误报和漏报的比例再一点点把阈值升回去。单类检测有个容易被忽略的事实没有其它类别做干扰时模型倾向把长得像虫子的碎叶影、稻秆关节都报成阳性。这种误报不会显著拉低 mAP因为在验证集上这些位置本来就没有真值框精度指标会很好看但部署到田间会变成灾难。所以 mAP 达标只是底线真正的验收要看“误报率”这个指标后面第 6 章会专门讲。提示验证集推理时先 conf0.05 再逐档上调比一开始就按 0.25 更合适能看清模型在低置信度下到底“看到”了什么。4.3 训练图像尺寸与批次容量的取舍加大 imgsz 的同时显存会线性上涨。以 YOLOv8n 为例imgsz640 配 batch16 大约占 8GB 显存imgsz1280 时 batch 降到 4 可能刚好。优先保 imgsz因为分辨率对小型目标的收益比 batch 大得多。如果显存实在吃紧还有一个折中办法训练时用 imgsz768 或 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。如果只是个别文件直接把它们整体改写成类别 0import 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 个 epochmAP 全程是 0现象loss 正常下降训练过程无报错但 results.csv 里的 precision、recall、mAP50 全是 0。模型看起来像在学其实没在学。原因最常见是 data.yaml 里的 val 路径写错。比如实际目录叫 validYAML 里写了 valUltralytics 在验证阶段找不到标签就把验证集当成纯背景图导致 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 按同名规则找标签自然找不到。解决彻底一点把所有图片和标签都重命名为纯数字编号去掉中文和 hashcd 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 fbph_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, keylambda x: x[1], reverseTrue) 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 模型还有一份能解释清楚“哪里漏、为什么漏”的评估结论。希望帮到你。本文还有配套的精品资源点击获取