高光谱遥感小样本分类的3D-CNN与SE注意力实战解析

发布时间:2026/8/26 7:01:02
高光谱遥感小样本分类的3D-CNN与SE注意力实战解析 简介高光谱图像包含数十至数百个连续波段每个像素都携带一条精细的光谱反射率曲线为遥感地物识别提供了丰富依据。像素级标注任务的核心是让模型从光谱与空间上下文中学习判别特征。然而实地勘测获取的标注样本成本极高公开数据集每类往往仅有几十个像素极易导致深度模型严重过拟合。针对这一小样本难题3D卷积神经网络能够同时在光谱维与空间维滑动卷积有效提取空谱联合特征引入SE注意力机制则可自适应增强有判别力的特征通道抑制冗余信息显著提升模型泛化能力。该方法在农业地块分类、环境监测、城市遥感等场景中具有广泛应用价值。文章围绕这一技术路线给出了从数据划分、patch提取、数据增强到模型搭建与评估的完整工程实践并提供了可复跑的PyTorch代码与论文写作支撑。 做高光谱图像分类这个毕设如果打算用深度学习来做最崩溃的时刻往往不是在跑模型而是在好不容易把训练代码跑通发现每类只有十几个训练样本测试精度死活上不去答辩老师随手翻了翻结果图问“你这块地怎么全分错了”。这个画面我见过太多次了。高光谱图像分类本身是遥感里一个非常经典的像素级标注任务通常靠深度学习模型去识别每个像素对应的地物类别但高光谱数据有个天然矛盾——波段特别多、信息密度大可像素级标注成本高得离谱公开数据集里每类标注样本常常只有几百个甚至几十个。所以“小样本”三个字才是这个课题真正难的地方而用深度学习在这个约束下做出能看的分类效果顺便把python源代码、文档说明和毕业论文都整理齐就是这篇文章想覆盖的完整链路。这篇文章写给正在做这个方向的同学尤其是需要独立完成整条技术路线的本科毕设选手。我会按“需求拆解→数据准备→模型实现→小样本优化→实验分析→论文写作”的顺序讲代码结构尽量贴合毕设常用的Git仓库组织方式你拿到手改改路径就能跑。1. 项目整体设计与思路拆解1.1 先搞清高光谱分类到底在解决什么问题高光谱图像和普通RGB图像最大的区别在于波段数。普通图像就R、G、B三个通道高光谱图像可以有几个甚至几百个连续波段每个波段对应一个窄的光谱区间相当于给每个像素采集了一条非常精细的光谱反射率曲线。不同地物——比如植被、水体、土壤、建筑——在这条曲线上会呈现出不同的吸收和反射特征理论上靠光谱曲线就能区分它们。所以高光谱分类的任务很直白对图像里每一个像素预测它是哪类地物。落实到技术上一般不会只用单独一个像素的光谱向量因为地物在空间上是有连续性的。实际情况中同一个类别的像素往往聚成一小片区域中心像素周围的邻域能提供很强的判别信息。这就是为什么主流做法会取以目标像素为中心的一个小patch作为模型输入让网络同时看到光谱信息和空间上下文也就是“空谱联合特征”。1.2 小样本问题从哪来这里有个很关键的事实高光谱数据集里的标注不是人随便框出来的而是靠实地勘测、地面采样得到的。比如Indian Pines这个经典数据集是在美国印第安纳州一块农田上空用AVIRIS传感器获取的像元级的真实类别标签要和地面实际作物类型逐一对应标注流程极其昂贵。所以公开数据集的标注量都很少。以Indian Pines为例整张图像只有145×145像素去除背景以后大概一万来个有标签的像素加起来分成16类。分到每个类别上少的类别只有20个像素多的类别才两千多个。如果按“小样本实验”的规格每类只拿10个甚至5个样本去训练整个训练集就100多个像素这对深度模型来说几乎是“弹尽粮绝”。这也就解释了为什么很多刚上手的人会发现模型在训练集上精度很高验证集上一塌糊涂。小样本深度学习本质上是在跟过拟合作斗争所有的技术手段——数据增强、预训练、正则化、注意力——都是为了让模型在小数据上学会更本质的光谱-空间特征而不是把有限的训练样本背下来。1.3 技术路线为什么选3D-CNN高光谱分类的经典方案其实很丰富。早期是支持向量机和随机森林直接把光谱向量当输入后来深度学习流行以后出现了三种主流网络结构2D-CNN、3D-CNN、以及混合2D3D的卷积网络。2D-CNN把高光谱波段压缩成几个通道或者做PCA降维后当多通道图处理卷积核只在空间维度滑动。优点在于模型轻、计算快缺点是对光谱维的建模不足毕竟波段之间的顺序关系是有意义的。3D-CNN卷积核同时在光谱维和空间维滑动能同时抽取空谱特征信息利用率最高但参数量大、计算量也大。混合结构先用3D卷积在光谱维建模再在后面接2D卷积做空间特征精细提取效果通常是最好的但结构复杂一些训练难一点。最终我选了“3D-CNNSE注意力”这个方案相当于在经典3D-CNN基础上加了通道注意力模块。选择原因是第一3D-CNN的空谱联合特征输出比较完整第二加入SE模块可以在训练样本很少的时候让网络自动放大更有判别力的波段和特征通道对泛化有实质性帮助第三这个组合对算力要求相对温和一个普通1080Ti或者云服务器就能跑完所有实验不用上去就砸一台A100。1.4 整体工作流从数据处理到论文成稿我的执行顺序是选数据集下载并整理成统一格式做归一化、PCA降维、Patch提取按小样本比例划分训练集、验证集和测试集搭建3D-CNNSE模型写损失函数和评估指标加入数据增强、预训练、正则化等优化手段跑一组基线和改进模型的对比实验出分类图、混淆矩阵、指标表用来写论文这个工作流基本对应毕业论文的第三章和第四章所以每一步的产出都要注意保存中间结果——分类图和指标表后期写论文时直接能用不用再重新跑一遍。2. 数据准备几乎决定成败的一步2.1 公开数据集怎么选目前高光谱分类方向最常用的是Indian Pines、Pavia University、Salinas、KSC、Houston等公开数据集。它们的传感器、分辨率、类别数都不同选择标准主要看两点一是你的模型能否在这个数据上训练得动二是论文里和已有方法对比时这些数据集是否是主流基准。我建议把Indian Pines作为主实验数据集因为它是这个方向约定俗成的“及格线”几乎所有对比论文都会汇报这个数据集上的结果。Pavia University作为第二数据集用来验证方法的泛化能力。还有个更细一点的选择Salinas的类别区分度较高分类相对容易适合用来判断你的模型是不是有结构性问题。三个数据集的大致参数数据集传感器空间尺寸可用波段数类别数标注像素量级Indian PinesAVIRIS145×14520016约1万Pavia UniversityROSIS610×3401039约4万SalinasAVIRIS512×21720416约5万2.2 小样本划分策略论文里必须有清晰的数据划分描述否则结果无法复现答辩时也容易被问“训练集测试集怎么分的”。我采用的做法是对每一类地物随机抽取固定数量比如10个的带标签像素作为训练集再抽10个作为验证集剩下的全部作为测试集。这就是典型的“每类N-shot”设置。为了结果稳定我会用同一个随机种子跑5次取均值。论文里需要写清楚这个设定因为不同论文对“小样本”的定义差别很大有人用每类30个有人用5个没有统一标准所以必须显式说明。另外一个容易忽略的点分训练集/测试集之前先把所有样本整理好再一次性划分而不是每跑一个实验都重新随机划分。固定住随机种子保证每次实验在不同数据子集上做避免因为随机性太大导致结论不可信。2.3 Patch提取与归一化高光谱图像分类的输入通常不是整张图而是围绕每个目标像素裁出的小patch。patch size选多大直接影响模型性能和显存占用。我对9×9、13×13、17×17都做过对比9×9在小样本下性能够用、显存友好13×13略好一点但提升很有限。最后选了9×9作为主设置因为小样本条件下patch太大会引入过多不相关背景信息反而干扰分类。边界像素的处理也要注意。图像边缘的像素没法取到完整的邻域patch直接丢弃会损失边缘信息。比较好的做法是用镜像填充也就是reflect padding把边界外的像素按镜像值补齐这样不会引入太多人造边界。归一化方面我做的是按波段做z-score归一化也就是每个波段单独计算均值和标准差将数据缩放到均值为0、方差为1。这一步对梯度下降非常重要因为高光谱原始反射率值可能在不同波段间差异很大不归一化会导致网络中某些通道的数值永远压过其它通道训练不稳定。2.4 数据增强的几种有用操作小样本场景下数据增强是性价比最高的一招。但高光谱图像的增强和自然图像不完全一样要同时考虑空间和光谱两个维度。我实际使用的增强组合是空间几何增强随机旋转90/180/270度水平或垂直翻转。模拟传感器不同角度拍摄的情况。光谱扰动对光谱曲线做轻微的随机偏移或缩放。模拟光照条件变化这个操作在自然图像里没有对应物但对高光谱特别有效。高斯噪声在小patch上加入零均值的高斯噪声让模型对传感器噪声更鲁棒。这里有一个重要细节几何增强时以中心像素为旋转中心确保中心像素的标签不变。如果旋转中心偏移了那个patch对应的标签就错了会导致训练数据污染。这个坑我踩过后来专门写了一个增强函数固定以patch中心旋转才把问题解决。3. 模型构建与源代码实现3.1 网络结构设计我的模型主体是一个带通道注意力的3D-CNN分类器。基本设计如下输入一个patch形状为(波段数, patch_size, patch_size)。这里波段经过了PCA降维从200维降到30维。第1层3D卷积卷积核大小为(7,3,3)即光谱维看7个相邻波段空间维看3×3邻域输出特征通道数8。第2层3D卷积卷积核大小为(5,3,3)输出特征通道数16。SE模块对特征图的通道维度做全局平均池化经过两个全连接层计算每个通道的权重再把权重乘回特征图。第3层3D卷积卷积核大小为(3,3,3)输出特征通道数32。最后接GlobalAveragePooling和全连接层输出各类别的logits。具体每层输出的形状变化我整理成了一张表方便对齐模型维度层输入形状输出形状说明Conv3D_11×30×9×98×24×9×9光谱核7空间核3BNReLU8×24×9×98×24×9×9批量归一化和激活Conv3D_28×24×9×916×20×9×9光谱核5空间核3SE16×20×9×916×20×9×9通道注意力Conv3D_316×20×9×932×18×9×9光谱核3空间核3GAP32×18×9×932全局平均池化FC32num_classes分类层这里的第一个维度1是3D卷积的输入通道数因为我们把原始patch当成单通道的3D张量处理。3.2 关键代码解读模型定义部分的示例代码PyTorch实现import torch import torch.nn as nn class SEBlock(nn.Module): def __init__(self, channels, reduction4): super().__init__() self.gap nn.AdaptiveAvgPool3d(1) self.fc nn.Sequential( nn.Linear(channels, channels // reduction), nn.ReLU(), nn.Linear(channels // reduction, channels), nn.Sigmoid() ) def forward(self, x): b, c, d, h, w x.shape y self.gap(x).view(b, c) y self.fc(y).view(b, c, 1, 1, 1) return x * y class Hybrid3DCNN(nn.Module): def __init__(self, n_bands, num_classes, reduction4): super().__init__() self.conv1 nn.Sequential( nn.Conv3d(1, 8, kernel_size(7, 3, 3), padding(3, 1, 1)), nn.BatchNorm3d(8), nn.ReLU(), nn.Dropout3d(0.2) ) self.conv2 nn.Sequential( nn.Conv3d(8, 16, kernel_size(5, 3, 3), padding(2, 1, 1)), nn.BatchNorm3d(16), nn.ReLU(), nn.Dropout3d(0.2) ) self.se SEBlock(16, reduction) self.conv3 nn.Sequential( nn.Conv3d(16, 32, kernel_size(3, 3, 3), padding(1, 1, 1)), nn.BatchNorm3d(32), nn.ReLU(), nn.Dropout3d(0.2) ) self.gap nn.AdaptiveAvgPool3d(1) self.classifier nn.Linear(32, num_classes) def forward(self, x): # x: (B, C, H, W)需要增加通道维 x x.unsqueeze(1) x self.conv1(x) x self.conv2(x) x self.se(x) x self.conv3(x) x self.gap(x).squeeze(-1).squeeze(-1).squeeze(-1) return self.classifier(x)训练循环中需要注意的几个设置优化器Adam初始学习率0.003配合CosineAnnealingLR余弦退火调度。损失函数带标签平滑的交叉熵标签平滑系数设0.1。这样可以防止模型对训练样本过拟合输出的概率分布也不会太极端。Batch Size64。这个值在小样本下可以适当小一点但太小会导致梯度噪声大训练不稳定。训练轮数最多100个epoch配合早停策略验证集精度连续15个epoch不提升就停止。3.3 评估指标怎么算高光谱分类论文里三个指标是标配OA、AA和Kappa系数。OA总体精度正确的预测像素数除以总测试像素数。AA平均精度每个类别单独算精度再对所有类别取平均这样能避免样本多的类别主导结果。Kappa系数衡量分类结果与随机分类相比的改善程度取值在-1到1之间越高说明一致性越好。这三个指标的计算都不复杂但要注意实验脚本里统一实现避免重复写。我在文档说明里专门有一个calculate_metrics.py文件输入预测结果和真实标签直接输出三个指标写论文的时候一键出数。3.4 代码工程化的一点建议毕设交的不只是算法还有能跑的代码和文档说明。我的建议是代码组织尽量模块化别把所有功能塞进一个train.py里。一个典型的仓库结构可以是project/ ├── config.py # 全局配置路径、超参数、数据设置 ├── data_utils.py # 数据加载、归一化、patch提取、数据增强 ├── model.py # 网络结构定义Hybrid3DCNN、SE等 ├── train.py # 训练和验证主脚本 ├── test.py # 测试脚本输出指标和分类图 ├── metrics.py # OA/AA/Kappa计算 ├── requirements.txt # 依赖库列表 └── README.md # 使用说明requirements.txt里主要列torch、numpy、scipy、sklearn、matplotlib这几个库就够版本号写清楚评审老师如果复现实验会方便很多。README里写清楚数据集下载链接、目录结构、运行命令这就是“文档说明”部分的核心。4. 小样本条件下的专项优化4.1 为什么常规训练在小样本下会崩如果把小样本下的深度学习比喻成“让一个新手只看过5张猫的照片就让他识别所有猫”常规训练下网络很快就把这5张照片的细节背下来了比如背景颜色、光照角度而不是学到“猫”这个类别的本质特征。这在小样本高光谱里更加严重因为一个patch里既有光谱信息又有空间信息冗余度很高模型很容易走捷径。所以小样本优化的核心思想就四个字增加约束。要么从数据侧让模型看到更多“猫”的样子数据增强、预训练要么从模型侧限制模型的自由度正则化、注意力要么两者结合。4.2 数据增强做对才有用前面提到的增强组合最需要强调的是“光谱扰动”这个操作。我在实验中发现单纯做空间翻转旋转精度提升有限但把光谱曲线随机偏移5%之后再训练验证精度能提高2到3个百分点。原因是高光谱图像对光谱变化非常敏感而真实场景中光照、大气条件的变化主要影响光谱强度让模型适应这种变化泛化能力自然就上去了。需要注意的是光谱扰动不能加太大否则会破坏类别可分性。我的经验是把光谱每个波段的数值乘以高斯分布采样出的系数均值1、标准差0.05这个幅度就比较安全。4.3 无监督预训练用大量未标注数据先学特征高光谱图像里有标注的像素很少但整张图像里大量未标注像素都是可以用的。我们可以先用所有像素包括无标签的做一次自编码器重构预训练让模型学会提取高光谱patch的通用特征再冻结大部分网络只微调分类头来拟合小样本标签。这个思路本质上就是无监督预训练有监督微调效果在Indian Pines小样本设置下非常明显。我实现的预训练路径是先用全部patch训练一个3D卷积自编码器重构输入patch预训练结束后把编码器部分作为Hybrid3DCNN的前几层初始化权重然后用小样本标签数据微调。相对从头训练这个方案在每类10个样本时能提升3到6个百分点。4.4 半监督伪标签扩训练集的高级技巧在已经有一个初步训练好的模型后可以对未标注像素进行预测把置信度超过阈值的预测结果当作伪标签合并进训练集再训练一轮。这个技巧在数据极少时有奇效但也容易引入错误标签所以阈值设置要保守比如置信度要超过0.95才保留。我做了一组简单实验每类用10个真实标签训练一个初始模型然后用模型对测试集里置信度大于0.95的样本打伪标签加入训练集再训练最终OA提高了2个百分点。不过这里必须提醒伪标签方法的实验设计要严谨千万不能把测试集样本的标签泄露给训练过程否则结果就是无效的。我最终论文里只把伪标签作为“扩展讨论”部分没有放在主实验里因为一旦评审质疑标签泄露整个结论都会垮掉。4.5 正则化组合拳怎么搭配给小样本模型加正则化我推荐从轻到重按顺序加Dropout/Dropout3D默认0.2起步。标签平滑系数0.1让模型不要对训练样本太自信。权重衰减weight decayAdam里设1e-4对全连接层效果明显。Early Stopping不是正则化但效果类似防止后段过拟合。这几个组合加下来模型在小样本下的稳定性会明显提升最典型的表现是多次重复实验的标准差变小了本文还有配套的精品资源点击获取