尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SAR2Agri:自监督预训练如何解锁SAR强度数据的农业监测潜能

SAR2Agri:自监督预训练如何解锁SAR强度数据的农业监测潜能 1. 这篇文章真正要解决的问题做农业遥感的人对下面这些场景应该不陌生眼下正是作物生长关键期急需一张清晰的影像做长势评估但光学卫星连续几周都被云遮住或者一个跨区域的作物分类项目好不容易收集了一批地面采样点却发现样本量远远不够训练一个深度学习模型又或者你手里有大量 Sentinel-1 雷达影像但不知道该怎么把它变成真正有用的农业监测信息。这些问题的根源在于农业监测对“持续观测”的要求和光学遥感“看天吃饭”的天然局限之间的矛盾。卫星微波遥感——尤其是合成孔径雷达SAR——理论上可以穿透云层、全天候工作是解决这个矛盾的理想数据源。但 SAR 数据本身是微波后向散射的物理测量结果和光学影像的“人眼可读”完全不同直接套用为光学影像设计的深度学习模型效果往往不理想而这个领域又极度缺乏标注数据。SAR2Agri 的思路是把自然语言处理里“预训练 微调”的范式迁移到 SAR 农业监测上。它利用海量未标注的 Sentinel-1 强度影像先让模型学习 SAR 时间序列中的物候变化规律再通过少量标注数据微调完成作物分类、物候监测等下游任务。换句话说它要解决的核心问题是如何在没有大量人工标注的前提下让深度学习模型真正学会理解 SAR 强度影像中的农业信息。这篇文章会从 SAR 强度数据的物理含义说起讲清楚 SAR2Agri 的自监督预训练思路再给出基于 Google Earth Engine 的数据获取、预训练和微调的完整代码实现最后总结实际工程中容易踩的坑。无论你是刚接触 SAR 的学生还是已经跑过几个遥感模型的工程师这篇文章都值得读完。2. 合成孔径雷达与农业监测的基础概念2.1 SAR 是什么为什么农业监测离不开它合成孔径雷达Synthetic Aperture RadarSAR是一种主动式微波遥感系统。它自己发射微波脉冲再接收地面反射回来的回波通过信号处理合成出高分辨率影像。因为不依赖太阳光照SAR 可以全天时工作因为微波波长较长SAR 可以穿透大部分云层全天候获取地面信息。这两点在农业监测中价值极大。农作物生长监测要求时间连续如果依赖光学影像在多云多雨的地区——比如中国的南方、东南亚、欧洲北部——经常一个月都拿不到一景干净的光学数据监测计划就断了。SAR 不受这个限制这也是为什么全球的农业监测系统几乎都把 Sentinel-1 作为核心数据源。2.2 SAR 强度数据农业监测最常用的信息SAR 影像有两种主要信息强度和相位。强度Intensity表示地面目标对微波的后向散射强度它受地表粗糙度、介电常数含水量、几何结构等因素影响相位Phase则用于干涉测量可以反演地形高度和毫米级形变。农业监测中强度数据是绝对主力。原因是作物的生长过程会直接改变冠层结构和含水量进而改变微波后向散射强度——作物高度增加、叶片密度增大、含水量变化都会在强度影像上留下可观测的信号。对于 C 波段的 Sentinel-1其 VH 极化对作物冠层的体积散射敏感VV 极化对土壤和茎秆结构敏感两者组合起来能捕捉丰富的作物物候信息。这里要澄清一个常见误解SAR 强度影像不等于“雷达照片”。它是一组物理测量值受入射角、轨道方向、地表湿度、雷达系统噪声等多重因素影响。同一块田在不同日期、不同轨道下的强度值可能有显著差异但这正是物候信息的体现关键在于如何建模这种时间序列变化。2.3 SAR2Agri 与传统方法的本质区别传统遥感分类流程是“特征工程 机器学习分类器”从 SAR 影像中人工设计纹理特征、后向散射统计特征再输入随机森林、支持向量机等模型。这种方法的问题是特征设计依赖专家经验而且很难捕捉时间维度的复杂依赖。另一种做法是端到端训练深度神经网络直接输入多时相 SAR 影像块输出分类结果。这种方法效果通常更好但它依赖大量高质量的标注样本。在农业领域标注数据获取成本极高——需要实地调查、专家判读、多方核验。这导致实践中的训练样本往往只有几百甚至几十个远不足以支撑深度模型。SAR2Agri 选择的路线是自监督预训练先在大量无标注的 Sentinel-1 时间序列上训练一个特征提取器让模型学会“看懂” SAR 强度数据中的时空变化规律然后在下游任务上用少量标注数据微调。它把“标注成本”转化为“算力成本”这是 SAR 农业监测走向实用化非常关键的一步。从实际项目角度看这种“先自学、再微调”的方式意味着你不再需要为了一个 5 类作物分类任务去收集几千个标注样本。3. SAR2Agri 的核心原理与自监督预训练思路3.1 为什么通用视觉预训练模型不适合 SAR很多初学者会问为什么不直接用 ImageNet 预训练的 ResNet 或 ViT 提取 SAR 特征原因有两个层面。第一数据分布差异巨大。ImageNet 是自然光学图像包含颜色、纹理、物体轮廓等视觉语义SAR 强度影像是单通道的物理测量值噪声特性、动态范围、空间统计特性完全不同预训练模型提取的特征难以直接迁移。第二农业监测的关键信息在时间维。单张 SAR 影像只能反映某个时刻的散射状态作物是否在生长、是否被收割需要观察时间序列的变化模式才能判断。通用视觉模型缺乏对这种时间依赖的建模能力。SAR2Agri 的核心思路正是针对这两点做设计所有训练都基于 SAR 强度数据的原生形式并在预训练阶段引入时间维度的学习任务让模型从海量时序影像中抽象出农业物候变化的通用规律。3.2 自监督预训练的任务设计自监督学习的核心问题是如何构造“伪标签”任务让模型在没有人工标注的情况下学习到有用的特征。SAR2Agri 采用的任务设计思路基于时间序列的自洽性。其核心思想是作物生长的过程具有时间连续性相邻时间的 SAR 影像应该共享大量信息而相隔较远的影像则呈现明显的物候差异。模型需要学会通过已知时间段的影像预测或重构另一时间段的影像从而理解时序变化规律。用一个类比帮助理解这就像让你只看一部电影的前半部分尝试推测后半部分的情节。如果你真的理解了故事的发展逻辑就能做得不错如果只是死记硬背画面就无法泛化。SAR2Agri 的模型在预训练阶段输入某段时间窗口内的 SAR 强度序列通过回波相关Echo Correlation等任务学习影像间的时序关联迫使模型编码器提取出真正随作物生长变化的物理特征而不是噪声或轨道系统误差。从论文题目“Learning SAR Intensity Representations”可以看出这项工作的重点不是在某个特定任务上刷精度而是找到一个通用的、可复用的 SAR 强度表征学习框架。这个表征一旦学会就可以迁移到作物分类、长势监测、物候提取、受灾评估等多个任务上。3.3 模型架构的核心组成从遥感深度学习的通用实践来看SAR2Agri 这类框架通常包含三个核心组件空间特征提取器负责从单时相 SAR 影像中提取空间特征通常使用卷积神经网络或视觉 Transformer。由于 SAR 是单通道数据输入层需要做适配。时间序列编码器负责建模多个时间点之间的依赖关系常用时序 Transformer、LSTM 或 3D 卷积。它需要捕捉作物生长的物候轨迹而不是仅仅拼接各时间点的特征。预训练任务头在预训练阶段用于完成特定的自监督任务预训练结束后可以丢弃只保留前两个组件用于下游任务微调。这种“空间编码 时间建模”的双分支结构在实际农业监测项目中同样适用。即使你不打算完整复现 SAR2Agri也可以借鉴它的框架设计思路来搭建自己的模型。需要说明的是论文中的具体网络结构、预训练任务的详细设计应以其正式论文和官方代码为准。本文后续给出的代码方案是实现同一思路的可运行参考版本重点展示工程实现路径。4. 环境准备与数据获取4.1 硬件与软件环境运行 SAR2Agri 类模型不需要特别夸张的硬件但要分清两种场景。如果你要完整复现论文的预训练实验需要在一整套多时相 SAR 数据集上训练建议使用 GPU 服务器显存至少 16GB。这一阶段计算量较大属于“基础设施投入”。更常见的场景是在预训练模型基础上做下游任务微调。此时单个 12GB 显存的 GPU 就够用了甚至可以用云 GPU 实例按需使用成本更低。大多数农业监测项目的实际情况是预训练模型已经公开你只需要微调硬件门槛并不高。软件环境方面推荐以下组合组件推荐方案说明操作系统Ubuntu 20.04 / 22.04Linux 环境兼容性最好深度学习框架PyTorch 2.x生态成熟便于改造模型遥感数据处理Google Earth Engine (GEE)获取 Sentinel-1 数据的最佳渠道Python 环境Python 3.9配合 conda 管理数据接口geemap / earthengine-api与 GEE 交互GPU 加速库CUDA 11.8 / cuDNN按 PyTorch 版本选择以上版本号为通用推荐值实际操作时请以官方文档的最新要求为准。4.2 Sentinel-1 数据的核心参数Sentinel-1 是欧洲空间局ESA的 C 波段合成孔径雷达卫星星座由 Sentinel-1A 和 Sentinel-1B已退役两颗卫星组成。在 GEE 中获取 Sentinel-1 数据时需要理解几个关键参数极化方式Sentinel-1 提供 VV 和 VH 两种极化。农业监测中通常同时使用两者VH 对冠层结构敏感VV 对土壤和下层结构敏感。干涉宽幅模式IW这是陆地监测默认的成像模式幅宽 250 公里分辨率 5m × 20m适合区域级农业监测。入射角不同轨道的入射角不同会影响后向散射绝对值。做时间序列分析时最好按相对轨道号Relative Orbit Number分开处理。GRD 产品GEE 中提供的是 Ground Range DetectedGRD产品已经过热噪声去除、辐射定标和地形校正这大大降低了数据预处理的工程量。SAR2Agri 这类工作通常直接使用 GRD 强度数据而不建议使用未处理的 Level-1 SLC 数据自行做聚焦和定标因为没有必要。4.3 数据获取策略获取训练数据时最常犯的错误是想把所有影像不分轨道、不分季节地混在一起。正确做法是确定研究的区域和作物生长季。选择 Sentinel-1 相对轨道号保证整个时间序列的入射角一致。设置合理的时间窗口例如从播种前到收获后。按 6 到 12 天间隔获取影像组成时间序列样本。获取对应的标注数据如作物类型图、地块边界用于下游微调评估。在 GEE 中Sentinel-1 影像的 ID 为COPERNICUS/S1_GRD可以按instrumentMode、orbitProperties_pass、relativeOrbitNumber_in_start等属性筛选。5. 基于 Google Earth Engine 获取 SAR 强度影像Google Earth EngineGEE是目前获取、处理和导出 Sentinel-1 数据最高效的工具。它内置了全球的 Sentinel-1 GRD 影像集合不需要本地存储海量数据也避免了自己写预处理流程。下面的示例演示如何按区域、时间、轨道和极化筛选影像并计算多时相的平均强度。// file: 01_s1_collection.js // 在 GEE Code Editor 中运行 var region ee.Geometry.Rectangle([115.8, 39.2, 117.0, 40.2]); var startDate 2020-04-01; var endDate 2020-10-31; var s1 ee.ImageCollection(COPERNICUS/S1_GRD) .filterBounds(region) .filterDate(startDate, endDate) .filter(ee.Filter.eq(instrumentMode, IW)) .filter(ee.Filter.eq(orbitProperties_pass, DESCENDING)) .filter(ee.Filter.eq(relativeOrbitNumber_in_start, 117)) .map(function(img) { var vv img.select(VV); var vh img.select(VH); // 将后向散射系数转为分贝dB更利于模型学习 var vvDb vv.log10().multiply(10.0).rename(VV); var vhDb vh.log10().multiply(10.0).rename(VH); return img.addBands(vvDb).addBands(vhDb); }); print(影像数量:, s1.size()); // 计算所有影像的平均强度快速预览 var s1Mean s1.select([VV, VH]).mean().clip(region); Map.centerObject(region, 10); Map.addLayer(s1Mean, {min: -25, max: 0, bands: [VH]}, S1 VH mean); Map.addLayer(s1Mean, {min: -25, max: 0, bands: [VV]}, S1 VV mean); // 导出平均强度影像到 Google Drive Export.image.toDrive({ image: s1Mean.toFloat(), description: s1_mean_intensity, folder: SAR2Agri, region: region, scale: 10, maxPixels: 1e13 });这段代码做了几件关键事情按区域、时间、成像模式、轨道方向和相对轨道号筛选影像避开不同入射角带来的系统差异将线性后向散射系数转换为分贝值这是 SAR 农业监测的常见做法因为分贝值更接近人眼对散射强度差异的感知也更容易让模型学习同时保留了 VV 和 VH 两个极化通道。在 GEE Code Editor 中运行这段代码后左侧图层会显示研究区域的平均 VV 和 VH 强度右下角控制台会输出影像数量。这个数量应该在 20 到 50 之间如果太少说明时间窗口或轨道筛选过严需要调整。6. 构建 SAR2Agri 自监督预训练数据集6.1 时间序列样本的导出策略有了影像集合之后不能直接把这些影像一股脑导出而是应该按“时间窗口”组织样本。比如构建一个输入长度为 12 的时间序列就需要把一个生长季的影像按时间顺序排列每隔 12 天左右取一景构成一个完整的时间维度。实际导出的数据组织方式建议如下datasets/ ├── pretrain/ │ ├── sample_001.npz │ ├── sample_002.npz │ └── ... └── finetune/ ├── train/ ├── val/ └── test/每个sample保存一个时空立方体形状为(T, C, H, W)其中 T 为时间步数C 为通道数通常为 2VV 和 VHH 和 W 为空间尺寸。同时保存该样本的元信息如中心坐标、时间戳、所属轨道。下面的 Python 脚本用于在 GEE 中导出一个区域内的所有时间序列块# file: 02_export_ts.py import ee import geopandas as gpd ee.Initialize() region ee.Geometry.Rectangle([115.8, 39.2, 117.0, 40.2]) start_date 2020-04-01 end_date 2020-10-31 tile_size 128 # 每个样本的空间尺寸 num_tiles_x 10 num_tiles_y 10 grid ee.FeatureCollection( ee.Geometry.Region(region).coveringGrid( ee.Projection(EPSG:32650), tile_size * 10, tile_size * 10) ) s1 ee.ImageCollection(COPERNICUS/S1_GRD) \ .filterBounds(region) \ .filterDate(start_date, end_date) \ .filter(ee.Filter.eq(instrumentMode, IW)) \ .filter(ee.Filter.eq(orbitProperties_pass, DESCENDING)) # 将影像集合转为按时间排序的波段堆叠 def to_db(img): vv img.select(VV).log10().multiply(10.0).rename(VV) vh img.select(VH).log10().multiply(10.0).rename(VH) return img.select([]).addBands(vv).addBands(vh) s1_db s1.map(to_db).sort(system:time_start) # 先导出每个时间步的强度影像供后续本地组装 dates s1_db.aggregate_array(system:time_start).getInfo() print(时间节点数量:, len(dates)) for i, d in enumerate(dates): image s1_db.filter(ee.Filter.eq(system:time_start, d)).first() export_name fs1_ts_{i:03d}_{d} task ee.batch.Export.image.toDrive( imageimage.toFloat(), descriptionexport_name, folderSAR2Agri_ts, regionregion, scale10, maxPixels1e13 ) task.start()这里用coveringGrid生成规则的网格每个格网对应一个空间位置。导出时按时间步分别导出影像便于后续在本地按时间维度组装样本。实际项目中你可能需要根据云量和影像缺失情况动态选择每个位置的有效时间步。6.2 本地数据构建组合时空立方体导出到 Google Drive 后在本地解压并组装时间序列样本。这一步的重点是检查时间步之间的空间对齐——如果 GEE 导出的影像分辨率一致且坐标系相同通常可以直接按像素对齐。# file: 03_build_pretrain_dataset.py import numpy as np import glob import os from datetime import datetime def load_timestep_feather(path): 读取单个时间步的 VV/VH 强度影像假设为 npy 文件 data np.load(path) return data # 期望形状 (2, H, W) 即 VV, VH def build_sample(sample_dir, time_steps, tile_size128, stride64): 从指向同一地理位置的时间序列目录中组装时空立方体。 - time_steps: 需要的时间步数量 - tile_size: 裁剪尺寸 - stride: 滑动窗口步长 ts_files sorted(glob.glob(os.path.join(sample_dir, *.npy))) if len(ts_files) time_steps: return None, None # 取前 time_steps 个时间步 ts_files ts_files[:time_steps] cube_list [] for tf in ts_files: frame load_timestep_feather(tf) # (2, H, W) cube_list.append(frame) cube np.stack(cube_list, axis0) # (T, 2, H, W) # 滑窗裁剪生成子样本 h, w cube.shape[2], cube.shape[3] samples [] for y in range(0, h - tile_size 1, stride): for x in range(0, w - tile_size 1, stride): patch cube[:, :, y:y tile_size, x:x tile_size] samples.append(patch.astype(np.float32)) return samples, cube # 示例处理一个目录 sample_dir path/to/exported_tiles time_steps 12 samples, full_cube build_sample(sample_dir, time_steps) if samples: print(f生成 {len(samples)} 个子样本每个形状: {samples[0].shape}) else: print(当前位置时间序列不足, time_steps, 个时间步)这个脚本的核心逻辑是对于每个地理位置按照时间顺序读取所有时间步的影像堆叠成(T, C, H, W)的时空立方体然后用滑动窗口切成更小的子块。这样做的目的是增加训练样本量同时保证整个时间维度信息完整。6.3 数据归一化与增强SAR 强度数据分布和一个典型的光学影像不同。直接输入原始 dB 值数值范围通常在 -30dB 到 5dB 之间不同区域、不同季节的分布差异很大。强烈建议做以下处理逐通道归一化计算训练集的均值和标准差执行标准化。SAR2Agri 这类自监督方法对数据分布较敏感。时间序列对齐如果某些样本的时间步比其他样本多可以截取或插值到统一长度。温和的数据增强在预训练阶段可以使用随机水平/垂直翻转、随机裁剪但要避免使用颜色抖动、高斯模糊等针对自然图像设计的增强方法因为它们会破坏 SAR 的物理散射特征。# file: 04_preprocessing.py import numpy as np class SARNormalizer: 基于统计量的 SAR 强度标准化 def __init__(self, mean, std): self.mean np.array(mean, dtypenp.float32) self.std np.array(std, dtypenp.float32) def __call__(self, cube): # cube: (T, C, H, W) return (cube - self.mean) / (self.std 1e-6) def random_flip(cube): 随机翻转增强flip 对物理散射特征影响可控 if np.random.rand() 0.5: cube cube[:, :, :, ::-1] # 左右翻转 if np.random.rand() 0.5: cube cube[:, :, ::-1, :] # 上下翻转 return cube.copy()预处理完成后数据集就可以作为自监督预训练的输入了。7. SAR2Agri 自监督预训练与微调实现7.1 自监督预训练模型构建下面提供一个符合 SAR2Agri 思路的 PyTorch 实现示例。模型使用 3D 卷积作为空间-时间特征提取器配合一个简单的时间预测头完成自监督任务。核心思想是输入一段时间序列模型需要预测下一个时间步的 SAR 强度影像。这是一个回归任务通过重构误差驱动模型学习物候变化规律。# file: 05_model.py import torch import torch.nn as nn import torch.nn.functional as F class SpatialEncoder(nn.Module): 空间特征提取使用 2D 卷积处理每个时间步 def __init__(self, in_channels2, feat_dim64): super().__init__() self.conv1 nn.Conv2d(in_channels, 32, kernel_size3, padding1) self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) self.conv3 nn.Conv2d(64, feat_dim, kernel_size3, padding1) self.bn1 nn.BatchNorm2d(32) self.bn2 nn.BatchNorm2d(64) self.bn3 nn.BatchNorm2d(feat_dim) def forward(self, x): # x: (B, T, C, H, W) b, t, c, h, w x.shape x x.view(b * t, c, h, w) x F.relu(self.bn1(self.conv1(x))) x F.relu(self.bn2(self.conv2(x))) x F.relu(self.bn3(self.conv3(x))) _, fc, fh, fw x.shape return x.view(b, t, fc, fh, fw) class TemporalEncoder(nn.Module): 时间序列编码使用 Transformer 捕捉时序依赖 def __init__(self, feat_dim64, num_heads4, num_layers2): super().__init__() self.pos_embed nn.Parameter(torch.randn(1, 100, feat_dim)) encoder_layer nn.TransformerEncoderLayer( d_modelfeat_dim, nheadnum_heads, dim_feedforward256, batch_firstTrue ) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) def forward(self, x): # x: (B, T, feat_dim) b, t, _ x.shape x x self.pos_embed[:, :t, :] return self.encoder(x) class SAR2AgriPretrain(nn.Module): 自监督预训练模型预测下一时间步的 SAR 强度 def __init__(self, in_channels2, feat_dim64, input_time12, patch_size128): super().__init__() self.input_time input_time self.spatial_encoder SpatialEncoder(in_channels, feat_dim) self.temporal_encoder TemporalEncoder(feat_dim) # 将时序特征映射回 SAR 强度图 self.head nn.Sequential( nn.ConvTranspose2d(feat_dim, 32, kernel_size3, stride1, padding1), nn.ReLU(), nn.Conv2d(32, in_channels, kernel_size3, padding1) ) def forward(self, x): # x: (B, T, C, H, W), T 包含历史所有时间步 b, t, c, h, w x.shape spatial_feat self.spatial_encoder(x) # (B, T, feat_dim, H, W) b, t, f, fh, fw spatial_feat.shape # 将空间特征展平成 token 序列 temporal_feat spatial_feat.mean(dim(-2, -1)) # (B, T, feat_dim) temporal_out self.temporal_encoder(temporal_feat) # (B, T, feat_dim) # 用最后一个时间步的时序特征重建下一个影像 last_feat temporal_out[:, -1, :] # (B, feat_dim) last_feat last_feat.view(b, f, 1, 1).expand(b, f, fh, fw) pred self.head(last_feat) # (B, C, H, W) return pred def pretrain_loss(pred, target): 预测下一时间步 SAR 强度的 L1 SSIM 混合损失 l1 F.l1_loss(pred, target) return l1这个模型的核心设计逻辑是空间编码器逐时间步提取空间特征生成(B, T, feat_dim, H, W)的特征张量时间编码器将所有时间步的特征压缩后建模时序依赖最后用最后一个时间步的时序特征去预测下一个时间步的完整强度影像。如果模型能够准确预测未来时间的 SAR 影像说明它已经学会了区域内作物生长的物候变化规律。7.2 预训练训练脚本# file: 06_train_pretrain.py import torch import torch.optim as optim from torch.utils.data import DataLoader, Dataset import numpy as np class SARDataset(Dataset): def __init__(self, data_files, time_steps12): self.data_files data_files self.time_steps time_steps def __len__(self): return len(self.data_files) def __getitem__(self, idx): cube np.load(self.data_files[idx])[arr_0] # (T, 2, H, W) cube torch.from_numpy(cube).float() # 输入前 time_steps-1 个时间步预测最后一个时间步 inputs cube[:self.time_steps - 1] # (T-1, 2, H, W) target cube[self.time_steps - 1] # (2, H, W) return inputs, target def train_one_epoch(model, loader, optimizer, device): model.train() total_loss 0.0 for inputs, target in loader: inputs inputs.to(device) target target.to(device) optimizer.zero_grad() pred model(inputs) loss pretrain_loss(pred, target) loss.backward() optimizer.step() total_loss loss.item() return total_loss / len(loader) if __name__ __main__: device torch.device(cuda if torch.cuda.is_available() else cpu) train_files [pretrain_data/001.npz, pretrain_data/002.npz] dataset SARDataset(train_files, time_steps12) loader DataLoader(dataset, batch_size8, shuffleTrue) model SAR2AgriPretrain(in_channels2, feat_dim64, input_time12).to(device) optimizer optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-5) for epoch in range(50): loss train_one_epoch(model, loader, optimizer, device) if epoch % 5 0: print(fEpoch {epoch}, Loss: {loss:.4f}) torch.save(model.state_dict(), sar2agri_pretrain.pt)这个训练循环本身并不复杂重点在数据加载的效率和正确性。实际项目中SARDataset需要实现高效的磁盘读取建议使用lmdb或webdataset格式存储数据避免单次读取大量小文件导致的 IO 瓶颈。7.3 下游任务微调预训练完成后丢弃模型的预测头保留空间编码器和时间编码器加上一个轻量分类头完成作物分类任务# file: 07_finetune.py import torch import torch.nn as nn import torch.optim as optim class SAR2AgriClassifier(nn.Module): 下游分类模型SAR2Agri 骨干 分类头 def __init__(self, backbone, num_classes5, feat_dim64): super().__init__() self.backbone backbone self.classifier nn.Sequential( nn.Linear(feat_dim, 128), nn.ReLU(), nn.Dropout(0.3), nn.Linear(128, num_classes) ) def forward(self, x): # x: (B, T, C, H, W) with torch.no_grad(): spatial_feat self.backbone.spatial_encoder(x) b, t, f, fh, fw spatial_feat.shape temporal_feat spatial_feat.mean(dim(-2, -1)) temporal_feat self.backbone.temporal_encoder(temporal_feat) feat temporal_feat.mean(dim1) # (B, feat_dim) return self.classifier(feat) def finetune_classifier(): device torch.device(cuda if torch.cuda.is_available() else cpu) # 加载预训练骨干 backbone SAR2AgriPretrain(in_channels2, feat_dim64, input_time12) backbone.load_state_dict(torch.load(sar2agri_pretrain.pt)) model SAR2AgriClassifier(backbone, num_classes5).to(device) # 冻结骨干只训练分类头 for param in model.backbone.parameters(): param.requires_grad False optimizer optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr1e-3) criterion nn.CrossEntropyLoss() # 训练循环略逻辑与预训练类似 print(微调就绪: 模型可训练参数, sum(p.numel() for p in model.parameters() if p.requires_grad)) if __name__ __main__: finetune_classifier()微调的策略很关键。在小样本场景下建议先冻结骨干、只训练分类头等分类头收敛后再解冻骨干以小学习率如 1e-5对整个网络做全量微调。这样可以避免在训练初期破坏预训练学到的表征。8. 运行结果与效果验证8.1 预训练收敛判断预训练阶段最直接的验证指标是预测损失的变化。如果模型真正在学习损失应该在一个合理的范围内逐步下降。但更重要的判断标准是预测的影像在视觉上是否具有物理合理性。可以周期性地采样几个验证样本将模型的预测影像与真实的下一时间步影像叠加对比。如果模型只是学会了输出区域的平均散射强度预测影像会非常模糊看不到农田地块的边界如果模型真正学到了物候规律预测影像中农田地块的纹理和边界应该与真实影像有一定相似性。# 查看训练过程损失曲线示例命令 tensorboard --logdir runs/sar2agri_pretrain从实际经验看如果预训练数据集合规、时间序列完整损失通常在 20 到 50 个 epoch 内显著下降之后进入平台期。8.2 下游任务效果评估下游农业监测任务的评估方法取决于具体场景作物分类任务计算总体精度OA、Kappa 系数、各类的 F1-score。物候监测任务计算预测物候日期播种期、成熟期与地面观测的均方根误差RMSE。长势评估任务计算预测生物量/叶面积指数与实测值的 R² 和 RMSE。关键对比实验是使用 SAR2Agri 预训练权重微调 vs 随机初始化训练。在标注样本有限的情况下前者应该在各项指标上有明显优势尤其是当训练样本少于 500 时。如果两者差异不大说明要么预训练任务设计不合理要么下游任务与预训练任务的相关性不足。# file: 08_evaluate.py import torch from sklearn.metrics import accuracy_score, f1_score, confusion_matrix def evaluate(model, loader, device, num_classes5): model.eval() all_preds [] all_labels [] with torch.no_grad(): for x, y in loader: x, y x.to(device), y.to(device) logits model(x) preds logits.argmax(dim1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(y.cpu().numpy()) acc accuracy_score(all_labels, all_preds) f1 f1_score(all_labels, all_preds, averageweighted) cm confusion_matrix(all_labels, all_preds) print(fAccuracy: {acc:.4f}) print(fWeighted F1: {f1:.4f}) print(Confusion Matrix:) print(cm) return acc, f1, cm评估时需要注意类别不平衡问题。农业分类数据集的典型问题是“玉米/水稻”等主粮作物样本很多而小众作物如中药材、杂粮样本极少。此时只报总体精度会误导人必须同时看各类别的 F1 和混淆矩阵。8.3 失败排查的第一步如果预训练不收敛或者微调效果不理想不要急着调模型结构先按下面的顺序排查检查数据预处理SAR 影像是否已转为 dB是否做了归一化时间序列是否对齐检查时间步顺序如果时间序列没有按时间排序模型就无法学习物候规律。检查训练损失是否震荡如果损失震荡剧烈先降低学习率或者增加梯度裁剪。检查标注数据质量下游微调效果差有时不是模型问题而是标注本身有噪声。9. 常见问题与排查思路在实际运行 SAR2Agri 相关项目时下面这些问题出现频率最高问题现象可能原因排查方式解决方案GEE 导出影像数量为 0筛选条件过严或区域不在 Sentinel-1 覆盖范围内检查筛选条件、调整时间窗口或轨道号放宽时间范围或取消 DESCENDING 限制本地组装时间序列时发现时间步缺失部分时间步因云层或轨道覆盖问题被过滤打印每个位置的时间戳检查缺失模式允许时间步长度不同或使用插值补充预训练损失不下降学习率过大、数据未归一化、预测任务过难查看损失曲线、检查数据分布降低学习率调整损失权重检查目标值范围微调时模型过拟合严重标注样本过少骨干模型参数被全部解冻观察训练集和验证集精度差距冻结骨干更久增加 Dropout使用数据增强VH 通道数值均为 0 或恒定极化通道选择错误或数据导出异常检查原始影像的波段名称确认使用 VV 和 VH 波段并检查导出日志模型预测的 SAR 影像过于模糊预训练任务回归目标多解特征被平均化观察预测影像的纹理细节改用更复杂的生成式损失或引入对抗训练这里特别强调两个容易被忽视的问题。第一GEE 中的 Sentinel-1 影像集合默认包含多种轨道模式如果不过滤instrumentMode会混入 StripmapSM模式的数据分辨率不同模型无法有效学习。第二SAR 强度影像中存在固有的斑点噪声如果不做多视处理或时间平均模型很容易把噪声当作特征来学习导致下游任务泛化性差。10. SAR2Agri 的最佳实践与工程建议10.1 时间序列窗口怎么选作物生长季通常在 100 到 180 天之间。Sentinel-1 的重访周期在赤道附近为 6 天在中纬度地区为 12 天左右。一个生长季大约能积累 10 到 25 景影像。建议时间窗口覆盖整个生长季从播种前半个月开始到收获后半个月结束这样模型能同时看到裸土、出苗、旺盛生长、成熟和收割后的完整变化过程。时间窗口过短模型无法学习完整的物候轨迹过长则引入过多无信息的地表变化。10.2 极化和轨道处理建议同时使用 VV 和 VH 极化不要只用单极化。VH 对作物冠层敏感VV 对土壤和下层结构敏感两者互补。按相对轨道号分开处理或作为输入特征传入。不同轨道的入射角不同后向散射值有系统偏差混在一起会干扰模型。升轨和降轨数据尽量分开建模或者至少作为通道拼接不要直接混合。10.3 结合光学数据是加分项SAR2Agri 专注于 SAR 强度表征学习但这并不意味着完全排斥光学数据。在实际情况中如果研究区域光学影像可用率较高建议将 Sentinel-2 的时间序列与 SAR 数据融合使用。比如在 SAR 影像缺失的时间步用光学影像补全或者将光学 NDVI 时间序列作为辅助输入。SAR 提供云雨天气下的连续观测光学提供更高的光谱分辨率和植被指数精度两者融合通常能带来更稳定的监测效果。10.4 小样本场景下的微调策略这是 SAR2Agri 发挥最大价值的场景。当每个类别的标注样本少于 50 个时建议先冻结全部骨干网络只训练分类头。在分类头收敛后以 1/10 的学习率微调骨干网络的最后两层。使用五折交叉验证评估稳定性。如果用 Transformer 作为骨干微调时务必使用较小的学习率和更长的 warmup避免注意力分布发生剧烈变化。10.5 生产环境部署的注意事项如果要把模型部署到生产系统还需要额外关注数据版本管理Sentinel-1 数据会更新训练数据和推理数据要记录对应的卫星、轨道和采集日期。推理效率Transformer 时间编码器在 CPU 上推理速度较慢生产环境建议使用 GPU 或 TensorRT 优化。持续学习农业物候模式在不同年份会有波动模型需要周期性用新一年的数据更新并评估漂移。11. 总结与后续学习方向这篇文章围绕 SAR2Agri 展开核心信息可以归纳为三点。第一SAR 强度数据是农业监测中不可替代的信息源但它的物理特性和光学影像差异巨大直接迁移通用视觉预训练模型效果有限。第二SAR2Agri 通过自监督预训练的方式让模型先在海量无标注的 Sentinel-1 时间序列上学习物候变化的通用表征再用少量标注数据微调完成下游任务把“标注成本”换成了“算力成本”这是 SAR 农业监测落地的一条实用路径。第三文章中给出的 GEE 数据获取、自监督预训练、下游微调和效果评估的完整代码可以直接作为实际项目的起点。如果你打算继续深入有几个方向值得关注。一是 SAR2Agri 论文中的具体网络结构和预训练任务细节建议仔细阅读原文并对照官方开源代码理解二是多模态融合把 Sentinel-1 和 Sentinel-2 的时间序列联合建模能进一步提升复杂场景下的监测精度三是更大规模的地理迁移实验检验预训练表征在不同气候区、不同作物类型之间的泛化能力四是将该思路迁移到高分辨率 SAR 卫星数据如 TerraSAR-X、国产 SAR 卫星探索不同波长和分辨率下的表征学习效果。最后提醒一句SAR 数据处理链路长从 GEE 导出、预处理、训练到部署每一步都可能引入误差。建议先用小区域、少量数据跑通完整链路再逐步扩大规模。把基础流程做扎实SAR2Agri 这类方法才能真正成为农业监测工具箱中的利器。
返回列表