简介:面向智能农业、计算机视觉学习者和算法落地工程师,这份PDF围绕YOLOv11在多作物叶片分析与病害识别中的完整应用展开,从精准农业背景、YOLO系列演进、网络结构(Backbone/Neck/Head)、损失函数与训练流程,到叶片数据集构建、标注规范、图像增强、模型训练与优化均有系统讲解,并给出小麦、温室蔬菜、果园等落地案例及挑战与展望,帮助读者建立从理论到实际项目部署的完整知识链路。资源为单个PDF文档,约1.94MB,共26页,支持目录章节跳转与阅读器大纲快速定位,文字图表显示完整,便于高效查阅。目前已有62人学习,适合正在做农业病害识别、目标检测课题或准备项目方案的用户参考使用。
1. 精准农业里为什么偏偏选 YOLOv11:从“看到病斑”到“来得及决策”
玉米大斑病在叶片上最初只是几个不起眼的小斑点,等它连成片,产量基本已经定局。YOLOv11多作物叶片分析技术,核心就是把目标检测直接搬进田间:用一个权重文件同时识别多作物的叶片正常与异常状态,定位病斑位置并给出病害类别,让植保从“事后补救”往“早期处理”前移。它解决的不是“图片里有没有病”这种小问题,而是田间实时检测在速度、精度和部署成本之间能不能同时站稳。适合做农业视觉落地、植保装备集成和科研数据处理的人。下面这套流程,我从网络结构选型、数据构建、训练参数到田间部署按实战顺序拆开讲,每个环节都给出能直接复现的做法。
2. 看懂 YOLOv11 网络结构:C3k2、C2PSA 与多作物病斑检测的匹配点
2.1 结构图上最值得看的三个模块,它们分别对应叶片病害的哪些麻烦
YOLOv11 的网络结构和前代相比,最直观的变化在 backbone 和 neck 部分:C3 模块被 C3k2 替换,C2PSA 引入了自注意力机制,再加上原有的 SPPF 和 anchor-free 解耦头。这三个改动不是为刷精度而刷精度,放到叶片病害场景里各有用途。
C3k2 比原来的 C3 更轻量,但下采样路径上信息丢失更少。叶片病斑有个特点,早期病斑可能只有几个像素,如果下采样太狠,小病斑的特征在深层特征图里直接消失了。C3k2 这种轻量结构能在保持深度的同时让特征更“细”,对斑点类、条斑类早期病害更友好。C2PSA 则是把自注意力加进了特征提取的中间层,模型能跨区域去关联病斑分布——比如锈病孢子堆往往沿叶脉走向分布,这种全局关系是普通卷积难以直接建模的。代价是计算量上升,所以它只在特定位置插入,不是整个网络全换。
SPPF 和 anchor-free 解耦头是另一组关键组合。叶片病斑大小差异极大,同一种病害早期是针尖大的点,后期能扩展成覆盖半片叶的大斑,SPPF 的多尺度池化让网络对不同尺度的病斑都有响应。解耦头把“这个位置有没有病”和“这是什么病”分开预测,多作物场景下类别多、类间形态相似度高,这种分离能减少类别和定位互相干扰。
看网络结构图时不建议逐层背参数,重点看三个信息:下采样倍数决定小目标极限、自注意力位置决定全局建模能力、输出头结构决定类别与定位是否解耦。我一般拿到一份结构图,先人工推一遍从输入到输出的特征图尺寸变化,就能估算它对小病斑的响应下限。这也是判断一个模型适不适合病害识别最快的方式。
2.2 为什么选单阶段检测,而不是“分割+分类”的两段式流程
农业视觉里经常有人一上来就想做语义分割,觉得把病斑像素级抠出来更“精细”。但实际落地时,分割的代价常在数据和生产两个环节同时暴露。像素级标注叶片病斑非常痛苦,病斑边缘在叶片上往往是渐变过渡,标注员对“边界在哪”的判定不一致,交给模型学到的边界也未必有物理意义;而施药决策需要的其实是一个位置和范围,检测框已经足够支撑“哪个网格该喷、喷多少”的决策逻辑。
YOLOv11 属于单阶段检测器,一次前向直接输出类别和边界框,在 Jetson 这类边缘设备上跑 640 输入能达到实时帧率。病害识别的部署环境多数在田间,供电、散热、成本都受限,实时性不是“锦上添花”,而是能不能用的前提。两段式流程精度也许高一点,但推理链路长、中间结果难调试,一旦某个环节崩了,整个流程不可用,这在田间是不可接受的。
另外,叶片病斑的“分割”本身也是伪需求。病斑边界模糊,即使像素级分割出来,对植保变量的贡献也有限。检测框配合置信度和面积估算,足够换算成病叶率和严重度。项目初期我建议坚决用检测框,把省下来的标注人力投入到更多样品的采集和多作物覆盖上,收益远高于抠边界。
2.3 多作物混合建模:一个权重文件里的类别结构与平衡策略
“多作物叶片分析”在模型设计上有一个绕不开的选择:是把所有作物的所有病害塞进一个模型,还是按作物分别训练。我的经验是,项目早期先做混合模型。原因很实际,单一作物单独训练意味着要维护多个模型、多个推理服务,田间一套设备要部署多份权重,更新和回滚都是麻烦;混合模型一个权重跑天下,维护成本低得多。
但混合建模对类别结构设计有要求。最怕的是类别设计成“属性组合”,比如“玉米_大斑病”“玉米_小斑病”“小麦_条锈病”这样一路铺开,类别一多,相似病害之间互相抢特征,掉点最快的往往就是这些容易混淆的类。建议类别名直接采用“作物_病害”的扁平结构,名称长一点没关系,关键是一个类目对应一个明确的有物理含义的目标。健康叶片也要单独设一类,否则模型会把所有不是已知病害的叶子都硬归到某个病害类里,误报率会很难看。
训练策略上,混合模型先在所有作物数据上跑通用特征,待收敛后如果某个作物仍然掉点,再拿这个作物的数据单独微调几轮。微调时注意保留混合训练得到的通用特征,不要用小学习率从零开始。这种“先混后分”的做法,比一上来就各训各的省资源,也比只在单一作物上训练更有泛化能力。
拿到预训练权重后,我会先跑一个最小验证,确认结构和自己环境匹配:
yolo detect predict model=yolov11n.pt source=test_leaf.jpg这条命令能同时验证三件事:权重文件是否能正常加载、当前 ultralytics 版本与权重是否兼容、推理链路是否通畅。输出结果里会给出每个检测框的类别和置信度,如果这一步就报错,说明环境或权重版本有问题,不要在训练阶段才排查。
from ultralytics import YOLO model = YOLO("yolov11n.pt") print(model.model)这段代码打印模型的完整结构,包含每个模块的参数数量和输出尺寸,适合在训练前确认加载的是不是预期版本。命令行直接看权重文件来源更简单,从 ultralytics 官方发布页下载时注意挑与当前安装版本配套的文件,跨版本权重偶尔能加载成功,但训练时 loss 表现会异常。
3. 多作物叶片数据怎么造:采集规范、标注边界与数据增强的落地清单
3.1 采集合规表:时段、角度、背景和分辨率直接决定模型上限
数据采集是病害识别项目里最容易被低估的一环。很多人先把模型跑起来,发现田间效果差,再回头补数据,这时采集成本已经翻倍。我一般会在项目启动时定一份采集规范,让所有采集人员照着执行,宁可第一批数据少一点,也不要脏。
一个可复用的采集规范表:
| 采集项 | 推荐做法 | 为什么 |
|---|---|---|
| 时段 | 上午 9-11 点、下午 3-5 点为主 | 光照均匀,避开正午强光造成的过曝和早晚露水造成的水珠干扰 |
| 角度 | 俯拍为主,辅以 30-60 度斜拍 | 覆盖不同部署机位,避免模型只认一种视角 |
| 背景 | 保留自然田间背景 | 含土壤、杂草、枯叶的负样本能显著降低误报 |
| 分辨率 | 原图不低于 1080p | 病斑早期小目标需要足够像素支撑;条件允许做微距补拍 |
| 存储 | 保留 EXIF 信息,按“作物/病害/日期”分目录 | 后续排查数据泄漏和分布漂移都依赖元数据 |
采集时要刻意覆盖发病早期、中期、晚期的样本。实际项目里最容易出现的情况是采集人员专挑症状明显的病斑拍,模型学到的都是“晚期病重”的特征,早期病斑识别能力几乎为零。我会在每个病害目录里按严重度再划子目录,训练前检查每个子目录的样本量,如果早期样本太少,宁可先不做严重度分级,也要保证早晚期都够模型见到。
3.2 标注规范:病斑边界怎么框、类目怎么设、谁该被跳过
标注规范直接影响模型和地面实况的一致性。叶片病害标注不需要像素级,但边界框的划定仍然有约定:单个独立病斑单独一个框,同一叶片上多个离散病斑分开框,不要用一个大框把所有斑点包起来。病斑连成片时,可以作为一个整框标注,但框边界要尽量贴合病斑实际范围,不要为了省事把整片叶子都框进去。
遮挡问题的处理更考经验。叶片互相重叠在田间无法避免,我的规则是:病斑被遮挡超过一半的不标注,遮挡面积小的正常标注。关键要前后一致,标一个不标一个会直接扰乱模型对目标的定义。焦外虚化的叶片不标,目标太小无法确认病种的叶片不标,保留这些负样本进背景。
类别设计上,项目早期不要做严重度分级。轻度、中度、重度在视觉上是连续过渡,标注员都经常拿不准,模型学出来的分级边界更不可靠。第一版模型只回答“有没有病、是什么病”,严重度通过检测框覆盖面积按面积阈值换算,后期有需要再单独建模。这个取舍能大幅降低标注一致性问题,也让模型收敛更快。
3.3 数据增强:光照扰动必须加,mixup 要克制
YOLOv11 在训练时默认开启 mosaic、翻转、色彩扰动等增强,但对叶片病害场景,增强策略需要调整。病害识别最需要的是光照扰动,因为田间光照变化极大,同一片病叶在直射光、阴影、阴天下视觉差异明显。我一般把 HSV 增强里的饱和度扰动调到 0.5-0.8,亮度扰动调大,让模型不至于把“阴影里的叶子”当成另一个目标。
模拟露水和雨滴也有实际意义。清晨叶片上的水珠会局部遮挡病斑,还会产生镜面反光,如果训练数据里没见过这种状态,落地时就会在清晨时段疯狂漏检。可以用随机叠加少量半透明椭圆光斑的方式做增强,低成本模拟水珠遮挡。另外,mosaic 增强对早期小病斑很有效,它把四张图拼在一起,等于变相提高了小目标在训练时的占比,但最后一个阶段要关掉 mosaic,让模型在真实尺度上做最后适配,否则推理时对小病斑的定位能力会被带偏。
mixup 在病害识别里要非常克制。它把病叶和健康叶混合,生出的合成图里“病斑叠在健康叶纹理上”,这种样本在现实中极少见,反而会让模型学到错误特征,增加误报。我的做法是直接用默认的增强,把精力花在提升真实数据多样性上。
3.4 数据质量检查脚本:训练前先干掉损坏图片和越界标注
训练集里混入损坏图片或越界标注,轻则训练不稳定,重则 loss 直接爆炸。我会在建完数据集后跑一个检查脚本,把所有脏数据一次性揪出来。
import os from PIL import Image def check_dataset(img_dir, label_dir, num_classes): bad_images, bad_labels, class_count = [], [], {} for name in os.listdir(img_dir): img_path = os.path.join(img_dir, name) label_path = os.path.join(label_dir, os.path.splitext(name)[0] + ".txt") try: img = Image.open(img_path) img.verify() except Exception: bad_images.append(img_path) continue if not os.path.exists(label_path): continue w, h = img.size with open(label_path) as f: for line in f: parts = line.strip().split() cls = int(parts[0]) if cls >= num_classes: bad_labels.append((label_path, "class overflow")) # YOLO 格式是归一化坐标,必须落在 0-1 之间 coords = list(map(float, parts[1:])) if any(c < 0 or c > 1 for c in coords): bad_labels.append((label_path, "coord out of range")) # 中心点可以不越界但框超出全图的,也标记 cx, cy, bw, bh = coords if cx - bw / 2 < 0 or cx + bw / 2 > 1 or cy - bh / 2 < 0 or cy + bh / 2 > 1: bad_labels.append((label_path, "bbox out of image")) class_count[cls] = class_count.get(cls, 0) + 1 print("bad images:", len(bad_images)) print("bad labels:", len(bad_labels)) print("class distribution:", class_count) check_dataset("images/train", "labels/train", num_classes=6)脚本逻辑分三层:第一步用 PIL 的 verify 检查图片文件本身是否完整,防止解码中途报错;第二步检查标注类别是否超出类别总数,这个是训练崩溃的最常见元凶;第三步检查坐标是否越界,YOLO 格式是相对原图的归一化坐标,中心点可以越界,但矩形框本身超出图片范围时检测目标就没有意义。脚本最后输出的类别分布表,用来发现样本极度不平衡的问题,比如某类只有几十个实例,这类在训练时基本学不出来。
注意脚本默认的 label 目录结构是 YOLO 格式的 txt,如果你的标注是 XML 的 VOC 格式,需要先完成 VOC 到 YOLO 的转换再做检查。转换时最容易出的坑是类别编号不对齐,例如 XML 里类名按字母排序后和 YOLO 的 names 列表不一致,检查脚本的类别越界判断能提前拦住这个问题。
4. 用 Ultralytics 跑通训练:环境配置、完整命令与 8 个关键参数
4.1 环境配置:适合 0 基础小白的最小核验顺序
YOLOv11 的主要训练入口是 ultralytics 库,环境搭建本身不复杂,但 pytorch 和 CUDA 的版本组合容易踩坑。我的建议是先装基础环境,再一次性验证全部组件,避免先装一堆依赖到训练时才报错。
python -m pip install ultralytics python -c "import ultralytics, torch; print(ultralytics.__version__, torch.__version__, torch.cuda.is_available())"第二条命令把三个最关键的信息一次打出来:ultralytics 版本、torch 版本、CUDA 是否可用。其中torch.cuda.is_available()返回 False 是最常见的问题,一般原因是安装的 torch 是 CPU 版本,重新按 CUDA 对应的版本安装即可。ultralytics 版本建议记录在项目依赖文件里,因为新版本有时会调整默认超参,复现结果时版本不一致会造成困扰。
环境搭建完成后,不要急着训练,先用小数据集跑 20 个 epoch,确认数据读取、损失计算、权重保存整条链路正常。这一步能暴露绝大多数环境问题,成本却比完整训练小得多,属于把大坑拆成小坑。
4.2 最小训练命令:从一份数据集 YAML 开始
训练入口是yolo detect train,它需要一个数据集配置文件和一个预训练权重。数据集 YAML 是训练的“总纲”,我建议写成相对路径,避免项目迁移后路径失效。
# leaf_crop.yaml path: /data/leaf_crop # 数据根目录 train: images/train val: images/val names: 0: corn_healthy 1: corn_nlb 2: wheat_leaf_rust 3: tomato_early_blight这份配置里最关键的是names的顺序和标注文件里的类别编号必须一一对应。检查脚本里num_classes=6就是和这里对齐的,编号一旦错位,模型训练不会报错,但推理结果全乱。path用绝对路径可以,但换机器时要同步改,我习惯把数据根目录固定在一个可配置的路径变量里。
训练命令我一般这样写:
yolo detect train \ model=yolov11m.pt \ data=leaf_crop.yaml \ imgsz=1280 \ epochs=150 \ batch=16 \ lr0=0.005 \ lrf=0.01 \ close_mosaic=10 \ patience=20 \ device=0这个命令选了几个和病害识别强相关的参数。imgsz=1280是因为叶片病斑小,640 输入下早期病斑只有几个像素,1280 能显著提升小目标召回率,但显存占用会翻倍,显存紧张时可以降到 960。close_mosaic=10表示最后 10 个 epoch 关闭 mosaic 增强,让模型在接近真实尺度的分布上收敛。lr0=0.005比默认值低,迁移学习场景下大学习率容易破坏预训练特征,前几个 epoch 的 loss 会异常升高。patience=20是早停,连续 20 个 epoch 验证指标不提升就自动停。
4.3 8 个必调参数:按病害识别场景推荐的取值区间
| 参数 | 推荐值 | 说明 |
|---|---|---|
| imgsz | 1280 或 960 | 病斑是小目标,640 会损失早期病斑特征,1280 优先 |
| batch | 显存允许则 16 起 | 小 batch 的 BN 统计不稳定,病害类别差异小更明显 |
| lr0 | 0.003-0.01 | 迁移学习不要用默认 0.01 以上,容易破坏预训练特征 |
| lrf | 0.01 | 决定最后学习率衰减到多少,0.01 适合微调收尾 |
| close_mosaic | 10-15 | 在最后几个 epoch 关 mosaic,避免小目标定位被带偏 |
| patience | 15-30 | 数据量大时可放宽到 30,减少无效等待 |
| label_smoothing | 0.05 | 病害类别间形态相似,平滑能减少模型过度自信 |
| weight_decay | 0.0005 | 默认即可,主要防过拟合 |
label_smoothing=0.05在病害识别里很值得开。大斑病和小斑病早期形态接近,模型容易对相似类给出过高置信度,平滑后模型会更谨慎,决策边界更稳健。weight_decay不用特殊调,默认值在中等规模数据集上表现稳定。
选预训练权重时,n/s/m 三档的取舍要看部署端。yolov11n 推理最快,适合 Jetson 这类边缘设备,但病斑细小、类别接近时精度略低;yolov11s 是平衡点,多数项目够用;yolov11m 精度更高,显存占用和推理延迟都上去,适合服务端批量处理。我一般先用 s 跑通全流程,确认数据没问题后再换 m 提精度,不会一上来就追求最大模型。
4.4 训练日志怎么读:不要只看 mAP,要看损失曲线的分离过程
训练结束后,ultralytics 会生成results.csv,里面有每一轮的全部指标。很多人只看最后的 mAP,但调试时更重要的是损失曲线。分类损失cls_loss和定位损失box_loss都应当平滑下降,如果某条曲线震荡剧烈,可能是学习率过高或数据里有脏样本。
最值得看的是验证集 mAP 的变化趋势。正常情况是前 30 个 epoch 快速上升,中段缓慢爬坡,后段趋于平稳。如果验证 mAP 在小范围反复横跳,多半是增强过强或类别不平衡。我习惯用命令直接找出验证集上表现最好的轮次:
python -c "import pandas as pd; d=pd.read_csv('runs/detect/train/results.csv'); print(d.sort_values('metrics/mAP50-95(B)', ascending=False).head(3))"这段代码把验证 mAP50-95 最高的三轮打出来,对应epoch列的值就是最佳权重所在轮次。默认情况下 ultralytics 会保存best.pt,但偶尔早停触发时会和手动记录不一致,这个命令能快速核对。注意 early stopping 保存的模型不一定是全局最优,它的判断基于累积表现,所以训练完成后必须对best.pt跑一次完整验证。
4.5 多作物分层评估:找出到底哪个作物在拖后腿
混合模型训练完后,整体 mAP 好看不代表每个作物都好。我会对验证集按作物分组做评估,把问题定位到具体作物上。做法是在验证集目录上按子目录组织,再逐类推理统计:
from ultralytics import YOLO import os model = YOLO("best.pt") crop_groups = {"corn": [], "wheat": [], "tomato": []} for name in os.listdir("val_images"): for crop in crop_groups: if name.startswith(crop): crop_groups[crop].append(os.path.join("val_images", name)) for crop, paths in crop_groups.items(): results = model.predict(paths, conf=0.25) total, hits = 0, 0 for r in results: if len(r.boxes) > 0: total += 1 # 这里按实际类别名判断命中,样例按玉米大斑病为例 if any(model.names[int(b.cls[0])] in ["corn_nlb", "corn_healthy"] for b in r.boxes): hits += 1 print(f"{crop}: {hits}/{total} = {hits / max(total, 1):.2f}")这段脚本按文件名前缀把验证图片分成作物组,逐组统计检测命中率。实际项目里,如果玉米组的病斑已经被识别,但小麦组一片混乱,就能明确判断是小样本问题还是类别混淆问题,再针对性扩充数据或微调。脚本中的类别判断逻辑要根据实际标注调整,核心思路是把“整体指标好看”拆成“每个作物都好看”。
5. 训练与田间实测避坑:5 个最容易翻车的问题和排查顺序
5.1 损失曲线直接飞到 NaN,整个训练中断
现象:训练跑了十几个 epoch,loss 突然变成 NaN,日志里开始出现nan标红,后续所有指标都失效。
原因:最常见的是数据集里混入损坏图片或标注坐标越界,模型在这些样本上计算梯度时直接溢出。其次是学习率过大,尤其用lr0=0.01以上在迁移学习时,前几个 batch 就容易梯度爆炸。第三类是类别数配置错误,names数量和标注文件里的类别编号不一致。
解决:先跑上一章的数据检查脚本,清洗越界标注和损坏图片;把lr0降到 0.003 以下重新跑;检查 YAML 里names是否从 0 开始连续编号。顺序不能反,先确认数据干净再动学习率,否则数据里埋的雷会在以后随时爆。
5.2 验证集 mAP 很高,但田间强光、阴影下漏检严重
现象:实验室测试集上识别准确率到 95% 以上,拿到田间在正午强光下一测,漏检率掉到 70%,模型像换了个人。
原因:训练数据里绝大多数图片是顺光拍摄的均匀光照样本,正午强光下的过曝、叶片阴影里的低对比度、反射光斑这些场景模型没见过。增强里的 HSV 扰动虽然模拟了颜色变化,但模拟不了强光下的高动态范围和阴影遮挡。
解决:回田间补采数据,重点补正午、阴天、树荫三种光照条件,每种至少 200 张。增强配置里把亮度扰动的幅度加大,同时加入模拟阴影遮挡的增强。数据量不够时,先降imgsz保证收敛,数据补齐后再恢复高分辨率训练。
5.3 mAP 漂亮但误报满天飞:模型把土壤纹理当成了病斑
现象:在病害严重的田块测试效果很好,到正常田块里一跑,枯草、土块、其他杂质都被框成病害,严重时误报数量比真病斑还多。
原因:训练集构造时只关注了正样本,背景全都是农田里的病害叶片,模型从未见过“健康植株周围有土壤杂草”的完整田间画面。它学到的不是“病斑长什么样”,而是“不绿的东西都是病”。
解决:必须往训练集里加入负样本,纯健康植株、土壤、杂草、枯叶都标注成背景,让模型学会区分“正常田间环境”和“病斑”。训练时conf阈值也要重新校准,验证集 mAP 高的模型在实际部署时阈值常要提高到 0.3-0.4 才能压住误报,具体值用一组田间实测图来确定。
5.4 多作物混合训练后,某一个作物全面掉点
现象:整体 mAP50-95 在 0.82,但单独看某个作物只有 0.6,其他作物都在 0.85 以上,拖后腿的很固定。
原因:这个作物的样本量明显少于其他作物,或者它的病斑形态和另一作物差异太大,在共享特征里被“平均”掉了。比如玉米叶片宽大,稻瘟病斑细碎密集,让它们在同一个分类头里竞争,弱势方特征就被压制。
解决:先用 4.5 节的分类别评估定位掉点作物,确认不是标注问题后,优先扩充这个作物的数据。不想快速扩数据时,可以把这个作物的病叶用更宽的增强参数单独过一遍,生成更多扰动样本参与训练。最后手段是做分作物微调,在混合模型基础上用该作物数据小学习率微调 30 个 epoch。
5.5 显存溢出:batch 和 imgsz 共同导致的 OOM
现象:训练刚开始,GPU 显存就报警,直接 OOM 中断,tensor 申请大小超出显存容量。
原因:imgsz=1280配合batch=16对显存需求大约是 640 输入的 4 倍,经常超出 8G 显存的极限。很多人以为是 batch 太大,实际主因是高分辨率下的特征图占用。
解决:先把batch降到 8 试稳,仍然溢出就把imgsz降到 960。优先保imgsz不优先保batch,因为病斑小目标的收益主要来自分辨率而不是批大小。还有个技巧是开batch=-1,ultralytics 会自动探测显存余量并给出建议 batch,但最终训练时手动设置回整数,避免自动值波动。
6. 把推理结果保存成可用记录:坐标输出、置信度阈值与模型复验
田间部署时,模型跑通只是第一步,更关键的是把结果保存成下游能消费的数据。植保系统的变量喷雾需要知道病斑在哪个位置、置信度多高、属于什么病,推理脚本输出的 JSON 或文本格式比可视化截图实用得多。
from ultralytics import YOLO import json, time model = YOLO("best.pt") # source 可以是单张图片、文件夹或视频流 results = model.predict( source="field_plot.jpg", conf=0.25, iou=0.45, imgsz=1280, save=False, ) out = [] for r in results: for box in r.boxes: cls = int(box.cls[0]) conf = float(box.conf[0]) xyxy = [round(v, 2) for v in box.xyxy[0].tolist()] out.append({ "class": model.names[cls], "conf": conf, "bbox": xyxy, "timestamp": time.time(), }) with open("result.json", "w", encoding="utf-8") as f: json.dump(out, f, ensure_ascii=False, indent=2)conf=0.25是推理时的置信度门槛,和训练时的label_smoothing配合调整。iou=0.45是 NMS 的去重阈值,值越大保留的框越少,多作物场景下病斑密集堆叠时可以适当放宽到 0.5。imgsz要与训练时保持一致,推理时缩小输入尺寸会直接影响小病斑召回。脚本里把时间戳一并写入,是为了回查问题时有现场记录可对照。
模型真正要下地前,我会固定一个“复验数据集”:从田间现场采集 500 张新照片,手工标注后不在训练时使用。每次模型更新后,用同一批数据跑一轮推理,把召回率和误报率变化记录下来。如果新模型比旧模型漏检多,不要急着替换,先查是不是数据分布变了;我在这上面吃过亏,模型版本迭代几次后,田间表现最好的反而是上一版,后来养成习惯,每次部署前都在复验集上跑对比,超过旧版才允许替换。这个教训现在成了我的固定流程。希望帮到你。
本文还有配套的精品资源,点击获取