尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

遥感解译为何不能直接微调ImageNet?多波段大图工程实践

发布时间:2026/9/17 20:24:34

资讯中心
01
ARTICLE

遥感解译为何不能直接微调ImageNet?多波段大图工程实践

遥感解译为何不能直接微调ImageNet?多波段大图工程实践
简介这份资料是商汤科技SenseRemote人工智能遥感解译的专题演示文稿共24页面向遥感影像处理、GIS应用与深度学习方向的从业者及高年级学生帮助读者系统了解AI技术如何落地遥感解译场景。内容从深度学习三大核心要素切入梳理GPU超算集群与自建深度学习平台Parrots的选型考量并针对遥感多波段、高光谱、大尺寸影像的特点讲解数据普适模型、场景专用模型、模型压缩与改进等设计思路。后半部分以云雪水体提取、路网提取、飞机关键点检测、舰船检测、变化检测、土地分类与建筑物提取等实例展示处理速度与精度表现并延伸到人工智能与GIS结合后的分类统计与三维建模应用。资源包仅含1个pptx文件约21.19MB单文件结构便于直接翻阅与摘取配图目前已有472人学习。适合作为遥感智能化解译的方案参考与汇报素材。1. 遥感解译为什么不能直接 finetune ImageNet 骨干网络翻完《SenseRemote 商汤人工智能遥感解译介绍》这 24 页最该记住的不是成果截图而是第 4 页的结论在 VOC、COCO、ImageNet 上直接 finetune 出来的 VGG、GoogLeNet、ResNet放到遥感数据上并不好用。很多人做人工智能遥感解译项目的第一反应就是拿 ImageNet 预训练权重换个输出层开训结果精度上不去、训练还慢。问题不在训练技巧波段数不是 3、位深不是 8bit、单景动辄上万像素、同一幅图里目标尺度跨两三个数量级。材料给出的四个方向——数据普适、遥感专用、模型压缩、模型改进——对应从数据管线、网络结构到训练平台和推理验证的完整链路。2. 多波段大图的数据管线从 rasterio 切片到指数通道注入2.1 三通道 8bit 假设失效的四种具体形态遥感数据进网络之前第一道坎不是模型而是数据组织。自然图像数据集里每张图都是 3×224×224 的 uint8而遥感影像的波段、位深、尺寸三个维度都是发散的。材料里提到「数据普适设计适用于 8bit、16bit 全色、RGB、多光谱、高光谱多种数据」落到工程上就是一张预处理分支表数据类型波段/位深典型来源直接套 ImageNet 预处理的后果全色 PAN1 波段8/16bit高分系列融合前产品通道数不匹配复制成三份会抹掉辐射差异RGB 真彩色3 波段8bit融合后产品能跑但位深压缩后阴影与浅水区分层变糊多光谱 MSS4–8 波段16bit常用中等分辨率星载数据近红外被丢弃植被与水体可分性明显下降高光谱数十至上百波段16bit机载/星载高光谱波段间强相关全量输入显存直接爆工程上的处理原则是「先保持物理量再做归一化」16bit 数据不要先压成 8bit 再存而是以 float32 保留原始辐射值在送入网络前才除以固定的位深上限。高光谱则常见做法是先做波段选择或 PCA 降到 20–40 维把降维矩阵保存下来推理阶段复用同一套矩阵否则训练和推理的输入分布会对不上。2.2 用 Window 读块而不是把整景读进内存材料里出现的影像尺寸有 12900×8000、18000×18000、15000×38000单景 16bit 多光谱用 float32 展开后轻松超过 10GB。下面的读块函数是整个数据管线的地基import numpy as np import rasterio from rasterio.windows import Window def read_patch(path, x0, y0, size, bandsNone, dtype_scale65535.0): 按窗口读取返回 (C,H,W) 的 float32 数组和有效像元掩膜 with rasterio.open(path) as src: win Window(x0, y0, size, size) idx bands or list(src.indexes) # 保持数据源原始波段顺序 arr src.read(idx, windowwin).astype(np.float32) nodata src.nodata if nodata is not None: valid arr ! np.float32(nodata) # 掩膜必须在归一化之前生成 arr np.where(valid, arr, 0.0) mask valid.all(axis0) else: mask np.ones(arr.shape[-2:], dtypebool) arr arr / dtype_scale # 固定分母保证跨景一致 return arr, maskwindow参数让 GDAL 只解码目标区域15 万像素级的图也能按块处理src.indexes决定波段顺序多光谱数据源之间的波段排列并不统一顺序搞错会直接把近红外当成红波段。归一化用固定的dtype_scale而不是每景 min-max是因为遥感成像光照和季节差异大逐景拉伸会让同一模型在不同景上的输入分布漂移训练时看起来 loss 下降换一景就崩。2.3 滑窗切片的重叠率与边界回退整景进不了网络就要切成 512×512 或 1024×1024 的瓦片。朴素切片会让跨边界的目标被拦腰截断舰船、飞机这类目标一旦被切标注和预测都会错位。常见做法是让步长小于瓦片边长形成重叠def gen_tiles(h, w, tile512, stride384): stride tile 产生重叠区边缘用回退保证每块尺寸一致 step stride if stride tile else tile for y in range(0, h, step): for x in range(0, w, step): x0 min(x, max(w - tile, 0)) y0 min(y, max(h - tile, 0)) yield x0, y0, min(tile, w - x0), min(tile, h - y0)stride384、tile512对应 25% 重叠线状地物和小目标场景常加到 50%。最后一行、最后一列用min(x, w - tile)回退是为了保证每块都是tile×tile否则 DataLoader 的collate_fn要处理变长张量BatchNorm 统计也会被短边瓦片污染。图像本身小于tile时取 0 偏移剩下的部分做反射填充。2.4 把 NDVI、NDWI 作为先验通道拼进去材料里「模型改进设计」提到增加面向像素解译中常用指数信息的网络学习。说白了就是不指望网络从原始波段里重新学出比值关系直接把指数算好当额外通道送进去。小样本场景下这一步能明显加快收敛def add_indices(arr, band_map, eps1e-6): arr: (C,H,W)band_map 记录各物理波段在数组中的下标 nir, red, green arr[band_map[nir]], arr[band_map[red]], arr[band_map[green]] ndvi (nir - red) / (nir red eps) # 植被敏感 ndwi (green - nir) / (green nir eps) # 水体敏感 return np.concatenate([arr, ndvi[None], ndwi[None]], axis0)eps防止反射率接近 0 时除零。指数的取值范围在 -1 到 1 之间和归一化后的波段值量级接近不需要额外做标准化如果你的骨干网络第一层是按 0.5 均值初始化的可以在输入后接一个1×1卷积让网络自己调整尺度。band_map必须按数据源实际波段顺序配置Landsat、GF-1 MSS、GF-2 的近红外位置都不一样。2.5 样本均衡别让背景瓦片淹没损失遥感分割任务里类别极不均衡水体、建筑这些目标可能只占瓦片的百分之几大量纯背景瓦片会让损失函数长期停在「全预测为背景」的局部最优上。除了在 loss 上做加权和 Dice 混合采样阶段也要过滤def keep_tile(mask, min_pos_ratio0.01, max_pos_ratio0.90): 按正样本占比筛瓦片剔除纯背景和几乎全目标的块 pos float(mask.mean()) return min_pos_ratio pos max_pos_ratiomin_pos_ratio设太小等于没过滤设太大会让训练集失去背景样本模型在真实场景里误检率高0.01 到 0.05 是分割任务的常用区间。max_pos_ratio用来剔除标注溢出或大范围同质区域这些块对梯度贡献单一容易让模型过拟合某一种地表类型。这套过滤逻辑建议离线跑一遍生成索引文件训练时只读索引避免每个 epoch 重复扫盘。3. 面向遥感的网络改造与模型压缩多尺度融合、空洞卷积与剪枝3.1 通用骨干在遥感图上的两个错位材料里那串conv1-1 … conv4-3 / fc5 / fc6 / pool_fusion / p1_interp的结构图本质上是在解决两个错位。第一个是尺度错位。0.8 米分辨率下翼展 45 米的飞机大约 56 像素2 米分辨率下长 184 米的舰船约 92 像素而一条贯穿 12900×8000 影像的等级道路细到只有几个像元宽。同一景里目标尺度跨两个数量级标准 ResNet 最后一级 stride 32 的下采样会让小目标在特征图上直接消失。第二个是结构错位。分割头从 stride 32 的特征上采样 32 倍边界会糊成锯齿道路和河流这类线状地物尤其明显。p1_interp这类命名暗示的做法就是把深层的语义特征插值回高分辨率尺度后与浅层特征相加而不是只用一个尺度做预测。3.2 一个可直接复用的多尺度融合头import torch import torch.nn as nn import torch.nn.functional as F class MultiScaleFusion(nn.Module): def __init__(self, c_low128, c_mid256, c_high512, out256): super().__init__() # 1x1 先把不同层通道压到同一维度降低插值后的显存峰值 self.proj_low nn.Conv2d(c_low, out, 1) self.proj_mid nn.Conv2d(c_mid, out, 1) self.proj_high nn.Conv2d(c_high, out, 1) self.fuse nn.Sequential( nn.Conv2d(out, out, 3, padding1, biasFalse), nn.BatchNorm2d(out), nn.ReLU(inplaceTrue), ) def forward(self, f_low, f_mid, f_high): size f_low.shape[-2:] out self.proj_low(f_low) out out F.interpolate(self.proj_mid(f_mid), sizesize, modebilinear, align_cornersFalse) out out F.interpolate(self.proj_high(f_high), sizesize, modebilinear, align_cornersFalse) return self.fuse(out)align_cornersFalse是必须的取 True 会在多次上采样后累积半像素偏移小目标边界对不齐标注。相加而不是 concat是因为 concat 之后接卷积的参数量随分支数线性增长三路以上的融合在 24GB 卡上很容易吃满。如果某个场景确实需要保留分支差异可以改成 concat 后接分组卷积代价是多一次显存拷贝。3.3 空洞卷积换感受野不换分辨率道路、河流的连续性依赖长距离上下文靠堆池化层换感受野会牺牲分辨率。用不同膨胀率的并行分支可以在保持特征图尺寸的前提下扩大感受野class ASPP(nn.Module): def __init__(self, in_ch, out_ch256, rates(6, 12, 18)): super().__init__() self.branches nn.ModuleList([ nn.Conv2d(in_ch, out_ch, 3, paddingr, dilationr, biasFalse) for r in rates ]) self.head nn.Sequential( nn.Conv2d(out_ch * len(rates), out_ch, 1, biasFalse), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), ) def forward(self, x): return self.head(torch.cat([b(x) for b in self.branches], dim1))paddingr与dilationr配对保证输出尺寸不变。膨胀率的选择和输入分辨率强相关在 stride 16 的特征图上用 18 的膨胀率等效感受野已经覆盖几百个原始像素对 8 米分辨率的云雪水提取够用但对付 0.8 米分辨率下的飞机关键点就偏大容易把小目标糊掉。这类模块建议只在最后一级特征上加浅层加会破坏纹理细节。3.4 剪枝、量化、蒸馏该怎么选材料里提到同一结构下通用网络压缩后效率能提升几十到上百倍。这个数字不是靠单一手段取得的通常是「通道剪枝 量化 输入分辨率裁剪」叠加的结果。选之前先看下表手段压缩对象典型收益精度影响落地难点通道剪枝卷积核数量2–5 倍需 finetune 恢复剪完结构不规则部署端要重写INT8 量化权重与激活显存与带宽 2–4 倍分割边界略降校准集必须覆盖多源数据知识蒸馏训练策略学生模型可显著变小受学生容量限制需要训好的教师网络输入降采样推理分辨率像素量降 4 倍小目标直接消失舰船、飞机场景不可用通道剪枝的判断依据常用 BN 层的缩放因子把γ接近 0 的通道连同对应卷积核一起删掉import torch.nn as nn import torch.nn.utils.prune as prune def prune_bn_channels(model, amount0.3): 按 BN 权重 L1 值做非结构化稀疏得到候选剪枝掩码 for m in model.modules(): if isinstance(m, nn.BatchNorm2d): prune.l1_unstructured(m, nameweight, amountamount) return model这段代码只产生稀疏掩码真正的部署收益来自「掩码 → 重写网络结构 → finetune 若干 epoch」。剪枝比例不要一次拉满每次 10%–20%剪完 finetune 到精度恢复再剪下一轮。要特别注意如果某个场景模型本身训练样本只有几百张剪到 50% 以上很难恢复精度这时候量化比剪枝更划算。INT8 量化还需要一份有代表性的校准集覆盖不同传感器、不同季节的数据只用夏季影像校准冬季场景掉点会很厉害。4. GPU 集群上的分布式训练DDP 骨架、显存策略与平台选型4.1 单卡先撞到的三堵墙材料里的算力描述是「8000 块 GPU 计算卡、10 个 GPU 集群、最大规模连接 800 块 GPU」这个量级的集群不是炫技而是被数据规模逼出来的。单卡训练遥感分割先撞三堵墙显存墙1024×1024×8 波段的输入加多尺度融合头batch size 上到 4 就见顶时间墙一个 epoch 扫几万张瓦片按天算数据墙CPU 解码 16bit 多波段 TIFF 的吞吐跟不上 GPU 的算力num_workers开到 16 都喂不饱。判断值不值得上分布式可以先用小规模估算单卡吞吐是 A 张/秒数据集有 N 张目标训练周期 T 天需要的卡数就是N / (A * 86400 * T)。如果算出来是 1.5 张卡先优化数据管线和混合精度别急着上多机。4.2 PyTorch DDP 的最小可用骨架import torch import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDP from torch.utils.data.distributed import DistributedSampler def setup(rank, world_size): dist.init_process_group(nccl, rankrank, world_sizeworld_size) torch.cuda.set_device(rank) def train(rank, world_size, dataset, model, loader_fn, epochs30): setup(rank, world_size) sampler DistributedSampler(dataset, num_replicasworld_size, rankrank, shuffleTrue, drop_lastTrue) loader torch.utils.data.DataLoader(dataset, batch_size8, samplersampler, num_workers8, pin_memoryTrue) model DDP(model.to(rank), device_ids[rank]) # 学习率随全局 batch 线性缩放配合 warmup 更稳 opt torch.optim.SGD(model.parameters(), lr0.01 * world_size, momentum0.9, weight_decay1e-4) for ep in range(epochs): sampler.set_epoch(ep) # 每个 epoch 重新切分避免样本顺序固定 for img, mask in loader: img img.to(rank, non_blockingTrue) mask mask.to(rank, non_blockingTrue) loss loader_fn(model(img), mask) opt.zero_grad(set_to_noneTrue) loss.backward() opt.step() dist.destroy_process_group()nccl是 GPU 间通信后端多机多卡场景比gloo快一个数量级。sampler.set_epoch(ep)容易被漏掉漏了之后每个 epoch 各进程拿到的样本切分完全相同等于没有打散数据收敛曲线会异常平滑但精度偏低。学习率按world_size线性缩放只是起点全局 batch 超过 256 之后建议改成平方根缩放并加 500 步 warmup。drop_lastTrue是为了避免最后一个不完整 batch 让各进程步数不一致DDP 在梯度同步时会卡住。4.3 显存不够时的四张牌手段省下什么代价建议开启条件混合精度 AMP激活显存约 30%–50%需要 loss scaling显存低于 24GB 时的默认选项梯度累积等效放大 batch不增显存单步耗时变长小卡想跑大 batch梯度检查点激活显存大幅下降反向重算慢 20%–30%骨干很深的分割网络channels_last卷积访存效率提升需要硬件支持现代 GPU 基本通用混合精度加梯度累积的组合写法如下scaler torch.amp.GradScaler(cuda) accum_steps 4 # 等效 batch 8 * world_size * 4 for i, (img, mask) in enumerate(loader): with torch.autocast(cuda, dtypetorch.float16): loss criterion(model(img), mask) / accum_steps scaler.scale(loss).backward() # 先 scale 再反传防止 fp16 下溢 if (i 1) % accum_steps 0: scaler.step(opt) scaler.update() opt.zero_grad(set_to_noneTrue)loss要先除以accum_steps否则累积后的梯度是正常值的 4 倍等效学习率被放大。scaler.update()负责动态调整缩放系数不要在累积过程中调用。梯度检查点用法是model(torch.utils.checkpoint.checkpoint(block, x))适合放在骨干最深的几个 stage 上全网络开会让训练速度掉太多。4.4 开源框架与自建平台的取舍边界材料里把几类平台的差异列得很直接整理成表看得更清楚平台类型单机多卡分布式训练灵活性主要代价伯克利系早期框架有限支持不支持中撑不住大数据量训练Facebook 系早期框架支持较弱高要写底层代码生产力低Google 系框架支持支持中显存占用高性能欠佳自建平台支持支持通信开销低面向超深网络与复杂建模自研与长期维护成本这里的判断标准不是「开源行不行」而是三件事分布式通信在大规模下的效率损失有多大、最新算法成果多久能拿到、模型规模继续膨胀时框架能不能跟上。团队规模小、模型在几十层量级用主流开源框架加 DDP 完全够一旦走到几百层、上千张卡、还要做模型压缩与定制算子自建平台的价值才体现出来。选型时建议先做一次两周的压测同一模型、同一数据、同样卡数对比吞吐和显存峰值再决定要不要投入自研。5. 滑窗推理拼接、吞吐量核算与 GIS 叠加验证推理阶段的拼接是个容易翻车的地方。训练时按瓦片切推理时如果简单地把各块预测贴回去重叠区会被覆盖而不是融合边界会出现明显的方格接缝。import numpy as np def merge_logits(canvas, weight, logits, x0, y0): 把单块 logits 累加到画布上同时累加计数权重 h, w logits.shape[-2:] canvas[:, y0:y0 h, x0:x0 w] logits weight[y0:y0 h, x0:x0 w] 1.0 return canvas, weight prob canvas / np.maximum(weight, 1e-6) # 重叠区取平均消除方格接缝weight用同一份计数累加边缘瓦片因裁剪补零产生的低置信度区域自然被邻块摊平。如果某类目标对边缘敏感可以把权重换成距离变换权重让块中心权重高、边缘权重低。再往下是耗时核算。材料里给出的几组实测数据可以直接用来反推硬件预算任务影像尺寸像素量耗时等效吞吐路网提取12900×80001.03 亿40 s2.6 M px/s舰船检测18000×180003.24 亿180 s1.8 M px/s变化检测双时相15000×380005.7 亿600 s0.95 M px/s飞机及关键点4873×1439700 万4 s1.75 M px/s把像素量和耗时相除会发现单卡吞吐大致落在 1–3 M px/s 区间变化检测因为要跑两期影像加变化判别吞吐只有单时相任务的一半左右。拿到这个基准值就能估算集群规模如果业务要求一天处理 200 景 1.8 万×1.8 万的多光谱数据一天 86400 秒、单卡吞吐 1.8 M px/s单卡理论处理量约 3.2 万景像素当量换算下来十几张卡就能覆盖剩下的瓶颈在数据落盘和结果后处理而不是推理本身。指标侧分割用 IoU 和 F1目标检测用小目标场景下的 mAP0.5 加 NMS 后的重复框率变化检测单独看漏检率因为漏掉一处真实变化比多报一处代价高得多。最后一步是把栅格结果叠到 GIS 里做面积统计材料里「基于用地类型进行统计匹配空间位置分类统计像元面积」说的就是这件事def area_stat(pred_mask, pixel_size_m): pixel_size_m 为地面采样距离1.5 米分辨率下单个像元面积为 2.25 平方米 px_area pixel_size_m ** 2 cls, cnt np.unique(pred_mask, return_countsTrue) return {int(c): int(n) * px_area for c, n in zip(cls, cnt)}分辨率一定要用重采样后的值很多项目直接拿原图分辨率算面积做完 0.5 米到 1 米的重采样后统计结果会差四倍。落地上建议先拿一景人工勾绘过的区域做对照把该场景的剪枝比例和推理分辨率定下来再推到整条产线比一次性全量跑完再返工省事得多。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。