ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv7实战:从数据标注到防治建议的农业虫害识别系统

YOLOv7实战:从数据标注到防治建议的农业虫害识别系统 简介这是一套基于YOLOv7的植物虫害识别与防治系统资源包面向从事农业智能化、计算机视觉研究的开发者与学习爱好者旨在解决农作物害虫种类多样、人工巡检效率低等问题实现对常见虫害的自动检测与防治提示。资源仅19个文件压缩包14.54MB包含1个Python辅助脚本listdir.py、1个Markdown格式的README教程以及15个PNG、1个JPEG和1个JPG图片样本其中README提供项目结构说明与运行指引py脚本可辅助批量整理图像数据集多张图片直观展示模型训练与虫害识别结果。目前已有89人学习下载适合中高级目标检测学习者参考可帮助读者快速掌握YOLOv7在农业场景下的数据准备、模型调用与检测效果评估思路获得较为完整的源码与配套讲解降低复现门坎。这套资源将项目源码、说明文档和效果示例打包在一起便于用户闭环学习YOLOv7的虫害检测流程。1. 先搞懂这套 YOLOv7 源码到底解决了什么问题一个做农业信息化的小团队带着一批水稻稻飞虱的图片找到我说自己标注了三千多张图用分类网络怎么都分不清“褐飞虱、白背飞虱、灰飞虱”更别说数出每张图里有几只。这就是植物虫害识别里最典型的需求既要判断有没有虫、是什么虫又要给出位置和数量方便决定打不打药、打哪里。市面上多数“植物虫害识别”文章只讲到了识别这一步而标题里带“防治”二字意味着它还要输出处置建议。用 YOLOv7 训练一个自己的虫害检测模型再把推理结果接到防治知识库这才是完整闭环。这套源码很适合两类人一是做毕业设计的学生需要完整演示“识别防治”而不只是贴一个模型二是刚入门目标检测的开发者想快速跑通 YOLOv7 部署全流程又不想从造轮子开始。它的代价也很明确要装 CUDA 环境、要准备标注数据、要忍受农业场景里小目标虫害的漏检这些下文都会拆开讲。2. 把环境和数据集准备好这一半工作量都藏在这里任何一个 YOLO 系项目拿到源码的第一件事不是看网络结构而是先让官方权重能跑起来。YOLOv7 用的是 PyTorch 框架对电脑的底线要求是显卡显存至少 4GB 起步能上 8GB 最好没有 NVIDIA 显卡也能用 CPU 跑但训练一个像样的虫害模型会等到怀疑人生。我建议的软件组合是 Windows 10/11 Anaconda PyCharm 本地 Git网上关于 Anaconda 安装教程、PyCharm 安装教程、Git 安装及配置教程的内容已经很多不需要在这个项目里再踩一遍工具链的坑。Python 解释器版本锁定 3.8-3.10不要用 3.12否则装 PyTorch 时很容易出现“找不到匹配版本”的红色报错。2.1 拉取 YOLOv7 源码和安装依赖的完整过程在 conda 里为这个项目单独建一个虚拟环境是逼自己养成好习惯虫害识别项目里常用的 opencv、pandas、matplotlib 版本很脆一旦和别的项目混在一起轻则依赖冲突重则 GPU 版本的 torch 被 pip 悄悄降级。按下面的顺序来conda create -n pest_yolo python3.9 -y conda activate pest_yolo git clone https://github.com/WongKinYiu/yolov7.git cd yolov7 pip install -r requirements.txt第一行创建虚拟环境时名字可以随意pest_yolo 只是说明这是“虫害 YOLO”项目。激活环境之后再 git clone避免 GitHub 仓库里的代码被下载到全局 Python 环境的 site-packages 里这是很多人的血泪教训。requirements.txt 里包含 torch、torchvision、opencv、numpy 这些核心库pip 会自动装。装完先做一次冒烟测试python detect.py --weights yolov7.pt --source test.jpg跑通之后你会在 runs/detect 目录下看到带检测框的结果图这就代表源码环境、权重文件、推理脚本三者都正常。后续训练自己的虫害模型时用的还是这套脚本只是把 weights 换成训练产物。常见做法是先把官方 yolov7.pt 跑通一次很多人在这一步直接跑自家数据集出了报错还以为是自己数据有问题其实连环境都没验证过。2.2 把 VOC/COCO 格式的虫害数据转成 YOLO 标签附可跑脚本环境就绪后的下一个大坑是数据格式。网上开源的虫害数据集大多带着 VOC 格式的 XML 标注或者 COCO 格式的 JSON 标注而 YOLOv7 只吃那种每行一个目标、五个数字的 txt 标注。这五列含义是第一列类别 id从 0 开始第二、三列目标中心点的 x、y 坐标除以图片宽高后的归一化值第四、五列目标框的宽度、高度同样是归一化后的比例。我自己写过一个转换脚本思路很简单遍历 XML 文件用 ElementTree 解析里面的 object 和 bndbox然后算出归一化坐标写进 txt。分号后面是我当年踩坑后补的启发式规则建议你直接抄import os import xml.etree.ElementTree as ET from PIL import Image xml_dir annotations img_dir images out_dir labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() img_name root.find(filename).text img_path os.path.join(img_dir, img_name) img_w, img_h Image.open(img_path).size lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_to_id: # 这里用非常规类名跳过的策略而不是报错退出 # 因为不少公开数据集里混着背景类和干扰类。 continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 过滤掉面积占比小于 0.001 的标注框 # 这类小框在虫害框标注里多是误标或灰尘。 if w * h * img_w * img_h / (img_w * img_h) 0.001: continue lines.append(f{class_to_id[cls_name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, xml_file.replace(.xml, .txt)), w) as f: f.write(\n.join(lines))这段代码里的 class_to_id 需要你自己维护一个字典比如{稻飞虱: 0, 螟虫: 1, 蚜虫: 2}。另外强调的是YOLO 的标注 txt 只接受归一化后的数值你要是把像素坐标直接写进去训练时 loss 会直接炸成 NaN因为框的宽高比都超过 1 了。转完格式后用python -c from PIL import Image; print(Image.open(test.jpg).size)随机抽查几张图确认图片尺寸和 XML 里 width/height 一致。有些公开数据集的 XML 图片尺寸和实际图片不一致多数是缩略图没同步更新标注这种数据训出来的模型框全是偏的。2.3 目录结构YOLOv7 的 train.py 在哪找数据如果你不打算改 YOLOv7 的训练代码就要把数据按固定结构放好。YOLOv7 读数据靠的是 data 目录下的 yaml 文件里面写 train、val 两个子集的路径和类别数。最小目录结构是datasets/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── pest.yaml对应 pest.yaml 的内容train: datasets/images/train val: datasets/images/val nc: 5 names: [褐飞虱, 白背飞虱, 灰飞虱, 稻纵卷叶螟, 二化螟]很多新手把 images/labels 里的 train 和 val 混在一起虽然能训但 val 里出现的图可能在 train 里也出现过最后的 mAP 高得虚高真正部署时会原形毕露。我一般是在划分之前对图片文件做一次 hash 去重特别是从公开渠道爬来的数据重复样本的比例可能超乎你想象。用hasattr和字典去重一次也不麻烦但大多数教程不会提醒这一步我见过有人 3000 张图里实际只有 900 张是唯一的训练结果自然离谱。3. YOLOv7 训练参数怎么调让虫害模型真的收敛数据准备好之后就到了整个项目里最耗时也最看经验的一步。农业生产里的虫害图片有三个特点它们决定了参数要怎么调第一虫体小在一张 640x640 的图上常只占几十个像素第二背景杂茎秆、土壤、阴影都会成为误检来源第三类别很不平衡某种虫占了一半样本另一种可能只有几十张图。直接拿默认参数开训大概率会在验证集上得到“蚜虫全检不出来、稻飞虱的精确率低得可怜”的结果。3.1 一张可以照抄的训练命令附参数说明YOLOv7 的训练入口是 train.py我踩过多个坑后沉淀出来一份适合小目标虫害的基础命令python train.py \ --data data/pest.yaml \ --weights yolov7.pt \ --cfg cfg/training/yolov7.yaml \ --epochs 150 \ --batch-size 8 \ --img-size 640 \ --workers 4 \ --device 0 \ --hyp data/hyp.scratch.p5.yaml逐项说明--batch-size 88GB 显存刚好能放下的值。显存小的机器千万别贪batch 太大直接 OOMout of memory显存溢出train.py 会在几百步后报“CUDA out of memory”。--img-size 640YOLOv7 默认训练分辨率。虫害小目标如果想提高召回率有人会试 960但显存占用几乎翻三倍速度和精度的平衡点要从 640 起步再往上试。--hyp data/hyp.scratch.p5.yaml这是超参文件里面控制学习率、mosaic 增强概率。农业图片背景杂乱我一般会把mosaic从 1.0 降到 0.5防止合成图里的虫害上下文被切碎得不成样子。--workers 4Windows 上 workers 建议不超过 4否则 DataLoader 容易报“BrokenPipeError”这是 PyTorch 的老毛病。训练开始后你能看到终端里实时滚动的 loss 数值。我在训练时会额外关注 loss 曲线里前 10 轮是不是已经下降到某个平台期如果 10 轮后 box_loss 仍徘徊在 0.08 以上说明学习率可能偏大或数据标注噪点过多我会停掉换个更低的学习率再训而不是傻等 150 轮跑完。很多人训练虫害模型“不收敛”不是模型问题是学习率和预热warmup阶段没过好。3.2 训练过程看什么results.png 里四个必须盯住的信号每训完一个 epochYOLOv7 会在 runs/train 目录下生成一个 results.png上面同时画出 box_loss、obj_loss、cls_loss、mAP0.5、mAP0.5:0.95 等多条曲线。多数新手只盯着最后的 mAP 看这是最大的思维误区。要分地块查看第一看obj_loss曲线它代表“有没有物体”的判断能力虫害检测中背景误检率高多半就是这个 loss 没降下去。曲线如果在后期出现锯齿形往复说明模型在背景和虫体之间反复摇摆。第二看box_loss它反映预测框和真实框的贴合度。虫体太小框稍微歪一点 IoU 就大幅下降因此这个 loss 对虫害项目敏感程度极高。第三看验证集上的mAP0.5虫害识别落地场景里 0.5 的 IoU 阈值已经够用不用太执着于 0.5:0.95。第四看val/recall召回率植保场景里漏检的代价比误检高漏了一只虫可能让整片田肆无忌惮地扩散。如果召回率低于 0.7说明大量虫体被漏检了要优先解决召回而不是精确率。如果发现 mAP 高了但召回率上不去常见的解决手段有三个把置信度阈值调低在 augments 里增加随机旋转角度或者把输入分辨率调大。这些都是可在训练命令里直接改的不需要动网络结构。3.3 什么时候该停早停与指标取舍YOLOv7 官方代码里没有内置 early stopping但你可以自己盯或者简单粗暴地把--epochs设成 150 然后每 20 轮手动看一次 results.png。虫害数据量一般不大两三千张图的话模型往往在 80-100 轮之间就已经收敛后面的轮次只是在“记忆”训练集验证集 loss 反而开始上升。这种过拟合在虫害识别里表现得特别明显训练 mAP 逼近 0.99验证 mAP 停在 0.6 左右且两线差距逐步拉大。我一般会用两个手段来对抗过拟合一是把hyp里的hsv_h、hsv_s增强开大一点让模型见到的颜色变换更多因为田间光照条件变化很大二是val集里刻意保留一些与训练集拍摄现场不同的样本比如白天拍的有 50 张、傍晚拍的放 20 张进验证集确保验证集真的在验证泛化能力而不是验证记忆能力。4. 从识别到防治把预测结果变成一套可执行的方案模型训得好只是完成了“认虫”这一步。标题里的“防治”二字要求系统能够在识别出虫害后给出对应的处理建议。这一章的核心是把 YOLOv7 的推理结果变成结构化数据再让一个简单的规则引擎根据虫种、数量、置信度输出防治策略。为了避免源码里的 detect.py 只能输出一张画了框的图片这种“演示级”尴尬我建议把推理封装成一个独立接口。4.1 把训练好的权重导出成 ONNX部署不再被 YOLOv7 源码绑死一个常见做法是把 PyTorch 权重导出成 ONNX 格式用 onnxruntime 来推理这样部署时不再需要拖着一个庞大的 PyTorch 环境。YOLOv7 仓库自带 export.py导出命令如下python export.py --weights runs/train/exp/weights/best.pt --img-size 640 --batch-size 1 --simplify这里--simplify会对计算图做常数折叠之类的优化导出的模型更小、推理更快。导出成功后会生成 best.onnx。不要怕 ONNX 文件它在 Windows 上的 CPU 推理速度比原版 PyTorch 模型要快不少而且可以用 C#、Java 或 Python 任意语言调用。4.2 用 onnxruntime 写一个可复用的推理函数附结构化输出随后我用 onnxruntime 写推理函数核心逻辑是读图 → 缩放为模型输入尺寸 → 前向推理 → 解析输出 → 过滤低置信度框。以下是关键类的简化版可放到独立的 inference.py 里import cv2 import numpy as np import onnxruntime as ort class PestDetector: def __init__(self, onnx_path, conf_thres0.25, iou_thres0.45): self.session ort.InferenceSession(onnx_path) self.conf_thres conf_thres self.iou_thres iou_thres def preprocess(self, img): # YOLOv7 导出时用的高度宽度这里必须和导出参数完全一致 img_resized cv2.resize(img, (640, 640)) img_norm img_resized.astype(np.float32) / 255.0 # 调整维度到 (1, 3, 640, 640)ONNX 模型要求的输入顺序 img_tensor img_norm.transpose(2, 0, 1)[None, :, :, :] return img_tensor def predict(self, img_path): img cv2.imread(img_path) tensor self.preprocess(img) outputs self.session.run(None, {self.session.get_inputs()[0].name: tensor}) # outputs[0] 的 shape 是 (1, 25200, 85)其中 85 5 类别数 preds outputs[0][0] valid preds[preds[:, 4] self.conf_thres] results [] for box in valid: class_conf box[5:].max() class_id box[5:].argmax() if class_conf self.conf_thres: continue cx, cy, w, h box[:4] x1 int(cx - w / 2) y1 int(cy - h / 2) x2 int(cx w / 2) y2 int(cy h / 2) results.append({ class_id: int(class_id), confidence: float(class_conf), bbox: [x1, y1, x2, y2] }) return results说明一下几个关键参数conf_thres 设得越低检测出来的框越多漏检越少但误检也会变多在虫害部署场景里我一般设 0.25 作为基线如果发现漏检太多往下调到 0.15代价是背景里的枯叶、水珠会被误认成虫。iou_thres 是 NMS 用的阈值用于去掉重叠框0.45 是默认经验值。真实部署时同一个虫体可能被多个框框住NMS 过后每个虫只会保留一个框。注意这里的 bbox 坐标是相对原图缩放前的坐标吗不是这里有个大坑如果输入图片不是 640x640坐标会和原图对不上。必须在 preprocess 里记录缩放比例回到原图尺寸时再换算。上面的代码为了保持篇幅省略了这一点实际使用时要把原图宽高传进来做等比例缩放否则画框位置会偏移不少。4.3 把“防治”落到系统里知识库表设计与规则判定识别结果出来后防治建议怎么来常见做法是维护一张防治知识表按“虫种 程度”给出建议。用 MySQL 还是 SQLite 都行看你的整体系统结构。如果项目还会安排用户管理、历史记录的模块直接上 MySQL 不亏。我这里给出的是 SQLite 版建表语句适合单机演示CREATE TABLE pest_control ( id INTEGER PRIMARY KEY AUTOINCREMENT, pest_name TEXT NOT NULL, density_level TEXT NOT NULL, suggestion TEXT NOT NULL, priority INTEGER DEFAULT 3 ); INSERT INTO pest_control (pest_name, density_level, suggestion, priority) VALUES (褐飞虱, 轻度, 暂不施药持续监测保护天敌, 1), (褐飞虱, 中度, 选用吡蚜酮兑水喷雾重点喷植株中下部, 2), (褐飞虱, 重度, 立即用药并考虑轮换用药防止抗药性, 3), (稻纵卷叶螟, 中度, 使用苏云金杆菌或甲维盐傍晚喷药, 2);这里的 density_level 怎么定义通常由“单张图中检测到的虫体数量”换算成每亩虫量但单图推全田本身就不科学。更合理的做法是让系统同时记录检测到的目标数量、图片采集面积如果固定高度拍摄面积可以估算然后粗略分三级0-5 只算轻度、5-15 只算中度、15 只以上算重度。这个阈值需要根据实际作物类型调整不能直接照搬但它给“防治系统”提供了一个可解释的输出逻辑评审或汇报时都能讲得清楚。我为这个推理加一个置信度聚合规则同一张图里同一虫种的所有检测框取平均置信度低于 0.3 时不下防治结论只在界面上提示“低置信度建议复核”。这是我从实际经历里总结出来的经验如果因为一次误检就让用户打药这个系统很快就会被弃用。5. 常见问题与避坑这几条不解决你训两天等于白训这个项目从环境搭建到最终跑通我估计 70% 的时间都花在各种报错和反直觉现象上。把最典型的几类问题按“现象 → 原因 → 解决”写在这里按顺序排查能省下大量时间。5.1 显存明明够训练中途却报 CUDA out of memory现象batch-size 设为 16训练到第 23 个 epoch 时突然 OOM之前都好好的。原因YOLOv7 训练时在前 10 个 epoch 会做 mosaic 增强合成后的图片尺寸比原图大同时 model EMA指数移动平均会额外占一部分显存并不是“开头不爆就永远不爆”。解决把 batch-size 降到 8或者把 img-size 从 640 降到 512。如果必须保持 640还可以在 train.py 里找到--cache-images参数把它打开让数据预加载到内存而不是显存这也能缓解峰值显存压力。5.2 粘虫板上的虫被识别成一个超大的框框住一片虫现象训练和验证 mAP 都挺高但在粘虫板或诱虫灯照片上模型把几十只虫整体框成一个框精确率很高但完全没法数数。原因标注数据里大部分是单虫小框模型学到的“虫”是单只特征当出现密集型虫群时它更倾向预测一个更大的框覆盖整个区域。解决在数据集中增加粘连场景的裁剪样本。把已标注的密集图片裁剪成 512x512 的小图重新做标注让模型见到更多“多虫同框”的情况。另外一个取巧做法是对 NMS 下手把 iou_thres 从 0.45 调低到 0.3让重叠框更容易被保留从而输出多个小框而不是一个大框。5.3 某一类虫子在验证集上 mAP 接近 0现象五类虫里蚜虫这一类的 mAP0.5 只有 0.05其他类都很正常。原因样本数量极度不平衡。蚜虫的图片可能只有 80 张而稻飞虱有 1200 张模型根本没学够蚜虫的特征。解决先做数据扩充对蚜虫图片做翻转、旋转、亮度扰动扩充到至少 300 张再在 yaml 里为蚜虫设置class_weights虽然 YOLOv7 原生没提供这个参数但可以在 loss 计算的代码里按类别对 cls_loss 加权。最简单的方法还是扩充样本靠损失函数加权是补救而非根治。5.4 训练画出来的框都在图片正中间现象训练开始不久验证集图片上大量预测框集中在画面中心区域框的位置毫无道理。原因标注 txt 里坐标归一化出错中心点坐标全部写成 0.5 左右模型学到的只是“目标的中心在中间”这个假规律。解决用脚本抽查 labels/train 目录里的 txt 文件打印每一行的坐标分布awk {print $2, $3} labels/train/*.txt | sort -n | uniq -c | head -50如果大量坐标集中在 0.5 附近说明转换脚本里没有把中心点除以图片宽高或者图片尺寸读取的是空值。回头检查导出脚本里的 img_w、img_h 是否从正确路径读取。5.5 验证集 mAP 高拿到田间随手拍的照片却惨不忍睹现象用测试集评估mAP0.5 有 0.87但用户手机随手拍的一张模糊照片模型一个目标都检测不出来。原因训练数据大多是“正拍、清晰、光线好”的摆拍图和真实场景存在极大的 distribution shift分布偏移。解决定向采集一批低质量样本逆光、抖动模糊、逆光下的叶背、傍晚自然光。每类至少加 50 张进训练集。如果实在没有真实数据用数据增强里的blur3、hsv_h0.05增加低质量样本的比例。这一步是决定模型能否从“比赛成绩”变成“田间可用”的关键你必须在一开始就刻意地“污染”训练集。6. 最后这一步能让你的虫害识别系统多一层实用价值方案走到“识别 → 给出防治建议”已经是一个完整的系统了但如果你想让演示效果更让用户信服可以再加一个简单但实用的环节多帧判定。单张图片的检测结果往往有随机性同一片叶子拍两次第一次检出 3 只虫第二次检出 7 只虫用户会质疑整个模型。常见做法是连续采集 5 张不同角度的图片分别推理后按虫种累加目标数量取中位数或平均值作为最终结果。这个逻辑在 PyQt 或 Web 界面里都不难实现但让系统稳定性看起来提升了一个量级。另一个可以增强可信度的小技巧是把防治建议里的“密度等级”计算逻辑直接展示在界面上。用户看到的不只是“建议使用吡蚜酮”一句话而是“本次共检测到褐飞虱 12 只属于中度建议用量为每亩 20-30 克兑水 30 升重点喷施水稻中下部”。这个透明度很重要能避免系统被当成黑匣子。在代码里实现时无非是把第 5 章分类规则与农药信息表关联起来纯文本拼接即可但这是整个“防治”部分最容易被评审老师或业务方认可的设计点。最后提醒一句不要指望一个模型把所有虫害都识别正确。我手里的方案里一定会保留一个“未知虫种”的分支——当所有检测框的置信度都在 0.3 以下时返回“无法判断建议上传更清晰图片或联系植保站”这比硬给一个错误结果体面得多。在农业场景中承认不确定性反而会让用户愿意持续使用系统。希望这套思路能帮你在 YOLOv7 虫害识别与防治系统的搭建过程中少踩几个坑。本文还有配套的精品资源点击获取
返回列表