做智慧农业方向的项目,大部分时间其实不是耗在模型上,而是耗在“数据到底靠不靠谱”这个问题上。尤其做农业害虫检测,网上公开数据集翻来覆去就那几个,背景单一、虫体大、场景干净,拿到田间地头一测,漏检、误检一大堆。我最近整理了一套自采的农业害虫检测数据集,总共3200张,全部按YOLO格式标注,覆盖多种常见大田害虫和果园害虫,专门为YOLOv5/YOLOv8这类目标检测模型准备。这篇就按实际使用的角度,把数据集构成、标注细节、训练流程和踩过的坑一次性说清楚。
这套数据集的定位比较明确:不是学术benchmark那种动辄几万张的规模,但也不是随手抓来的网图堆砌。3200张图覆盖了5类常见害虫,包含大田作物、果树、温室蔬菜三种场景,每个场景都考虑了不同光照、不同角度、不同密度的情况。适合谁用?适合正在做智慧农业项目、需要快速验证YOLO模型效果的朋友,也适合学生拿来做毕业设计或者比赛。如果你已经跑通了YOLO的基础流程,这套数据可以直接拿来做迁移学习;如果你还是刚入门,跟着下面的实操步骤走一遍,也能把整个数据准备到模型训练再到部署的链路搞清楚。
1. 项目概述与数据集核心价值
1.1 为什么农业害虫检测这么依赖数据集
农业害虫检测和通用目标检测有个很大的不同——环境太复杂。田间的虫子小、颜色和叶片接近、经常藏在阴影里,而且同一片叶子上可能同时有好几种害虫。这种情况下,模型学的不是“虫子长什么样”,而是“在这个环境中虫子长什么样”。数据集如果只在实验室条件下拍,背景干净、虫体清晰,模型学到的特征到了地里就失效了。
我最初踩过这个坑。用某个公开数据集训练完之后,拿到大棚里测番茄上的蚜虫,检测框乱跳,把水滴、土块、甚至叶片边缘都当成虫,而真正的蚜虫却漏掉了一大片。原因就是训练集的背景太单调,模型把“绿色背景上的深色小点”当成了特征,而不是真正学习虫子的轮廓和纹理。所以后来做这套数据集的时候,我特别强调场景多样性,甚至刻意保留了一部分有轻微遮挡、光照不足、虫子趴着的图片,目的就是让模型在训练阶段就见够这些“难缠的样本”。
另一个现实因素是标注成本。害虫目标通常是密集的小目标,一张图上可能有几十上百个虫体,人工框选的工作量非常大。3200张图看着不多,实际标注下来,我算了算有效标注框超过了26000个,平均每张8个框左右,密集的图有几十个框。这个标注密度在农业数据集里已经算比较有代表性的了。
1.2 3200张图能做什么,不能做什么
很多朋友会问,3200张是不是太少了?其实要看场景。如果是从零训练一个模型,3200张肯定不够,YOLO在这种规模下容易过拟合。但实际操作中,我们几乎不会从零训练,而是基于COCO预训练权重做迁移学习,这就有本质区别了。YOLOv8的骨干网络在COCO上见过大量视觉语义,我们只需要在它的基础上微调,让它适应农业场景的具体特征。
用这套数据,我在YOLOv8s模型上训练200个epoch,mAP@0.5能做到0.82左右,mAP@0.5:0.95在0.55上下。这个水平,做田间初步监测和虫情预警已经够用了。当然,如果你需要的是那种毫米级精度的定量虫口统计,3200张肯定不够,至少得再补充几千张不同地点的图片,还需要实测校准。
这套数据适合做的方向大概是三类:一是害虫种类识别与计数,作为虫情测报的基础;二是农药喷洒装置的目标定位模块,指导喷头定点作业;三是教育演示和算法验证,跑通整个流程。不适合的场景是极端复杂的自然植保环境,比如需要区分害虫天敌、区分虫龄阶段这类细粒度分类任务,数据量还支撑不了。
2. 数据集构成与标注规范拆解
2.1 图像来源与场景多样性设计
数据集的图像全部来源于真实的田间和温室拍摄,没有用网络爬虫批量抓取,也没有用合成图来充数。拍摄设备以手机和普通数码相机为主,分辨率在1280x720到4032x3024之间不等。这个分辨率跨度其实是故意的,因为实际部署的时候,不同农户手里的设备、不同监控摄像头的分辨率差异也很大。模型只有见过不同分辨率的图像,才不至于在低分辨率输入时直接崩溃。
场景划分上,我按三类来组织。第一类是大田作物,主要拍的是小麦、玉米、水稻上的害虫,这部分图像背景相对开阔,有大量天空和土壤暴露,光照变化剧烈;第二类是果树类,主要拍苹果、梨、桃树上的害虫,背景比较复杂,常常有枝条、果实的遮挡,而且叶片形状多样;第三类是温室蔬菜,比如番茄、黄瓜上的蚜虫和粉虱,这类场景光照相对均匀,但叶片茂密,虫子喜欢聚集在嫩芽和叶背,对检测来说其实是难度最高的。
这三类场景的比例大约是4:3:3。为什么大田场景占更多?因为大田是虫情测报的主要应用场景,而且大田图像里背景误检率通常更高,需要更多样本帮助模型区分。温室的场景虽然数量少一些,但样本的密度高,特别是蚜虫聚集的情况,一组图片里往往包含大量目标。
2.2 YOLO标注格式与目录结构
这套数据集全部采用YOLO格式标注,就是标准的txt文件,每一行代表一个目标框,格式是:
class_id x_center y_center width height注意这些坐标值都是归一化的,除以了图像宽高,范围0到1之间。这样做的好处是跟图像分辨率无关,训练时YOLO读取原图后会自动映射回实际像素位置。
具体目录结构是这么组织的:
agri_pest_dataset/ ├── images/ │ ├── train/ 2450张 │ ├── val/ 450张 │ └── test/ 300张 ├── labels/ │ ├── train/ 对应txt标注 │ ├── val/ 对应txt标注 │ └── test/ 对应txt标注 ├── data.yaml └── classes.txt这里有个小建议:images和labels的目录名保持一致,最好都用train/val/test三段式。YOLO官方代码读取数据时,默认逻辑就是找images同级目录下的labels,如果你自己改过目录名,配置data.yaml的时候就容易出问题。
类别的划分也要提前想清楚。我的数据集包含5类:
| 类别ID | 名称 | 典型场景 |
|---|---|---|
| 0 | aphid(蚜虫) | 温室蔬菜、果树新梢 |
| 1 | locust(蝗虫) | 大田禾本科作物 |
| 2 | corn_borer(玉米螟) | 玉米叶腋、茎秆 |
| 3 | cotton_bollworm(棉铃虫) | 棉花、番茄果实附近 |
| 4 | spider_mite(红蜘蛛) | 果树叶片背面、温室作物 |
选择这几类的原因,一个是它们在农业生产中危害大、监测需求迫切;另一个是它们的形态差异明显,标注的时候框不容易混淆,适合作为YOLO学习的起步集。如果你的项目需要识别其他害虫,完全可以在这个类别表上扩展,但是注意新类别的数量、角度、背景也要尽量多样化,否则加进去反而会带偏模型。
2.3 数据划分策略与标注质量检查
划分数据的时候,我放弃了完全随机的做法,而是按“拍摄时间+拍摄地块”来划分。为什么?因为同一时间、同一地块拍的照片,背景和光照非常相似,如果这些图片既出现在训练集又出现在验证集,那验证分数会虚高,真正到了新地块就露馅了。按地块划分,虽然训练难度大一点,但验证集能更真实地反映模型的泛化能力。
标注质量检查是很多人容易忽略的环节。txt文件是纯文本,肉眼很难看出问题,所以我自己写了几个简单检查逻辑:
- 检查每张图对应的txt是否存在,避免训练时报错找不到标签;
- 检查所有框的坐标是否在0到1之间,超过范围的说明标注错误;
- 检查是否有空标注的图片,这类图片训练时可以保留,但需要设置好loss忽略逻辑;
- 随机抽几张图,把标注框可视化画出来,确认框是否紧贴目标边缘。
第四步看着麻烦,但其实特别有效。我抽图的时候发现过一批红蜘蛛的标注框整体偏大,把叶片边缘也包进去了,导致模型早期收敛特别慢。后来重新修正了那批标注,训练指标立刻就有明显提升。
3. YOLO模型训练实操流程
3.1 环境选择与安装
做YOLO训练,当前最省心的还是用Ultralytics的YOLOv8代码库,直接pip安装就行。PyCharm作为IDE完全够用,配合Python 3.9以上的环境,运行起来很顺畅。
我在一台显卡为RTX 4060 (8GB显存)的机器上做训练,配置基本流程是这么几步:
先创建虚拟环境,然后安装PyTorch和Ultralytics:
conda create -n pest_yolo python=3.9 conda activate pest_yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics装完之后可以快速验证一下:
python -c "import ultralytics; print(ultralytics.__version__)"如果返回版本号,说明环境没问题。如果你用PyCharm,记得把解释器指向这个conda环境,否则后面跑脚本的时候会莫名其妙的报“找不到模块”。
3.2 数据集配置文件data.yaml
YOLO训练前,关键是写对data.yaml。我的配置是这样的:
path: D:/datasets/agri_pest_dataset train: images/train val: images/val test: images/test nc: 5 names: ['aphid', 'locust', 'corn_borer', 'cotton_bollworm', 'spider_mite']path字段是数据集的根目录,train、val、test填的都是相对于根目录的路径。这里最容易踩坑的是路径分隔符,Windows下尽量用正斜杠,或者用path直接指到具体盘符,否则在Linux和Windows之间切换代码的时候,路径经常出问题。文件名和类别列表的顺序一定要和标注txt里的class_id严格对应,比如class_id为0的就是names列表里的第一个类。
3.3 训练命令与参数选择
在配置齐全之后,直接跑训练命令:
yolo detect train data=data.yaml model=yolov8s.pt epochs=200 imgsz=640 batch=16这里几个参数的选择,我是反复试过的。模型选yolov8s不是因为它精度最高,而是综合了显存和速度。yolov8n虽然轻,但小目标检测能力弱,田间场景本来就目标小,用n级会吃亏;yolov8m精度会好一些,但在8GB显存上batch只能降到8左右,训练速度明显变慢。最终用了s级,batch=16,显存占用大约6.5GB,算是在精度和速度之间比较平衡的选择。
imgsz我用了640而不是1280。为什么?害虫检测任务中目标确实小,理论上输入分辨率越高越好。但640是一般YOLO预训练模型的默认输入尺寸,迁移学习时输入尺寸和预训练分布一致,收敛更稳。如果你的显存有16GB以上,可以试试1280输入配合m模型,mAP大概还能再涨两三个点,但推理速度会明显下降。
epochs设200轮看起来多,但其实YOLO有早停机制,如果连续50个epoch验证指标没有提升就会自动停止。我实测跑完大概在第170轮左右自动停了。
训练过程中可以查看训练日志的关键指标。我习惯盯三个:train/loss、val/loss和metrics/mAP50。如果train/loss持续下降但val/loss不再变化,说明模型开始过拟合;如果mAP50超过0.8且还在缓慢上升,说明还没收敛完,可以多吃些epoch。
3.4 训练结果评估与推理验证
训练结束后,best.pt会生成在runs/detect/train/weights/目录下。先看验证集上的指标,再用测试集做一次推理。测试集和训练、验证集完全隔离,能反映出模型在没见过场景上的表现。
推理验证的时候,我用的是最简单的命令行方式:
yolo detect predict model=runs/detect/train/weights/best.pt source=D:/datasets/agri_pest_dataset/images/test conf=0.25conf设置的是置信度阈值,默认0.25。田间场景误检多,如果你的业务对误报零容忍,可以适当调高到0.4,但代价是少量害虫目标会被漏掉。这种取舍没有绝对标准,完全取决于你的应用场景:做预警可以放宽阈值,怕漏报;做精准喷药,误报带来的农药浪费更严重,阈值就得提高。
推理结果输出在runs/detect/predict目录下,逐张图检查检测框和置信度。要注意的是,conf只是过滤低置信度框的阈值,YOLO本身的NMS会进一步合并重叠框。如果你发现同一个虫体出现两个重叠框,可以适当提高NMS的iou阈值参数,我一般习惯把iou设为0.45到0.5之间。
4. 害虫场景下的针对性调优
4.1 小目标检测的三种有效策略
农业害虫检测最大的痛点是目标太小。3200张数据里,小于32x32像素的框占了将近一半。这种小目标在大分辨率图像上几乎只有十几个像素,卷积核下采样几次之后特征就消失了,模型学不到足够的信息。
我实际测试下来,有效改善小目标检测的办法有三个。
第一个是Mosaic数据增强。YOLO在训练时默认开启Mosaic增强,把四张图拼接成一张,相当于强制模型学习小目标在多种背景下的样子。但Mosaic增强在训练后期会导致模型定位不稳定,所以Ultralytics在最后10个epoch会自动关闭部分增强。这个机制默认就有,不需要额外设置,但你要知道它的存在,不要因为增强后的图像看起来很怪就以为数据处理错了。
第二个是提高输入分辨率到768或者896。如果显存允许,这一步比任何模型结构层面的修改都直接。分辨率提升一倍,小目标的像素面积提升四倍,特征强度完全不在一个量级。用768分辨率在8GB显存上batch只能设8,但训练出来的模型比640分辨率的mAP50普遍高2到3个点。
第三个是分块推理。对于超高分辨率图像,先把图切成几块,分别检测再合并结果。这个方案适合部署在田间的固定摄像头,因为拍摄位置固定,可以直接对固定区域做分块,不会因为目标跨块导致同一个虫体被识别两次。
4.2 不同害虫之间的类别不平衡问题
数据集的5类害虫,样本量其实并不均衡。蚜虫最多,占了接近四成;红蜘蛛最少,只有总数的一成多,而且红蜘蛛体积极小,肉眼都很难区分。这种天然的不均衡会导致模型偏向于多数的蚜虫,红蜘蛛的召回率明显偏低。
处理类别不均衡,光靠权重还不够。我最初试着在loss函数里调高红蜘蛛的权重,结果红蜘蛛是上来了,蚜虫的误检率却跟着涨了。后来改用数据增强策略,专门针对红蜘蛛做水平翻转、旋转、HSV色域扰动,把红蜘蛛的有效样本量扩了一倍。这样模型学到的是红蜘蛛本身的纹理特征,而不是单纯的类别频率偏差。
另外,我保留了约800张负样本图——就是画面上没有害虫的正常叶片、土壤、果实图。这些负样本在训练时没有对应标注,但模型会被迫在这些图上输出低置信度,对降低误检率有很大帮助。如果你的数据集里没有负样本,田间背景的误检会很头疼,这是很多项目组忽略的一环。
4.3 损失函数观察与收敛判断
YOLOv8的损失函数包含三部分:box loss(边框回归)、cls loss(分类损失)、dfl loss(分布焦点损失)。训练日志里显示的train/box_loss、train/cls_loss、train/dfl_loss就是这三项。我习惯每20个epoch看一眼它们的趋势。
正常情况:box_loss从初始值稳步下降,最后趋于平缓在0.8到1.2之间;cls_loss下降相对快,但如果你的类别容易混淆,比如蚜虫和红蜘蛛颜色相近,cls_loss的下降曲线会变得很慢,这时就要考虑增加特征明显的标注样本。dfl_loss是YOLOv8特有的,它反映的是边界框坐标分布的学习情况,数值偏大说明框的位置不稳定,通常是标注框本身不够贴合目标边缘。
如果一开始loss大幅震荡然后暴涨,最常见的原因是学习率太高。Ultralytics默认使用余弦退火策略,初始学习率0.01,如果出现爆loss,可以加个参数:
yolo detect train data=data.yaml model=yolov8s.pt epochs=100 lr0=0.005把初始学习率降一半,通常就能稳住。
5. 常见问题与排查技巧实录
5.1 训练完之后类别名总是对不上
这个坑非常常见。训练时候data.yaml里的names顺序是['aphid', 'locust', ...],但推理代码里如果用了旧的class列表,比如默认的COCO类别,那预测脚本就会把蚜虫当成“person”,蝗虫当成“car”。排查方法很简单:推理前先打印一下模型绑定的names,确认和data.yaml一致。最简单是在predict命令后面显式指定配置文件:
yolo detect predict model=best.pt source=test.jpg data=data.yaml加上data参数后,Ultralytics会从配置里加载正确的类别名。
5.2 显存不足或者训练中途OOM
8GB显存跑batch=16、imgsz=640刚好卡在边上,有时会中途报OOM。做法是把batch降到8,或者用真分数精度(AMP)训练。YOLO默认是自动混精度的,但如果你的CUDA和PyTorch版本不匹配,AMP可能没生效。可以这样强制开启:
yolo detect train data=data.yaml model=yolov8s.pt batch=8 imgsz=640 amp=True如果还想省显存,可以在data.yaml里设置cache=False,不缓存图像到显存。不过这个操作会增加磁盘IO时间,不推荐在机械硬盘上做,容易拖慢训练。
5.3 验证集指标好但田间实测效果差
这个问题大概率是数据划分出了问题。前面说过,按时间、地块划分数据非常重要。如果你发现验证集mAP有0.85,但一到新地块就崩,建议检查一下训练集和验证集是否混入了同一批拍摄照片。还有一个常见因素:验证集可能分辨率偏低,而田间摄像头输出的原始分辨率很高,模型没见过这么高分辨率的输入。
这时建议对齐部署时的图像处理逻辑,训练时imgsz和目标输入分辨率尽量一致,或者在部署代码里先对图像做缩放再喂给模型。还有一点,田间光线变化特别大,如果验证集都是在晴天拍的,而实际部署在阴天或者逆光环境下,效果也会下降。理想的做法是拍摄时尽量覆盖不同天气时段,训练验证的数据分布才接近真实。
5.4 边缘设备部署时模型太大
把训练好的YOLOv8s部署到Jetson Nano这类低算力设备上,8GB显存都不够,推理速度可能只有几帧。这种情况我建议换yolov8n,然后用这个数据集重新训练,模型大小能从22MB下降到6MB左右,推理帧率翻倍。或者做量化,把模型转成FP16或者INT8格式。INT8量化会损失1到2个mAP点,但对害虫这种目标尺寸相对固定、背景不算极端多变的场景,影响不算大。
如果设备算力实在弱,还可以考虑用TensorRT加速。Ultralytics官方提供了导出引擎:
yolo export model=best.pt format=engine device=0导出前置条件是本机安装了TensorRT且GPU架构与目标设备一致。这一步的坑在于引擎文件和显卡架构强绑定,在RTX卡上导出的engine,拿到Jetson上不一定能用,需要在Jetson本机上重新导一次。
6. 这套数据集后续还能怎么扩展
用到这一步,其实整套数据集的实战链路已经很完整了。但我在实际项目中还有一个习惯,就是持续累积“难例”。每次田间部署回来,把误检严重的图片挑出来,补充标注后定期加入训练集,重新微调。这样做三个月后,模型对特定地块的适应性会明显变强。这套3200张的数据集不是终点,而是基准,真正有价值的模型都是在一个可靠基线上不断迭代出来的。建议每个做智慧农业检测项目的朋友都建立自己的难例库,哪怕每周只有几十张图,长时间积累下来的效果远比一次性堆几千张网图要好得多。
另外,数据集本身还可以往多模态方向做扩展。比如同一张图上配套采集温度、湿度、作物生长期信息,训练时把这些信息作为辅助输入,预测害虫爆发风险。这个方向目前还有很大探索空间,但前提还是得把单模态的目标检测做扎实,不然多模态再花哨,基础识别不准,后面全是空中楼阁。
这套数据集的构建和训练经验,我几乎每天都在用。最大的体会就是:不要追求数据集数量上的好看,关键看你的任务场景和数据类型。3200张精标、分场景、包含负样本的数据,在智慧农业场景里的实用价值,可能比两万张随手爬来的模糊网图高得多。如果你手头正好在准备做农业害虫检测,这套流程可以直接按着走一遍,先把模型跑起来,再根据你手边的实际拍摄数据去迭代扩展,很快就能看到效果。