ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于YOLO的玉米病害识别实战:数据、训练与部署全记录

基于YOLO的玉米病害识别实战:数据、训练与部署全记录 简介目标检测技术作为计算机视觉的核心任务在农业智能化中发挥着关键作用。通过深度学习模型对农作物图像进行实时分析能够有效识别病害区域并精准定位。YOLO凭借速度与精度的平衡成为此类实际项目的首选框架。围绕玉米病害识别场景完整展示了数据准备、标注规范、模型训练与调优、部署推理的工程化流程并针对训练指标为0、小目标漏检等典型问题给出了排查方案。无论入门开发者还是农业工程师都能从中获得可复用的实践经验。 玉米病害对产量的影响有多狠干农业的人心里都有数。大斑病、小斑病、锈病这些常见病害一旦大面积爆发减产百分之二三十是常事碰上流行年份甚至更高。但问题是病害识别这件事以前靠人眼经验老到的植保员能看出来普通种植户往往等病斑蔓延了才发现一耽误就是最佳防治窗口。我做的这个基于YOLO的玉米病害识别项目说白了就是把这套人眼识别经验搬到模型里用手机或者电脑摄像头对着玉米叶片拍一张照片模型能在几百毫秒内告诉你是哪种病害给出置信度。整个项目打包成了一个zip里面包含数据集、标注文件、训练代码、权重文件和推理脚本拿到手就可以直接跑。需要说明的是本文不是某个论文的理论复现而是我在实际做这个项目时的完整记录包括数据集怎么整理、模型怎么调参、部署时踩了哪些坑以及最让我头疼的训练指标全是0是怎么解决的。无论你是刚入门的开发者想找个实际项目练手还是农业领域的工程师要做类似的目标检测任务这篇文章应该都能给你省下不少弯路。1. 项目整体设计为什么是YOLO以及这个包里面到底有什么1.1 目标检测选型时的几个考量在动手之前我其实也纠结过用分类网络还是检测网络。如果只是判断一张叶片有没有病用ResNet这类分类网络就够了。但实际场景里一张照片上可能同时有健康叶片和病斑叶片病斑位置、大小、形状都不一样分类网络要么把整张图裁出来逐块判断要么就会丢失位置信息根本没法告诉用户病斑在叶片的哪个区域。所以这个项目从一开始就确定要目标检测而不是图像分类。目标检测框架里YOLO系列是绕不开的选择。我当时的对比思路是这样的Faster R-CNN精度高但速度慢嵌入式设备跑不动SSD速度快但小目标检测能力一般YOLO系列在精度和速度之间平衡得最好而且生态成熟从数据标注到训练部署的工具链都非常完整。尤其对于玉米病害这种需要快速响应的场景YOLO的实时性优势非常明显。而且项目后续打算部署到边缘设备上YOLO轻量级的特性也更合适。我选的基线是YOLOv8。原因很简单它是当时生态最成熟的版本ultralytics库封装得很好训练、验证、导出一条龙而且默认的anchor-free设计对小目标病斑的检测更友好。后来社区出了YOLO11我也试过但最终交付版本还是基于v8微调的因为稳定压倒一切。1.2 .zip包里到底装了什么既然标题叫基于YOLO的玉米病害识别.zip那这个包的目录结构我得先交代清楚不然拿到手都不知道从哪儿开始看。我最终交付的包结构是这样的corn-disease-yolo/ ├── dataset/ │ ├── images/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ ├── labels/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ └── data.yaml ├── weights/ │ ├── best.pt │ └── last.pt ├── scripts/ │ ├── train.py │ ├── detect.py │ ├── export_onnx.py │ └── split_dataset.py ├── configs/ │ └── train_config.yaml └── requirements.txtdataset目录放的是整理好的数据集images和labels一一对应全部是YOLO格式。weights目录放的是训练好的权重best.pt是验证集上表现最好的模型last.pt是最后一轮的结果。scripts目录里的train.py负责训练detect.py是推理脚本export_onnx.py用于把模型导出成ONNX格式做跨平台部署split_dataset.py用来划分数据集。这个结构的好处是训练、推理、部署全部解耦你在数据集上重新训练或者直接用我给的权重推理都不需要改其他模块的代码。我后面第三节会详细讲每个脚本的用法和参数这里先对整体有个概念就行。2. 数据集构建玉米病害识别里最容易被低估的一环2.1 数据从哪来采集和开源结合但一定要做清洗很多人拿到这个项目第一件事就想跑训练但我得说一句实际的模型精度百分之七八十由数据决定模型架构只占剩下的部分。玉米病害识别的数据集如果只靠网上随便爬的图片训练出来基本是废的因为图片质量参差不齐标注混乱类别分布离谱。我的数据来源有三个。第一是公开数据集最经典的是PlantVillage里面有玉米大斑病、锈病、健康叶片的分类图片但PlantVillage本身是分类数据集不是检测数据集我需要先做裁剪和重新标注这个后面细说。第二是自己去田间拍的找了两片玉米地在不同天气、不同时段、不同角度拍叶片拍的时候注意光线的变化——晴天强光、阴天散射光、早晚低照度下的叶片表现差异很大如果只在一种光照下训练模型到实际场景里立刻露馅。第三是从农业植保网站和论文配图里搜集的图像这部分版权风险要注意只用来补充样本量我实际交付的时候把有明确版权问题的图片都过滤掉了这是必须做的合规动作。数据拿到手之后清洗工作才是重头戏。我踩过最大的坑是PlantVillage里很多图片是单张叶片放在纯色背景上拍的而田间的照片是复杂的背景有土壤、杂草、其他叶片重叠。如果训练集里全是干净背景模型会偷懒直接学习背景特征而不是病害特征。做了个简单测试用纯背景图片训练的模型拿到田间复杂背景的照片上mAP直接掉了一半。所以清洗时要尽量删掉过于理想的纯色背景图片或者把它们作为数据增强的一部分做背景融合。2.2 YOLO格式标注规范和转换流程YOLO格式的标注是每个目标一行文本格式是class_id x_center y_center width height前三个类别索引从0开始四个坐标值都是归一化后的比例值取值范围0到1。举个例子如果一张640x640的图片里一个病斑的边界框是左上角(160, 200)、右下角(480, 500)那么归一化之后的x_center就是(160480)/(2640)0.5y_center是(200500)/(2640)0.546875width是(480-160)/6400.5height是(500-200)/6400.46875。这一行的完整内容就是0 0.5 0.546875 0.5 0.46875。我当时用的标注工具是LabelImg因为它可以直接导出YOLO格式不需要自己做坐标转换。但用LabelImg有个要注意的地方它输出的标注文件是txt每张图片对应一个同名的txt文件如果图片是img_001.jpg标注就是img_001.txt。图片放images目录标注放labels目录目录层级和文件名必须完全一致否则训练时根本加载不到标注。如果你手里的数据是COCO格式的JSON标注或者VOC格式的XML标注需要转换。我写了一个简单的转换脚本核心逻辑如下def voc_to_yolo(xml_path, output_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) with open(os.path.join(output_dir, os.path.basename(xml_path).replace(.xml, .txt)), w) as f: for obj in root.iter(object): class_name obj.find(name).text class_id class_names.index(class_name) bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h f.write(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n)转换完成之后一定要抽查几个标注文件和图片做可视化对比。我自己写了个简单的可视化脚本把标注框画回原图用肉眼看边界框是否贴合病斑边缘。这一步不能偷懒因为坐标转换如果有偏移模型训练出来边界框位置会系统性偏左或偏上而且很难排查。2.3 data.yaml配置类别名称和路径必须严格对应数据准备的最后一步是写data.yaml这是YOLO训练时的数据配置文件。我的data.yaml长这样path: /path/to/corn-disease-yolo/dataset train: images/train val: images/val test: images/test nc: 4 names: [corn_blight, corn_rust, corn_gray_leaf_spot, healthy]这里有个细节我配了4个类别前三类是病害——大斑病、锈病、灰斑病第四类是健康叶片。加健康叶片这个类别看似没必要但实际非常有用。因为模型在推理时会把整张图里所有目标都框出来如果不设健康叶片类健康叶片也会被强行归到某个病害类里产生大量误报。加上健康叶片类之后模型学会了区分有病的区域和没病的区域误报率明显下降。path字段我一开始写的是相对路径结果换个机器跑训练就报错找不到数据集所以后来都改成绝对路径了这也是一个经验。3. 模型训练全流程从参数配置到调优心得3.1 环境准备和硬件要求训练YOLOv8Python环境我用的是3.10PyTorch版本2.1.0以上CUDA 11.8。显卡方面如果你手头是GTX 1660这种入门卡跑小的模型也凑合但建议至少RTX 3060级别显存8G以上不然batch size开不大训练速度会很痛苦。安装依赖非常简单ultralytics这个库把大部分东西都封装好了只需要一行命令pip install ultralytics但这只是理想情况我实际遇到过numpy、opencv、torch版本互相打架的情况。建议用conda先建一个干净的虚拟环境conda create -n corn_yolo python3.10 -y conda activate corn_yolo pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu118注意torch要从PyTorch官方源装如果直接用pip install torch装出来的可能是CPU版本训练速度差得非常远。我第一次就是没注意这茬CPU版跑了一整夜才跑了几个epoch后来换成CUDA版速度提升了二十倍都不止。3.2 训练脚本和关键超参数解析我的train.py其实非常短因为ultralytics把训练逻辑都封装好了from ultralytics import YOLO model YOLO(yolov8s.pt) results model.train( data/path/to/dataset/data.yaml, epochs200, patience30, batch16, imgsz640, device0, workers8, optimizerSGD, lr00.01, lrf0.01, momentum0.937, weight_decay0.0005, warmup_epochs3, augmentTrue, seed42 )关键参数逐个说一下。model用yolov8s.pt做预训练权重而不是从零开始训练原因是YOLO在COCO上已经学到了通用的物体特征玉米叶片和病斑虽然不在COCO类别里但纹理、边缘、颜色这些底层特征是可以迁移的。从零训练的话数据集得够大才行我这种几千张的规模根本不够。用s版本而不是n或m是因为s在精度和速度之间比较平衡n太小精度不够m太大训练和部署成本高。imgsz设为640这是YOLOv8的默认输入尺寸。有些人会觉得越大越好比如1024但我实测下来对于玉米病斑这种不算特别小的目标640够了再大训练速度明显下降精度提升却微乎其微。如果你的病斑特别小比如只有十几个像素那可以考虑用1280但代价是显存占用成倍增长。batch设为16在8G显存下是个安全的数值。如果你的显卡显存不够调成8甚至4也可以但要注意batch太小会引入噪声batch-norm层的统计量不稳定训练容易震荡。一个实用的做法是先用小batch试跑几个epoch如果loss正常下降再逐步增大。优化器我选的是SGD而不是AdamW。这里很多人不理解YOLOv8默认是AdamW但我在目标检测任务上实验下来SGD配合warmup和余弦退火训练后期loss曲线更平滑收敛效果更好。AdamW前期收敛快但后期微调能力一般容易在最优解附近震荡。3.3 训练过程的监控和判断训练跑起来之后不能只看loss数值就完事我习惯盯几个关键指标。第一个是box_loss和cls_loss的下降趋势这两个loss应该在训练前期快速下降中后期平缓。如果loss训练集降但验证集不降那就是过拟合的苗头patience参数会自动触发早停。第二个是mAP50和mAP50-95的变化前者是IoU阈值0.5时的平均精度后者是0.5到0.95区间内的平均值。对于玉米病害识别场景mAP50更直观因为它对定位框要求没那么苛刻更接近实际应用的判断标准。我还习惯在训练完看混淆矩阵这个ultralytics会自动生成。混淆矩阵能直观看到哪些类别之间容易混淆比如大斑病和灰斑病因为它们的病斑都出现在叶片上颜色接近边界模糊模型有时会分不清。看到混淆矩阵之后我会回头检查这两类的标注样本是否清晰如果标注本身就有歧义模型学出来的效果也会打折扣。补充样本或删掉模糊标注是常见的修正手段。3.4 训练过程中的一个意外情况指标全是0这里必须单独说一个我遇到的问题因为热搜词里就有yolo训练指标全是0这确实是个常见坑。我刚开始训练的一版跑完100个epoch之后训练日志里显示的mAP50、mAP50-95全部是0precision和recall也是0但loss一直在下降。这种情况看着特别迷惑。排查了一下午最后发现问题出在数据集标注上labels目录里的txt文件其实都是空的。原因是我的split_dataset.py脚本在做训练验证集划分时把图片复制到了images目录但标注文件因为文件名匹配逻辑写错没有跟着图片走导致labels目录下是空的。YOLO训练时遇到空的标注文件不会报错只会跳过这个样本所以loss还在正常下降但模型没有学到任何真实的目标信息验证时自然检测不到任何物体所有指标都是0。排查方法很简单训练前跑一遍数据集验证脚本统计images和labels目录中文件的数量是否一致并检测txt文件内容是否为空import os img_dir path/to/images/train label_dir path/to/labels/train img_count len([f for f in os.listdir(img_dir) if f.endswith(.jpg)]) label_count len([f for f in os.listdir(label_dir) if f.endswith(.txt)]) print(fImages: {img_count}, Labels: {label_count}) empty_labels 0 for f in os.listdir(label_dir): if f.endswith(.txt): if os.path.getsize(os.path.join(label_dir, f)) 0: empty_labels 1 print(fEmpty label files: {empty_labels})这个脚本我现在每次训练前都跑一遍花半分钟能避免浪费十几个小时的训练时间。各位如果遇到指标全是0的情况第一步就查标注文件八成是标注文件缺失、为空、或者类别索引越界。4. 模型部署从训练机到实际场景的距离4.1 模型导出从PyTorch到ONNX再到边缘部署训练好的模型是PyTorch格式的best.pt但要在不同平台部署通常需要导出成更通用的格式。我的export_onnx.py脚本核心逻辑如下from ultralytics import YOLO model YOLO(weights/best.pt) model.export(formatonnx, imgsz640, dynamicFalse)导出的ONNX模型可以直接用onnxruntime或OpenCV的DNN模块加载。这里有个参数需要注意dynamicFalse也就是固定输入尺寸为640x640。如果设置成True输入尺寸可以动态变化但推理速度会下降。我实际使用中固定尺寸完全够用而且能获得更好的推理性能。如果你的目标是部署到手机端还可以进一步导出成NCNN格式把模型量化成FP16甚至INT8体积可以压缩到几MB推理速度也更快。但代价是精度会有轻微下降需要在设备上实测确认不能想当然认为量化无损。4.2 推理脚本实现细节detect.py是我提供的推理脚本它的核心流程是读图、预处理、模型推理、后处理、画框、输出结果。这里有个容易被忽视的细节是预处理必须与训练时一致否则精度会受影响。训练时图片被缩放到640x640同时做了letterbox处理保持宽高比多余部分填充灰色推理时也必须做同样的预处理。如果不做letterbox直接拉伸图片变形会导致边界框位置偏移检测精度明显下降。我实际用的推理代码如下from ultralytics import YOLO import cv2 model YOLO(weights/best.pt) img cv2.imread(test.jpg) results model.predict(img, conf0.35, iou0.45, devicecpu) for result in results: boxes result.boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) coords box.xyxy[0].cpu().numpy().astype(int) print(fClass: {model.names[cls_id]}, fConfidence: {conf:.2f}, fBBox: {coords.tolist()})conf0.35是置信度阈值低于这个值的检测框会被过滤掉。这个阈值设置很关键设太高会漏检设太低会误报。我的测试结论是在手机拍摄的清晰照片上0.35到0.45之间表现比较稳定。实际应用中如果你用的是无人机拍的远距离图像病斑比较小且模糊建议把阈值降到0.25能明显减少漏检的数量。4.3 部署形态推荐从PC到边缘设备不同场景适合不同的部署形态我总结了三种。第一种是PC端部署最简单直接把detect.py跑在Windows或Linux电脑上适合实验室、农业技术站等场景。配合摄像头可以做成实时监测对着叶片移动摄像头屏幕上实时框出病害位置。第二种是边缘设备部署典型的是Jetson Nano、Raspberry Pi加摄像头。这种方案适合温室或田间的固定监测点每隔一段时间自动拍照上传分析。Jetson Nano上可以跑TensorRT加速用半精度FP16推理检测速度能到30FPS以上完全满足实时需求。树莓派4B性能弱一点但加上NPU加速棒也能跑。第三种是手机端部署用NCNN或MNN框架把模型量化后集成进App农户拿出手机对着玉米叶片拍照App直接给出病害类别和置信度。这是最贴近终端用户的使用方式但开发工作量也最大需要处理相机适配、模型下载更新、离线推理等一系列问题。我给这个项目做的是PC端和边缘端的部署方案手机端目前只做了技术验证还没做成完整的App。5. 常见问题与排查技巧实录5.1 小目标漏检玉米病斑太小怎么办玉米病害早期病斑可能只有十几个到几十个像素YOLOv8在640分辨率下对小目标的检测能力有限。我实际测试过当病斑直径小于20像素时漏检率会显著上升。针对这个问题我试过几个办法。第一个办法是提高输入分辨率把imgsz从640调到1024甚至1280。代价是训练速度和推理速度都下降而且显存占用变大。如果显卡只有8G1280分辨率下batch只能开到4训练稳定性会受影响。我实际测试下来1024相比640能提升约8%的小目标召回率但训练时间增加了约一倍是否值得取决于你的场景需求。第二个办法是对叶片区域做切片。如果应用场景是无人机拍的整片玉米地不要直接检测整张大图先把图像切分成多个小图块每个图块单独送给模型检测再合并结果。这样相当于放大了病斑在输入图像中的占比小目标变成了相对较大的目标检测效果立竿见影。切片重叠率建议设在20%左右避免病斑正好落在切分线上。第三个办法是尝试YOLO系列中带attention机制的变体我试过在C2f模块后加SE模块小目标的召回率有一点提升但训练时间增加了不少最终没有采用因为在玉米病害识别场景里牺牲训练效率换取几个百分点的召回率不值得。如果你对精度有极致追求可以试试这个方向。5.2 数据不平衡锈病样本太多灰斑病样本太少怎么办我的数据集里锈病的样本明显比其他病害多因为田间锈病本身就高发拍起来容易。这导致模型对锈病的检测精度很高但灰斑病的精度就差强人意。最直接的办法是给少数类增加数据或者对少数类做更强的数据增强比如翻转、旋转、光照变化。还有一个办法是设置类别权重ultralytics里没有直接暴露这个参数但损失函数本质上是可以改的用加权的BCE loss替代默认的loss能有效提升少样本类别的检测精度。实际工作中我给灰斑病增加了样本量从200多张补到500张左右mAP50从0.68提升到了0.83。这个提升比任何模型结构优化都明显再次印证了数据的价值。5.3 不同环境下泛化能力差室内的模型到田间就失灵这是所有农业视觉项目都会遇到的核心问题。模型在室内拍的单张叶片照片上测mAP能到0.9一到田间复杂背景、不同光照下就掉到0.5甚至更低。原因是训练集和测试集存在很大的分布差异。解决办法有几个方向。一是采集数据时尽量覆盖多样化的场景不同天气、不同时段、不同生育期都拍一些。二是使用更强的数据增强包括HSV变换、随机光照、添加噪声等让模型对光照变化不敏感。我在训练时开启了ultralytics自带的augment增强包含mosaic、翻转、旋转、缩放等能在一定程度上增加泛化能力。三是用自适应对比度增强做预处理因为玉米叶片在强光下容易过曝暗光下细节看不清通过调整对比度可以让模型更关注病害特征而不是光照变化。还有一个小技巧在训练集里加入一些负样本就是没有任何病害的复杂背景图比如土壤、杂草、玉米秆的局部特写。这样模型能学会区分背景区域和叶片区域误检率会大幅下降。我加了100多张负样本之后误检率降了一半左右。5.4 推理速度慢如何优化到实时检测如果目标是实时检测比如对着玉米叶片移动摄像头推理速度是关键指标。我测量过在PC上RTX 3060ONNX格式的模型推理一张640x640图片大约需要30-50ms可以跑30FPS左右已经算实时了。但在CPU上推理时间可能到200-400ms达不到流畅效果。CPU推理优化有几个方向一是用ONNXRuntime的CPU算子优化打开线程数配置二是用OpenVINO框架做推理CPU上相比onnxruntime还能快一倍左右三是将模型量化成INT8用OpenVINO的量化工具跑一遍校准速度提升明显精度损失在可接受范围内。如果是在Jetson设备上TensorRT是必选项。我导出的最优配置是FP16精度推理时间约15ms60FPS以上完全没问题。TensorRT的优点是它能把网络层融合优化减少计算量但缺点是模型结构如果有自定义层导出时容易报错。ultralytics对TensorRT的兼容性做得不错我用v8没有遇到问题。结尾一点感受做完这个项目我最深的体会是农业AI真正难的不是算法而是数据和质量意识。YOLO本身是一个非常成熟的开源工具训练流程快得甚至让人产生一种随便跑跑就能出好模型的错觉。但实际做到能上线、能给农户用的程度大部分时间都花在了数据清洗、标注检查、场景适配这些脏活累活上。回头看看收益最大的几个改进全都是在数据和预处理上做的功夫而非模型结构本身。如果你也想做类似的农业识别项目我的建议很直接先把你手里的数据弄得干干净净再去纠结用YOLOv8还是YOLO11前者的收益至少占七成。这个zip项目已经开源了完整的数据集划分和训练推理代码拿到手不需要改太多就能复现。如果你在复现的过程中遇到问题尤其是数据加载、指标为0这类经典坑欢迎随时来交流我已经把这些坑都踩过一遍了。本文还有配套的精品资源点击获取
返回列表