简介一份面向遥感与计算机视觉学习者的语义分割开发教程系统梳理从技术原理到实际应用的关键环节。文档聚焦Swin TransformerUNet、Samba等前沿编码结构对比遥感影像与自然场景分割在数据特点、技术挑战及评价指标上的差异并给出基于Pytorch-Unet的多类别分割实现路径此外还综述了高分辨率影像分割的传统与现代方法能帮助读者快速形成完整技术脉络。全包仅1个docx文件大小16KB内容集中、便于查阅。目前已有225人学习下载。借助这份教程读者可掌握主流分割模型的架构思路、全局特征提取优势以及土地覆盖分类、灾害评估、农业监测等典型场景的落地要点为后续研究或项目选型提供直接参考。1. 遥感图像语义分割开发先分清“看得到”和“分得清”拿到一张 0.5 米分辨率的航空影像人和车的轮廓模糊成几个像素点屋顶和沥青路面在光谱上几乎长一个样——这就是遥感图像语义分割和普通街景分割最大的不同。遥感图像语义分割开发教程这件事核心不是把某个分割网络跑通而是把「大尺寸、多波段、类别极度不均衡」这三座山搬开让模型在建筑物提取、耕地监测、水体识别这些真实任务上输出能直接用的矢量底图。本文面向两类人刚接触遥感、手里有数据和 GPU 但不知道从哪下手的算法工程师以及做 GIS 二次开发、想把深度学习塞进现有生产流程的开发者。我会按数据 → 模型 → 训练 → 排错 → 提点的顺序给你一条能照抄的落地路径也把那些不跑一遍根本发现不了的坑提前标出来。2. 拿到遥感影像先别急着炼丹标注格式、数据集划分与预处理2.1 标注格式为什么遥感项目大多选 RLE 而不是多边形遥感图像标注和自然图像标注有本质区别。自然图像里一个物体边缘清晰标一个多边形框很自然但遥感影像里一栋建筑的房顶可能有十来个角一片水域的边缘是锯齿状的如果用多边形标注一个 1024×1024 的切片里可能有上百个多边形GeoJSON 文件会膨胀到几十兆训练时每次解析都是一次性能灾难。所以遥感语义分割项目里业界最常见的做法是直接用 RLERun-Length Encoding或单通道 PNG 索引图做标注前者是 COCO 的默认格式后者是 Kaggle 类比赛的主流格式。从开发角度讲我更推荐用单通道 PNG每个像素存一个类别 ID0 是背景1 是建筑2 是道路……这样做的好处有两个。第一可视化方便随便找个调色板就能把标注叠在原图上检查第二训练时不用解析 JSON直接把 PNG 读成 numpy 数组喂给 loss数据加载路径短一半。RLE 的优势在于压缩率高适合存储和传输但工程上多一道编解码工序得不偿失。转换的时候有一个必踩的坑PNG 的类别 ID 不能乱设。比如你标了 4 类背景是 0那 PNG 里绝对不能出现 255 这种值否则交叉熵损失会把 255 当成第 256 个类别直接报维度错误或者梯度爆炸。我一般会在转换脚本里加一段校验统计标注数组的 unique 值发现超出类别范围的直接报错退出省得训练到一半才翻车。2.2 大图切片与波段处理把 1024×1024 的瓦片喂给模型遥感影像很少是一张 512×512 的小图通常是一景几十 GB 的 TIF宽高都是几万像素。这种图没法直接进模型常规操作是切瓦片。切瓦片要保证两点训练样本的分布和整景影像一致以及切割后的瓦片之间有足够重叠来避免物体被切断。我习惯的切片参数是 1024×1024、重叠 256 像素。重叠的意义在于一栋建筑如果正好落在切片边界上朝左的样本能看到它的一半朝右的样本能看到另一半模型在训练时见过的形态就完整了。推理时同样要保留这部分重叠最后用拼接算法把重叠区融合掉这个细节放到第 5 章专门讲。波段处理是另一个容易翻车的地方。光学遥感影像通常有 4 个波段R、G、B、NIR。很多教程直接拿前三个波段当 RGB 喂给在 ImageNet 上预训练过的 backbone这不算错但因为丢弃了 NIR模型对植被和水体的区分能力至少掉 35 个点的 mIoU。如果预训练模型要求三通道输入一个折中方案是把 NIR 波段的信息融进 RGB比如用 NDVI 指数图作为额外通道接在输入后面或者更简单的把 R、G、B、NIR 四通道的前三个换成经过直方图匹配的 RGB 组合。这里没有标准答案我通常的做法是先用三通道跑一版基线再用四通道或者加 NDVI 跑一版对比差异不大就选计算快的方案。预处理这里还有一个隐藏坑影像的位深。很多卫星影像原始是 16 位整型像素值范围 065535而预训练模型期望的输入是 0255 的 8 位图。直接把 16 位图除以 256 并不总有效因为原始影像的直方图通常集中在暗部线性压缩会把对比度压没。常规做法是取 2% 和 98% 分位数做线性拉伸把分位数之外的值截断掉再映射到 0255。这个操作虽然朴素但比你随便挑个 max 值做归一化靠谱得多。2.3 数据集划分按区域拆别按文件随机拆数据集划分是遥感语义分割项目里最容易被低估的一步也是最容易让模型精度虚高的地方。如果你按瓦片文件随机划分训练集和验证集同一栋建筑的不同切片可能同时出现在两边——因为这个建筑跨了切片边界或者两张瓦片有 256 像素的重叠区。验证集里塞进了一堆训练时见过的像素mIoU 虚高 24 个点是常事等模型部署到新区域时原形毕露。正确做法是按空间区域划分。假设你有 5 景影像把其中 3 景整体切成瓦片做训练1 景做验证1 景留作测试景与景之间不共享任何像素。如果只有一景大影像就按经纬度网格把影像分成左半和右半左边训练、右边验证。数据量不够时宁可在训练集上做更强的数据增强也不要让验证集泄漏。用代码描述一下这个划分逻辑import json from shapely.geometry import Polygon train_regions [ Polygon([(120.0, 30.0), (120.5, 30.0), (120.5, 30.5), (120.0, 30.5)]), Polygon([(121.0, 31.0), (121.5, 31.0), (121.5, 31.5), (121.0, 31.5)]), ] val_regions [ Polygon([(122.0, 32.0), (122.5, 32.0), (122.5, 32.5), (122.0, 32.5)]), ] def assign_tile_to_split(tile_polygon): for region in val_regions: if tile_polygon.intersects(region): return val for region in train_regions: if tile_polygon.intersects(region): return train return ignore这段代码的思路是每个瓦片都有自己的地理边界用 intersects 判断它落在哪个区域里落在验证区域内就进验证集。注意我用的是 intersects 而不是 contains因为瓦片边界可能覆盖区域边界的边缘只要相交就进对应集合保证验证集覆盖完整。你在实际项目里可以根据行政区边界或者经纬度网格来定义这些多边形这就是「按区域划分」的最小实现。3. 从 U-Net 到 DeepLabV3遥感语义分割的模型选型与结构取舍3.1 语义分割算法选型U-Net 是基线DeepLabV3 是主力遥感语义分割的模型池子里能打的就那几个U-Net、DeepLabV3、PSPNet最近还有 SegFormer 和 ConvNeXt 系列。我的建议是别追新先把 U-Net 跑通作为基线再用 DeepLabV3 提精度。原因很实在——U-Net 结构简单skip connection 让浅层细节直接传到深层在小数据集上不容易过拟合调试成本最低DeepLabV3 的空洞卷积金字塔能捕获多尺度上下文对遥感影像里「同一类物体尺度差异极大」的问题更友好。从结构上看DeepLabV3 的 Encoder-Decoder 结构天然契合遥感任务的重心编码器用空洞卷积扩大感受野在不降低特征图分辨率的前提下看到更大范围解码器逐步恢复空间分辨率把建筑物边缘的细节找回。相比 PSPNet 的金字塔池化DeepLabV3 在多尺度特征融合上多一个显式的解码器边缘精细度有明显提升。U-Net 的优势则是天然的多尺度融合——每一步上采样都拼接对应层的特征不需要额外设计。这里有一个常见的认知误区以为语义分割算法的精度完全由网络结构决定。实际上在遥感数据上backbone 的预训练权重对最终精度的影响往往比结构本身更大。我做过一组对比实验同样的 DeepLabV3 结构ResNet-50 和 ResNet-101 的差异大概有 2 个 mIoU但换一个在更大规模数据上预训练过的 EfficientNet能再涨 23 个点。所以选模型的优先级是先选一个预训练权重足够强的 backbone再考虑结构层面的改动。各模型对比可以参考这个表模型特点遥感适用场景显存占用U-Netskip connection 保留细节小数据集、首次做基线低DeepLabV3空洞空间金字塔池化多尺度地物并存的中大型数据集中PSPNet金字塔池化捕捉全局上下文大范围场景如整景影像分类中高SegFormer无卷积、分层 Transformer超大影像、需要高效推理高3.2 Backbone 与编码器ResNet 还是 EfficientNet 要看显存Backbone 的选择决定了两件事特征表达能力和显存占用。遥感影像通常比自然图像大输入分辨率 1024×1024 是常态backbone 的显存开销会被放大。如果显卡是 11G 的 RTX 2080Ti跑 ResNet-50 编码器的 DeepLabV3 没有问题但换成 ResNet-101 就可能要减 batch size。我见过不少人在这一步翻车张嘴就要 ResNet-152 做编码器结果 batch size 降到 2 还 OOM训练一天下来 mIoU 没比基线高多少。显存不够时我的经验是按这个顺序做减法先减 batch size再减输入分辨率最后才是换轻量 backbone。因为 batch size 影响的是梯度稳定性分辨率影响的是细节保留而 backbone 直接决定模型表达能力的上限。如果你只有一个 8G 显存的卡干脆用 ResNet-18 或 MobilenetV3 做编码器输入分辨率保持 1024效果通常比 ResNet-50 配 512 分辨率更好。遥感任务里细小的地物太多512 输入连一个停车位都看不清再强的 backbone 也白搭。另一个值得一试的方案是使用已经集成在分割库里的 EfficientNet 编码器。EfficientNet-B3 参数量只有 ResNet-50 的七成精度不差显存占用还低。但要注意EfficientNet 的预训练权重对遥感影像的适配性没有 ResNet 好因为 ResNet 在 ImageNet 上学到的纹理特征更通用。我的习惯是先用 ResNet-50 跑通再替换成 EfficientNet 微调比较两者的实际精算收益而不是一开始就在 backbone 选择上纠结。3.3 多尺度与空洞卷积小目标为什么需要更大的感受野遥感影像里一个典型的场景一大片农田里有一栋孤立的房子房子在影像里可能只占 50×50 像素。普通卷积网络的感受野如果不够大模型只能看到房子周围都是农田很容易把房子也归类成农田。这就是需要多尺度信息的原因——让模型在判断一个像素时不仅看它周围几个像素还要看到更广的上下文。DeepLabV3 解决这个问题的方案是空洞空间金字塔池化ASPP。它用多个不同膨胀率的空洞卷积并行处理特征图膨胀率分别为 6、12、18相当于用不同大小的网络在多个尺度上扫描同一个区域。加上全局平均池化那一支模型既能捕捉细碎的局部特征也能感知全局语义。但空洞卷积有一个隐蔽的副作用gridding 效应。高膨胀率的空洞卷积采样点之间是稀疏的连续堆叠多层后特征图某些位置永远采不到有效信息形成网格状噪声。这在遥感影像上表现为建筑物边缘出现规则的网格状误分类。解决方案有两个一是用 DeepLabV3 里自带的解码器结构在最后融合时用 3×3 卷积消除网格痕迹二是训练时加入 dilation 率的随机扰动让模型适应不同膨胀率。第二个方案实现起来麻烦我一般先用第一个方案如果网格痕迹明显再考虑替换高膨胀率层为普通卷积加池化。多尺度还有一条更朴素的路输入多尺度预测。训练时把原图和 0.5 倍、1.5 倍缩放的图分别送进模型预测结果按权重融合。这招在小目标占比高的数据集上效果好但代价是推理耗时涨到原来的三倍。实际工程里我通常只在离线评估的时候用多尺度融合刷指标线上推理还是用单尺度毕竟生产环境对延迟的要求摆在那里。4. 用 PyTorch 从零跑通遥感语义分割完整训练脚本与关键参数4.1 Dataset 类实现切片读取与即时增强遥感分割的数据加载核心痛点在于影像太大、内存放不下。解决方案是「懒加载 即时切片」Dataset 里只存每个瓦片的路径和坐标信息在getitem被调用时才读取对应区域。下面给出一个可运行的示例把关键逻辑拆开讲清楚。import numpy as np import rasterio import torch from torch.utils.data import Dataset class RemoteSensingDataset(Dataset): def __init__(self, image_path, label_path, tile_size512, stride256, transformsNone): self.image_path image_path self.label_path label_path self.tile_size tile_size self.stride stride self.transforms transforms # 读取元信息不加载像素数据 with rasterio.open(image_path) as src: self.height src.height self.width src.width with rasterio.open(label_path) as src: self.label_height src.height self.label_width src.width # 生成所有切片左上角坐标 self.tiles [] for y in range(0, self.height - tile_size 1, stride): for x in range(0, self.width - tile_size 1, stride): self.tiles.append((y, x)) def __len__(self): return len(self.tiles) def __getitem__(self, idx): y, x self.tiles[idx] with rasterio.open(self.image_path) as src: image src.read([1, 2, 3], window((y, y self.tile_size), (x, x self.tile_size))) with rasterio.open(self.label_path) as src: label src.read(1, window((y, y self.tile_size), (x, x self.tile_size))) image image.transpose(1, 2, 0) # C, H, W - H, W, C if self.transforms: image, label self.transforms(image, label) image torch.from_numpy(image.transpose(2, 0, 1)).float() / 255.0 label torch.from_numpy(label.astype(np.int64)) return image, label这段代码里有三个关键设计。第一个是 window 参数rasterio 支持直接读取感兴趣区域不会把整张影像加载进内存这是遥感影像训练的基础能力。第二个是 stride 参数切片步长小于切片尺寸时会产生重叠样本等于免费的随机裁剪增强步长大于切片尺寸则会让训练样本之间有间隙可能漏掉部分地物。第三个是标签读取读标签时也必须用 window保证标签和图像的空间范围完全一致这一步错位是最常见的训练事故来源。transforms 参数的类型建议用 albumentations 的 Compose它有专门的双输入版本同步处理图像和掩膜。注意别用 torchvision 的 transforms那个不支持掩膜变换训练时会出现 图像旋转了但标注没跟着转 的诡异错误。4.2 损失函数与评估指标先跑通交叉熵再谈 Lovász损失函数的选择直接决定训练的收敛行为。交叉熵是默认基线它的优点是梯度稳定、实现简单对于类别均衡的数据集效果很好。但遥感数据的类别分布几乎永远不均衡一幅影像里植被可能占 40%建筑占 15%道路占 8%水体占 5%剩下的都是背景。交叉熵在类别不均衡时会偏向样本多的类别导致道路、水体这类小类别永远学不好。解决不均衡的常规思路是给损失函数加类别权重。权重计算方式一般用中位数频率平衡某个类别的权重等于全局中位数频率除以该类别的出现频率。这样样本少的类别获得更高的权重梯度更新时被放大。代码实现如下def median_frequency_balancing(label, num_classes): frequencies np.zeros(num_classes) for c in range(num_classes): frequencies[c] np.sum(label c) # 计算中位数频率权重 median np.median(frequencies[frequencies 0]) weights np.zeros(num_classes) for c in range(num_classes): if frequencies[c] 0: weights[c] median / frequencies[c] else: # 不存在该类时设零避免影响总损失 weights[c] 0 return torch.from_numpy(weights.astype(np.float32))这个函数统计每个类别在训练集标签中出现的总像素数然后计算权重数组。用加权交叉熵跑通基线之后再考虑 Lovász Softmax。Lovász 损失直接优化 IoU 相关的代理损失对类别不均衡数据有更好的指标表现但它是个凸函数之外的黑匣子训练不稳定需要调低学习率。我的习惯是第一轮训练用加权交叉熵把模型跑到 mIoU 50 左右然后冻结骨干网络用 Lovász 损失微调最后一两层。评估指标方面遥感分割业界主看三个mIoU类别平均交并比、F1 分数和整体像素精度。mIoU 是最硬的指标因为它在所有类别上求平均不会被大类别主导F1 适合报告建筑物这类单类别提取任务像素精度只有参考价值99% 的像素精度可能意味着模型把建筑整个漏掉了。4.3 训练参数速查与学习率策略训练参数是玄学最多的部分这里写一组我在大多数遥感分割项目里验证过可用的起点参数参数名推荐值说明输入分辨率512×512 或 1024×1024显存紧张选 512精度优先选 1024Batch size816单卡小于 4 时建议启用梯度累积初始学习率1e-3AdamW换用 SGD 动量时调整为 1e-2 并搭配 Poly 衰减学习率调度Poly 衰减power0.9比 StepLR 更适合分割任务训练轮数60120 epoch遥感数据增强充分时100 epoch 才收敛完数据增强RandomCrop Flip Rotate90不推荐 ColorJitter遥感影像颜色变化意义不大学习率调度是这里最容易犯错的地方。StepLR 按固定步长衰减学习率在分割任务上经常导致收敛变慢或者陷入局部最优。Poly 衰减是分割界的事实标准每个 iteration 按公式lr lr_init * (1 - iter/total_iter)**power衰减power 取 0.9让模型在训练后期自然进入微调状态。实现时直接用 PyTorch 的 LambdaLR 传入这个公式即可。还有一个细节遥感影像数据增强不需要 ColorJitter。因为不同时相遥感影像的颜色差异来自光照和大气条件神经网络应当对此不敏感而不是靠增强去拟合这种差异。用 RandomCrop 从大图上随机抠一块配合随机翻转和旋转就足够覆盖绝大多数变化。5. 遥感语义分割开发避坑指南标注错位、类别不均衡与推理拼接的真实故障5.1 现象预测结果整体偏移建筑边缘和真实位置差了几十个像素这个故障很诡异训练过程看起来正常loss 在降mIoU 在升但把预测结果叠加到原图上所有物体边界都错位了。原因几乎总是出在数据预处理——影像 TIF 和标注 TIF 的坐标系或分辨率不一致。比如影像原始分辨率是 0.5 米标注在 ArcGIS 里导出时被重采样成了 1 米那标注天然就偏了半个物体。这个错位不会影响训练 loss因为模型学到的是「带偏移」的映射关系但推理时就露出真面目。解决方法是训练前做严格的对齐检查。写一个脚本在影像和标注上分别取五个同名地物点检查它们的像素坐标是否一致。坐标不一致时先检查投影坐标系再用 gdal.Warp 把标注重采样到和影像相同的分辨率与坐标系。这个检查必须放在切瓦片之前因为切完之后瓦片数量太多已经没法手工验证了。5.2 现象道路和建筑完全学不出来验证集的 mIoU 卡在 40 上不去类别不均衡的典型表现是背景和植被的 IoU 很高建筑和道路的 IoU 几乎为零。模型把所有像素都预测成背景类别就能拿到一个还不错的 loss因为背景占比太高整体损失被它主导。你从 loss 曲线看不出异常只有逐类别看 IoU 才能发现问题。解决方案分两步走。第一步是前面提到的类别权重交叉熵把少样本类别的梯度放大第二步更粗暴——做样本重采样。具体做法是统计每个瓦片里建筑类别的像素占比如果低于某个阈值比如 2%就以 30% 的概率丢弃这张瓦片。这样可以人为提高建筑占比高的样本在训练集中的权重。两个方案配合使用通常能把道路和建筑的 IoU 拉高 10 到 15 个点。5.3 现象推理拼接出的整景图上出现整齐的网格状接缝大图推理的拼接算法如果只做简单拼接瓦片边缘会因为感受野不足产生预测不一致两条相邻瓦片的分界线清晰可见。解决方法是重叠推理加融合推理时每个瓦片向外扩一圈上下文比如扩展 128 像素预测完成后只取中间区域的结果边缘区域丢弃这样每次预测都有完整的上下文信息。如果想要更平滑的过渡可以采用加权融合策略重叠区域的预测结果按距离中心的远近分配权重靠近边缘的权重低靠近中心的权重高。下面是一个最小实现思路def fused_inference(model, big_image, tile_size512, overlap128): h, w big_image.shape[:2] result np.zeros((h, w, num_classes), dtypenp.float32) weight_map np.zeros((h, w, 1), dtypenp.float32) for y in range(0, h, tile_size - overlap): for x in range(0, w, tile_size - overlap): y_end min(y tile_size, h) x_end min(x tile_size, w) tile big_image[y:y_end, x:x_end] pred model(tile) # shape: (num_classes, h, w) # 生成三角权重中心为1边缘为0 temp np.ones((h_tile, w_tile)) weight np.minimum(np.minimum(r, r_max - r), np.minimum(c, c_max - c)) weight np.clip(weight / (tile_size // 4), 0, 1) result[y:y_end, x:x_end] pred * weight weight_map[y:y_end, x:x_end] weight result / weight_map return result.argmax(axis2)这段代码里有几个关键设计。第一个是步长取 tile_size - overlap保证相邻瓦片有重叠区域第二个是生成了三角形状的权重让中心区域的预测结果占据主导第三步是除以累加的权重图本质是做了一个归一化融合。这种方法能有效消除接缝只是推理时间会增加约 30% 到 50%需要提前规划。5.4 现象用官方预训练权重微调训练 loss 一开始就震荡最后也不收敛这不是参数问题几乎可以确定是输入数据分布和预训练权重期望的分布对不上。ImageNet 预训练权重期望 01 范围的 RGB 输入且做过 ImageNet 均值和标准差的归一化。如果你直接输入原始 DN 值比如 010000 的辐射亮度值梯度必然爆炸。解决办法有两个。严格做法是在 Dataset 里做归一化统计训练集的均值和标准差按 ImageNet 的方式标准化偷懒做法是用官方给的 transform 参数初始化但输入之前先把原始值除以 10000 再乘 255。注意这个步骤必须和推理时保持一致否则训练和推理的数据分布不同模型相当于被「骗」了。我见过不少项目训练时精度不错上线一跑预测结果全是噪声排查半天发现是上线推理代码里忘了除以 10000。这类问题的排查口诀是训练怎么预处理推理就怎么预处理一个标点都不能差。另一个被忽视的收敛毒药是 BatchNorm 在遥感大图上的失效。BN 层依赖 batch 内统计量遥感影像色彩模式变化大batch size 小的时候 BN 统计量震荡剧烈。如果模型里有 BN 层batch size 低于 4 时一定要开梯度累积模拟大 batch或者换成 GroupNorm 替代 BN。6. 把模型精度吃干榨净CRF 后处理与切片推理的内存优化技巧训练和推理跑通之后还能从两个角度再榨一点精度后处理优化和推理策略优化。CRF条件随机场是分割任务里最经典的后处理手段它的原理朴素但有效——把像素之间的空间关系编码成能量函数让相邻且颜色相似的像素倾向于归到同一类别。遥感影像里一个建筑物屋顶的颜色基本一致CRF 能把这些碎块修正成完整的屋面板边缘也更加规整。使用 CRF 时有一个技巧不要把概率图整张丢给 CRF而是先用 0.5 的阈值做一次硬分类再仅在置信度低的位置使用 CRF 修正。因为 CRF 的处理速度很慢全图处理一张 1024×1024 的图要几百毫秒只修低置信度区域可以把时间压缩到几十毫秒。如果部署环境对延迟极其敏感CRF 这个后处理可以直接砍掉因为它提升的 mIoU 通常不会超过 1.5 个点。内存优化方面大影像推理时显存是主要瓶颈。假设输入图是 10000×10000 像素按 512 的瓦片推理显存占用完全没问题但问题是推理时间太长10000×10000 意味着大约 400 张瓦片一次推理要处理几百次。一个常见的加速做法是把预测结果分区域并行把推理函数包装成多进程任务每张瓦片丢给一个进程处理。但要注意 PyTorch 模型的 GPU 推理本身就在单卡上串行多进程加速只在 CPU 推理时有意义。我个人的习惯是推理时把输入分辨率降到 768步长设 512重叠 256。这样做是因为遥感大图推理的瓶颈通常在数据传输和 Python 循环上而不是模型本身。分辨率降到 768 后瓦片数量减少约 40%mIoU 的损失可以忽略不计。偶尔遇到特别重要的项目我才会用 TTA测试时增强——把每张瓦片翻转四次分别推理取平均精度能涨 12 个点代价是推理时间变成五倍。这个技术要慎用只在最终评估时开。说一个踩过的坑CRF 后处理经常被拿来刷测试集的 mIoU但如果你的目标是把模型部署成生产服务CRF 不能进线上推理链路否则每个请求的 RT 会暴涨。我的做法是训练和验证时用 CRF 修正后的结果计算 mIoU确认模型本身已经足够好线上版本不放 CRF用普通拼接推理接受 1 个点以内的精度损失换取响应速度。这样既不牺牲开发阶段的评估效果也不让线上服务变得不可用。最后整理一下开发阶段最重要的几个习惯数据集划分按区域切割验证指标逐类别看而不是只看 mIoU训练和推理的预处理代码保持完全一致。这些都是踩过坑之后才刻进肌肉记忆的东西。遥感影像语义分割不同于一般的 CV 任务它的数据形态决定了 70% 的问题出在数据侧而不是模型侧先把数据管好模型自然会给你回报。希望这些经验和代码能帮你在遥感图像语义分割开发这条路上少走几次弯路。本文还有配套的精品资源点击获取