高光谱图像分类中的特征感知与协同表示方法解析
简介文档系统梳理了应用特征感知与协同表示FP-CRC的高光谱图像分类方法面向遥感图像处理、机器学习及高光谱分析领域的研究者与工程人员。内容从协同表示基本框架出发介绍多层稀疏表示、联合类内协同表示、空间感知协同表示、区域自适应与字典自适应协同表示、加权正则化协同表示、多特征字典学习协同表示、概率多核协同表示等代表性算法的思路与局限并重点阐述FP-CRC在图像重建、光谱特征感知和空间特征感知三方面的改进配合公式推导与流程图帮助读者理解从问题建模到分类实现的完整过程。资源包共1个文件为docx格式大小436KB适合直接阅读、标记批注或作为论文写作参考。目前已有108人学习下载对需要快速掌握高光谱图像分类前沿方法或开展相关研究的读者是一份精炼且有参考价值的文献资料。 高光谱图像的分类问题本质上是“高维但小样本”的博弈一幅图动辄上百个波段相邻波段高度相关真正起判别作用的可能就十几个而训练标注像素又少得可怜。特征感知与协同表示的组合正是冲着这两点来的——先用特征感知把波段和局部邻域里有用的信息挑出来再用协同表示让全体训练样本共同参与决策而不是像稀疏表示那样强行拉少数原子。这套路在常见的公开高光谱数据集上通常能把总体分类精度提升3到8个百分点而且不需要很大计算代价。文章适合正在做遥感地物分类、或者想把协同表示玩明白的同学照着步骤能直接复现。2. 协同表示分类的核心原理为什么高光谱分类需要它2.1 高光谱图像和传统分类流程不匹配在哪普通RGB图像只有三个波段高光谱图像动辄上百个波段数据在内存里的组织形式是一个三维立方体宽、高、光谱维。对某个像素做分类本质上是对它的一维光谱向量做判别。但这个向量太长了相邻波段之间相关性极高直接丢给K近邻或支持向量机时会撞上高维空间里的维数灾难维度越高、样本越稀距离度量越不可靠。我最早入行时习惯先把所有波段铺平成一个大矩阵然后直接做PCA降维再丢给随机森林。这条链路能跑但有两个问题PCA是无监督的它保留的是方差最大的方向不等于判别力最强的方向随机森林对高光谱里强相关的波段特征做分裂容易把冗余权重分散。它们都没解决好“特征感知”这件事。高光谱分类的突破口不在于换更强的分类器而在于先解决“用什么特征去分类”。特征感知要做的就是让特征从原始波段里被有针对性地筛选和加权协同表示分类则是基于这个特征空间去坐最后的判决。两者前后串起来刚好补上传统流程的空白。2.2 从稀疏表示到协同表示L2正则化的解析解稀疏表示分类SRC的思路是用全体训练样本作字典去线性表示测试样本但约束是系数尽可能稀疏也就是让少数样本说话。这个约束放在人脸识别里很漂亮但在高光谱上水土不服高光谱不同地物的光谱曲线彼此交叠很严重强制稀往往选中了错误的字典原子而且L1范数没有解析解每次都要迭代求解计算量偏大。协同表示分类CRC把L1正则换成L2正则让所有类别的训练样本共同参与表示数学上写成min_{α} ||y - Dα||_2² λ||α||_2²其中y是测试样本的特征向量D是全体训练样本构成的字典矩阵每一列一个原子α是对应的表示系数λ是正则化参数。由于是L2正则解析解直接可得α (D^T D λI)^{-1} D^T y注意这个式子里的投影矩阵P(D^T D λI)^{-1} D^T只依赖于字典D和测试样本y无关。训练阶段把这个矩阵算好缓存住测试阶段对每个样本只需要一次矩阵乘法就能得到系数速度极快。我在17000多个测试像素上做过对比同样特征的条件下CRC比SRC快至少一个数量级而且精度不降。2.3 分类判决各类重建残差怎么用得到全局系数α后并不会直接拿它预测类别而是要分两步走。先把α按类别拆开α_c表示只属于第c类的那部分系数其余类的系数一律当成零。然后用每个类别自己的字典原子D_c和系数α_c去重建测试样本y得到残差r_c ||y - D_c α_c||₂最后取残差最小的那个类别作为预测结果。这个设计很巧妙虽然系数是全体样本共同求出来的但判决时只看每一类单独重建得怎么样。某类地物如果是测试样本真正的归属它在协同表示里的贡献会集中到自己类别的原子重建误差自然会小其他类的原子则被挤到不起眼的位置重建出来的东西偏差很大。2.4 特征感知和协同表示的分工逻辑与协同嵌入既然CRC的输入特征直接决定残差质量特征感知的核心任务就是把原始高光谱数据映射到更适合协同表示的特征空间。一个最常见的做法是在特征感知时用判别性的波段加权而CRC训练和分类也同时在这一特征空间里进行。这样做的好处很直接平滑类内样本拉升类间距离让残差判决更干净。实际管线是端到端串起来的先把高光谱数据做归一化和必要去噪然后用特征感知模块对原始光谱或者邻域块做加权变换再从特征感知的结果中抽取训练集和测试集。字典在特征空间里构建CRC的投影矩阵也在同一空间里训练。如果特征感知改变了特征空间字典必须同步重建这个顺序不能搞反后面避坑章节我会细讲。3. 特征感知模块构建从波段到空间邻域两步走3.1 特征感知从哪里开始归一化与波段筛查特征感知的第一步不是设计花哨的卷积结构而是先把最基础的数据清洗做掉。高光谱数据里有几种波段是典型的干扰项大气吸收波段信噪比极低出现的全是噪声条纹有的波段在同类地物内部差异甚至大于类间差异对判别毫无帮助。我的做法是先查看整体波段的均值光谱曲线把那些曲线毛刺特别剧烈、样本能量明显异常的波段直接删掉这比任何算法加权都来得可靠。接着是归一化。不少教程默认按像素做归一化也就是每个像素的光谱向量除以自己最大值这在多数场景下会破坏绝对反射率信息因为同类地物不同像素之间的明暗差异会被抹平。更稳妥的是按波段做标准化每个波段在所有训练像素上求均值和方差然后整体转换让每个波段落在统一的能量尺度上。特征感知的加权值也正是基于这个归一化后的特征来计算。3.2 光谱感知用Fisher判别比分波段加权特征感知里最轻量、最不容易翻车的实现是给每个波段算一个判别力权重然后把原始光谱乘上这个权重。判别力可以用Fisher比分来量化分子是各个类别波段均值的离散程度分母是各类内部方差的加和比值越大说明这个波段越能区分不同地物。下面这段代码实现Fisher权重import numpy as np def fisher_weight(X, y, eps1e-6): 计算每个光谱波段的 Fisher 判别权重。 X: 训练特征矩阵形状 (n_samples, n_bands) y: 训练标签形状 (n_samples,) classes np.unique(y) global_mean X.mean(axis0) between np.zeros(X.shape[1]) within np.zeros(X.shape[1]) for c in classes: X_c X[y c] mean_c X_c.mean(axis0) n_c X_c.shape[0] # 类间离散程度类别均值与全局均值的差距 between n_c * (mean_c - global_mean) ** 2 # 类内离散程度类内样本的方差累加 within ((X_c - mean_c) ** 2).sum(axis0) fisher_score between / (within eps) fisher_score fisher_score / (fisher_score.max() eps) return fisher_score这里的关键参数是eps作用是在类内方差接近零的波段上防止除零溢出。需要注意如果某个波段在所有类别里都是一条水平线它的Fisher分会接近零加权后会被压制到几乎不参与后续计算而判别力强的波段权重会接近1成为协同表示的主要依据。实际使用时我会把X换成归一化后的矩阵并把得到的权重直接乘到训练和测试特征上。有一个坑Fisher权重必须在训练集上计算不能混入测试集否则等于变相偷看了测试数据的信息精度虚高。3.3 空间特征感知局部邻域的整形与拼接光谱特征感知只用到了像元本身的一条曲线而高光谱图像还有一个天然优势——空间连续性。同一块地物在空间上往往是成片出现的相邻像素的光谱高度相似。把这种邻域信息纳入特征感知能显著压低单像素的噪声干扰。常见的做法是以目标像素为中心开一个大小为p×p的窗口把窗口内所有像素的光谱向量直接拼接成一维特征或者先求邻域均值再拼上中心像素原始光谱。窗口大小一般取3到9太大会跨到别的类别边缘反而把特征搞浑。我通常用5×5窗口先取窗口内光谱均值再和中心像素拼接得到一个维度为2倍波段数的特征向量。这里有一个空间感知的变体值得试试对窗口内每个像素和中心像素求光谱角距离把距离值作为空间权重视角下的邻域聚合依据。光谱角距离定义为两个光谱向量之间的夹角余弦它比欧氏距离对光照强度变化更鲁棒在高光谱里很实用。不过它把每个邻域像素的加权变成了非线性操作协同表示里的线性字典就不好直接表达了所以实践中我还是优先用均值拼接损失一点空间感知的精确度但保住协同表示的速度。3.4 特征感知模块的落地封装与输出规范为了让整条链路可复现特征感知应该封装成一个独立的变换类输入原始数据和标签输出变换后的特征矩阵。这样训练集和测试集可以用同一套参数变换不会出现不一致。以下是一个简洁的封装示例class FeatureAwareTransform: def __init__(self, modefisher, patch_size0): self.mode mode self.patch_size patch_size self.band_weight None def fit(self, X_patches, y_train): # X_patches 是经过 patch 处理后的特征也可能是原始像素 if self.mode fisher: self.band_weight fisher_weight(X_patches, y_train) return self def transform(self, X_patches): if self.band_weight is not None: return X_patches * self.band_weight return X_patches这个封装的要点是fit和transform分开fit只吃训练数据transform对训练和测试一视同仁。后续做交叉验证时会特别安心因为不会把测试集的信息泄漏到特征加权里。波段权重在实际数据上最好看一眼如果出现某个波段权重异常高、其他全部接近零说明类内方差计算里可能有样本过少或者噪声异常要回到数据清洗步骤排查。4. 协同表示分类器实现与参数设置先跑通再优化4.1 用Python实现协同表示分类器的最小完整代码理解了CRC的数学原理实现就非常直接。核心工作是两件事训练阶段预计算投影矩阵测试阶段对每个样本求系数并计算各类重建残差。下面给一个完整的numpy实现没有外部依赖复制就能用import numpy as np class CollaborativeRepresentationClassifier: def __init__(self, reg_lambda1e-2): self.reg_lambda reg_lambda self.D None # 字典矩阵每行一个原子 self.y None # 字典原子对应的标签 self.classes None self.P None # 预计算投影矩阵 def fit(self, X_train, y_train): self.D np.asarray(X_train, dtypenp.float64) self.y np.asarray(y_train) self.classes np.unique(self.y) d self.D.shape[1] # 求解投影矩阵 P (D^T D λI)^{-1} D^T DtD self.D.T self.D self.P np.linalg.solve( DtD self.reg_lambda * np.eye(d), self.D.T ) def predict(self, X_test): preds [] for x in np.asarray(X_test): alpha self.P x best_class None best_residual float(inf) for c in self.classes: idx np.where(self.y c)[0] # 只用当前类别对应的系数做重建 x_recon self.D[idx] alpha[idx] residual np.linalg.norm(x - x_recon) ** 2 if residual best_residual: best_residual residual best_class c preds.append(best_class) return np.array(preds)代码里有两个值得注意的细节。一是用np.linalg.solve而不是np.linalg.inv因为solve直接做LU分解数值稳定性更好速度也更快。二是预测时对每个测试样本都要遍历一次类别外观上像两层循环但核心矩阵乘法已经由预计算P承担内层只是索引和残差计算所以实际跑起来依然很快。配合特征感知模块的完整调用方式如下先对训练特征做FA_transform.fit和transform再在scikit-learn里划分训练集测试集最后实例化CRC模型训练预测。测试集的样本不需要再单独做任何拟合操作。4.2 字典构造与每类原子数量的选择CRC的字典是直接拿训练样本拼成的不需要像传统字典学习那样iteration训练。但每类放多少原子进去很有讲究。如果某类训练样本太多字典里这一类占了绝对优势协同表示求出来的系数自然偏爱它分类精度会被样本最多的类别绑架。如果某类样本太少则该类的重建误差波动很大容易误判。我的经验是每类取50到200个原子具体根据总样本量调配。超过200个之后矩阵D^T D的规模随之变大求逆成本快速上升但分类精度的提升却趋于平缓。做高光谱的公开数据集时每类100个原子已经是性价比很高的配置。还需注意如果特征感知引入了patch窗口不同像素之间存在空间重叠直接随机抽样会让训练集和测试集共享部分邻域信息精度虚高。更严格的划分方式是按空间区域切割或者先对图像做像素筛选再按块划分训练测试集。这块在避坑章节会展开。4.3 协同表示的必调参数λ、特征维度与窗口大小协同表示分类虽然参数不多但每一个都很吃数据。第一个是正则化参数λ。它控制系数解的平滑程度λ太小D^T D接近奇异求逆不稳定λ太大系数被压缩得过狠各类重建残差都变大判别能力被削弱。在特征归一化到0到1之间的前提下λ从0.01开始调用对数网格在1e-4到1之间搜索是常见的做法。我在Indian Pines数据集上λ取0.01左右就能得到不错结果如果特征做了标准化且包含负值λ要适当放大到0.1量级。第二个是特征维度。经过Fisher加权后特征维度和原始波段数一致通常会有大量弱判别波段权重接近零它们还在占用字典矩阵的列空间。我的做法是在特征感知后叠加一个PCA截断把维度压到30到60。这样协同表示里D^T D的规模从几百下降到几十求逆稳定性显著改善精度反而经常提升。第三个是窗口大小。它和地物尺度密切相关田块纹理粗的地方用7×7不会跨边界城市建筑密集区用3×3更稳妥。这个参数没法一口吃成胖子我会做一组网格搜索结合分类评估指标来定。后面验证章节会教你怎么系统评估。4.4 五分类小实验从特征感知到协同表示的完整流水线为了把前面几节串起来这里给一个完整但精简的流水线示例输入是已经切割好的patch矩阵from sklearn.decomposition import PCA from sklearn.model_selection import train_test_split # X_raw: 原始特征y: 标签假设已经按 patch 做好了 fa FeatureAwareTransform(modefisher) fa.fit(X_raw_train, y_train) X_fa_train fa.transform(X_raw_train) X_fa_test fa.transform(X_raw_test) # PCA 压缩到 50 维 pca PCA(n_components50) X_pca_train pca.fit_transform(X_fa_train) X_pca_test pca.transform(X_fa_test) # 协同表示分类 crc CollaborativeRepresentationClassifier(reg_lambda0.01) crc.fit(X_pca_train, y_train) y_pred crc.predict(X_pca_test)这条流水线每次都能稳定跑通作为基准参照非常合适。后续改动特征感知模块或者调整λ都在这条链路上做对照。5. 避坑清单高光谱分类实战中的常见问题与排查5.1 训练测试划分不干净导致精度虚高一旦开了patch窗口像素之间就会产生空间重叠。如果不加控制地随机划分训练集测试集同一个5×5窗口里的像素可能一部分进了训练集、另一部分进了测试集模型等于提前见过邻居信息测试精度虚高得离谱。我见过有人报出99.8%的精度比正常结果高出近10个百分点很不正常。解决的办法是按图块划分。把图像分割成若干互不重叠的方块区域一部分区域拿来做训练、一部分做测试边界上扩一圈窗口时也不允许从测试区借用信息。这个做法会让精度数字下降一些但才是真实反映模型泛化能力的结果。5.2 λ取值像玄学先看特征尺度再调参同一个λ值在A数据集上效果好换到B数据集上一塌糊涂这种体验在CRC上非常常见。根源在于λ是和特征尺度绑定的。如果特征数值整体在0到1之间D^T D的对角元素大体在10量级λ取0.01是合理的如果特征没有归一化、数值在几千的量级D^T D对角元素动辄上百万0.01的λ等于不存在D^T D照样病态。排查时先打印D^T D的迹或最大特征值然后让λ比这个特征值小一到两个数量级。我一般以1e-2为起点跑一个对数网格搜索再根据分类评估指标选择最优值不让λ靠感觉拍脑袋。5.3 波段冗余和残留噪声波段拖累特征感知特征感知的加权基于Fisher判别分但它只考虑了单个波段的判别能力没考虑波段之间的相关性。高光谱里经常有十几二十个波段高度相关Fisher分会给它们都赋予较高权重但它们提供的其实是同一条信息等于把冗余特征放大了好几倍。排查方法是看特征感知后的相关矩阵如果存在一团相关性超过0.95的波段组就要做一次PCA或者删除其中一个代表波段。我习惯先做波段分组去相关再做Fisher加权顺序不要反过来否则加权会把冗余放大得更加厉害。5.4 小样本条件下特征感知过拟合当训练样本特别少时Fisher权重很容易被个别异常样本带偏。比如某类地物只有十几个像素其中一个像素因为云阴影导致光谱异常它会把该类均值拉偏波段权重计算出现明显波动。这种过拟合在测试集上表现为整体精度下降但局部某个类的精度暴跌。解决的办法是给Fisher权重的分子增加一撮平滑或者在类别样本量过少时直接退化为不加权特征。另一个经验是对权重做时序平滑高光谱相邻波段的物理含义是连续的权重突变往往代表噪声干扰用滑动平均把权重曲线拉平效果常常出乎意料地好。5.5 计算开销爆炸矩阵规模失控协同表示里最重的操作是求D^T D的逆当特征维度和字典原子数量都上去以后这个操作会吃掉大量内存。我曾在特征维度200、原子数5000的情况下直接让内存溢出进程被杀。这里的一个常见误用是没有压缩特征就硬跑。解决的方式有两板斧第一特征感知后接PCA降维把特征维度压到50甚至更低第二限制每类原子数量。如果还是不够可以换用迭代求解比如用共轭梯度法近似求α不必精确求逆速度会快很多代价是精度略有下降。高光谱数据量本身不算小算力评估一定要放在前头别等内存溢出了才回头改。6. 验证技巧与进阶调整让分类评估指标真正反映模型水平6.1 用OA、AA和Kappa做三指标对照别只看Overall Accuracy高光谱分类的标准评估配置不是打印一个混淆矩阵就完事。总体精度OA是所有被正确分类像素占总数的比例但它会被样本多的类别带节奏。比如一个数据集中农田类占了一半像素农田分类精度高就拉动OA虚高而植被类和建筑类分得稀烂也看不出来。所以还要看平均精度AA它是每个类别精度的算术平均对样本少的类别更敏感。第三个是Kappa系数它把随机猜对的概率剔除掉数值在0到1之间分数越接近1说明一致性越强。交叉验证也要配合同样的三指标。我习惯用5折在每一折里重新做特征感知、PCA、CRC训练和评估最后把五折的OA、AA、Kappa取平均和标准差。标准差尤其重要如果同一组参数下五折结果差到5个百分点以上说明划分方式对结果影响过大需要回到按块划分的方案重新设计。6.2 进阶技巧把特征感知和协同表示端到端联动优化到这里基础方案已经能稳定跑了。如果你想在这个方向继续深入有一个性价比很高的进阶路径把特征感知的波段权重从Fisher固定值改成可学习参数让协同表示的残差作为损失去反向传播更新权重。具体做法是先让特征感知模块输出和原始波段等长的可训练权向量分类器部分依然计算CRC的各类重建残差损失就用一个对残差做归一化指数映射的交叉熵来定义。整套东西可以包装成轻量的autograd模块在GPU上用小学习率跑几十轮就能收敛。这也是特征感知这个方向的魅力所在它和协同表示的组合天然具备可微性不像稀疏表示那样到处是断点。当你把固定Fisher权重换成可学习参数后会有一种模型自己在高光谱波段里找重点的感觉。最后说一句个人的体会遇到精度瓶颈时先别急着换更复杂的网络结构老老实实检查数据划分是否泄漏、特征感知的加权是不是被冗余波段污染、λ有没有跟特征尺度对齐这三处往往就是分类评估指标上不去的根源。把每一处细节都过一遍之后你会发现在高光谱这种数据上简洁的方案搭配正确的细节远比堆砌复杂模块可靠得多。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →