种过番茄或者去过大棚的人都知道种地最难的不是浇水施肥而是“看叶识病”。叶子发黄可能是缺水可能是缺素也可能是早疫病、晚疫病、叶霉病——等肉眼能确认的时候往往已经蔓延开来了。这也是智慧农业里为什么要做作物叶片检测的原因。但真正动手做这块的开发者都很清楚模型不是瓶颈数据才是。市面上的通用数据集一大堆可真正能用于田间场景、能支撑YOLO训练的高质量农业数据少得可怜。今天想跟大家聊的就是一套3100张的番茄健康叶片检测数据集配套YOLO格式标注面向智慧农业场景。这篇文章既是对这套数据集的完整复盘也是给准备入坑作物检测、植保无人机、大棚智能化管理这些方向的朋友一份可直接抄作业的实操指南。无论你是第一次听说YOLO还是已经在跑目标检测的老手都能从里面找到能用上的东西数据集怎么拆、训练参数怎么定、误差怎么排查、模型怎么部署到边缘设备上。1. 先看懂这套番茄叶片数据集1.1 3100张图到底意味着什么3100这个数字看上去不大但放在农业检测场景里它已经是一条非常合理的分界线。做目标检测的老手应该能理解在一类目标形态相对固定、背景复杂度有限的任务里几千张经过认真标注的图完全足够训练出一个能上线的模型。尤其是当下大家都喜欢用迁移学习用YOLOv8或YOLOv5的预训练权重做起点数据需求量可以再降一个量级。这套数据集围绕“番茄叶片健康状态检测”这个核心目标采集了不同生长阶段、不同光照条件下露地和大棚种植的番茄叶片图像。图像里有健康的绿色叶片也有带病斑、黄化、卷曲等问题叶片。每个目标都按YOLO格式输出txt标注文件文件名一一对应类别以整数表示并配套names映射。整体结构可以说是标准的YOLO数据集骨架训练前不用再费劲去转格式。当然不同渠道下载的数据集类别数量和划分细节可能有差异。有人拿到的是2类健康、患病有人拿到的是3类或4类健康、早疫病、晚疫病、叶霉病等。这不影响使用但拿到手第一件事一定要打开data.yaml看一眼搞清楚几个标号分别对应什么病害再去训练。否则后面看混淆矩阵的时候你会发现两个类别标签对不上白白浪费时间。1.2 智慧农业场景里数据比模型更值钱很多人一开始学YOLO习惯拿COCO、BDD100k这类公开数据集练手。这类数据集的特点是类别丰富、目标尺度跨度大、场景多样化模型学到的是“通用视觉能力”。而农业检测完全是另一个路子场景高度垂直目标形态非常具体。番茄叶片要么是绿的要么是长了斑的背景大多是泥土、支架、薄膜类别少但细节差别极其关键。这意味着什么意味着通用模型直接拿来识别番茄叶片效果一定不理想。你需要的是贴合农业场景的分布数据也就是今天这套3100张图的定位。我在实际项目里反复验证过一个经验同一个YOLOv8模型用通用数据集预训练后直接推理和用农业数据微调后推理mAP可能从0.3直接跳到0.85以上。差别全在数据。所以我不建议大家把注意力全放在调模型结构上。YOLO这类单阶段检测器已经足够成熟真正决定项目上限的是数据采集是否覆盖足够多的环境变化、标注框是否精确、类别是否平衡。3100张图如果拍摄时段、光照、叶龄覆盖得足够丰富它能发挥的价值远超同等数量的通用场景图片。1.3 数据集的目录结构长什么样下载完解压之后打开文件夹你应该会看到这样的骨架tomato_dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── img_0001.jpg │ │ └── ... │ ├── val/ │ └── test/ └── labels/ ├── train/ │ ├── img_0001.txt │ └── ... ├── val/ └── test/每一张jpg对应一个同名txt。txt里面每一行是一个目标框五个数字依次是类别序号、归一化中心点x、归一化中心点y、归一化宽度、归一化高度。例如0 0.5234 0.6750 0.3210 0.2400 1 0.2110 0.4319 0.1102 0.0988第一行的含义是类别0的目标中心位于图像宽度52.34%、高度67.50%的位置目标宽度占整张图的32.10%高度占24.00%。所有坐标都归一化到0到1之间不需要再乘图像尺寸。这一点新手经常弄混写训练脚本前建议拿一张标注图可视化一下确认框的位置对不对。2. 从0到1用YOLO训练番茄叶片检测模型的完整流程2.1 环境准备PyCharm下的YOLO安装很多朋友习惯用PyCharm写Python这点我支持因为它对虚拟环境和调试的支持非常友好。建议为这个项目单独建一个虚拟环境不要跟其他项目混在一起避免依赖冲突。YOLO的安装其实非常简单依赖的核心就是ultralytics这个包。命令行里执行pip install ultralytics如果是全新环境还应该装对应版本的PyTorch。NVIDIA显卡用户建议装CUDA版本训练速度快一个数量级pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121安装完成后先用一行代码验证环境是否可用import torch from ultralytics import YOLO print(torch.cuda.is_available())如果输出False说明PyTorch没装成GPU版或者驱动不匹配。后边文章里我会专门列一个排查清单。2.2 整理数据集并写对data.yaml不管原来是train/val/test怎么划分的我建议你重新划分一下按经验比例来训练集占70%、验证集占20%、测试集占10%。如果原本只给了train和val可以把train里抽出一部分当test或者直接用val当test来评估。测试集的作用是模型最终交给生产环境前的“摸底考试”所以最好不要让训练过程碰它。data.yaml是整个训练的中枢配置文件写错一个路径就会前功尽弃。标准写法如下path: /home/user/datasets/tomato_dataset train: images/train val: images/val test: images/test nc: 2 names: 0: healthy 1: diseased这里有几个容易踩坑的地方。第一path千万别写相对路径最好写绝对路径否则换设备、换目录之后yml失效。第二train和val写的是相对于path的子目录不用写成images/train这种全路径更不要在路径里加斜杠前缀。第三nc是类别总数必须和names列表长度一致否则会直接报错或者训练时类别错乱。2.3 训练参数怎么定epochs、batch、imgsz数据准备好了在PyCharm里写一段训练脚本或者直接命令行跑都行yolo detect train \ datatomato_dataset/data.yaml \ modelyolov8s.pt \ epochs200 \ imgsz640 \ batch16 \ device0这里选了yolov8s作为起点。为什么不选更大号的yolov8l或yolov8x番茄叶片检测是嵌入式设备常用的场景后续往往要部署到大棚巡检机器人或者无人机上算力有限。而且这个数据集的类别少、目标尺寸相对固定用s级完全够用了。倍数级的参数量白白增加推理延迟非常不划算。epochs我建议起步100到300。数据量3000多张、基础模型预训练好的话200个epoch一般足够收敛再多了就过拟合了。imgsz用640是平衡速度和精度的老办法。如果你发现叶片上的病斑非常细小可以考虑调到1280但相应的训练时间会翻倍显存占用也会增大自己权衡。batch大小主要看GPU显存12GB显存跑yolov8sbatch 16是比较安全的。2.4 数据增强是与不是YOLO默认开启了一系列数据增强包括马赛克增强、随机平移、翻转、颜色扰动等。这些默认参数在通用场景表现优秀但在农业场景里有些默认增强反而帮倒忙。比如随机旋转90度番茄叶片在自然状态下绝大多数是竖着长的如果不加约束地转90度、180度模型会学到一些脱离实际的“时空扭曲特征”。颜色扰动也是双刃剑因为叶片病斑识别的关键就是颜色过度改变色调有可能让模型学到错误映射。更常规的操作是用ultralytics提供的配置项关闭或调低部分增强。在脚本里这么控制from ultralytics import YOLO model YOLO(yolov8s.pt) model.train( datatomato_dataset/data.yaml, epochs200, imgsz640, batch16, hsv_h0.01, hsv_s0.5, hsv_v0.3, degrees10, scale0.3 )degrees控制最大旋转角度我建议给10度以内模拟真实拍摄时的角度抖动。scale控制在0.3左右保证训练过程中目标框不会缩放得过于夸张。3. 训练结果怎么解读从损失到mAP再到模型部署3.1 损失函数不是只看一个数双击打开训练时的控制台日志你会看到一堆与损失相关的数值老手一般盯这几个box_loss、cls_loss、dfl_loss。这是YOLOv8的三个核心损失组件。box_loss用的是CIoU损失衡量预测框和真实框在位置、宽高、长宽比上的差异。cls_loss是分类损失衡量类别判断的准确度。dfl_loss是分布焦点损失主要用于让预测框的边界回归更加锐利。对番茄叶片这个任务来说box_loss和cls_loss是需要重点盯的。如果box_loss降不下去说明标注框本身有误差或者目标之间遮挡太严重如果cls_loss波动大说明类别特征不够明显或者数据里两个类别的图像数量不均衡。训练到后期理想状态是三条线都平滑下降并逐渐趋于水平。如果val loss先降后升train loss还在继续降这就基本确定过拟合了。这时候优先考虑增加数据增强强度、增加数据量、调低模型复杂度而不是继续死磕epochs。3.2 mAP指标看懂验证集在说什么训练结束后控制台会输出每个类别的平均精度AP和整体mAP。这里最有用的是两个指标mAP0.5和mAP0.5:0.95。前者是IoU阈值取0.5时的平均精度后者的阈值在0.5到0.95之间取平均值更严格。对叶片检测这类目标边缘不规整的任务mAP0.5超过0.9就已经是非常好的成绩了因为叶片病斑的边缘本来就是渐变的标注的时候也不是每个人都能标得完全一致。mAP0.5:0.95相对会低一些0.6到0.8都很正常。如果你追求的是实打实的落地效果日常决策请以mAP0.5为主不要一拍脑袋去追求苛刻的IoU阈值。训练完成后还可以用下面的命令做一次正式推理from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetomato_dataset/images/test, conf0.35, saveTrue )conf是置信度阈值我会建议设在0.3到0.4之间。太低会冒出一堆误检太高会把一些低置信度的真实病斑漏掉。把结果图和标注图对比着看能直观看出模型的行与不行。3.3 部署导出ONNX和TensorRT训练好的模型不能总在Python环境里跑农业生产现场往往是在Jetson AGX Orin这类边缘设备上推理。YOLO支持直接导出多种格式yolo export modelruns/detect/train/weights/best.pt formatonnx导出成ONNX后可以用ONNX Runtime推理如果更极端要求帧率可以进一步转成TensorRT engine在Jetson平台上能跑出非常可观的帧率。导出时记得指定opset版本和动态输入尺寸这样部署时就不用拘泥于固定的640x640分辨率。4. 真实场景中的踩坑与排查4.1 标签与路径错误的排查这种问题最多。训练时最常见的报错是找不到图片文件或标签文件运行到一半直接给你抛FileNotFoundError。原因基本就是data.yaml里的path写错了或者images和labels下的train/val目录没对齐。用下面的代码在训练前快速检查一遍import os root /home/user/datasets/tomato_dataset for subset in [train, val]: img_dir f{root}/images/{subset} lab_dir f{root}/labels/{subset} imgs set(os.listdir(img_dir)) labs set(os.listdir(lab_dir)) print(subset, len(imgs), len(labs)) imgs_no_lab [f for f in imgs if f.replace(.jpg, .txt) not in labs] labs_no_img [f for f in labs if f.replace(.txt, .jpg) not in imgs] print(missing labels:, imgs_no_lab[:5]) print(missing images:, labs_no_img[:5])图片名和标签名必须一一对应任何一张图缺失对应txtultralytics在数据集校验阶段就会报错。我自己踩过最坑的一次是下载完zip解压之后部分文件名带了不可见字符格式检查全过一到训练就抽风。建议拿到数据集先跑一遍上面的脚本。4.2 训练时loss为NaN这个现象常见于刚开始训练的前几个epochloss直接变NaN之后越跑越离谱。多数情况下是梯度爆炸。最简单的处理办法是把学习率调低或者插一段学习率预热。YOLO默认都有warmup但如果你改了优化器或者沿用了一个不合理的自定义学习率就会触发这个鬼问题。我通常会把初始学习率设到0.01最终学习率通过余弦退火降到0.001以下。如果还是NaN查一下数据里有没有异常图像——比如全黑的图片、全白的图片、尺寸为0的图片。某些异常图片会导致归一化计算出现除零随后梯度爆炸。用脚本剔除所有分辨率小于10x10的图像再试一次。4.3 目标小、密集、重叠导致的漏检番茄叶片病害早期往往是一个小斑点目标很小加上叶片之间互相遮挡模型很容易漏检。这种问题在训练损失上很可能看不出明显的异常但在田间实地测试时原形毕露。处理思路有三个方向。第一把输入分辨率从640提到960或1280让小目标占用更多像素。第二把mosaic马赛克增强里的随机裁剪调低一点防止小目标在裁剪过程中被裁没了。第三如果条件允许用slice推理把大图切成小块分别检测再合并结果小目标检出率会有明显提升。4.4 类别不平衡怎么办一套数据里健康叶片图像数量往往是病斑叶片的好几倍。类别不平衡会导致模型偏向多数类患病叶片被漏检。看训练日志中的每个类别AP如果两个类别AP差得特别大就要考虑这个问题了。经典的解决方法是计算每个类别的样本数通过过采样或欠采样来调整训练集的类别比例。还有一个相对省事的办法使用ultralytics提供的class_weight参数手动给少数类加权重。我在一套茶树病害数据上试过给少数类加1.5倍权重之后mAP提高了将近四个点非常见效。5. 这套数据集还能往哪些方向延伸5.1 细粒度病害分级的可能性现在很多数据集做到“是否患病”就停了但农业生产里更重要的是得知道病情到了什么程度才能决定用不用药、用多少药。这套3100张数据如果后续扩展出分级标注——比如轻、中、重度——下游就能直接对接精准施药系统。以我的经验做分级比单纯分类复杂得多。早期的病斑面积可能只占叶片面积的1%需要更高分辨率的图像甚至需要额外的小目标标注策略。如果后续自己采集数据建议同一叶片多角度拍几张再让植保专家给每张图打等级标签这种数据训练出来的模型价值会翻倍。5.2 结合多模态数据做更准的诊断叶片特征并不是植物健康状态的唯一信号。我在做设施农业项目的过程中发现结合环境传感器数据——空气温湿度、土壤湿度、光照强度往往能提前几小时预测病害风险。视觉检测负责“定位”环境数据负责“预测”两者结合才是完整的智慧农业解决方案。这套叶片数据集可以作为多模态方案里的视觉分支。比如用YOLO检测出部分病斑之后把检测框内的图像特征和当时大棚传感器数据拼接起来输入到一个轻量级分类模型里对早期锈病的识别准确率可以提升不少。这也是当前行业里正在尝试的方向值得有关注的开发者多想一想。5.3 从目标检测向实例分割迁移3100张的bbox标注是很好的起点但它没有像素级掩码信息。如果未来你想做精准喷洒碰到的需求往往是“把病斑区域精确抠出来”这时候目标检测的矩形框就不够细腻了。可以考虑用YOLOv8-seg在现有标注基础上重新生成掩码。操作上手工标注分割掩码的工作量非常大建议先跑一个预训练的分割模型把自动生成的掩码放在标注工具里修能将标注时间缩短一半以上。后续也可以把这份数据包装成一个实例分割数据集适用面会广很多。6. 写在最后动手前的那点忠告如果你以前只跑过COCO、用官方预训练权重跑过demo那么第一次用农业数据集训练时最大的敌人不是模型不收敛而是你心里对“农业模型应该达到什么效果”没有底。我的个人体会是第一次拿到这类数据集先别急着调参先用默认参数快速跑到50个epoch看看指标量级再决定下一步怎么调。盲目的调参只会让你越调越乱。从3100张番茄叶片数据出发最快的路径其实是数据检查、yaml配置、预训练权重微调、看mAP、到田间采集更多边缘样本循环这个过程。别想着一个模型解决所有问题农业场景的环境变化远比想象中复杂持续采集数据、持续迭代才是智慧农业项目落地的真相。希望这篇文章能帮你把第一步走得稳一些少踩几个我踩过的坑。