基于改进YOLOv5的烟叶病害检测系统:从模型优化到PPT交付全流程

发布时间:2026/10/12 8:26:23
基于改进YOLOv5的烟叶病害检测系统:从模型优化到PPT交付全流程 简介这份PPT资源面向计算机视觉与深度学习方向的学生、科研人员及农业智能化从业者围绕基于改进YOLOv5的烟叶病害检测系统展开可用于课程汇报、毕业设计答辩或项目方案参考。压缩包内仅含1个pptx文件大小约10.34MB以图文并茂的幻灯片形式呈现完整技术脉络。内容涵盖烟叶病害对烟草经济作物的影响、传统诊断方法的局限、嵌套式YOLOv5识别思路以及数据集采集标注、labelimg工具使用、环境搭建、训练与识别效果、系统整合等环节并延伸至YOLO算法改进方向、激活函数与损失函数选择、网络结构修改等研究要点。目前已有3123人学习下载适合希望快速理解目标检测落地农业场景、梳理改进YOLOv5思路的读者参考借鉴。1. 烟叶病害检测系统从YOLOv5改进到PPT交付的完整落地路径烟叶病害检测这件事真正下过田的人都知道痛点在哪发病初期叶片上的斑点小、边界糊、跟健康叶脉纹理混在一起人眼都要凑近了看更别说让模型在复杂光照下稳定识别。这也是为什么很多团队选了 YOLOv5 做基线却在自建数据集上翻车——不是模型不行是改进点和数据特性没对齐。这套「Python基于改进YOLOv5的烟叶病害检测系统」要解决的正是从数据采集、模型改进、训练调参到最终用 PPT 把技术方案讲清楚的全链路问题。它适合两类人一类是手里有烟叶图像数据、想跑通检测 pipeline 的算法工程师另一类是需要把检测系统做成汇报材料、参加评审或答辩的学生和项目负责人。下面按「先立住原理、再动手复现、最后避坑」的顺序拆开讲。2. 改进YOLOv5到底改哪里烟叶病害的四个技术抓手2.1 先看清烟叶病害检测和通用目标检测的差别通用 COCO 数据集上的 YOLOv5 调参经验直接搬到烟叶病害上大概率会失望。原因有三层。第一层是目标尺度烟叶病害的早期病斑在整张图里可能只占几十个像素属于典型的小目标而 YOLOv5 默认的三尺度检测头里负责小目标的 P3 特征图经过多次下采样后病斑的细节信息已经丢得差不多了。第二层是类间相似度不同病害比如赤星病、黑胫病、花叶病在早期阶段的颜色和纹理非常接近模型很容易把 A 病判成 B 病这不是靠加数据量能简单解决的。第三层是背景干扰田间拍摄的烟叶图像里土壤、杂草、阴影、反光都会进入画面模型如果没学好区分前景和背景误检率会居高不下。所以「改进 YOLOv5」不是随便换个 backbone 或者加个注意力模块就叫改进得针对上面这三个问题下手。常见的改进方向有四类注意力机制增强让模型聚焦病斑区域、特征融合结构优化把浅层细节和深层语义更好地结合、损失函数调整解决正负样本不平衡和小目标回归问题、以及数据增强策略的针对性设计。下面逐个说清楚每个方向改什么、为什么改、怎么落地。2.2 注意力机制在 backbone 和 neck 的哪些位置插入注意力机制的核心作用是让网络学会「看哪里重要」。在烟叶病害检测里病斑区域就是需要重点关注的地方而背景和健康叶面应该被抑制。常见的做法是在 YOLOv5 的 backbone 末端或 neck 的特征融合层插入 CBAMConvolutional Block Attention Module或 SESqueeze-and-Excitation模块。以 CBAM 为例它包含通道注意力和空间注意力两个子模块。通道注意力负责判断「哪些特征通道更重要」空间注意力负责判断「特征图的哪些位置更重要」。对于烟叶病害这种小目标检测空间注意力的价值更大因为它能帮助模型在低分辨率特征图上仍然定位到病斑所在的空间区域。具体插入位置一般选两个一是 backbone 的 C3 模块之后、SPPF 之前这里特征图分辨率还比较高空间信息保留得比较完整二是 neck 的 PAN 结构中上采样和下采样路径的拼接处这里做注意力可以引导特征融合时偏向病斑相关的通道和空间位置。代码层面的改动通常是在models/common.py里新增 CBAM 类然后在models/yolo.py的 Detect 头部之前或 backbone 中注册。下面是一个 CBAM 的最小实现import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction16): super().__init__() # 全局平均池化和全局最大池化并行各自经过一个共享的MLP self.avg_pool nn.AdaptiveAvgPool2d(1) self.max_pool nn.AdaptiveMaxPool2d(1) self.mlp nn.Sequential( nn.Conv2d(channels, channels // reduction, 1, biasFalse), nn.ReLU(inplaceTrue), nn.Conv2d(channels // reduction, channels, 1, biasFalse) ) self.sigmoid nn.Sigmoid() def forward(self, x): avg_out self.mlp(self.avg_pool(x)) max_out self.mlp(self.max_pool(x)) return self.sigmoid(avg_out max_out) * x class SpatialAttention(nn.Module): def __init__(self, kernel_size7): super().__init__() # 沿通道维度做平均和最大池化拼接后用一个大卷积核生成空间注意力图 self.conv nn.Conv2d(2, 1, kernel_size, paddingkernel_size // 2, biasFalse) self.sigmoid nn.Sigmoid() def forward(self, x): avg_out torch.mean(x, dim1, keepdimTrue) max_out, _ torch.max(x, dim1, keepdimTrue) concat torch.cat([avg_out, max_out], dim1) return self.sigmoid(self.conv(concat)) * x class CBAM(nn.Module): def __init__(self, channels, reduction16, kernel_size7): super().__init__() self.channel_att ChannelAttention(channels, reduction) self.spatial_att SpatialAttention(kernel_size) def forward(self, x): x self.channel_att(x) x self.spatial_att(x) return x这段代码里reduction控制通道注意力的压缩比默认 16 是原论文的推荐值如果你的通道数本来就少比如小于 64可以改成 8 甚至 4否则压缩后信息损失太大。kernel_size控制空间注意力的感受野7 是常用值如果病斑特别小可以降到 3但太小会丢失上下文信息。插入位置的选择比模块本身的设计更影响最终效果建议先在 neck 的 P3 分支上试因为那里直接对应小目标检测。2.3 特征融合改进BiFPN 和 ASFF 怎么选YOLOv5 默认的 neck 是 PANet 结构做的是自顶向下和自底向上的双向特征融合。但在烟叶病害检测里PANet 有两个不足一是不同尺度的特征直接相加或拼接没有做加权浅层的细节特征和深层的语义特征被同等对待二是小目标依赖的 P3 特征在多次融合后可能被稀释。BiFPNBidirectional Feature Pyramid Network的思路是给每个尺度的特征加一个可学习的权重让网络自己决定哪些尺度的信息更重要。ASFFAdaptively Spatial Feature Fusion则是在空间维度上做自适应融合对每个位置学习一个融合权重。两者都能用但侧重点不同BiFPN 更偏向尺度级别的加权ASFF 更偏向空间位置的加权。对于烟叶病害这种病斑位置不固定、尺度变化大的场景我一般会先试 BiFPN因为实现相对简单对训练稳定性的影响也小。BiFPN 的核心改动是在特征融合时引入权重参数class BiFPN_Concat(nn.Module): def __init__(self, dimension1): super().__init__() self.d dimension # 可学习的融合权重初始化为1训练中自动调整 self.w nn.Parameter(torch.ones(2, dtypetorch.float32), requires_gradTrue) self.epsilon 1e-4 def forward(self, x): # 对两组特征做加权求和权重经过ReLU保证非负再归一化 w torch.relu(self.w) weight w / (torch.sum(w, dim0) self.epsilon) return weight[0] * x[0] weight[1] * x[1]这个模块替换掉 YOLOv5 中原有的Concat操作用在 neck 的每个融合节点上。epsilon是防止除零的小量不要去掉。训练初期权重接近均匀分布随着训练推进网络会自动加大有用尺度的权重。需要注意的是加了 BiFPN 之后模型参数量会增加如果显存紧张可以把 backbone 的宽度系数调小一档。2.4 损失函数调整小目标回归的痛点怎么解YOLOv5 默认用的是 CIoU Loss 做边界框回归。CIoU 考虑了重叠面积、中心点距离和长宽比对一般目标够用但对小目标有个问题当预测框和真实框都很小的时候中心点距离的微小偏差会导致 IoU 剧烈波动梯度不稳定。烟叶病害的早期病斑恰恰就是这种小目标。常见的改进方向有三个一是换成 EIoU Loss把长宽比的惩罚项拆开分别计算宽和高的差异收敛更稳定二是引入 Focal Loss 的思想对难样本加大权重三是用 WIoUWise-IoULoss根据锚框的质量动态调整梯度增益。我一般会先试 EIoU因为改动最小效果也比较稳。def bbox_eiou(pred, target, eps1e-7): # pred和target格式为[cx, cy, w, h] px, py, pw, ph pred[:, 0], pred[:, 1], pred[:, 2], pred[:, 3] tx, ty, tw, th target[:, 0], target[:, 1], target[:, 2], target[:, 3] # 计算IoU inter_w (torch.min(px pw/2, tx tw/2) - torch.max(px - pw/2, tx - tw/2)).clamp(0) inter_h (torch.min(py ph/2, ty th/2) - torch.max(py - ph/2, ty - th/2)).clamp(0) inter inter_w * inter_h union pw * ph tw * th - inter eps iou inter / union # 中心点距离惩罚 rho2 (px - tx) ** 2 (py - ty) ** 2 # 最小外接框的宽高 cw torch.max(px pw/2, tx tw/2) - torch.min(px - pw/2, tx - tw/2) ch torch.max(py ph/2, ty th/2) - torch.min(py - ph/2, ty - th/2) c2 cw ** 2 ch ** 2 eps # EIoU分别惩罚宽高差异 rho_w2 (pw - tw) ** 2 rho_h2 (ph - th) ** 2 eiou iou - rho2 / c2 - rho_w2 / (cw ** 2 eps) - rho_h2 / (ch ** 2 eps) return 1 - eiou这段代码可以直接替换 YOLOv5 的utils/loss.py中的 CIoU 计算部分。eps的作用是防止除零一般设 1e-7 就够。换成 EIoU 后训练前几个 epoch 的 loss 曲线可能会比 CIoU 略高这是正常的因为惩罚项更严格收敛后的 mAP 通常会好 1 到 3 个点。3. 从零跑通训练数据集准备、配置修改与训练命令3.1 烟叶病害数据集的标注规范和格式转换数据集是整个系统的地基。烟叶病害检测的数据集一般有两种来源一是田间实拍二是实验室可控环境下拍摄。实拍数据更贴近真实场景但标注难度大实验室数据干净但模型泛化到田间会掉点。建议至少保证 70% 以上是实拍数据。标注工具用 LabelImg 或 Roboflow 都行关键是标注规范要统一。烟叶病害的标注有三个容易出问题的地方一是病斑边界模糊时不同标注员的框大小差异很大建议制定一个规则比如「以肉眼可见的变色区域外扩 2 个像素」二是同一片叶子上的多个病斑是标成一个框还是多个框要提前定好一般建议分开标因为后续可能要做病斑计数三是遮挡情况如果病斑被叶脉或相邻叶片遮住超过 50%建议标为 difficult 或者直接不标。YOLOv5 需要的标注格式是每行class_id x_center y_center width height坐标都归一化到 0 到 1 之间。如果你用的是 VOC 格式的 XML需要转换import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, classes, output_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 归一化并转换为中心点宽高格式 x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines))classes列表的顺序要和后续data.yaml里的names完全一致否则类别会错位。归一化保留 6 位小数是 YOLOv5 的惯例精度足够且不会让文件过大。转换完成后建议随机抽 20 张图用labelImg重新打开检查一遍确认框的位置和类别没错。3.2 data.yaml 和模型配置文件的必改项YOLOv5 的训练入口是train.py但它依赖两个配置文件数据集配置data.yaml和模型结构配置yolov5s.yaml或你选的其他版本。data.yaml里必须改的项path: /home/user/yolo_smoke # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 4 # 类别数根据你的病害种类改 names: [healthy, brown_spot, black_shank, mosaic] # 类别名顺序要和标注一致nc和names是最容易出错的地方。如果你标注了 4 类但nc写了 3训练不会报错但最后一类的标签会被忽略模型永远学不会。names的顺序如果和标注时的classes列表不一致模型会把 A 病判成 B 病而且 loss 曲线看起来还挺正常这种坑最隐蔽。模型配置文件的改动取决于你做了哪些改进。如果加了 CBAM需要在backbone或head的对应位置插入模块定义如果换了 BiFPN需要把Concat替换成自定义的BiFPN_Concat。改完之后一定要用python models/yolo.py --cfg your_config.yaml跑一遍确认模型能正常构建打印出的参数量和层结构符合预期。3.3 训练命令与关键超参数设置训练命令本身不复杂关键是超参数怎么设。下面是一条我常用的训练命令python train.py \ --weights yolov5s.pt \ --cfg models/yolov5s_cbam.yaml \ --data data/smoke_disease.yaml \ --epochs 200 \ --batch-size 16 \ --img-size 640 \ --lr0 0.01 \ --lrf 0.01 \ --patience 30 \ --device 0 \ --project runs/train \ --name smoke_v1--weights用预训练权重即使你的类别和 COCO 完全不同预训练权重也能加速收敛这是血泪经验从零训练在小数据集上几乎必翻车。--lr0是初始学习率0.01 是 YOLOv5 的默认值但如果你的数据集小于 2000 张建议降到 0.005 甚至 0.001否则前期 loss 震荡会很大。--lrf是最终学习率相对于初始学习率的比例0.01 表示余弦退火到 0.0001。--patience是早停耐心值30 表示如果 30 个 epoch 验证集 mAP 没提升就停止训练这个值不要设太小烟叶病害数据集上模型经常在 50 个 epoch 之后才出现明显提升。--img-size默认 640但如果你的病斑特别小可以提到 1024 甚至 1280代价是显存占用翻倍。如果显存不够先把--batch-size降到 8 或 4再考虑降--img-size。另外YOLOv5 默认开启了 Mosaic 和 MixUp 增强在小数据集上这两个增强很有效但如果你的病斑本身就很密集Mosaic 拼接后可能导致一张图里病斑过多反而增加学习难度这时候可以用--mosaic 0.5降低触发概率。4. 训练过程中的避坑与排查五条血泪记录4.1 现象loss 从第一个 epoch 就 nan原因通常有三个学习率太大、数据标注里有非法值比如坐标超出 0 到 1 范围、或者 batch 里有空标签文件。先检查标注文件用脚本扫一遍所有 txt看有没有坐标小于 0 或大于 1 的行。如果标注没问题把--lr0降到 0.001 再试。还有一个容易被忽略的点如果你改了损失函数自定义 loss 里出现了除零或 log(0)也会导致 nan检查eps有没有加。4.2 现象mAP 一直在 0.1 到 0.2 之间徘徊这种「学不动」的情况八成是data.yaml的nc或names和标注文件不匹配。用python utils/general.py里的检查脚本或者自己写一个统计脚本确认标注文件里的 class_id 最大值是否等于nc - 1。另一个常见原因是图片路径不对YOLOv5 找不到图片时会静默跳过导致实际训练集比你以为的小很多。训练日志里train: Scanning...那一行会显示找到的图片数量对一下就知道。4.3 现象验证集 mAP 很高但实际测试时误检严重这是典型的过拟合加数据分布不一致。验证集和训练集如果来自同一批数据、同一时间段拍摄mAP 会虚高。解决办法是确保验证集里的图片来自不同的拍摄批次或不同的田间区域。另外如果测试时的光照条件和训练数据差异大模型会把阴影或反光误判为病斑。可以在训练时加入随机亮度、对比度增强YOLOv5 的--hsv-v和--hsv-s参数就是干这个的适当调大能提升光照鲁棒性。4.4 现象加了注意力模块后训练变慢但精度没提升注意力模块不是万能的插入位置不对反而会引入噪声。如果加在 backbone 的浅层那里特征图分辨率高注意力计算量大但浅层特征本身语义信息弱注意力学不到有用的东西。建议先只在 neck 的 P3 和 P4 分支上加观察 mAP 变化。如果两个 epoch 后 mAP 没有明显提升趋势就撤掉。另外注意力模块的初始化很重要如果权重初始化得不好训练初期会压制有效特征可以把注意力的最后一层卷积初始化为接近零的值让网络从「不加注意力」的状态开始学。4.5 现象训练到一半显存溢出YOLOv5 训练过程中显存占用不是恒定的Mosaic 增强和不同尺寸的输入会导致波动。如果训练到一半 OOM先把--batch-size降一档同时开启--accumulate做梯度累积比如--batch-size 8 --accumulate 2等效于 batch 16。另外--img-size如果设了 1024 以上建议开启--rect做矩形训练减少 padding 带来的显存浪费。还有一个隐藏的显存杀手是验证阶段如果验证集图片尺寸不统一YOLOv5 会按最大尺寸 padding可以在data.yaml里把验证集图片统一 resize 到和训练一样的尺寸。5. 把检测系统讲成一份能过评审的PPT结构、图表与演示技巧5.1 技术汇报PPT的章节骨架怎么搭一份烟叶病害检测系统的技术PPT评审最关心的不是你的模型有多复杂而是「你解决了什么问题、怎么证明解决了、还能怎么用」。建议按这个骨架组织第一页讲问题和场景用一张田间病害照片加一句「早期病斑人眼识别准确率不足 60%」把痛点立住第二到四页讲技术方案包括数据集构建、YOLOv5 改进点、训练策略每页只讲一个核心改动配一张结构图或对比图第五页放实验结果用表格对比改进前后的 mAP、参数量、推理速度第六页讲系统集成和部署方式最后一页放 demo 截图或视频帧。不要把所有改进点堆在一页里评审记不住。每个改进点单独一页用「改了什么 → 为什么改 → 效果如何」三段式讲。图表比文字重要mAP 对比用柱状图训练曲线用折线图检测效果用前后对比图。PPT 里的图片导出时注意分辨率YOLOv5 的检测结果图默认是 640 或 1280 宽直接贴进 PPT 可能糊建议用--img-size 1280重新推理一遍再截图。5.2 用Python脚本批量生成检测结果对比图手工截图效率太低而且不同页的字体、边框不统一。我一般写一个脚本批量跑推理并生成带标注框的对比图直接插入 PPTimport cv2 import torch import os # 加载训练好的模型 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/smoke_v1/weights/best.pt) model.conf 0.4 # 置信度阈值PPT展示可以调低一点让框更多 model.iou 0.45 img_dir test_images save_dir ppt_images os.makedirs(save_dir, exist_okTrue) for img_name in os.listdir(img_dir): img_path os.path.join(img_dir, img_name) results model(img_path) # 渲染检测框line_width控制框线粗细适合PPT展示 rendered results.render()[0] cv2.imwrite(os.path.join(save_dir, fpred_{img_name}), rendered)conf阈值在 PPT 展示时可以比实际部署时低一些比如部署用 0.5PPT 用 0.4这样能展示出更多检测框视觉效果更丰富。但要注意如果阈值太低导致误检框太多评审反而会质疑模型精度所以建议同时准备一组高阈值的结果作为对比。render()返回的是 RGB 格式的 numpy 数组如果后续要用 PIL 处理注意通道顺序转换。5.3 答辩时被问到「你的改进为什么有效」怎么答这个问题几乎每场评审都会出现。不要只回答「因为加了注意力」要从数据和模型两个层面解释。数据层面烟叶病害的病斑具有小目标、类间相似、背景干扰强的特点这些特点导致基线模型在哪些具体指标上表现差。模型层面你加的模块从机制上如何缓解这些问题比如 CBAM 的空间注意力让模型在低分辨率特征图上仍能定位病斑BiFPN 的加权融合让浅层细节不被深层语义淹没。最后用消融实验的数据收尾比如「去掉 CBAM 后 mAP 下降 2.3 个点去掉 BiFPN 后下降 1.8 个点」这比任何解释都有说服力。如果评审追问「为什么不用 Transformer」可以回答Transformer 在数据量充足时确实有优势但烟叶病害数据集通常只有几千张Transformer 的归纳偏置弱小数据集上容易过拟合而 CNN 加注意力是在有限数据下更稳妥的选择。这个回答既展示了技术视野又体现了工程判断力。5.4 一个我踩过的坑PPT里的mAP数字和代码跑出来的对不上有一次答辩前夜我发现 PPT 里写的 mAP 是 0.87但重新跑val.py出来是 0.84。查了半天原因是 PPT 里的数字来自训练过程中某个 epoch 的验证结果而val.py用的是best.pt加默认的 NMS 参数。训练时的验证和独立验证的 NMS 阈值、置信度阈值可能不同导致 mAP 有差异。后来我养成了一个习惯PPT 里所有数字必须用最终权重加固定参数重新跑一遍val.py确认并且把命令和参数写进备注页。这个习惯帮我避免了好几次尴尬。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询