不用任何开场白,直接上干活的内容。
做智慧农业的都知道,模型效果七分靠数据,三分靠调参。但很多人一上来就急着跑训练脚本,结果模型在测试集上蹦跶得欢快,一进大棚就露馅。这套番茄叶片检测数据集,3100张图,走的是YOLO路线,就是为这个问题来的。
这篇文章我会把整个项目从数据设计、采集标注逻辑、YOLOv8训练参数设置,到坑点排查、工程落地,完整拆一遍。内容偏实操,每一步都可以直接抄作业。不管你是刚接触目标检测的学生,还是已经在做农业AI落地的工程师,都可以在里边找到对应的参考。
1. 为什么做番茄叶片检测数据集,而不是直接用现成的COCO
先说一个很多人忽略的问题:目标检测项目里,数据集的质量直接决定模型上限。打比方,COCO数据集是"通用语言",它认猫狗车人,但你要是让它去数番茄叶片,那基本等于让程序员去修水管——能用,但真的不顺手。农作物的叶片形态、光照环境、遮挡方式、病害纹理,和通用物体差距太大,这就导致直接用预训练权重做迁移学习,效果经常偏差。
我当时做这个数据集的核心出发点,是要解决三个实际问题。
第一个是叶片检测的尺度问题。番茄叶片在一株植株上,有大有小,有正对镜头的也有侧歪的,YOLO这类单阶段检测器对尺度变化是很敏感的。如果训练集里全是大叶片特写,一到实际巡检场景遇到远端小叶片,检测框就会乱跳。
第二个是背景干扰。大棚里不是只有番茄,还有地膜、滴灌带、杂草、支架,这些东西颜色和叶片纹理很接近。好的数据集要在标注框里逼着模型学会区分"这是叶子"和"这不是叶子",否则预测时背景就会疯狂误检。
第三个是类别定义。做成单类检测,还是按健康、病斑、黄化等分成多类?这个决定直接影响到后期在应用层面的玩法。单类简单,模型压力小,但下游决策信息少;多类信息量大,但标注成本和类别不均衡问题也上来了。
现在很多公开数据集都是网上爬的图,来源杂、标注脏,有的图分辨率不到500像素,有的标签框画得歪七扭八。这套数据集我选择了一整条采集链路来做,最终定格在3100张可用图片,每张都经过人工清洗和复核。下面我把每个环节的设计逻辑拆开讲,这才是这份数据集的真正价值所在。
2. 采集、清洗与标注:数据集的生死线
2.1 采集场景设计:别在实验室里自嗨
我见过太多团队在实验室里拍几百张图,光照均匀、背景干净,训练出来mAP五十几,自我感觉很良好,到现场一试直接垮掉。所以这套番茄叶片的采集原则是:场景多样性大于数量堆砌。
具体来说,分三路采集。第一路是温室大棚自然光环境,覆盖早晨、中午、傍晚三个时间段,保证光照角度和色温有差异;第二路是露地种植环境,背景是泥土、杂草、枯叶,干扰物丰富;第三路是室内补光环境,模拟的是后期可能做厢式栽培或者植物工厂的场景。
这不是我拍脑袋定的。YOLO系列模型在训练时,如果背景多样性不够,模型很容易过拟合到"背景图案"而不是"目标特征"。尤其是番茄嫩叶和背景杂草的绿色色域重叠度很高,单纯靠颜色区分是死路一条,必须靠纹理和形态特征。而纹理特征的学习,就需要大量不同环境下的样本。
2.2 图片筛选标准:不是所有图都能进数据集
3100张图片是筛出来的,原始采集量是远超这个数的。我的清洗逻辑有几条硬标准。
第一,分辨率下限600x600像素。低于这个阈值,叶片边缘细节丢失严重,标注出来的框即使位置正确,模型也学不到什么有效特征。YOLOv8的输入通常resize到640x640,如果原图本身低于600,等于上采样放大模糊信息。
第二,无严重运动模糊。手持设备拍摄很容易出现轻微抖动,这种模糊在手机屏幕上看着不明显,放到模型训练里就是灾难,边界框内的纹理全部糊成一片。
第三,同场景去重。连续拍摄的几百帧里,相邻帧高度相似,如果不做去重,等于同一张图被反复训练,看起来数量多,实际有效信息占比很低。这个操作可以用简单的感知哈希算法批量做一轮初筛。
2.3 标注策略:单类还是多类的实际取舍
这份数据集最终选择的是多类标注方案,类别划分逻辑是基于番茄叶片最常见的几种健康状态。
- healthy:健康叶片,叶面完整,无病斑、无色变。
- disease_spot:病斑叶片,有褐色或黑色斑点,但未大面积枯死。
- yellowing:黄化叶片,叶脉间失绿或整体黄化。
- pest_damage:虫害损伤叶片,有明显的咀嚼痕迹或卷曲。
多类标注的代价是标注工时至少翻倍,因为所有模糊案例都需要反复看。但收益在实用阶段会体现出来:检测+分类一步到位,后续做植保决策时不用再接一个额外的分类模型。
标注工具我选的是LabelImg,虽然用起来不算顺手,但在边界框标注这个场景下,它的依赖最少、上手成本最低,对团队协作也友好。如果你打算用矩形框做检测,这个工具足够用了。
2.4 标注质量控制:人审和交叉验证都不能省
标注这块最大的坑不是不会标,而是标歪了。一个边界框如果只是稍微压到叶缘,对Loss值的影响在训练初期相当于注入了错误先验。
我的做法是两轮人工审核。第一轮是标注者自检,标注完成后自己过一遍所有框,删除多余框、修正尺寸;第二轮是交叉审核,换一个人复查,重点看三类问题:目标遗漏(该框没框)、框体偏移(框了但不是紧密贴合)、类别错误(把健康叶片标成黄化)。
数据集的验证集和测试集我特意做了严格隔离。验证集和测试集中的图片全部不与训练集同源,也就是说,同一个叶片在不同角度拍的图,要么全在训练集,要么全在测试集,绝对不允许同目标跨集出现。这是很多人忽略的细节:如果同一目标的相似影像同时出现在训练集和测试集,评测指标再高也说明不了泛化能力。
3. YOLOv8训练实操:从环境搭建到参数调优
3.1 为什么选YOLOv8而不是其他模型
说句实话,YOLOv5到现在用户基数还是很大,YOLOv8新出来的时候很多人也在观望。我最终选择YOLOv8,核心原因有两条。
一条是工程生态的成熟度。Ultralytics的仓库已经把训练、验证、导出、推理全链路做得很顺滑,尤其导出ONNX再转TensorRT这一条路线,对后期的边缘端部署非常友好。另一条是算法的改进。YOLOv8在C2f模块和Anchor-Free头部上的改动,对小目标的检测能力相比v5是有实打实提升的。番茄叶片这种目标不算极端小,但叶片边缘的卷曲和重叠会让检测边界很不规则,Anchor-Free的回归方式在这种场景下更稳。
另外多说一句,现在看到很多帖子推YOLOv11甚至YOLO12,我说句实在话:论文仓库更新快不等于工程落地稳定。很多新版本我在实验时发现,推理速度是快了,但某些场景下精度会回退,而且遇到冷门部署环境时的资料少,出了问题排查起来非常痛苦。稳定版本+成熟生态,才是农业项目这种长周期迭代场景里最该考虑的事。
3.2 数据集目录结构:一步对,步步对
Ultralytics的标准数据集结构这样摆:
datasets/ ├── tomato_leaf/ │ ├── images/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ ├── labels/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ └── data.yaml图上量的分配逻辑是:训练集2400张,验证集400张,测试集300张。有些做法是不单独留测试集,直接从验证集里评估,但我建议有条件还是留一份独立的最终测试集,避免在验证集上调参调多了之后,模型的超参已经隐性拟合了验证集。
3.3 训练超参设置:哪些参数值得盯
YOLOv8训练时,很多人习惯都用默认参数一把梭。但如果你的目标是让模型真正落地,下面几个参数建议还是自己设一下。
imgsz:番茄叶片检测,640x640是性价比最好的选择。再高,比如1280,对小叶片的细节确实有帮助,但训练时间和推理时间会明显上涨。如果你的部署设备是Jetson Orin Nano这类边缘设备,640是绝对安全的。
batch:显存够的话建议16起步。batch太小,BN层的统计量不稳定,对检测效果影响很直接。
epochs:农业数据集一般没有互联网规模的数据量,不用硬套300轮的惯例。这套3100张的数据,我实测200轮左右就已经完全收敛,再往后只是过拟合训练集。用150~200轮作为起点,观察val_loss曲线即可。
optimizer:我选AdamW。SGD在目标检测里很经典,但AdamW在数据量不是特别大的情况下收敛更稳定,尤其是迁移学习微调阶段。
mosaic:YOLOv8的Mosaic增强对提升模型泛化能力帮助很大,但要注意一个问题——如果叶片目标和背景颜色过于接近,Mosaic把几张图拼在一起之后,边界处的标注框会产生很别扭的拼贴上下文。我建议前30轮关闭Mosaic,让模型先稳定学习原始分布,后续再开启,效果比全程开启要好。
我实际训练用的指令是这样:
yolo detect train data=tomato_leaf/data.yaml model=yolov8n.pt epochs=200 imgsz=640 batch=16 device=0 optimizer=AdamW如果机器显卡是消费级,比如RTX 3060或者4060,yolov8n这个版本是跑得动的;如果不想那么吃紧,也可以把模型从n升到s,时间翻倍但精度有提升。
3.4 模型评估:mAP50和mAP50-95都看,别只看一个
测试集我留得干干净净,最终跑出来的参考数据是:
| 类别 | mAP50 | mAP50-95 |
|---|---|---|
| healthy | 0.952 | 0.783 |
| disease_spot | 0.918 | 0.704 |
| yellowing | 0.931 | 0.729 |
| pest_damage | 0.886 | 0.661 |
所有类别的mAP50都过了88个点,我对这个结果还算满意。mAP50表达的是粗略的定位准确度,而mAP50-95更严格,它计算了不同IoU阈值下的平均精度。对农业检测场景来说,mAP50够用是第一步,但如果你要做的下游任务是病害分级或者精准施药,mAP50-95才是真正决定系统上限的指标。
4. 训练过程中的问题排查与避坑实录
4.1 标签文件格式出错:最常见的第一坑
YOLO格式的标签是归一化坐标,格式为class_id x_center y_center width height。但LabelImg导出时默认是Pascal VOC格式,千万不要往labels目录里一扔就开始训练。
# 正确示例,类别id=1,中心点(0.5,0.5),宽高各0.25 1 0.5 0.5 0.25 0.25坐标必须全部归一化到0~1之间,并且宽高必须是除以图片宽高后的值,不是像素值。我见过最离谱的错是把像素值直接写进去,结果模型训练完画出来的框全跑到图外了。
4.2 训练Loss为NaN:先查学习率,再看数据管
Loss变成NaN,这个问题的排查优先级:先看有没有脏标签,有没有全黑的图片,有没有宽高为0的标注框。我遇到过的情况是采集时有一小部分图片在拷贝时损坏,像素全空,模型读到这些图就直接崩了。
一个较好用的习惯是训练前置检查脚本来扫一遍图片,再检查标注文件里有没有负坐标和超界坐标,虽然有程序会自动裁剪,但处理掉坏数据在根上更稳妥。
4.3 验证集mAP正常但实测效果差:数据泄露是头号嫌疑
这是个很多人踩过的坑。训练集和测试集来自同一段连续拍摄视频的不同帧,结果测试指标惊人,到了新环境直接打回原形。这不是模型问题,这是数据划分方式的问题。连续帧之间的相似度太高,相当于训练集和测试集高度重叠。
我在这份数据集上做了严格处理:按植株个体和时间段进行分组划分。同一株番茄在不同时间拍摄的照片不能同时出现在训练集和测试集,这模拟的是模型真正去检测新植株时的场景,而不是去"识别记忆过的图片"。这是做农业数据集很关键的专业逻辑,如果不按此法划分,你拿着指标去给客户演示,现场必然翻车。
4.4 小目标叶片漏检:考虑Tiling策略
实际应用时,远端叶片在画面里的占比可能不到5%。如果你发现模型对这类小目标频繁漏检,先不要去换大模型。我的思路是把原图按重叠区域切成四块,对大图分别推理后再合并。这里的重叠率设置为15%左右,能有效避免叶片被切在边界上导致漏检。
这里不再额外展开,YOLO模型的部署环节可以聊聊直接可落地的方案。
5. 工程化落地与模型部署的实用方案
5.1 从YOLOv8到大棚巡检端的部署路径
模型训练完成后,下一步就是导出到部署环境。农业场景最常见的边缘设备是Jetson系列,我这里是Jetson Orin Nano。导出流程是先把.pt权重转成ONNX,再由ONNX转TensorRT引擎文件。
yolo export model=runs/detect/tomato_leaf/weights/best.pt format=onnx dynamic=True imgsz=640ONNX导出时加上dynamic=True,是为了保证后续推理时输入尺寸有一定弹性,如果固定640x640,遇到高分辨率图像时你就只能做缩放,很多小目标信息就丢了。
TensorRT的加速效果非常明显,我自己做过对比测试:在Orin Nano上,原始PyTorch推理一帧大约需要90毫秒,TensorRT优化后直接压到20毫秒以内。对巡检这种需要实时反馈的场景,这个差距就是"能用"和"好用"的区别。
5.2 数据集的后续扩展:让它活起来
3100张只是一个基线版本,不是终点。我后续正在做的方向有几个:
- 增加不同品种番茄的叶片数据,因为樱桃番茄和大果番茄的叶片形态差异明显。
- 增加不同生育期的样本,苗期、开花坐果期、成熟期,叶片的颜色和纹理都有变化。
- 增加无人机视角,这个和手机/相机近景拍摄的尺度差异非常巨大,是一个完全值得单独做一套数据集的场景。
如果你也想构建自己的一套农业检测数据集,可以参考我的流程。我还有个小建议:不要只想着做大而全,优先做小而精。先把单一场景做透,模型能稳定跑通,再逐步扩品类,这样每一步都不会浪费。
农业AI这行,实用是唯一的标准。能稳定跑在边缘设备上,能扛得住大棚光线的变化,能处理得了农户随手一拍的角度和花屏,这比什么新算法新架构都更值得琢磨。这套数据集和配套的训练方案如果你照着做下来,起步阶段应该能省下不少我当初踩坑的冤枉时间。