1. 为什么我决定亲手做一份番茄叶片检测数据集
做智慧农业项目的人应该都有过这种体会:明明公开数据集一抓一大把,但真到了自己棚里,模型就各种“水土不服”。这几年我一直在做大棚作物的视觉检测,用的目标检测框架是YOLO,前期为了省事,试着拿通用农作物数据集直接训,结果拿到番茄大棚里一测,漏检率直接让我怀疑人生。后来我才明白,不是YOLO不行,而是数据集本身和实际场景压根不匹配。
市面上的公开农业数据集存在几个通病。第一,作物种类杂,很多数据集把番茄、辣椒、黄瓜混在一起,每一类的样本量都不够,模型训练时容易“顾此失彼”;第二,图像背景太干净,实验室或网图占了很大比例,叶片是干净的、背景是纯色的,可真实大棚里的叶片有遮挡、有阴影、有滴灌带、有杂草,模型没见过这种“脏乱差”的环境;第三,病害类目定义混乱,同样是叶片黄化,在不同数据集里可能叫缺素、可能叫早衰、甚至被归到病斑里,这种标注不一致会把模型训练过程搅得一团糟。
所以去年我开始做一个专门针对番茄健康叶片检测的数据集,最终整理出3100张图像,并用YOLO格式做了标注。这份数据集的定位很明确:只服务于番茄叶片的状态检测,覆盖健康叶片、常见叶部病害、缺素黄化和虫害这几类,重点解决“棚里能不能用”的问题。
这篇内容不是单纯分享一个数据集链接,我更想把从采集、筛选、标注到标YOLO训练的全过程记录下来,包括中间踩过的坑和调整思路。如果你也在做智慧农业方向的目标检测,或者想自己做一份能落地的作物数据集,可以参考下我的做法。
这个数据集适合谁?一种是做农业AI算法开发的工程师,训练检测模型时需要现成数据;另一种是农学背景、想自己跑通YOLO流程的研究人员;还有一种就是和我一样在棚里搞巡检机器人、做移动端病害诊断的实践者。三种人的需求不一样,但底层都离不开一份靠谱的数据集。
2. 3100张数据集的前期准备:采集路线、设备选择与筛选逻辑
2.1 采集地点怎么选:不要只盯着一类大棚
很多人做数据集喜欢图省事,去一个温室大棚拍一天就完事。但你要知道,模型最怕的就是“源分布单一”。如果所有训练图都是在同一个大棚、同一种光照、同一个相机角度下拍的,模型学到的可能不是“番茄叶片长什么样”,而是“这个大棚里的番茄叶片长什么样”。拿去别的棚一测,光照一换、背景一变,精度立刻崩。
我这次采集跑了三个地方:一个是连栋温室,光照均匀、环境整洁,适合拍健康叶片和早期病症;一个是农户的普通拱棚,光照杂乱、有遮荫网、地面有地膜反光,叶片上还经常粘着灰尘和水渍;还有一个是露天试验田,光照强烈、风大、叶片晃动严重,能拍到逆光和阴影条件下的叶片状态。这三个场景加起来,基本覆盖了番茄种植最常见的环境。
每块采集点我都按“早、中、晚”三个时段各拍一轮。早上的露水还没干,叶片上会有水珠,能模拟喷灌后的状态;中午顶光强,能拍到高光和阴影对比;傍晚光线柔和,是多数移动端巡检设备的实际工作时段。
2.2 设备不用太贵,但有一个参数必须达标
拍数据集的设备我用的是两部手机加一台微单,手机主摄都有4800万像素,微单用来拍微距特写。很多人纠结要不要上工业相机,我的看法是没必要。目标检测数据集追求的是“贴近真实部署环境”,你的部署端是什么摄像头,采集端就尽量接近那个画质水平。巡检机器人搭载的也就是普通工业USB相机或网络摄像头,画质和手机主摄差不多甚至更低。
但有一个参数必须注意:分辨率不能低于1920×1080。YOLO训练时会统一缩放到640×640,如果你原图只有800×600,放大后边缘纹理丢失,小病斑就更难检测了。我实际采集时统一用最大分辨率保存,微距特写用4K,棚内全景用1080P以上,这样后续做裁剪和增强时有充足的像素余量。
拍摄角度也有讲究。不要只拍俯视平铺的叶片,真实场景中巡检摄像头大概率是从侧面或斜上方看的,叶片之间互相遮挡很常见。所以我每片目标区域都会从正上方、45度斜角、侧面三个角度各拍几张,保证姿态多样性。
2.3 从几千张素材到3100张:我的清洗规则
采集工作结束,素材总量大概到了5000多张,但最后只留下3100张。中间淘汰了将近一半,淘汰标准很明确:
- 模糊图片:手持拍摄时轻微抖动,放大后叶脉纹理不清晰,这种直接删;
- 过曝和欠曝:叶片主体一片死白或死黑,再好的标注也救不回来;
- 非目标物体占主体:比如拍到了大棚骨架、地面杂草,或者人像占了半幅画面;
- 重复帧:连续拍摄中构图几乎相同的,只保留清晰度最高的那一张;
- 叶片占比过小:叶片在整幅图里占不到5%,这类适合做背景而不是训练样本,删。
清洗还有一个容易被忽略的点:别只留下“完美的”叶片。很多做农业数据集的人习惯挑健康、完整、光线好的叶子,结果模型在真实场景里稍微遇到遮挡就失灵。我特意保留了一批有阴影、有遮挡、叶片边缘有残缺、背景里混着滴灌带的图像,这些“不完美样本”反而是模型泛化能力的关键。
清洗后的数据集按拍摄场景做了分层,保证三个采集点的图像比例大致均衡,避免模型因为某个场景样本过多而产生过拟合。
3. 类目设计与标注规范:决定模型上限的关键一步
3.1 类目划分,我最终定了这四类
做目标检测数据集时,类目设计是最容易拍脑袋、也最影响模型上限的环节。一开始我也想做得“全”,把番茄常见的早疫病、晚疫病、叶霉病、灰霉病全都单独建类,甚至想细分到病原菌级别。后来标注了几百张后我放弃了——很多病害在叶片上的初期表现极其相似,标注员根本分不清,模型更学不会。
我最后把类别收敛为四类:
| 类别名称 | 定义说明 | 典型特征 |
|---|---|---|
| healthy_leaf | 健康叶片 | 叶色均匀、无明显病斑和变形 |
| leaf_spot | 叶部病斑类 | 叶片上出现圆形或不规则病斑,颜色为褐、黑、灰 |
| yellowing | 缺素黄化 | 叶片整体或叶脉间黄化,没有明显坏死斑 |
| insect_damage | 虫害损伤 | 有虫咬缺口、虫粪、白色网丝或卷曲变形 |
这四类基本覆盖了番茄叶片在田间最常见的状态,而且类与类之间的视觉差异相对明显,模型区分起来难度适中。对于叶霉病、早疫病这类难以和普通叶斑区分的病害,我统一归入leaf_spot,不强行细分。
还有一个类目设计的细节值得说说:要不要加background类?我的做法是不加。YOLO本身会把图像中未标注的区域当作背景处理,单独建一个background类反而会让负样本比例失衡。如果你想过滤无关物体,可以在推理阶段用置信度阈值控制,不需要在训练阶段增加类目。
3.2 标注工具选型与标注边界约定
标注工具我推荐X-AnyLabeling,它比老牌的LabelImg好用太多,支持自动保存、快捷键标注,还能调用预训练模型做自动预标注,省不少力气。如果你习惯在线标注,Roboflow也可以,它可以自动做标签统计分析,但是需要上传云端,对农业数据的隐私性要求比较高的项目慎用。
我用的标标注流程是:先用X-AnyLabeling打开一批图片,通过预标注模型生成初始框,人工再逐张修正。这个流程比纯手动画框效率至少翻一倍。
但工具只是辅助,真正决定数据集质量的是标注规范。我把规范写清楚,并强制多名标注员统一执行:
- 目标框贴着叶片边缘,不加额外边距,也不截断叶片内部;
- 同一叶片同时有病斑又有虫眼时,只标面积占比大的那一类,避免一框两义;
- 叶片遮挡严重时,只标可见部分,不脑补完整叶片形状;
- 病斑面积占叶片总面积超过5%才标leaf_spot,低于5%的不标,避免大量微小噪声框;
- 黄化和病斑并存时,优先标病斑,因为病害的经济危害程度更高。
这套规则单独拿出来看都很简单,但实际执行中最大的问题是对“轻微黄化”的尺度把控。同一个叶片,标注员A觉得算yellowing,标注员B觉得颜色还算正常,这种标注意见分歧如果不解决,训练出的模型会在临界区域一直摇摆。
解决方法是做了一次“标注判例库”。我把容易有分歧的100张图抽取出来,专人逐张定夺,把“黄化与健康的边界案例”“病斑与虫伤的边界案例”固化下来,作为后续标注的参考图集。每个新加入的标注员上岗前先看判例库,再试标50张,准确率达到要求后才正式开工。
3.3 二次校验:用统计手段发现标注异常
人工标注完成后,我做了两轮质量校验。
第一轮是抽样目检。从3100张图里随机抽了300张,把标注框叠加显示在图上,逐张看框的位置、大小和类别标签是否匹配。重点关注两类问题:框是否严重偏离目标、类别是否张冠李戴。抽样发现的主要问题是小病斑漏标,个别图像中叶片的病斑面积不小,但标注员只标了最大的几个,漏掉了中等的,这类问题靠抽样能发现,但效率不高。
第二轮是统计校验。我把标注结果的JSON/YOLO txt文件全部解析出来,统计每个类别的样本数量、目标框宽高比分布、目标框面积占比分布。有个非常典型的异常信号:如果leaf_spot类目标框的面积占比普遍偏小,说明标注员在“小病斑要不要标”上又回到了随意状态。此时我会把这个类别的样例重新拉出来,组织二次补标。
这里多说一句,标注框的宽高比分布也很值得看。番茄叶片大多是横向伸展的,如果大量框的高远大于宽,说明标注员可能把竖着的叶片截得太紧,或者把相邻叶片错误地包含进一个框,这些都需要回头修正。
4. YOLO训练配置与调参实践:从默认参数到真正能用的模型
4.1 数据划分:别让同源图片串了“答案”
数据准备好后,第一件事不是急着训练,而是做划分。我见过太多人直接用随机函数把train/val一拆就完事,结果同一批拍摄序列里的连续帧分别跑到了训练集和验证集里。这种划分在农业数据上会造成严重的“数据泄漏”,因为连拍帧之间构图几乎相同,模型相当于提前看到了验证题的答案,验证指标虚高,一到新场景就现原形。
正确的做法是按采集单元划分,而不是按单张图片划分。我的数据集中,同一个拍摄点、同一株番茄、同一时段连拍的图像归为一个单元,整个单元要么全部进入训练集,要么全部进入验证集。这样划分后,训练集2100张、验证集600张、测试集400张,比例大约是7:2:1。测试集单独锁起来,只用一次,留给最终评估。
4.2 模型选型与超参数:从YOLOv8n开始
框架我选了YOLOv8,原因很实际:生态成熟、文档齐全、部署方便,不管后续导出ONNX还是跑在Jetson上都很顺手。版本方面建议直接用ultralytics的最新release,自带预训练权重,方便做迁移学习。
模型体量方面,不要一上来就选最大的x模型。农业叶片检测任务的目标尺寸通常偏小,叶片本身不算大,病斑更小,yn系列的浅层特征图分辨率更高、对小目标反而更友好。我先用yolov8n.pt跑了一轮基线,确定数据没问题后再往s、m的规模试。
训练命令我用的核心参数如下:
yolo detect train \ data=tomato_leaf.yaml \ model=yolov8n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ patience=20 \ project=./runs/tomato \ name=leaf_det_v1 \ cache=True \ device=0数据配置文件tomato_leaf.yaml里要重点检查path、train、val、test路径是否正确,names顺序要和标注的类别索引完全一致。这个文件写错一步,训练出来的模型类别标签就会错位,最后推理时出现“健康叶片被标成病斑”之类的乌龙。
几个超参数的选择理由我展开说一下:
- imgsz=640是YOLO系列的标准输入尺寸,兼顾精度和速度。如果计算资源允许,想提升小病斑检出率可以试1024,但训练时间会明显增加;
- batch=16在8GB显存下刚好合适,显存不够就降到8,不要硬撑;
- patience=20是早停参数,验证集指标连续20个epoch不提升就停,这套数据大约在90到110个epoch之间达到最优,早停能避免过拟合;
- cache=True把图像缓存到内存里,第一次训练会慢一点,后续迭代速度明显提升。
4.3 评估指标怎么读:别被mAP50骗了
训练结束后我最先看的是mAP50和mAP50-95。mAP50是一个相对宽松的指标,只要预测框和真实框的IoU超过0.5就算命中,做农业巡检这个指标很容易冲到0.9以上。真正考验模型的是mAP50-95,它要求预测框和真实框的重合度非常高,这直接关系到病斑位置估算的准确性。我的基线模型mAP50做到0.91,但mAP50-95只有0.72,说明框的位置还不够准,后续还需要优化。
除了mAP,一定要看混淆矩阵。混淆矩阵能告诉你模型具体在哪些类之间混淆。我的模型刚训出来时,leaf_spot和insect_damage之间误检率偏高,原因是虫害叶片上经常有褐色坏死组织,视觉上和病斑重叠。后来我在标注规范里增加了一条“虫害和病斑并存时优先标病斑”,才把这个混淆压下去。
还有个细节:农业检测任务里,漏检和误检的代价是不同的。病斑漏检意味着病害扩散风险,虫害误检成病斑可能造成不必要的用药。所以在选择置信度阈值时,不能只盯着默认0.25,我会用验证集跑一遍precision和recall曲线,根据实际应用场景决定阈值。如果目标是田间快速筛查,我会把阈值调低到0.15,优先保证召回率;如果目标是自动打药决策,阈值上调到0.35,优先保证准确率。
5. 训练过程中踩过的坑与数据质量验证
5.1 坑一:验证Loss震荡不下降,根因是标注不一致
第一版模型训练了大概30个epochs后,训练集Loss正常下降,但验证集Loss像锯齿一样上下跳动,mAP也一直提不上去。我排查了学习率、batch size、数据增强等多个因素,最后把训练集里的标注框调出来可视化,才发现问题出在yellowing类上。
一部分标注员把“轻微黄化”标成了yellowing,另一部分只标“重度黄化”,导致同类目标框内的视觉内容差异极大,模型训练时来回横跳。我重新定义了yellowing的判定标准:叶脉间黄化超过叶片总面积40%才算yellowing,否则视为健康叶片。按新标准把yellowing类全部重新过了一遍,重训后验证曲线立刻平滑了很多。
这个坑给我的教训是:确认数据标注一致性,比调任何超参数都重要。一旦发现Loss异常,先看数据、再看标注,最后才考虑模型和参数。
5.2 坑二:Mosaic增强对小目标不友好
YOLOv8默认开启Mosaic增强,它对大目标检测效果很好,能大幅丰富背景多样性。但对于番茄叶片这种“目标稀疏、小目标多”的数据集,Mosaic反而帮了倒忙。
原因不难理解:Mosaic把四张图缩小拼接,原本就很小的病斑经缩放后可能只有几个像素,模型根本学不到有效特征。我在训练时发现,虽然数据增强让mAP50有所提升,但mAP50-95反而下降了,说明小目标的定位精度变差了。
解决方案是把Mosaic增强比例从默认的1.0降到0.5或直接关闭。ultralytics中可以在hyp.yaml里修改mosaic参数,或者在训练命令行加mosaic=0.5。关闭后小目标检测能力明显回升,代价是背景多样性略降,但靠我前面采集时特意保留的“不完美样本”基本能弥补。
5.3 数据质量验证的三种实用手段
训练完成后,我习惯用三种手段验证数据集和模型质量,缺一不可。
第一种是回灌验证。把训练集图片重新喂给训练好的模型,逐张检测,统计漏检率。如果模型在训练集上都有明显的漏检,大概率是标注漏标或目标本身太小,需要回补标注;正常情况训练集回灌漏检率应该接近0。
第二种是特征图可视化。用Grad-CAM生成模型关注区域的热力图,叠加在原图上。健康叶片模型重点关注叶脉纹理,病斑类模型重点关注病斑边缘,如果热力图关注区域和人类认知明显不符,说明模型学到的特征有问题,需要回查数据。
第三种是真机部署测试。把模型导出ONNX后部署到大棚摄像头旁边,让它跑一整个白天。这个测试不做任何处理,就看模型在真实光照变化、叶片晃动、摄像头抖动下的表现。我实测发现模型下午三四点钟容易出现漏检,后来定位到是侧光造成的叶片阴影干扰,靠补充下午时段的训练数据解决。
6. 智慧农业落地时的表现与后续扩展思路
6.1 从实验到田间的表现落差
模型在验证集上mAP50做到0.91,当时我觉得这个问题算是解决了,结果架到大棚里跑了半天,实际检出率只有七成多。落差来自几个地方:棚里的光线比数据集里的更复杂,逆光时叶片边缘有强烈光晕;摄像头的位置比手机低,视角更仰;风机一开,叶片一直在晃动,图像有轻微运动模糊。
对待这种落差,我现在的态度是:不要想着训练集里加几个增强就能解决,最好的办法是采集“目标部署环境”的数据,做一次小规模的针对性补充训练。我把大棚摄像头的现场视频按帧抽了400张,用已有的模型做预标注,人工修正后混入原训练集,再做一轮20个epoch的微调。这个流程走完后,现场检出率回升到了八成以上。
如果你也想做类似的闭环迭代,这里有个小建议:现场采集的视频不要一录完就删,按时间段、天气、光照状态存好,每次模型升级后都用同一批回放视频做回归测试,确保模型在修好一个问题的同时没有把另一个问题搞坏。
6.2 数据集的后续扩展路径
目前这份3100张的番茄健康叶片检测数据集,定位是“能快速用起来的基础版”。后续我有三个扩展方向:
第一个方向是病害细分。现在leaf_spot类把多种病害合并在一起,对于预警是够用的,但如果要指导精准用药,需要细分到早疫病、晚疫病、叶霉病等具体病种。这要求每类病害的样本量至少再翻一倍,并且要请植物病理专家对标注做背书。
第二个方向是分割标注。YOLOv8本身支持实例分割,在检测框的基础上给叶片勾轮廓,能提供病斑面积占比等更精细的信息。我已经开始对部分叶片做分割标注,输出格式为多边形坐标,后期可以训练yolov8-seg模型,直接输出病斑面积和占叶比。
第三个方向是多模态结合。番茄叶片黄化的原因很多,缺氮、缺镁、缺铁在可见光图像上差异不明显,但如果结合多光谱相机采集的NDVI数据,就能在模型层面区分更多生理性障碍。这个方向目前还在实验阶段,需要硬件配合,但一旦跑通,智慧农业的检测就不只是“看见什么病”,而是“预判将要生什么病”。
我在实际做这个数据集的过程中,最深的一点体会是:做农业数据集,最难的不是技术,而是你能不能持续盯着真实场景看。蹲在大棚里拍叶片、分病斑、抠黄化边界,这些事情看起来琐碎,但每一条规范、每一张图背后的取舍,最后都会在模型精度上得到回报。如果你也在计划做自己的作物检测数据集,不必一上来就追求上万张的大规模,先用几千张把采集、标注、训练的闭环跑通,再去滚雪球扩大数据量,这条路更稳也更省成本。