简介:这是一套基于Python构建的无人机病虫害智能识别与精准施药系统资源包,面向计算机、农业工程等专业的学生及开发者,适用于毕业设计、课程设计或实际项目二次开发。资源完整覆盖图像分类与识别、模型训练及验证流程,包含CrossViT等模型实现、数据集处理、训练与验证脚本,并配套项目PDF文档,可帮助理解从数据组织到模型部署的完整链路。压缩包共25个文件,以Python源码(.py)为主,辅以数据集处理与训练日志(.txt)、编译缓存(.pyc)及许可证说明,整体大小4.74MB;目录按模型、数据、工具脚本等模块划分,便于快速定位。源码已经过严格测试,可放心参考并在此基础上扩展功能,例如更换骨干网络、调整训练参数或接入实时识别模块。目前已有61人学习,适合希望快速搭建智能农业识别系统或借鉴完整项目框架的读者。
1. 无人机病虫害识别项目:从一张带病斑的叶片到精准施药方案
农田巡检的身影还在人工下地一张张翻叶子的时候,这套系统已经能通过无人机拍回的图像,直接判断作物得了什么病、严重程度如何,并给出对应的施药建议。基于Python实现的无人机病虫害智能识别与精准施药系统,本质上是一个把图像分类模型和植保决策规则串起来的完整工程:前端是无人机采集的图像,中间是CrossViT视觉Transformer在跑推理,后端把识别结果映射成药方。对做毕业设计或课程设计的同学来说,它最大的价值不是一个孤立的训练脚本,而是一套包含数据采样、模型训练、验证、推理、项目文档的完整链路,源码经过严格测试,照着跑能复现结果,在这个基础上扩展成自己的课题也顺手。
2. 技术栈与目录结构:从CrossViT到分布式训练的完整工具箱
2.1 技术选型:为什么是CrossViT而不是ResNet
这个项目解决的是农业场景的图像分类问题,但病虫害识别有几个和通用分类不太一样的地方:病斑往往只占叶片的一小块区域,类别之间差异细微,而且无人机在不同高度拍到的尺度变化很大。如果只用ResNet这类纯卷积网络,局部纹理能抓到,但全局上下文关系偏弱;如果只用标准ViT,又容易在小数据集上过拟合。CrossViT的思路是双分支:一个分支用较小的patch提取局部细节,另一个分支用较大的patch捕获全局结构,最后把两个分支的token融合起来。这样对“叶片上某一块病斑 + 整株作物的生长状态”这种组合特征特别合适。
从工程角度看,项目在models目录里同时放了crossvit.py和t2t子模块,说明实现上不是从timm里现成拉一个模型,而是自己搭了CrossViT的变体。t2t(Tokens-to-Token)模块的作用是把图像先切成重叠的token序列,再逐步扩大感受野,减少token数量。相比直接线性切patch,t2t能保留更多局部结构,对病斑边缘这种弱特征更友好。整套模型用PyTorch实现,训练和推理都走标准的nn.Module接口,二次开发时不管是换backbone还是接自己的分类头都很方便。
我一般在看这类项目时会先确认一件事:模型是用ImageNet预训练权重初始化,还是从零训练。这个项目源码里crossvit.py和t2t的写法保留了加载预训练权重的入口,实际跑的时候推荐加载timm提供的预训练模型,否则在小规模病害数据集上收敛会很慢。后面讲训练部分会细说。
2.2 目录结构逐层拆解
解压源码包后,根目录下文件不多,但分工很清晰。我按从入口到内部的顺序整理了一份结构:
. ├── main.py # 训练入口,负责解析参数、初始化模型、调用engine ├── main_vaild.py # 验证入口,通常读取训练好的checkpoint做精度评估 ├── valid.py # 单独验证脚本,可以指定单张图或整个验证集 ├── test_leaf.py # 单张叶片图像的快速推理脚本,返回类别和置信度 ├── classify.py # 分类推理脚本,一般用于批量图像预测 ├── engine.py # 训练/验证引擎,封装一个完整的epoch ├── datasets.py # 数据集定义,处理目录结构、标签映射、图像读取 ├── samplers.py # 采样器,常用类别均衡采样处理不平衡样本 ├── trans.py # 数据增强与预处理,训练/验证/推理各一套 ├── utils.py # 工具函数,包括指标计算、日志、模型保存等 ├── models/ │ ├── __init__.py # 模型注册入口,统一导出crossvit等结构 │ ├── crossvit.py # CrossViT模型定义,双分支Transformer │ └── t2t/ # Tokens-to-Token模块,用于patch序列化 ├── run_with_submitit.py # 基于submitit的分布式训练启动脚本 ├── requirements.txt # Python依赖清单 ├── log.txt # 训练日志示例,跑通后的输出参考 └── 项目文档.pdf # 系统设计说明、使用指南和展示PPT内容这里要注意一个细节:根目录下的main_vaild.py拼写是vaild而不是valid,这是原项目里的小习惯,不是错误,跑的时候别改文件名,不然引用会断。另外log.txt是项目自带的一次训练日志,安装好环境后可以打开对一下,看正常输出应该长什么样。项目文档PDF里包含了演示PPT的内容,初跑项目之前建议先翻一遍,里面往往有作者记录的实验环境版本和生成log.txt时用的具体命令,这是排查环境问题最快的线索。
models/t2t目录里放的是一组Token序列化相关的模块,包括tokenizer和transformer block。crossvit.py会引用t2t里的组件来构造两个分支,然后通过交叉注意力把两个分支的特征融合。理解这个结构对后面的二次开发很重要:如果你想替换成更轻量的模型,只要改models下的实现,并保证engine.py调用模型时的接口不变就行。注意根目录下还有LICENSE和__pycache__这些文件,__pycache__是本地运行后生成的缓存,可以忽略,不用提交到自己的仓库。
2.3 依赖清单:requirements.txt里有什么
requirements.txt是项目能跑起来的最低保障。虽然原始文件里没有逐个列出版本号,但按这类项目的常见做法,核心依赖基本是这几类:
- torch和torchvision:提供张量运算、数据加载接口和视觉预训练模型。
- timm:ImageNet预训练权重来源,也是很多Transformer模型的参考实现。
- opencv-python和Pillow:图像读写与预处理。
- numpy:数值计算。
- submitit:run_with_submitit.py需要它做分布式训练调度,本地单卡跑可以不装。
我没法保证requirements.txt里每个包版本都兼容,实际上在本地复现时最容易翻车的就是版本错位。建议按requirements.txt装完后,先跑一个最简单的数据加载测试,再启动训练。一个常见的做法是手动指定PyTorch的大版本,比如torch 2.x配合对应的torchvision,然后让timm使用较新的1.x版本。如果timm版本太旧,可能不支持CrossViT的某些层;如果太新,又可能因为一些接口改名导致模型定义报错。遇到这类问题,优先看官方变更日志,而不是强行改代码。
2.4 参数与配置的管理习惯
main.py这类训练脚本通常会接收大量命令行参数,这是一个健壮的工程习惯。常见的参数包括data_dir、model、epochs、batch_size、lr、num_classes、img_size、checkpoint_dir等。你在运行任何脚本时,先用--help扫一遍参数名是避免踩坑的最好方式:
python main.py --help这个命令会列出所有可接受的参数名,比直接翻代码快很多。项目里main_vaild.py和valid.py也支持类似参数,但参数名不一定完全一致,因为valid.py可能被设计成只接受checkpoint和image两个参数。理解这种差异对后面的避坑很重要。
2.5 配置漂移:train、valid、infer三套流程如何共用一份配置
main.py、main_vaild.py和test_leaf.py通常各自解析参数,这意味着同一个模型有可能在三处被配置成不同尺寸或不同类别数。常见做法是维护一个config对象,把data_dir、num_classes、model_name等字段集中管理,训练时把config序列化到checkpoint里,验证和推理时直接反序列化出来,避免手动重复传参数。
这个设计细节很容易被忽略,但实际踩坑概率极高。你会发现训练时报告准确率90%,用main_vaild.py一跑变成30%,原因往往不是模型坏了,而是验证脚本里img_size或num_classes和训练时对不上。项目源码里checkpoint文件的保存逻辑值得仔细看,如果它已经内置了config存储,那你在二次开发时也沿用这个习惯;如果没有,建议自己加上,能省很多排查时间。
3. 核心模块拆解:从叶片图像到施药决策的完整链路
3.1 数据流设计:datasets.py 与 samplers.py 如何组织样本
病虫害数据集最常规的组织方式是按类别分文件夹,每个文件夹名字就是病害标签,比如“leaf_rust_1”表示叶锈病等级1。datasets.py需要做的就是把根目录下的子文件夹扫描一遍,生成image路径到标签索引的映射,然后按比例切出训练集和验证集。
我比较关注的是它是否使用了类别的层级信息。很多病虫害数据集光靠文件夹名没法区分“病害种类”和“严重程度”两个维度,比如“rust_1”和“rust_2”其实是同一病害的不同等级。如果直接当成互不相关的类别来训练,模型容易把严重程度当成主要区分特征,反而忽略病害种类。这个项目在samplers.py里做了采样器设计,说明它对这种样本不均衡问题有考虑。
# samplers.py 中常见的一种做法:按标签频次反向加权 import torch import torch.utils.data as data class BalancedSampler(data.Sampler): def __init__(self, labels, num_samples=None): self.labels = torch.tensor(labels) self.num_samples = num_samples or len(self.labels) class_counts = torch.bincount(self.labels) self.weights = 1.0 / class_counts[self.labels].float() def __iter__(self): # 每次迭代按权重采样,权重越小的类别被抽中的概率越高 indices = torch.multinomial(self.weights, self.num_samples, replacement=True) return iter(indices.tolist()) def __len__(self): return self.num_samples这段代码的核心是torch.multinomial,它根据每个样本权重的概率分布做有放回采样。类别数量少的样本权重更大,被抽中的概率更高,从而缓解了类别不平衡。注意num_samples默认等于数据集长度,但实际训练时可以设置成期望的迭代步数乘以batch size,让每个epoch的样本数不完全等于原始数据集大小。
实际使用中建议配合数据增强一起用。因为过采样容易让模型对少数类反复看到同一张图,增加泛化风险。trans.py里如果已经写了随机翻转、裁剪、颜色抖动,那训练时把这个增强打开,少数类每张图都会有不同的增强版本,效果会稳很多。
3.2 模型构建:crossvit.py 与 t2t 的双分支结构
CrossViT的核心是两个分支。小patch分支(比如patch_size=8)负责局部细节,大patch分支(比如patch_size=16)负责全局结构,两个分支各自过几层Transformer后,通过交叉注意力交换信息。下面是一个简化的构造示意:
# models/crossvit.py 中模型构造的简化示意 import torch import torch.nn as nn from .t2t import T2TModule class CrossViT(nn.Module): def __init__(self, img_size=224, num_classes=10): super().__init__() self.branch_small = T2TModule(img_size=img_size, patch_size=8, embed_dim=384) self.branch_large = T2TModule(img_size=img_size, patch_size=16, embed_dim=384) self.fusion = nn.MultiheadAttention(embed_dim=384, num_heads=8) self.head = nn.Linear(384, num_classes) def forward(self, x): # 两个分支分别提取token序列 tokens_s = self.branch_small(x) # 保留局部细节 tokens_l = self.branch_large(x) # 捕获全局结构 # 将两个分支的token拼接后做交叉注意力融合 tokens = torch.cat([tokens_s, tokens_l], dim=1) context, _ = self.fusion(tokens, tokens, tokens) # 取全局池化结果做分类 out = self.head(context.mean(dim=1)) return out这里T2TModule是我按项目里t2t目录的名字写的占位,实际实现里它会把图像切成重叠的token,并通过两轮Transformer层逐步减少token数量。值得注意的融合方式是torch.cat之后做一次MultiheadAttention,这样两个分支的信息不再是被简单拼接后直接分类,而是先进行了一次全局交互。实际项目中可能还会在每个分支末端加一个CLS token,然后再融合,原理类似。
参数说明:img_size决定了t2t模块的输入尺寸,常见的是224;patch_size小分支用8、大分支用16时,两个分支的序列长度不同,但embed_dim保持一致才能做后续融合;num_classes要和你数据集的类别数一致。改造时如果想换成ResNet之类的主体,只需要保留这个类的外部接口,内部把两个分支换成两个不同感受野的CNN骨干,然后同样做特征融合即可。
3.3 训练引擎:engine.py 里的一个epoch发生了什么
engine.py承担的职责是标准的训练驱动:从dataloader取batch、前向、计算损失、反向更新梯度,以及在epoch结束或固定间隔时做验证。这个项目的engine.py还承担了保存最优模型的逻辑,一般会按验证集准确率或F1分数来决定是否覆盖当前最优checkpoint。
# engine.py 中训练一个epoch的核心流程 def train_one_epoch(model, dataloader, criterion, optimizer, device, log_interval=50): model.train() running_loss = 0.0 for step, (images, labels) in enumerate(dataloader): images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if step % log_interval == 0 and step > 0: avg_loss = running_loss / (step + 1) print(f"step {step}, loss {avg_loss:.4f}")这段流程看起来很简单,但实际项目里需要在训练过程中同步维护一个EMA(指数移动平均)版本的模型参数,或者定期做一次验证来记录最优acc。如果只打印loss,很难判断模型是否过拟合。我一般会在流程里加一个early stopping计数器,连续几个epoch验证指标不升就提前终止。
损失函数和优化器通常在main.py里设置。分类任务默认用nn.CrossEntropyLoss,优化器选择AdamW或Adam,学习率初期可以设1e-4到3e-4。如果模型用了预训练权重,通常会把backbone部分的学习率调低一点,比如让Transformer主干用1e-4,分类头用3e-4,这样微调更稳定。
3.4 推理链路:classify.py 与 test_leaf.py 如何落地施药决策
推理链路是这套系统区别于普通图像分类项目的地方。classify.py加载训练好的权重后,不只是输出一个类别编号,还会把类别编号映射回病害名称和施药建议。test_leaf.py更像一个单张图片的演示脚本,适合本地跑一张叶片看效果。
# test_leaf.py 简化示意:加载模型,输出病害类别和置信度 import torch from models.crossvit import CrossViT model = CrossViT(num_classes=CLASS_NUM) model.load_state_dict(torch.load("best_checkpoint.pth", map_location="cpu")) model.eval() from PIL import Image from trans import get_inference_transform img = Image.open("test_leaf.jpg").convert("RGB") transform = get_inference_transform() tensor = transform(img).unsqueeze(0) # 增加batch维度 with torch.no_grad(): logits = model(tensor) probs = torch.softmax(logits, dim=1) cls_id = probs.argmax(dim=1).item() conf = probs[0, cls_id].item() print(f"预测类别ID: {cls_id}, 置信度: {conf:.3f}")上面这段代码里的get_inference_transform来自trans.py,通常包含resize、归一化等与训练时一致的预处理步骤。注意推理时一定要关闭model.training状态,并且用torch.no_grad()包住前向,否则会占用不必要的显存。
精准施药决策的核心在类别ID到施药建议的映射表。比如类别0对应“稻瘟病-轻”,施药建议可能是“亩用三环唑可湿性粉剂20克,无人机飞行高度2米,喷幅3米”;类别1对应“稻瘟病-重”,建议中会提高药量和喷洒时长。这个映射表通常写在classify.py中,以字典或外部JSON文件维护。改造时最需要小心的是类别ID顺序必须与训练时一致,否则会出现识别正确但施药建议错配的尴尬情况。
4. 训练与运行指南:从零跑通一个病虫害识别模型
4.1 环境准备与数据组织
先准备Python环境。推荐用conda创建一个独立环境,避免把系统Python弄乱。命令行里按顺序执行:
conda create -n plantguard python=3.10 -y conda activate plantguard cd 项目根目录 pip install -r requirements.txt如果requirements.txt里没有包含全部依赖,或者版本冲突,手动补装以下常用包:
pip install torch torchvision timm opencv-python pillow numpy装完依赖后,先用一段小代码验证数据加载是否正常。看一下datasets.py里默认读取的数据目录结构,通常是这样:
data/ ├── train/ │ ├── rice_blast/ │ ├── rice_bacterial_blight/ │ └── ... └── val/ ├── rice_blast/ └── ...每个子文件夹名就是类别标签。如果你的数据是这种格式,直接把根目录路径传给训练脚本即可。如果原始数据是CSV标注格式,需要先写一个小脚本把图像按标签移动到对应文件夹,因为项目的datasets.py大概率是按目录结构读取的。
4.2 训练:main.py 的关键参数与启动方式
main.py是训练的总入口。它内部会经历:解析命令行参数、建立数据集和dataloader、初始化模型并加载预训练权重、构造优化器和学习率调度、然后调用engine.py里的训练循环。命令行启动方式类似:
python main.py --data-dir ./data --model crossvit --epochs 100 --batch-size 32 --lr 3e-4 --num-classes 10各参数含义:
--data-dir:数据根目录,必须是train和val子文件夹的上一级。--model:指定模型结构,当前项目可用的值一般是crossvit。--epochs:总训练轮数,小数据集100轮足够,但要看验证曲线。--batch-size:单卡batch大小。显存不足时从32降到16或8,同时可以相应调小学习率。--lr:初始学习率。微调场景下3e-4是一个比较稳的起点,如果loss震荡,降到1e-4试试。--num-classes:类别总数,必须和你的数据文件夹数量一致。
训练过程中控制台会每隔一定步数打印loss,每个epoch结束后打印验证准确率。项目里自带的log.txt可以作为对照,看你的输出格式是否和它对得上。
如果显存不够,一个很实用的替代方案是降低输入分辨率。CrossViT在224分辨率下的计算量较大,如果改成192或160,显存占用会下降不少,准确率一般只掉1到2个点。修改时找到main.py里设置图像尺寸的地方,把--img-size改成192即可。另外,当batch-size减小后,建议同步将学习率按比例缩小,比如batch从32降到16,lr从3e-4降到2e-4,否则梯度噪声变大,模型容易不稳定。
4.3 验证与推理:main_vaild.py、valid.py、test_leaf.py 怎么用
训练完成后,先用main_vaild.py在验证集上做一次完整评估:
python main_vaild.py --data-dir ./data --model crossvit --checkpoint ./checkpoints/best.pth这个脚本会遍历整个验证集,计算平均准确率、每类准确率,可能还会输出混淆矩阵。main_vaild.py和valid.py的区别,我理解是前者是主流程的一部分,会读取main.py训练时保存的配置;后者更适合单独快速验证一个指定权重,比如你刚换了一个数据增强方案,想对比效果。
如果只想看单张图,用test_leaf.py:
python test_leaf.py --image ./test_images/leaf_01.jpg --checkpoint ./checkpoints/best.pth输出会打印预测的类别ID、类别名和置信度。这个脚本适合在部署现场快速验证模型文件是否损坏、预处理是否正确。注意test_leaf.py内部可能没有做类别名映射,打印的可能是整数标签,需要对照数据集里文件夹的顺序去理解。如果打印出来的置信度普遍很低,比如所有类别都在0.5左右,说明模型没有正常收敛,先回训练阶段检查loss曲线。
4.4 分布式训练:run_with_submitit.py 何时使用
项目里带了run_with_submitit.py,这是一个基于submitit库的分布式启动脚本。如果你的训练数据量很大,比如几十万张无人机图像,单卡要跑几十个小时,可以考虑用它在多卡服务器上跑。它的作用是把训练命令提交到多卡环境,自动分配节点和GPU。
python run_with_submitit.py --data-dir ./data --epochs 100 --num-gpus 4如果是本地单机单卡,不要用这个脚本,直接跑main.py即可。分布式训练涉及环境变量、单卡batch size与全局batch size的换算,调试成本高。对大多数毕业设计来说,单卡训练够了。如果你想试试多卡,务必记住数据加载部分的num_workers不要设置过高,否则会在数据读取阶段出现CPU瓶颈,导致GPU利用率上不去。
4.5 训练日志与指标解读
训练结束后,不要只看最后一个epoch的accuracy。建议从中期开始记录验证集的每类准确率和平均准确率。这个项目自带log.txt就是很好的模板。如果你发现训练loss持续下降,但验证准确率反而下降,这是典型的过拟合信号。解决办法是增大数据增强强度、增加dropout比例,或者把epoch数减小。
另一个常见现象是loss下降很慢,然后突然跳到正常范围。这通常发生在交叉熵损失中,模型初期把所有样本都预测成均匀分布。不用慌,多跑几个epoch就会收敛。但如果过了10个epoch还没有变化,就要检查学习率和数据预处理了。
5. 避坑指南:我在这类项目里踩过的五个坑
5.1 坑一:复现时遇到“模块找不到”,根目录要设成PYTHONPATH
现象:运行python main.py时,报错ModuleNotFoundError: No module named 'models.crossvit'。
原因:项目里大量使用了from models.crossvit import ...这种绝对导入,但Python默认不会把当前命令行所在目录加入模块搜索路径,尤其是当你从别的路径调用脚本时,很自然就找不到。
解决:进入项目根目录再运行,或者在运行前执行export PYTHONPATH=.。如果是从PyCharm这类IDE里跑,要先把根目录标记为Sources Root。验证方法很简单,进入根目录后执行python -c "import models.crossvit",不报错就说明路径没问题。我一般会在自己的项目里直接写清楚这两步,但这份源码包里没有统一说明,所以第一次跑通常在这里卡住。
5.2 坑二:训练loss不降,大概率是数据预处理不一致
现象:loss一直徘徊在初始值附近,比如2.3左右,准确率也不升。
原因:trans.py里的归一化使用了ImageNet的mean/std,比如mean=0.485。如果你的图像不是标准的RGB三通道,或者尺寸没有正确resize到模型输入尺寸,模型看到的实际上是乱码,自然学不动。
解决:检查trans.py中训练和推理的transform是否一致。特别是推理时如果用了不同的resize方式,训练时积累的分布就会被破坏。另外用PIL读取后要convert("RGB"),灰度图或RGBA图都要先转成RGB。我在调试时会单独写一段代码,把transform后张量输出成图片,用肉眼看一眼有没有被裁成奇怪形状,这比盯数值高效得多。
5.3 坑三:类别不平衡导致模型变成“健康叶识别器”
现象:验证集整体准确率90%+,但查看每一类的准确率,病害严重的类别只有30%。
原因:病害类别天然是少数类,如果采样器没有生效,模型为了降低整体损失,会倾向于把所有样本都预测成占多数的健康类。
解决:在main.py里确认是否真的启用了samplers.py中的BalancedSampler,且传给DataLoader时设置了sampler而不是shuffle=True。注意两者不能同时用,否则DataLoader会报错。如果效果还不够,可以在损失函数上给少数类加权重,比如CrossEntropyLoss(weight=class_weights)。验证是否生效的方法是看每个epoch的loss是否比不使用时更高,如果更高,说明少数类的贡献确实被放大了。
5.4 坑四:跑分布式脚本卡死,submitit需要特殊的启动环境
现象:run_with_submitit.py在本地跑了半天,进程一直在等,没有启动训练。
原因:submitit设计的场景是slurm这类集群调度器,在本地没有slurm时它会尝试启动本地队列,但经常因为端口、权限等问题卡住。
解决:本地复现一律不要用这个脚本。如果你确实要测试分布式逻辑,先装standalone模式,并设置local=True之类的参数。否则检查python -c "import submitit; print(submitit.__version__)"确保安装正确。老实说,对课程设计而言,分布式训练是加分项但不是必需项,单卡跑通已经足够展示工程能力。
5.5 坑五:模型checkpoint文件损坏,加载时shape不匹配
现象:加载.pth文件时,报错RuntimeError: Error(s) in loading state_dict。
原因:这个项目里保存的state_dict里包含完整的模型参数名,如果你中途改过num_classes,或者换了一个不同结构模型,之前的权重自然对不上。
解决:加载前先打印torch.load(checkpoint).keys(),和当前模型参数逐一比对。我一般会在保存时额外存一个args.json记录训练配置,加载时直接从中读取num_classes和img_size,避免手滑改错。如果只是分类头不匹配,可以尝试加载除head以外的权重,然后随机初始化新的head。这个办法在迁移学习场景里非常常用。
6. 二次开发:把识别模型接进自己的施药控制流程
这套系统最容易改造的地方在classify.py到施药决策的映射部分。如果你想把它真正对接无人机飞控,我建议先不要动训练代码,而是把推理链路抽成一个独立模块,输出结构化结果。
具体做法是把classify.py里的预测函数包装成predict(image_path),返回一个字典,包含class_id、class_name、confidence和suggested_actions。这样不管后续是接入HTTP服务还是写入CSV,都不用再改模型代码。比如可以写成:
def predict(image_path): # 省略模型加载和预处理 result = { "class_id": cls_id, "class_name": label_map[cls_id], "confidence": conf, "dosage": dosage_map[cls_id]["ml_per_mu"], "height": dosage_map[cls_id]["flight_height_m"], } return result后面接Web服务时,只需要在这个函数外面套一层FastAPI或Flask路由。class_id是定位到的病害类型,confidence可以作为施药执行的可信度门槛,比如置信度低于0.7就触发人工复核。这个思路比把整个训练脚本部署上线要干净得多,因为训练脚本里大量参数对推理环境是无意义的。
如果你想迁移到新的作物数据集,核心步骤是:按照第4.1节的数据目录格式整理新数据,然后修改main.py里的--num-classes,并考虑是否复用这个项目的预训练权重。由于病害图像的视觉特征有一定的共通性,比如病斑边缘纹理、褪绿区域形状,用原来作物上训练出的权重初始化新模型的浅层,收敛速度会比从零开始快很多。具体做法是在main.py里找到预训练权重的加载逻辑,把它改成pretrained=False或手动移除最后一层分类头的参数。
我踩过的教训是:改造前一定要先看log.txt里的训练日志格式,确认项目跑通时的指标水平。否则你改了一轮之后,连“变好还是变差”都判断不清。从那以后我每次拿到一个视觉分类项目,都强制先跑通原始训练脚本、保存一份baseline结果,再做任何结构上的改动。希望帮到你。
本文还有配套的精品资源,点击获取