ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python改进YOLOv5烟叶病害检测系统:小目标病斑与工程复现

Python改进YOLOv5烟叶病害检测系统:小目标病斑与工程复现

简介:这份PPT围绕基于改进YOLOv5的烟叶病害检测系统展开,面向计算机视觉、深度学习目标检测方向的学习者,以及需要完成课程设计、毕业设计或项目汇报的读者,重点回应烟叶病害识别中传统人工诊断不及时、准确率有限的问题。整包仅含1个pptx文件,约10.34MB,页面以研究背景、方法流程、实验设置与结果展示为主,便于直接用于汇报或二次整理。内容从烟草病害对烟叶质量与农业生产的影响切入,依次梳理传统诊断方法的局限、嵌套式YOLOv5的数据采集标注训练与系统整合思路,并展示病害种类、位置和数量的标注效果;同时涉及labelimg标注、环境搭建、训练识别、Adam与SGD优化器选择、激活函数与损失函数对比、网络结构改进等知识点。已有3131人学习,可为目标检测改进、农业视觉应用及论文选题提供较完整的参考框架。

1. 从一张病斑图到答辩PPT:Python改进YOLOv5烟叶病害检测系统到底解决什么问题

如果你手里有一批烟叶病斑图,还要把检测结果做成一份能讲清楚的PPT,这套Python基于改进YOLOv5的烟叶病害检测系统就不是“跑个模型”那么简单。它要解决的是三件事:病斑在叶片上尺度小、背景杂乱时容易漏检;训练指标好看但演示时翻车;PPT里只有mAP曲线,讲不清数据、改进点和误检边界。适合做农业视觉课题的开发者、需要交付课程设计或技术方案的同学,也适合想把检测流程固化成可复现工程链的一线人员。下面按数据、改进、评估、避坑和PPT验证顺序拆开讲。

2. 烟叶病害数据从田间到YOLO标签:采集、清洗与格式转换的四个边界

烟叶病害检测的第一道门槛不是模型,而是数据。病斑可能只有几十个像素,叶片堆叠、反光、阴影和土壤背景都会干扰标注。常见做法是先固定采集协议,再做清洗和格式转换。这里不要一上来就调网络,数据口径不统一,后面改进YOLOv5的收益会被噪声吃掉。

2.1 田间采集的四个硬指标:光照、背景、病级与重复样本

采集时我一般盯四个指标。光照上,避免正午直射和闪光灯,强反光会让病斑边缘消失;背景上,叶片要尽量平铺,但也要保留一部分堆叠和土壤背景,否则模型到了现场容易把“干净背景”当成先验。病级上,早期斑点和晚期大面积枯斑要分开记录,不能只标“有病/无病”,否则检测头学到的是叶片整体颜色而不是病斑形态。重复样本上,同一片叶子连续拍十几张要控制比例,不然训练集和验证集看似随机划分,实际仍然高度相似。

一个可执行的清洗规则是:按叶片编号分组划分训练集、验证集和测试集,而不是按图片随机打散。这样验证集才能暴露真实泛化问题。常见做法是训练集占70%,验证集20%,测试集10%,但同一片叶子的不同角度只能落在同一个集合。若某个类别少于200个标注框,先别急着做复杂改进,优先补数据或做针对性增强。

提示:病斑类别名称一旦确定,后续标签转换、data.yaml和PPT图表都沿用同一套英文名,不要中途改中文名再映射。

2.2 用Python把标注转成YOLOv5可读的txt标签

常见标注工具导出的是VOC XML,而YOLOv5需要每张图对应一个txt,每行格式为“类别id 中心x 中心y 宽 高”,且全部归一化到0到1。下面脚本做转换、边界裁剪和空框过滤。类别顺序必须和后面data.yaml里的names完全一致。

import os import xml.etree.ElementTree as ET # 类别顺序必须与data.yaml中的names一致 CLASSES = ["healthy", "brown_spot", "mosaic", "bacterial_blight"] def voc_to_yolo(xml_path, txt_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in CLASSES: continue cls_id = CLASSES.index(name) 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) # 裁剪到图像边界,防止标注越界导致归一化异常 xmin = max(0.0, min(xmin, img_w)) xmax = max(0.0, min(xmax, img_w)) ymin = max(0.0, min(ymin, img_h)) ymax = max(0.0, min(ymax, img_h)) w = xmax - xmin h = ymax - ymin if w <= 1 or h <= 1: continue xc = (xmin + xmax) / 2.0 / img_w yc = (ymin + ymax) / 2.0 / img_h nw = w / img_w nh = h / img_h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {nw:.6f} {nh:.6f}") with open(txt_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) if __name__ == "__main__": xml_dir = "datasets/raw_xml" txt_dir = "datasets/labels_all" os.makedirs(txt_dir, exist_ok=True) # 实际使用时从图片读取宽高,这里用示例尺寸说明参数位置 img_w, img_h = 1920, 1080 for name in os.listdir(xml_dir): if not name.endswith(".xml"): continue stem = os.path.splitext(name)[0] voc_to_yolo( os.path.join(xml_dir, name), os.path.join(txt_dir, stem + ".txt"), img_w, img_h )

这段代码的关键参数是CLASSES和img_w、img_h。CLASSES顺序决定类别id,不能随意调换;img_w和img_h必须来自对应图片真实尺寸,批量处理时建议用Pillow逐张读取,而不是写死。脚本还做了边界裁剪,解决标注框超出图像边缘的问题;宽高小于1像素的框直接丢弃,避免归一化后出现零面积目标。转换完成后抽查几张图,把txt画回原图,看框是否贴合病斑。血泪经验是:不要只检查文件数量,框偏移半片叶子的问题在训练损失里不一定立刻暴露。

2.3 数据集划分与data.yaml的必填字段

转换完标签后,按叶片编号分组划分目录。目录结构可以保持YOLOv5常见的images/train、images/val、labels/train、labels/val。data.yaml只写必要字段,路径尽量用相对路径,换机器时少改一处。

path: datasets/tobacco_leaf train: images/train val: images/val test: images/test nc: 4 names: ["healthy", "brown_spot", "mosaic", "bacterial_blight"]

path是数据集根目录,train/val/test写相对path的图片目录,YOLOv5会按同名规则去找labels下的txt。nc必须等于names长度,names顺序必须和转换脚本里的CLASSES一致。常见错误是names里多一个空格或中文逗号,训练能启动但类别映射错位,评估时混淆矩阵会显示得莫名其妙。划分脚本建议单独保存划分清单,把每张图的叶片编号、集合归属写进csv,后面做PPT的数据分布图也直接复用这份清单。

检查项合格标准常见翻车点
标签格式每行5列,归一化到0到1写成像素坐标
类别顺序转换脚本与data.yaml一致中途增删类别
划分方式按叶片编号分组同一叶片跨集合
空标签无病斑图保留空txt或单独整理空txt被误删导致错位

3. 改进YOLOv5的落点:小目标病斑、遮挡与轻量化的取舍

改进YOLOv5不要为了“改进”而堆模块。烟叶病害检测的主要矛盾是小目标病斑、叶片遮挡和背景干扰。常见有效的落点是:在特征融合前加轻量注意力、提高小目标检测层的输入分辨率、控制模型宽度避免演示端跑不动。下面给一个可复现的轻量注意力模块和训练配置,重点看参数为什么这样设。

3.1 为什么原版YOLOv5在烟叶病斑上会漏检

原版YOLOv5的检测头对大中目标更友好,早期病斑只有几十像素,经过多次下采样后特征响应很弱。叶片边缘和叶脉的纹理又和病斑接近,模型容易把纹理当成目标。另一个问题是数据里健康叶片占比高,背景类别不均衡,置信度阈值一调高就漏检,一调低就误检。改进时优先做三件事:保留更高分辨率的浅层特征、在通道维度上抑制背景响应、用更严格的病斑级评估而不是只看整图mAP。

注意力模块不是越复杂越好。烟叶图像颜色分布集中,通道注意力比空间注意力更稳;如果数据里病斑位置分布很散,再加入轻量空间分支。下面这个SEBlock结构简单,插入到Backbone末端或检测头前都容易改,显存压力小,适合课程设计和单卡演示环境。

3.2 加一个轻量注意力模块的代码实现

import torch from torch import nn class SEBlock(nn.Module): def __init__(self, channels, reduction=16): super().__init__() hidden = max(8, channels // reduction) self.pool = nn.AdaptiveAvgPool2d(1) self.fc = nn.Sequential( nn.Linear(channels, hidden, bias=False), nn.SiLU(inplace=True), nn.Linear(hidden, channels, bias=False), nn.Sigmoid() ) def forward(self, x): b, c, _, _ = x.shape y = self.pool(x).view(b, c) y = self.fc(y).view(b, c, 1, 1) return x * y # 使用示例:假设输入特征通道为256 if __name__ == "__main__": x = torch.randn(2, 256, 40, 40) block = SEBlock(256, reduction=16) y = block(x) print(y.shape)

channels必须和插入位置的输出通道一致,reduction控制压缩比例,16是常见起点;通道数很小的时候用max(8, channels // reduction)兜底,避免隐藏层过窄。forward里先全局平均池化得到通道描述,再经过两层线性层和Sigmoid生成权重,最后逐通道相乘。它不改变特征图尺寸,所以插入后不需要改检测头锚框。实际修改时,常见做法是在models/common.py里注册模块,再在模型yaml的某个C3后替换或追加。别一次改三处,先加一处做A/B对比,否则涨点也说不清是哪来的。

3.3 训练命令与超参数配置:从预训练权重到冻结策略

训练命令要能复现,参数要写进实验记录。下面是一条常见的单卡训练命令,预训练权重用YOLOv5常见的小模型权重,具体文件名按本地已有权重替换。

python train.py \ --img 640 \ --batch 8 \ --epochs 100 \ --data data/tobacco.yaml \ --weights yolov5s.pt \ --project runs/tobacco \ --name exp01_se \ --cache \ --workers 4 \ --freeze 10

--img 640是输入分辨率,病斑特别小时可以试800,但显存和推理耗时都会上升;--batch 8适合单卡显存有限的场景,显存够可以加到16;--epochs 100是起点,看验证集mAP连续多轮不涨再停;--data指向2.3的yaml;--weights是预训练权重,空字符串表示从零训练,一般不推荐;--project和--name决定输出目录,方便对比实验;--cache把图片缓存到内存,数据量大时注意内存;--workers是数据加载线程;--freeze 10冻结前10层,小数据集上能减少过拟合,数据量足够时可以不加。

参数建议起点调整依据
img640病斑像素小于20时试800
batch8显存占用和梯度稳定性
lr00.01冻结层多时适当降低
lrf0.01控制末期学习率
conf-thres0.25演示漏检多时降到0.2再评估
iou-thres0.45病斑密集时试0.5

训练时不要只看loss下降。每10轮抽一批验证图做可视化,重点看病斑框是否贴合、有没有把叶脉框进去。改进模块如果让mAP涨了0.5个点但推理慢了一倍,要判断演示环境能不能接受。轻量化不是越轻越好,烟叶病害检测系统最终要落在可解释的工程链上,PPT里能讲清“为什么加、加了之后哪类病斑变好”比单纯堆指标更有说服力。

4. 训练完别急着做PPT:mAP之外的评估、可视化与误检归因

训练日志里的mAP只能说明整体重叠度,不能直接回答“早期病斑漏没漏”“背景误检多不多”。做PPT前先把评估做厚:病斑级召回、置信度分布、混淆矩阵和误检案例。否则答辩现场被问一句“这个mAP对病害分级有什么用”,很容易卡住。

4.1 看mAP之外还要看什么:病斑级召回与置信度分布

病斑级召回的做法是:对每张验证图,按真实标注框逐个判断是否被同类别预测框以一定IoU命中,统计每个类别的召回率。整图mAP会把大斑和小斑混在一起,病斑级召回能暴露小目标问题。置信度分布则把真正例和假正例的分数分开画,如果假正例集中在0.3到0.5,说明阈值附近不稳定,演示时换一张背景图就可能翻车。

我一般会输出三张表:每个类别的 Precision/Recall、不同置信度阈值下的漏检数、误检案例清单。误检清单要带图片名和预测框坐标,后面做PPT的“失败案例分析”直接引用。注意测试集不要反复调阈值,调参阶段用验证集,最终报告用测试集。

4.2 用Python画PR曲线和混淆矩阵的落地脚本

YOLOv5训练目录下会生成results.csv,列名可能随环境有细微差异,先打印列名再画图。下面脚本读取训练记录,画mAP曲线并保存,方便放进PPT。

import pandas as pd import matplotlib.pyplot as plt csv_path = "runs/tobacco/exp01_se/results.csv" df = pd.read_csv(csv_path) df.columns = [c.strip() for c in df.columns] print(df.columns.tolist()) plt.figure(figsize=(8, 5)) plt.plot(df["epoch"], df["metrics/mAP_0.5"], label="mAP@0.5") plt.plot(df["epoch"], df["metrics/mAP_0.5:0.95"], label="mAP@0.5:0.95") plt.xlabel("epoch") plt.ylabel("metric") plt.legend() plt.tight_layout() plt.savefig("ppt_assets/map_curve.png", dpi=200)

csv_path指向实验输出目录,metrics/mAP_0.5和metrics/mAP_0.5:0.95是常见列名;如果列名带空格,先strip。dpi 200保证PPT里不糊。混淆矩阵不建议自己手画,常见做法是运行验证脚本并保存混淆矩阵图,再和病斑级召回表一起放进PPT。图表下的结论要具体,例如“brown_spot召回从0.72提升到0.79,但mosaic在强光样本上仍然偏低”,不要只写“效果良好”。

4.3 误检翻车现场怎么归因:背景、标注与尺度

误检归因按三步走。先看背景:把预测框周围裁剪出来,判断是不是叶脉、土壤反光或相邻叶片边缘。再看标注:有些“误检”其实是漏标,尤其是密集小斑,标注员只标了大的。最后看尺度:统计误检框的宽高分布,如果集中在极小框,说明小目标分支或输入分辨率不够;如果集中在大框,可能是类别定义不清,把健康黄化也标成了病害。

导出TorchScript并跑单张图片推理,是验证“训练环境之外能不能用”的最小闭环。常见导出命令如下:

python export.py \ --weights runs/tobacco/exp01_se/weights/best.pt \ --include torchscript \ --img 640

--weights指向最佳权重,--include torchscript表示导出TorchScript,--img与训练分辨率保持一致。导出后不要直接做PPT,先在另一台机器上加载导出的模型跑一张图,确认预处理、归一化和输出解析一致。推理前后处理对不上,是演示翻车的常见原因,模型本身不背这个锅。

5. 烟叶病害检测系统避坑:从标注到演示的5类翻车记录

这一章按“现象→原因→解决”记录常见坑。烟叶病害检测系统的坑很少集中在网络结构,更多在数据口径、阈值和演示环境。下面5条是我更愿意在训练前就检查的。

5.1 数据与标注的坑:病斑框越画越大之后

现象:训练loss正常下降,但验证图里病斑框明显偏大,小斑点被框进整片叶脉。

原因:标注规范只写了“框住病斑”,没有规定是否包含晕圈。不同标注员对边缘理解不同,模型学到的是平均后的模糊边界。

解决:在标注规范里加两条:框只包含病斑主体,不包含明显健康组织;边缘模糊时以可见变色区域为准。转换脚本里加面积过滤,对宽高超过叶片面积一定比例的框人工复核。PPT里放一张标注规范对比图,比放十张训练曲线更能说明数据质量。

现象:验证集mAP很高,测试集一换试验田就掉得很厉害。

原因:按图片随机划分,同一片叶子的不同角度同时进了训练集和验证集,验证集泄漏。

解决:按叶片编号或采集批次分组划分,划分清单落csv。测试集至少保留一个独立采集日期的数据,PPT里明确写出划分口径。

5.2 训练与推理的坑:验证集涨点、现场却漏检

现象:加了注意力模块后mAP涨了,但演示时早期小斑仍然漏。

原因:改进模块作用在中后期特征,小目标浅层信息没有被充分利用;同时置信度阈值沿用了默认值,小斑分数普遍偏低。

解决:先做病斑级召回统计,确认漏检集中在小框还是特定类别;再考虑提高输入分辨率或在浅层特征上加轻量分支。阈值在验证集上按类别扫一遍,不要一套阈值走天下。

现象:训练环境推理正常,换一台机器加载模型报错或结果全空。

原因:预处理不一致,训练时用了归一化和RGB顺序,推理时用了BGR或没归一化;导出模型和权重加载方式混用。

解决:把预处理写成一个固定函数,训练、验证、导出推理共用;导出后跑固定测试图,和原权重输出做数值对比。演示前至少在两台机器上各跑一次。

5.3 演示与PPT的坑:换台电脑就翻车

现象:答辩现场上传图片后界面卡住,或者检测框位置整体偏移。

原因:演示端没有锁定依赖环境;图片被前端压缩后尺寸变化,后端仍按原图尺寸映射框。

解决:演示环境提前装好固定依赖,准备离线权重和示例图;前后端约定好原图尺寸传递,框坐标按原图归一化再回写。PPT里不要只放成功案例,留一页误检和漏检边界,现场被问到时能接住。

现象:PPT里改进点讲得很热闹,被问“这个模块为什么适合烟叶”时答不上来。

原因:实验只做了涨点对比,没有做消融和可视化,改进理由停留在“别人也这么加”。

解决:补一组消融:原版、加模块、加模块并调阈值,固定数据划分和训练轮数;再补注意力热力图或特征图对比。PPT只讲一条主线:问题是什么、改哪里、哪类病斑变好、代价是什么。

6. 把系统讲成可复现的工程链:PPT最后一页的验证清单与进阶技巧

最后一页别放“谢谢聆听”,放一张可复现验证清单。我的习惯是把PPT技术页分成四块:数据口径、改进落点、评估边界、演示环境。每块只留一个可验证数字,例如“按叶片分组划分,测试集独立采集日期”“注意力模块插在检测头前,消融固定100轮”“brown_spot病斑级召回0.79,mosaic强光样本仍偏低”“演示端锁定预处理函数,两台机器输出一致”。这样别人照着清单能复现,也能看出边界。

验证项最低通过标准记录位置
标签回画抽查20张框贴合病斑数据检查记录
数据划分同一叶片不跨集合划分csv
消融实验原版与改进同口径对比实验记录表
导出推理另一台机器单图结果一致演示检查表
误检案例至少5张带归因PPT失败案例页

进阶技巧是给每个类别单独记录一个“演示阈值”,不要在全系统共用0.25。做法是在验证集上按类别扫0.15到0.5,选召回和误检折中的点,再把阈值写进配置文件。另一个技巧是把误检案例按背景、标注、尺度打标签,PPT里只放最具代表性的三张,剩下的放附录。这样现场追问时有据可查,不会陷入“模型玄学”的解释。我自己踩过的最深坑是训练指标很好看,演示时却因为预处理不一致整体偏移,后来养成了导出后先在另一台机器跑固定测试图的习惯。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进