去年秋收前,我跟着一个做智慧农业的团队在几个示范农场蹲了两个月,任务是给一套作物监测与产量预测系统做模型替换——把原来那套靠人工设计特征加传统机器学习的老流程,换成以视觉大模型和多模态时序模型为核心的新链路。说白了,就是让机器不仅"看见"地里长什么样,还能"算明白"这季大概能收多少。这件事的核心,就是大模型在作物监测与产量预测中的应用:用预训练视觉模型做长势、病虫害、倒伏的识别,用多模态融合模型把遥感影像、气象、土壤墒情、农事记录串成一条时间线,最后回归出地块级的产量估计。
它解决的问题很实际。传统方案靠几台气象站加几张抽样图,覆盖面窄、滞后严重,等到发现叶片发黄,往往已经错过最佳干预窗口;产量预测更是靠老农经验和几年平均值,误差动辄两成。而大模型的价值在于把非结构化的图像数据和结构化的传感器数据放进同一个表征空间,让"看图"和"看数"互相补位。
这篇内容适合三类人看:一是刚接手农业AI项目、不知道从哪下手的算法工程师;二是手里有地块数据、想搭一套监测系统的农业技术负责人;三是对多模态落地感兴趣的开发者,可以把它当成一个真实场景的工程复盘。我尽量把参数、计算、踩过的坑都写清楚,能直接抄的部分我会标出来。
1. 为什么要把大模型塞进农田里:整体设计与选型拆解
1.1 传统作物监测与产量预测到底卡在哪
先说清楚老方案的瓶颈,不然容易陷入"为了用大模型而用大模型"的自我感动。我们接手的那套系统大概是这样的:无人机带一台普通RGB相机,一周飞一次,人工挑几十张图看长势;土壤墒情靠三个埋在地里的传感器;产量预测用一个线性回归,输入是积温、降水量和去年产量。听起来也能跑,但问题一层套一层。
第一层是覆盖稀疏。一个几百亩的地块,三个传感器代表不了什么,土壤的空间异质性远比想象中大,坡上和坡下的墒情能差出一倍。第二层是信息维度单一,RGB图像看不出近红外波段的差异,而作物冠层的氮素状况、水分胁迫恰恰藏在这些波段里。第三层是滞后性,人工看图是事后判断,等肉眼能分辨出病斑,病原已经扩散了。
第四层也是最要命的:特征靠人拍脑袋。老系统里那些特征——平均绿度、纹理熵、颜色直方图——都是人根据经验挑的,换个作物、换个品种、换个生长季,这套特征就废了,得重新调。这本质上是个"手工特征工程"的路子,天花板很低。大模型进来,最直接的收益就是用预训练表征替掉这些手工特征,把"人工找规律"变成"模型自己学规律"。
还有一个隐性成本:农事记录的非结构化。农户记的施肥、打药、灌溉日志全是自由文本,"上周打了点叶面肥""雨前追了一次"这种,传统模型根本没法用。大模型的文本理解能力在这里刚好能派上用场,把非结构化记录抽成结构化事件,再喂进时序模型。这一点在项目初期被严重低估,后来发现它对产量预测的贡献不比图像小。
1.2 大模型在这套体系里到底扮演什么角色
很多人一听大模型就想到聊天机器人,其实在农业场景里它至少承担四个角色,得分开看,别混成一锅。
角色一:视觉骨干网络。用在大规模自然图像上预训练过的视觉模型(ViT、ConvNeXt 这类)作为特征提取器,替换掉原来从头训练的小CNN。农业图像标注数据少,从头训必然过拟合,而预训练模型自带的纹理、形状、颜色先验能显著降低对标注量的依赖。我们实测下来,同样的标注量,换成预训练骨干后病虫害分类的准确率能从 76% 提到 88% 左右。
角色二:开放词汇检测器。传统检测模型只能识别训练集里定义好的类别,新增一种病害就得重新标注重新训。而基于图文对齐的开放词汇模型,可以用"叶片上的黄色锈斑"这样的文本描述去检索目标区域,新增类别只需要改提示词。这对农业太友好了,因为病虫害种类实在太多,一个模型不可能全覆盖。
角色三:多模态融合器。把图像特征、时序气象特征、土壤特征映射到统一空间做融合。这一步是整个链路的技术核心,后面第 3 节会详细展开。
角色四:农事文本结构化。用小参数量的大语言模型做信息抽取,把自由文本的农事记录转成"日期-操作类型-投入品-用量"的四元组。这块用 7B 以下的模型微调就够,不需要很大的算力。
注意:不要指望一个模型把所有事都干了。我见过有人试图用一个通用大模型直接吃图像加文本输出产量,结果误差大得没法看。分角色、分模块,每个模块职责单一,才是能维护的工程方案。
1.3 三种技术路线的取舍与我的选型理由
落地时其实有三条路可以走,我列个表对比一下,这也是当时项目组争论最久的地方。
| 路线 | 做法 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| 纯视觉端到端 | 直接用大视觉模型吃整张图回归产量 | 链路短,工程简单 | 完全忽略气象与土壤,跨年泛化极差 | 单季短周期作物,快速验证 |
| 传统特征+树模型 | 手工指数(NDVI等)+ XGBoost | 可解释强,算力低 | 依赖人工特征,天花板低 | 数据量小、预算有限的起步阶段 |
| 多模态融合(选用) | 视觉骨干+时序模型+跨模态注意力 | 泛化好,可解释性可补救 | 工程复杂,数据要求高 | 有连续多年数据积累的地块 |
我最终选第三条,理由很实在:产量这个东西本质上是"基因型 × 环境 × 管理"的乘积,环境里有光照、温度、水分,管理里有施肥、灌溉,这些信息全都不在图像里。只用图像预测产量,等于让人闭着眼睛猜体重。而纯手工特征路线我们试过一版,跨年验证的 MAPE 稳定在 20% 以上,没法交付。
多模态融合的代价是要处理时间对齐问题——图像是离散时相,气象是逐小时,土壤是逐小时但可能缺测,农事记录是事件式的。这些东西对不同齐,模型学到的就是噪声。第 2 节我会专门讲这块怎么处理。
另外提醒一句:别一上来就追求大参数。我们最后用的视觉骨干是 86M 参数的规模,产量回归头只有不到 2M 参数。在农业这种样本量有限的领域,模型越大越容易记住"哪块地去年产量高"这种伪相关。参数量翻倍带来的提升,往往不如多标注两百张图。
2. 数据底座:作物监测的输入从哪来、怎么处理
2.1 多源数据采集的硬件组合与关键参数
数据源这块,我们最后定的是"无人机多光谱 + 固定式物联网 + 气象站 + 人工农事记录"四条腿走路。每一条的参数选择都有讲究,我挨个说。
无人机多光谱是主力。选了五通道的机型,波段是蓝(450-520nm)、绿(520-590nm)、红(630-690nm)、红边(700-730nm)、近红外(770-810nm)。为什么必须要红边?因为红边波段对叶绿素含量最敏感,NDVI 在冠层茂密时会饱和(值全挤在 0.8 以上分不出差异),而红边指数 NDRE 在高生物量阶段还能保持区分度。这是很多人踩过的坑:只用 RGB 加近红外算 NDVI,到了生长中后期所有地块的数值都差不多,等于白测。
地面分辨率怎么定?公式是:
GSD = 像元尺寸 × 飞行高度 ÷ 镜头焦距
假设相机像元尺寸 3.45μm,镜头焦距 8mm,飞到 120m 高度,则 GSD = 3.45e-6 × 120 ÷ 8e-3 ≈ 0.052m,也就是 5.2 厘米。这个精度足够分辨单株作物和叶部病斑。如果飞到 200m,GSD 就变成 8.6cm,病斑级别的识别就开始吃力了。所以高度不是随便定的,得先想清楚你要识别的最小目标有多大,一般要求 GSD 小于目标尺寸的三分之一。
重访周期方面,我们按 5-7 天一次,关键生育期(拔节、抽穗、灌浆)加密到 3 天。一个生长季大概飞 25-30 次。这个频率是多光谱相机的续航和电池数量决定的,不是想飞就能飞。
固定式物联网补连续性。每个地块布 5-8 个土壤墒情节点,测 10cm、20cm、40cm 三个深度的体积含水率和温度,采样间隔 30 分钟。通信走 LoRa,因为地块里没有蜂窝信号覆盖,而且 LoRa 的功耗低,一节电池能撑一整个生长季。这里有个细节:节点埋设位置要按土壤类型分区布点,不能均匀撒网——均匀布点在有坡度的地块上会系统性地低估或高估。
气象站一个片区一台。测辐射、气温、湿度、风速、降雨。辐射数据很关键,后面算参考蒸散量要用。气象站的位置要避开灌溉设备和建筑遮挡,否则风速和辐射数据全是错的。
| 数据源 | 采集频率 | 关键参数 | 常见问题 |
|---|---|---|---|
| 无人机多光谱 | 5-7天/次 | GSD 5-6cm,5通道 | 云影导致反射率失真 |
| 土壤墒情节点 | 30分钟 | 10/20/40cm 含水率 | 探头与土壤接触不良 |
| 气象站 | 逐小时 | 辐射、温湿、风速、雨量 | 传感器积尘漂移 |
| 农事记录 | 事件式 | 操作类型、用量、日期 | 文本自由、时间含糊 |
2.2 数据清洗与时空对齐的实操要点
这一步是整个项目里最枯燥、但最决定成败的环节。我可以说,模型效果的上限,在数据清洗阶段就基本定死了。
第一件事是辐射定标。无人机拍的原始影像是数字量化值(DN),不是真实的反射率。必须用飞行前拍的灰板(反射率已知的标定板)做线性变换,把 DN 转成反射率。不做这一步,今天阴天飞的图和上周晴天飞的图根本没法比。我们第一次做的时候忘了标定板,结果时序曲线的波动七成来自光照变化,跟作物一点关系没有。
第二件事是云影检测与掩膜。田块上空飘一片云,下面的像元反射率会掉一大截。简单的阈值法不够用,因为云影和正常的阴影(比如树影)容易混。我们最后用的是亮度加局部方差的双阈值,再配合相邻时相的插值补洞。被掩掉的像元占比超过 30% 的那次飞行,直接废弃重飞。
第三件事是几何配准。每次飞行的航线和姿态都不同,正射影像的坐标系会有几十厘米的偏移。必须把所有时相的影像配准到同一个基准网格上,否则你在时相 A 提取的像元和时间相 B 提取的像元根本不是同一株作物。我们用的是以地块边界为约束的特征点配准,然后把所有影像重采样到统一的 5cm 网格。
第四件事是时空对齐。这是多模态融合的前提。我的做法是:以无人机飞行日为主时间轴(因为它是离散的、最稀疏的),把其他所有数据聚合到这个时间点上。土壤墒情取飞行日前后 6 小时的均值,气象数据取飞行日前 7 天的累计值和均值,农事记录取距离最近的一次操作并标注间隔天数。这样每个时相样本就是"一张多波段影像 + 一组聚合特征 + 一组农事事件编码"。
提示:时间窗口的选择会影响模型的可解释性。比如"前 7 天累计降雨"这种特征,物理意义清晰,农技人员能看懂;而"前 7 天降雨的傅里叶变换系数"就没法解释。在农业场景,可解释性直接影响用户敢不敢用你的预测结果。
2.3 小样本问题:标注策略与半监督的折中方案
农业数据最大的痛点是标注贵。一张影像人工标注病斑区域要 3-5 分钟,一个生长季几千张图,标完人就废了。我们用了三个办法缓解。
一是分层抽样标注。不平均用力,先按长势指数把地块分成高、中、低三层,每层按比例抽样,保证训练集覆盖整个分布。同时把历史病害高发区和边缘行(边行效应明显)单独加采样。这样用 30% 的标注量就能拿到接近全标注的效果。
二是用大模型做预标注。先用开放词汇检测模型跑一遍,输出候选框和伪标签,人工只做修正。实测标注效率提升 2.5 倍左右,但要注意伪标签的置信度阈值不能太低,宁可漏检也不能引入大量错标,否则模型会把错误固化。
三是自监督预训练。手头有大量无标注的多光谱影像,用掩码重建的方式先做一轮自监督预训练,让骨干网络先学会农业图像的纹理和光谱分布,再用少量标注微调。这个方法在小样本下提升很明显,我们在只有 800 张标注图时,自监督预训练让分类准确率多了 6 个百分点。
具体到自监督的实现,核心是掩掉影像的随机块,让模型重建:
import torch import torch.nn as nn class MaskedSpectralReconstruction(nn.Module): """多光谱影像的掩码重建预训练头""" def __init__(self, in_ch=5, embed_dim=384, patch_size=16): super().__init__() self.patch_size = patch_size # 把每个 patch 的 5 通道像素拉平成向量 self.patch_embed = nn.Linear(in_ch * patch_size * patch_size, embed_dim) self.decoder = nn.Sequential( nn.Linear(embed_dim, embed_dim * 2), nn.GELU(), nn.Linear(embed_dim * 2, in_ch * patch_size * patch_size) ) def forward(self, patches, mask): # patches: [B, N, C*P*P] mask: [B, N] 布尔,True 表示被掩掉 x = self.patch_embed(patches) x = x * (~mask).unsqueeze(-1) # 只保留可见 patch 的信息 recon = self.decoder(x) loss = nn.functional.mse_loss(recon[mask], patches[mask]) return loss这套东西跑起来不复杂,但有个坑:掩码比例别设太高。自然图像上常用 75%,多光谱影像我们试下来 40%-50% 效果最好,因为波段之间高度相关,掩太多模型能靠其他波段"作弊"猜出来,学不到真正的空间结构。
3. 模型落地:从视觉监测到产量预测的实现路径
3.1 长势与病虫害识别:视觉链路怎么搭
监测这一支的产出物是三类:长势分级、病虫害区域、倒伏/缺苗面积。三者的技术路径不一样,我分开讲。
长势分级其实是个回归问题,不是分类。我们用冠层叶绿素含量和叶面积指数(LAI)作为监督信号,让模型从多光谱影像直接回归出这两个值。为什么用它们做中间量?因为它们是农学上公认的长势指标,农技人员听得懂,而且和最终产量有明确的机理联系。如果直接回归"长势等级",标签的主观性太强,不同人标出来的结果能差一级。
LAI 的反演有个经典做法是用辐射传输模型 PROSAIL 生成模拟数据集,再用真实数据做迁移。PROSAIL 可以按叶片参数和冠层结构参数正向生成光谱曲线,这样能造出大量带真值的样本。但模拟和真实之间总有差距,我们用了域适应:先在全模拟数据上预训练,再用少量实测 LAI 数据(用冠层分析仪测的)做微调。
病虫害识别分两步走:先用开放词汇检测模型定位可疑区域,再用一个细粒度分类器判断具体类别。为什么不用一步到位的检测?因为病害类别太多,细粒度差异又小(比如不同真菌造成的叶斑),单阶段模型很难同时兼顾定位和分类。
这里说个我们踩过的坑:病害样本的类别极度不平衡。健康样本占九成以上,几种主要病害各占几个百分点,罕见病害只有几十张图。直接训,模型会退化成"全部预测健康",准确率还能有 90% 多,但毫无用处。解决办法是焦点损失(Focal Loss)加权 + 对少数类做有针对性的数据增强(旋转、色彩抖动、局部遮挡模拟)。评估指标也不能用准确率,得看每一类的召回率和宏平均 F1。
注意:数据增强在多光谱影像上要小心。普通的色彩抖动能用在 RGB 上,但多光谱的波段之间有物理相关性,随手把近红外通道调亮会造出物理上不存在的反射率组合。要做就做波段一致性的变换,比如整体乘一个光照系数,模拟光照强度变化。
倒伏和缺苗属于形态学问题,用语义分割更合适。倒伏表现为冠层结构倾斜、纹理方向改变;缺苗表现为规则网格上的空洞。这两个任务的标注成本比病害低,因为目标大、边界清楚,用半自动的分割模型辅助标注就能搞定。
3.2 多模态融合:把图像、气象、土壤、农事记录串成一条时间线
这是整个项目最核心也最难的部分。我先说结论:我们最后用的是"双塔编码 + 跨模态注意力 + 时序聚合"的结构。
塔一:视觉编码器。每个飞行时相的多光谱影像切成 patch,过预训练骨干,得到一个 patch 序列的表征,再池化成该时相的一个向量。注意这里要保留一定的空间信息,不能一上来就全局平均池化,因为病害是局部现象,全局池化会把局部信号稀释掉。我们的做法是保留一个 8×8 的空间网格表征。
塔二:数值编码器。把聚合好的气象、土壤、农事特征做标准化后,过一个小的 Transformer 编码器。数值特征维度大概 40 维左右,包括:7 天累计降雨、7 天平均气温、积温、辐射累计、三个深度的土壤含水率、距上次灌溉天数、距上次施肥天数、氮磷钾累计投入量等。
融合方式。用跨模态注意力,让视觉的每个空间 token 去查询数值特征,得到加权的视觉表征。这个设计的物理直觉是:不同环境条件下,影像里不同区域的"重要性"不同。比如干旱年份,土壤含水率低的区域对应的影像特征权重会更高。这一步之后再做时序聚合。
时序聚合。用 4 层的时序 Transformer,输入是 25-30 个时相的融合表征,输出是整季的全局表征。这里的关键是位置编码要用真实的时间间隔,而不是简单的序号。因为飞行间隔不均匀(有的是 3 天,有的是 7 天,雨天还要跳),用序号编码会让模型误以为间隔相同。
整个融合模型的伪代码大致是这样:
class CropFusionModel(nn.Module): def __init__(self, vis_dim=384, num_dim=40, d_model=256, n_heads=8, n_temporal=4): super().__init__() self.vis_proj = nn.Linear(vis_dim, d_model) self.num_proj = nn.Linear(num_dim, d_model) self.cross_attn = nn.MultiheadAttention(d_model, n_heads, batch_first=True) # 用真实时间间隔构造正弦位置编码 self.temporal_encoder = nn.TransformerEncoder( nn.TransformerEncoderLayer(d_model, n_heads, batch_first=True), num_layers=n_temporal ) self.head = nn.Sequential( nn.Linear(d_model, 128), nn.GELU(), nn.Dropout(0.2), nn.Linear(128, 1) ) def forward(self, vis_seq, num_seq, time_days): # vis_seq: [B, T, N, vis_dim] num_seq: [B, T, num_dim] B, T, N, _ = vis_seq.shape v = self.vis_proj(vis_seq).reshape(B * T, N, -1) n = self.num_proj(num_seq).reshape(B * T, 1, -1) # 视觉 token 查询数值 token fused, _ = self.cross_attn(v, n, n) # [B*T, N, d_model] fused = fused.mean(dim=1).reshape(B, T, -1) # 时相表征 # 真实时间间隔的位置编码 pe = sinusoidal_by_days(time_days, fused.size(-1)) x = self.temporal_encoder(fused + pe) return self.head(x.mean(dim=1)).squeeze(-1) # 地块级产量有个细节值得单独说:跨模态注意力的输入顺序。我们一开始是让数值 token 去查询视觉 token,效果明显更差。原因不难理解——数值特征只有 40 维左右,信息量小,让它当 query 相当于用一根细管子去吸一大池水。反过来让视觉 token 当 query,相当于视觉特征主动去环境信息里找自己需要的上下文,灵活得多。
3.3 产量预测的建模思路与评估方式
产量预测这块,我想强调一个反直觉的点:别把它当成一个纯回归问题。因为产量受极端事件影响很大(一场冰雹全毁、一次倒伏减产三成),而回归模型对极端值很敏感,一两年的异常会把整个模型带到沟里。
我们最后用的是"分位数回归 + 期望校正"。模型同时输出 10%、50%、90% 三个分位数,用分位数损失训练。这样不仅给了点估计,还给了不确定性区间。实际交付时农技人员最看重的其实是这个区间——"这块地大概能收 600 公斤,但也可能只有 480",这比一个精确到小数点的数字有用得多。
分位数损失的实现是标准的:
def quantile_loss(pred, target, quantiles=(0.1, 0.5, 0.9)): losses = [] for i, q in enumerate(quantiles): e = target - pred[..., i] losses.append(torch.maximum(q * e, (q - 1) * e).mean()) return torch.stack(losses).sum()评估方法上,最容易犯的错是随机划分数据集。同一块地不同年份的样本高度相关,随机划分会导致同一块地的一部分样本进训练集、一部分进测试集,模型只要记住"这块地历史上产量高"就能刷出一个漂亮但虚假的分数。我们第一次这么干,测试集 MAPE 只有 9%,高兴了不到一天,换成按年划分就跳到了 23%。
正确的做法是留一年交叉验证(leave-one-season-out):拿三个生长季的数据训练,用第四个生长季测试,轮换。这样能真实反映模型在新年份的泛化能力。另外还建议加一个按地块的划分作为补充,防止空间泄漏。
指标上,MAPE 要报,但别只看它。产量低的地块 MAPE 天然偏高(分母小),会拉高整体数值。建议同时报 RMSE(绝对误差)和地块产量的加权绝对误差。我们的最终交付标准是:留一年验证的 MAPE ≤ 12%,RMSE ≤ 每公顷 0.8 吨。
3.4 几个必须算对的派生指标
模型特征里有一批是农学上的派生指标,算错了整个模型都在学噪声。这部分看似基础,但我见过太多项目在这里翻车。
积温(GDD)是最常用的物候驱动变量:
GDD = Σ max((T_max + T_min) / 2 - T_base, 0)
T_base 是作物的生物学零度,不同作物不一样,喜温作物常用 10℃,有些用 8℃ 或 15℃。这个参数必须按作物查清楚,用错了整条物候曲线全歪。累积通常从播种日开始算。注意上限温度的处理:超过作物上限温度(比如 30℃)的热胁迫,有些模型会把超出部分扣掉或者单独建一个"热害积温"的变量,这个看具体作物,不了解就别乱加。
参考蒸散量(ET0)用来量化水分需求,用 Penman-Monteith 公式算,需要辐射、气温、湿度、风速四个量:
ET0 = [0.408Δ(Rn - G) + γ·900/(T+273)·u2·(es - ea)] / [Δ + γ(1 + 0.34u2)]
这些参数里,净辐射 Rn 是最容易出错的,因为它需要太阳辐射数据。如果气象站没有辐射传感器,用日照时数折算会引入不小误差(我们测过,误差能到 15%)。所以前面说气象站必须测辐射,不是可选项。
水分胁迫系数 Ks我一般用简化版:
Ks = (TAW - Dr) / (TAW - RAW)
TAW 是总可用水量,RAW 是易利用水量,Dr 是根区消耗量。这个值在 0 到 1 之间,越小表示胁迫越严重。它能和土壤墒情数据交叉验证——如果算出来的 Ks 是 0.9 但实测含水率很低,那多半是某个参数搞错了。
植被指数我建议至少算四个:NDVI 看整体绿度,NDRE 看叶绿素(高生物量阶段用),NDWI 看冠层水分,SAVI 在裸土比例高时比 NDVI 稳(因为它加了土壤调节因子 L)。
def spectral_indices(cube): """cube: [H, W, 5] 通道顺序 蓝 绿 红 红边 近红外""" b, g, r, re, nir = (cube[..., i].astype("float32") for i in range(5)) eps = 1e-6 ndvi = (nir - r) / (nir + r + eps) ndre = (nir - re) / (nir + re + eps) ndwi = (g - nir) / (g + nir + eps) savi = 1.5 * (nir - r) / (nir + r + 0.5 + eps) return dict(ndvi=ndvi, ndre=ndre, ndwi=ndwi, savi=savi)这些指数在喂进模型前,我建议不要只取均值。地块内的指数分布形态本身就有信息——均值相同的两块地,一块均匀一块斑驳,长势完全不同。所以我们每个指数都取均值、标准差、5% 和 95% 分位数、以及变异系数,五个统计量。这一手让产量预测的误差降了大概 2 个百分点,性价比极高。
4. 部署与工程化:从实验室到田间地头
4.1 边缘端推理的模型压缩与硬件选型
实验环境里跑得通,不代表田里能用。农户不会为一套监测系统配一台服务器,实际部署点是合作社的简易机房或者无人机的机载计算单元。这就必须做压缩。
我们做了三步。第一步是结构化剪枝,把视觉骨干的通道数按重要性裁掉 30%,精度损失不到 1 个百分点。第二步是知识蒸馏,用一个更大的、训练充分的模型当教师,指导压缩后的学生模型。这一步在分类任务上补回了大部分精度,因为软标签携带的类间相似性信息比硬标签丰富得多。第三步是 INT8 量化,用校准集统计激活值范围。量化后模型体积从 92MB 降到 24MB,在边缘盒子上的单帧推理从 310ms 降到 95ms。
| 压缩阶段 | 模型体积 | 单帧延迟 | 分类准确率 |
|---|---|---|---|
| 原始模型 | 92MB | 310ms | 88.4% |
| 剪枝后 | 67MB | 220ms | 87.6% |
| 加蒸馏 | 67MB | 220ms | 88.1% |
| INT8量化 | 24MB | 95ms | 87.8% |
硬件选型上,我们的经验是别追求顶级算力。田间的核心制约不是算力而是功耗和散热。一个 25W 的边缘盒子能靠自然散热撑住,而 60W 的必须有风扇,风扇进灰就是故障源。我们选的是 15-25W 档的推理盒子,配 4GB 内存,跑量化后的模型正好。整机成本控制在两千元以内,一个合作社配两台,覆盖周边几个地块,这个账农户算得过来。
提示:边缘设备的固件升级要留远程通道,但别做自动升级。农业设备的使用窗口很集中,如果在关键生育期推了个有问题的固件,损失没法挽回。我们吃过这个亏,后来改成手动确认 + 灰度。
4.2 数据回流闭环与模型迭代节奏
系统上线不是终点。作物监测模型有个特点:分布会漂移。新品种、新栽培方式、新的病害流行株,都会让模型慢慢失准。所以必须建立数据回流机制。
我们的做法是:边缘端把每次推理的输入、输出、置信度都记日志,通过夜间低峰期回传到中心。对置信度低于阈值的样本、以及预测结果与农户人工记录差异大的样本,自动打上"待审核"标记,定期抽样送人工标注。这批数据进入下一轮训练,形成一个闭环。
迭代节奏上,我的建议是一个生长季一次大版本,中间只做小修。不要在生长季中间频繁更新模型,因为模型一变,前后预测结果的连续性就断了,农户会无所适从。而且中途变更模型,出了问题也分不清是新模型的锅还是天气的锅。我们后来定了个规矩:模型版本号绑定生长季,季中只允许修复明确的 bug。
另外提一个容易被忽略的点:模型输出的呈现方式。同样一个 0.72 的病害概率,用"高风险"三个字呈现,农户可能觉得是危言耸听;用"建议三日内到田块东侧复查"呈现,接受度就高得多。技术输出要转成行动建议,这一步不做,再准的模型也是摆设。
4.3 成本结构与投入产出的大致账
说实话,这套系统的投入不算低。一个中等规模示范点的初始投入大概包括:多光谱无人机一套、边缘推理盒子两台、土壤墒情节点二十个左右、气象站一台,加上开发调试的人力。如果是首次做,软件部分要占大头。
但账不能这么算。要看它替代了什么、优化了什么。我们做过对比:用了监测系统后,农药喷施次数平均减少了一次半(因为能精准定位发病区域,不用全田普喷),灌溉用水降了约 12%(按墒情数据触发而非按固定日程),这两项的节省在两三个生长季内就能覆盖硬件投入。产量预测的价值则体现在销售环节——提前知道大概产量,议价和仓储安排都会主动很多。
不过我得诚实说一句:小地块不划算。几十亩的规模,硬件摊销下不来,人工巡田可能更经济。这套东西的盈亏平衡点大概在几百亩以上,规模越大边际成本越低。
5. 常见问题与排查实录
5.1 典型故障速查表
这两年攒下来的问题不少,我把高频的整理成表,遇到时可以先照着排查。
| 现象 | 可能原因 | 排查方法 | 处理方式 |
|---|---|---|---|
| 时序曲线整体跳变 | 未做辐射定标或标定板失效 | 检查飞行前标定板影像的反射率 | 重新标定,废弃该次数据 |
| 所有地块指数趋同 | NDVI 饱和 | 看数值是否集中在 0.8 以上 | 改用 NDRE 或 SAVI |
| 模型只预测健康 | 类别极度不平衡 | 看各类召回率 | Focal Loss + 定向增强 |
| 跨年泛化骤降 | 数据集随机划分导致泄漏 | 检查划分方式 | 改留一年交叉验证 |
| 预测值普遍偏高 | 训练集年份偏丰产 | 看各年产量均值分布 | 按年做目标归一化 |
| 边缘端偶发超时 | 内存不足触发换页 | 监控推理时的内存峰值 | 降 batch 或再量化 |
| 传感器数据断档 | LoRa 信号被冠层遮挡 | 检查信号强度历史 | 抬高天线或加中继 |
| 病害定位框漂移 | 图像几何配准不准 | 叠加地块边界看偏移 | 重做配准基准 |
表格里我最想强调的是"跨年泛化骤降"这一条。这个错误太隐蔽了,因为随机划分下的指标非常漂亮,很容易让人产生虚假的信心。判断方法很简单:看一眼训练集和测试集是不是包含同一地块的不同年份,如果是,指标基本不可信。
5.2 几条只有踩过才知道的心得
第一条:先跑通一条最窄的链路,再谈融合。我们一开始就想做完整的多模态,结果数据对齐没搞定,两个月都在debug。后来退回来,先只用影像做长势回归,跑通了再逐步加气象、加土壤、加农事。每加一个模态都能看到明确的增量,这样既好定位问题,也方便向甲方证明每一分投入的价值。
第二条:一定要有农技人员参与标注和验证。算法团队看影像只能看出"这里颜色不一样",农技人员能看出"这是缺氮不是病害"。我们有一次模型把缺镁的症状识别成了某种真菌病害,两条链路给出的处置建议完全相反。后来固定了每周一次的联合复核,这类错误少了很多。领域知识不是靠模型自己悟出来的。
第三条:评估指标要和业务对齐。我们最初追的是分类准确率和回归 RMSE,后来发现农技人员真正关心的是"漏报率"——把有病的地块判成健康,后果比反过来严重得多。所以后来把召回率作为第一指标,准确率让步。这个取舍必须提前和业务方对齐,不能等交付了再吵。
第四条:留一手人工复核通道。模型再准也有失效的时候,尤其是遇到没见过的病害或者极端天气。系统里必须有一个"标记异常并转人工"的入口,而且要让一线人员用得顺手。这个功能看起来不性感,但它是系统能被长期信任的基础。
第五条:数据资产比模型值钱。模型架构换来换去,性能差异也就几个百分点,但连续多年、多地块、带完整农事记录的数据集,是真正的护城河。所以从第一天起就要把数据规范定好——命名规则、元数据字段、版本管理。我们前期的数据命名混乱到什么程度呢,同一块地有三种写法,后期清洗花了整整三周。
第六条:别忽略气象数据的质量。我们排查过一次很诡异的预测偏差,最后发现是气象站的辐射传感器积了一层灰,测出来的辐射值系统性偏低 20%,导致 ET0 全算小了,模型以为一直不缺水。传感器要定期清洁校准,这个维护动作必须写进运维手册,不能靠人自觉。
我个人在实际操作中的体会是,农业AI这个方向和互联网场景最大的不同,是它对"确定性"的要求反而更高。农户不需要你解释注意力权重,他需要知道明天该不该去东边那块地看看。模型的不确定性要转化成可执行的建议,精度要转化成能省钱的动作,这套系统才算真正落地。至于模型本身用什么架构,说实话,在数据质量达标的前提下,从 ViT 换成 ConvNeXt 或者别的什么,收益远没有把配准做准、把标注做对来得大。