简介:基于小波变换与平行注意力的多源遥感图像分类源码,面向遥感图像处理研究人员、深度学习开发者及论文复现者,提供一套完整可运行的分类实现。资源源自北京航空航天大学学报2023年论文,可用于土地利用分类、环境监测、灾害预警等场景。包体共56个文件,压缩包约2.97MB,包含19个Python源文件(模型定义、训练测试、数据可视化等)、31个编译缓存文件、YAML环境配置文件与依赖清单,以及论文PDF和说明文档,结构清晰便于部署复现。已有159人学习下载。源码集成了FusatNet、AsyFFNet、DFINet等多个网络模型,配合小波变换工具库,支持多源遥感数据融合训练。通过平行注意力机制增强特征选择能力,让分类更精准。配套环境配置和报告脚本,可帮助读者快速搭建实验环境,理解论文核心模块,为后续改进和应用提供坚实基础。
1. 多源遥感图像分类,为什么不能只靠加深网络
光学影像和合成孔径雷达(SAR)影像构成的多源遥感数据,在实际地物分类任务里几乎是标配:光学影像有丰富的光谱和纹理信息,SAR 却能在阴雨和多云条件下稳定成像。两边的优势互补非常诱人,但直接把它们拼在一起送进卷积网络,效果往往差得离谱。原因在于光学和 SAR 的成像机理完全不同,像素值分布、噪声特性、空间分辨率都不一致,粗暴拼接等于逼着网络自己学一套隐式的对齐和权重分配,这对数据量和训练时间都是巨大负担。
小波变换在这里的价值不是去噪这么简单。它能把图像分解成低频主体和高频细节两个层次,相当于把不同尺度的信息先拆开,再让网络分别处理,避免深层下采样把微小的地物细节整个抹掉。平行注意力机制解决的是另一个问题:多源特征融合时,网络不知道该信光学影像的哪个通道、也不知道 SAR 影像的哪个空间位置更可靠,平行注意力让通道和空间两个维度的筛选同时进行,互为补充。
这篇文章面向正在做遥感分类课题的研究生,以及需要在遥感影像上落地分类模型的算法工程。全文会围绕「小波变换能拆出什么、平行注意力怎么搭、两者怎么接进分类网络、训练和评估时什么指标才真正管事」这条线展开,所有做法都会落到可运行的源码层面,参数和踩坑点一并给出。
2. 小波变换在遥感图像里的角色:从去噪到特征增强
小波变换在遥感领域的应用,最常见的说法是去噪,但在分类网络里它的作用远不止于此。多源遥感图像分类面对的三个现实问题——SAR 相干斑噪声、光学与 SAR 之间的分布差异、小目标地物在下采样中丢失——恰好都能和小波分解的特性对应上。
2.1 多源遥感图像的三个现实问题
第一个问题是 SAR 影像的相干斑噪声。SAR 图像天生带颗粒状的乘性噪声,直接丢给卷积网络,网络前几层的大部分卷积核都在拟合噪声纹理,而不是地物结构。用高斯滤波能平滑掉一部分噪声,但边缘也一起模糊了,属于杀敌一千自损八百。
第二个问题是两源图像的分辨率和灰度分布不在一个数量级。光学影像通常是 8bit 或 16bit 的多波段数据,SAR 影像即使是同一区域,像素值反映的是后向散射系数。把它们归一化到 0-1 后拼接,网络需要自行学习两个分布之间的映射,这个映射本质上是非线性的,浅层网络根本拟合不动。
第三个问题是小目标检测与分类。遥感图像里船只、小建筑物、零星树木这类地物可能只占几十个像素,经过三层 stride=2 的下采样,特征图上的响应几乎被抹平,分类头根本看不到这些目标的存在证据。
2.2 小波分解的子带结构与参数选择
二维离散小波变换(DWT)一次分解产生四个子带:LL(低频近似)、LH(水平高频)、HL(垂直高频)、HH(对角高频)。LL 保留了图像的主体结构,三个高频子带分别记录不同方向的边缘和纹理。下一层分解继续作用在 LL 上,形成多分辨率金字塔。
遥感分类场景里,小波基的选择直接影响分解效果。Haar 小波最简单,但它的不连续性会让重建图像出现方块效应,用于 SAR 去噪时会在平坦区域留下人为痕迹。我一般会选 Daubechies 族的 db2 或 db4,它们的支撑长度适中,对遥感影像里常见的缓变地物边界拟合得比较好。分解层数取 2 层,太少则高频噪声分离不干净,太多则 LL 子带分辨率损失过大。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 小波基 | db2 / db4 | 平衡重构质量与计算量 |
| 分解层数 | 2 | 三源及以上数据建议保持 2 层 |
| 阈值方式 | 软阈值 | 硬阈值容易产生振铃 |
| 阈值估计 | VisuShrink / 自适应局部阈值 | 全局阈值对非平稳的遥感噪声不友好 |
| 边缘处理 | 对称延拓(symmetric) | 避免周期性延拓在边界产生虚假高频 |
2.3 小波变换图像增强的 Python 实现
用 PyWavelets 库在 Python 里做小波分解和重构非常直接。下面的代码演示了如何对 SAR 单通道影像做小波去噪,同时保留高频细节用于后续特征提取。
import pywt import numpy as np def wavelet_denoise_sar(image, wavelet='db2', level=2, mode='soft'): """ 对 SAR 图像做小波软阈值去噪,保留低频主体, 同时对高频子带做衰减而非彻底置零,避免细节完全丢失。 image: (H, W) float32 数组, 值域建议归一化到 [0, 1] """ coeffs = pywt.wavedec2(image, wavelet=wavelet, level=level, mode='symmetric') # coeffs[0] 是 LL 子带, 其余是每层的 (LH, HL, HH) 三元组 sigma = np.median(np.abs(coeffs[-1][-1])) / 0.6745 # 噪声标准差估计 threshold = sigma * np.sqrt(2 * np.log(image.size)) # VisuShrink 阈值 # 对高频子带做软阈值处理, 系数保留符号以避免边缘方向反转 new_coeffs = [coeffs[0]] for detail in coeffs[1:]: new_detail = tuple(pywt.threshold(d, threshold, mode=mode) for d in detail) new_coeffs.append(new_detail) return pywt.waverec2(new_coeffs, wavelet=wavelet, mode='symmetric') # 对光学多光谱影像的每个波段单独调用, 或对 PCA 第一主成分做 denoised_sar = wavelet_denoise_sar(sar_channel)这段代码里值得关注的是噪声标准差估计那一行。用 HH 子带的中值绝对偏差来估计噪声水平,是遥感图像处理里的标准做法,它不依赖图像的整体均值和方差,对 SAR 这种非平稳噪声更稳健。阈值公式里的image.size是总像素数,影像越大阈值越高,过滤掉的噪声越多,但也要警惕把细小的真实地物一并削弱,所以最后用了软阈值,系数向零收缩但不截断。
2.4 小波分解作为特征图的用法与常见误用
去噪只是预处理层面的用法,更强的做法是把小波分解的结果直接当作多尺度特征图输入网络。常见做法是对影像做一次二级分解,得到包含 LL、LH、HL、HH 的七个子带,把它们按通道维度组织成一个多通道张量,替代原本的单通道输入。
这里有一个很容易踩的坑:不要把 LL 子带和其他三个高频子带直接做批归一化。LL 的量级和分布特征与三个高频子带完全不同,混在一起归一化会抹掉它们之间的相对关系,网络等于又回到了看不见尺度差异的状态。正确的做法是先按子带分别归一化,再拼接,或者在网络第一层就用 depthwise 卷积分别处理不同子带。
另一个常见误用是在小波重构后直接上池化。既然小波分解已经得到了低频近似,合理的做法是用步长为 2 的卷积在 LL 子带上继续提取特征,或者干脆用 LL 子带替代一次池化操作,这样信息损失比 max pooling 更可控。
3. 平行注意力机制:通道与空间并行筛选多源特征
注意力机制在遥感分类里的作用,本质上是让网络学会「看哪里」和「信什么」。前者是空间注意力,后者是通道注意力。常见的做法是把通道注意力接在空间注意力前面,形成一条串行链路,但串行结构存在的问题是:通道筛选后的结果输入到空间分支时,已经被加权过一次,空间分支无法看到原始特征的全貌。
3.1 为什么这里用平行注意力而不是串行结构
多源遥感图像的融合场景里,光学影像和 SAR 影像的噪声模式、信息密度不一样,甚至它们的通道数都不一样(光学多光谱有 4-8 个波段,SAR 通常只有 1-2 个极化通道)。如果通道注意力和空间注意力串行,比如先算通道权重再算空间权重,那么 SAR 影像里通道数少、单通道信息量大的特点会导致通道注意力几乎不起作用;反过来先算空间注意力,光学影像的丰富光谱信息又被缩减了。平行注意力让两个分支各自独立计算,最后把两个权重相乘,两个维度的重要性都不会被对方先验地掩盖。
从可解释性的角度看,平行注意力输出的两组权重图(通道权重向量和空间权重矩阵)分别对应了「哪种地物光谱最可靠」和「影像的哪些区域最值得关注」,这两个维度的分析逻辑与遥感专家的判读经验一致,方便后续做分类结果的可视化和溯源。
3.2 平行注意力模块的 PyTorch 实现
下面是一个可插入任意卷积网络的平行注意力模块,输入形状为 (B, C, H, W),输出保持形状不变。
import torch import torch.nn as nn class ParallelAttention(nn.Module): """ 平行注意力模块: 通道注意力与空间注意力并行计算, 输出相加融合。 之所以用相加而不是相乘, 是避免两路权重都小于1时特征幅度过度收缩。 """ def __init__(self, in_channels, reduction=8): super().__init__() # 通道注意力分支: 全局平均池化 + 两层MLP self.channel_mlp = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_channels, in_channels // reduction, 1), nn.ReLU(inplace=True), nn.Conv2d(in_channels // reduction, in_channels, 1), nn.Sigmoid() ) # 空间注意力分支: 1x1 卷积降维后卷积生成空间权重 self.spatial_conv = nn.Sequential( nn.Conv2d(in_channels, 1, kernel_size=1), nn.Sigmoid() ) def forward(self, x): # 通道注意力权重 shape: (B, C, 1, 1) ch_weight = self.channel_mlp(x) # 空间注意力权重 shape: (B, 1, H, W) sp_weight = self.spatial_conv(x) # 两个分支并行相乘后相加 return x * ch_weight + x * sp_weight这个模块的关键参数是reduction,它控制通道注意力 MLP 的压缩比率。reduction=8表示先把通道数压缩到原来的 1/8 再恢复,这个值设置过大会损失通道间的相关性,设置过小则参数冗余。遥感多光谱数据我一般保持 8,SAR 数据通道数少,可以调到 4。
空间注意力分支只用了一层 1x1 卷积,理由是多源影像经过前期特征提取后,空间维度的关键信息主要落在局部区域(如建筑群边界、水体边缘),不需要过大的感受野。如果换成 3x3 卷积会增加不少计算量,对精度的提升在遥感分类任务里通常不明显。
3.3 双流主干与小波子带的融合设计
平行注意力模块加在哪里,直接决定整个模型的表现。多源遥感图像分类里典型的主干结构是共享权重的双流网络:光学影像和 SAR 影像各自经过一个相同结构的编码器分支,在中间层的融合点汇合。
| 融合位置 | 流程与张量尺寸变化 | 适用场景 |
|---|---|---|
| 输入级融合 | 小波子带与原始影像拼接 → (B, C+7, H, W) → 单流网络 | 两源影像空间分辨率一致,追求简单实现 |
| 特征级融合(推荐) | 双流分别提取 → 各自过平行注意力 → 通道拼接 → (B, C1+C2, H, W) → 分类头 | 两源影像特征分布差异大,需要分别建模 |
| 决策级融合 | 两支分别分类 → 对 Softmax 概率加权平均 | 两源影像单侧噪声极大时应急用的兜底方案 |
特征级融合是我在多数项目里优先考虑的方案,因为它在灵活性和精度之间最平衡。具体做法是:光学流提取到第 4 个 stage 时输出特征 F_opt,SAR 流对应输出 F_sar,把两者分别送进平行注意力模块,得到加权后的特征后再沿通道维度拼接。这样每个模态先通过注意力强调了自己内部最有判别性的维度,拼接后的特征不会出现某一模态主导的情况。
小波子带在这个架构里的接入点是第二层或第三层 stage。将小波分解得到的高频子带 resize 到当前特征图尺寸后,与空间注意力分支输出的权重做逐元素相乘,相当于用高频细节对空间注意力权重做一次校正。这个操作能让那些在光学影像里被云遮挡、在 SAR 影像里被噪声掩盖的微小目标重新获得权重。
3.4 源码实现里最常踩的三个形状问题
平行注意力模块本身实现简单,麻烦总出在输入数据准备阶段。第一个问题是小波分解后的子带尺寸不一致。pywt.wavedec2在图像尺寸不是 2 的整数次幂时,会返回不同尺寸的子带。解决办法是在分解前对影像做 padding 到能被 $2^{level}$ 整除,或者用pywt.pad配合对称延拓,保证每个子带尺寸相同。
第二个问题是光学多波段影像的通道顺序。部分遥感数据集以 (H, W, C) 存储,而 PyTorch 卷积输入要求 (C, H, W)。np.transpose转换后小波分解需要对每个通道单独进行,不要在通道维上直接分解。
第三个问题是 batch size 与归一化统计量。注意力模块里的nn.Sigmoid对输入幅度敏感,如果 batch size 只有 2-4,批归一化的均值方差估计不稳定,会直接影响注意力权重的分布。我的做法是用层归一化替代前几层的批归一化,或者在主干里保留sync_bn但把注意力模块放在归一化之后的特征上。
4. 分类训练策略与损失函数:长尾地物与噪声鲁棒性
模型架构搭好后,训练策略往往比结构设计更能决定最终精度。多源遥感图像分类的任务天然存在两个问题:不同地物类别的样本数量极度不均衡(比如水体、裸地样本多,某些特定建筑类型样本很少),以及噪声标签(尤其是 SAR 影像解译的标注结果经常有错)。这直接影响了损失函数和训练超参的选择。
4.1 分类损失函数的选择与组合
遥感图像分类的常见套路是直接用 Softmax 交叉熵,但对于长尾分布的地物类别,这个损失函数会把大量梯度分配给高频类别,低频类别的分类精度总是提不上去。我在处理这类问题时会用标签平滑的交叉熵配合类别权重。
import torch.nn.functional as F def weighted_label_smooth_loss(logits, labels, cls_weights, smoothing=0.1): """ logits: (B, num_classes) 未经过 softmax 的原始输出 cls_weights: (num_classes,) 每个类别的样本数量倒数归一化后的权重 """ log_probs = F.log_softmax(logits, dim=-1) smooth_loss = -log_probs.mean(dim=-1) # 均匀分布那一项 # 构造平滑后的标签分布 n = logits.size(-1) with torch.no_grad(): smoothed = torch.zeros_like(log_probs).fill_(smoothing / (n - 1)) smoothed.scatter_(1, labels.unsqueeze(1), 1.0 - smoothing) loss = -(smoothed * log_probs * cls_weights.unsqueeze(0)).sum(dim=-1) return (loss + smoothing * smooth_loss * cls_weights[labels]).mean()cls_weights的计算方式是每个类别样本数的倒数再归一化,让少数类别的样本在 loss 中获得更大的梯度。标签平滑系数smoothing=0.1是为了防止模型对标注不完全准确的训练集产生过拟合——SAR 影像的解译标注边界经常是模糊的,硬标签会让模型强行记住不确定的边界。
训练初期我还会叠加一个 focal loss 变体,它的核心思想是让模型集中注意力在分类困难的样本上。遥感场景里难样本通常就是那批被云覆盖一半、或者小到只有几十个像素的目标。实际使用时把加权交叉熵作为主损失,focal loss 作为辅助损失,两者加权相加,辅助损失的权重从 0.5 开始逐步衰减到 0,避免训练后期难样本中的噪声被过度放大。
4.2 训练超参数参考
多源遥感图像分类的训练超参数和常规 ImageNet 分类有较大差异,主要因为遥感数据集的规模通常在几万张量级,远小于自然图像数据集,且类别数少则十来个、多则上百个。
| 超参数 | 推荐值 | 调试说明 |
|---|---|---|
| 初始学习率 | 3e-4 | AdamW 下比常见 1e-3 更稳,预训练主干用 1e-4 |
| Batch Size | 16-32 | 取决显存,小 batch 配 LayerNorm 代替 BatchNorm |
| 优化器 | AdamW | weight_decay 设 1e-2 到 5e-2,防止大模型过拟合 |
| 学习率策略 | Warmup 5 epochs + Cosine Annealing | warmup 让注意力模块的 Sigmoid 输出不至于开局饱和 |
| 训练轮数 | 60-100 | 遥感图像分类任务 100 轮左右足够收敛 |
| 数据增强 | RandomCrop + Flip + 色彩抖动 | 不要对 SAR 影像做色彩增强,破坏后向散射的物理意义 |
SAR 影像的数据增强与光学影像必须分开写。对 SAR 做颜色抖动、随机亮度这类增强是物理上错误的操作,因为 SAR 的像素值对应的是地表粗糙度和介电常数,随机改变亮度等于人为制造了不存在的散射特性。SAR 侧只做几何增强(翻转、旋转、裁剪),光学侧可以加色彩类增强。
4.3 分类评估指标:整体准确率会骗人
遥感图像分类的评估指标上,整体准确率(Overall Accuracy, OA)是最直观但也最容易误导人的指标。在类别长尾分布下,哪怕某一类地物完全没被识别出来,只要其他类别样本多,OA 依然能到 90% 以上。
真正要看的分类评估指标是这三个:Kappa 系数、宏平均 F1(Macro-F1)和混淆矩阵。Kappa 系数衡量的是分类结果与随机分类相比提升了多少,数值低于 0.6 说明模型并没有学到有效的判别特征。宏平均 F1 对每个类别单独计算 F1 再平均,低频类别和低频类别的贡献一致,不会被高频类别淹没。混淆矩阵则用于定位具体的类别混淆对——在遥感分类里最常见的混淆对是「裸地」和「低密度建筑区」,两者在 SAR 影像上的后向散射特征很接近。
from sklearn.metrics import cohen_kappa_score, f1_score, confusion_matrix import numpy as np def evaluate_classification(all_labels, all_preds, class_names): """ 针对遥感多类别长尾分布的综合评估 """ kappa = cohen_kappa_score(all_labels, all_preds, weights='linear') macro_f1 = f1_score(all_labels, all_preds, average='macro', zero_division=0) # weighted_f1 与 OA 的相关性高, 只作辅助参考 weighted_f1 = f1_score(all_labels, all_preds, average='weighted', zero_division=0) cm = confusion_matrix(all_labels, all_preds) # 计算每个类别的类别准确率 (Recall) recall = cm.diagonal() / (cm.sum(axis=1) + 1e-7) print(f"Kappa: {kappa:.4f} | Macro-F1: {macro_f1:.4f}") for i, name in enumerate(class_names): print(f"{name:12s} Recall: {recall[i]:.4f}")在实际项目中,我会额外统计每个类别 recall 的最小值。如果某个类别的 recall 长期低于 0.5,先检查标注质量,再看是否需要为该类别合成训练样本。遥感图像不做随机过采样,因为简单的样本复制会让模型对特定影像的噪声纹理产生记忆,正确做法是用小波重构来生成合理的变换样本。
4.4 验证集划分里的一个隐蔽错误
遥感图像的验证集划分有个容易忽略的陷阱:相邻影像之间存在空间相关性。如果训练集和验证集的地块在地理位置上相邻,模型实际上见过验证集区域的周边上下文,评估结果会偏乐观。从业资料里把这种泄漏称为「空间数据泄漏」,在Kaggle 这类竞赛里见过不少翻车案例,在地理信息相关的实际项目里影响更大。
正确做法是按地块(patch)划分而不是按单张切片划分。把整幅遥感影像划分成若干互不重叠的大地块,每个大地块内部再切训练样本,训练集和验证集使用不同的大地块。这样验证集与训练集的间距至少大于一个地块的尺寸,空间自相关的影响能被控制住。
5. 快速验证小波与注意力模块是否生效的技巧
模型训练完,精度指标再好看,也需要回答一个问题:精度提升到底是小波变换和平行注意力带来的,还是单纯因为模型参数变多了?这直接关系到这套方案能不能迁移到新的数据集上。答案来自消融实验,但消融实验怎么做才有效率,有具体的门道。
最基本的消融设计是三个配置:完整模型(小波 + 平行注意力)、去掉小波分支只保留平行注意力、两者都去掉退化为普通的双流拼接网络。三组实验用同一份数据、同一组超参数、同样的训练轮数。最后对比 Macro-F1 和 Kappa。如果加了小波分支后宏平均 F1 提升了不到 0.5 个百分点,说明小波在这个数据集上只是锦上添花,可以为了推理速度砍掉;如果提升超过 2 个百分点,说明数据集里确实存在大量依赖高频细节才能区分的地物。
第二个值得做的验证是检查平行注意力两个分支的权重分布。把空间注意力分支的输出做可视化,如果权重高亮区域与影像里的地物边界高度吻合,说明注意力学到了有物理意义的位置信息;如果高亮区域随机散布,就要检查是不是训练轮数不足,或者批归一化统计量不稳定。
第三个小技巧是手动构造一个损坏样本集来测试模型的鲁棒性。从测试集中挑一批光学影像加入模拟云遮挡,再挑一批 SAR 影像加入更强的高斯噪声,分别输入到完整模型和消融模型里,观察精度的下降幅度。这个测试能直接回答「小波去噪到底有没有在输入端起到稳定作用」——如果加了小波分支后精度下降幅度更小,说明它确实让特征提取对噪声更鲁棒;否则,说明输入级的小波处理只是把噪声搬了个位置。
最后说一个模型扩展的方向。当数据源从两个增加到三个(比如加入高光谱影像)时,平行注意力模块不需要任何结构性改动,直接把第三个模态接入双流主干变成三流结构即可。小波分解同样逐模态独立进行。真正需要调整的是融合层——第三模态加入后,通道拼接的参数量会激增,这时可以考虑把融合方式从拼接改为加权求和,权重由一个小型的注意力网络动态生成。这种做法能在扩展模态的同时控制模型体积的增长,是工程上比较实用的演进路径。
本文还有配套的精品资源,点击获取