高光谱图像分类:融合张量嵌入与图半监督学习应对小样本挑战
1. 项目概述与核心挑战
高光谱数据分类,这个听起来有点学术的词,其实就是给遥感卫星拍回来的“超级照片”里的每一个像素点,打上它代表什么地物的标签。比如,这片是玉米地,那片是水体,那块是建筑区。这活儿是遥感领域最基础也最核心的任务之一,从农业估产、环境监测到城市规划,都离不开它。
但这事儿干起来,有个老大难的问题:标注数据太少了,也太难搞了。高光谱图像动辄几百个光谱波段,数据维度高得吓人,一个像素点就包含了几百个特征值。想让机器学习模型学会分辨这些特征,传统方法需要海量已经标好类别的像素点(也就是有标签数据)去“喂”它。然而,给高光谱图像做标注,可不是在电脑上点点鼠标那么简单。它往往需要领域专家跑到实地去,对照着卫星图像,一寸一寸地确认地物类型,成本极高、耗时极长,还容易出错。这就导致我们手里头有海量的高光谱图像数据(无标签数据),但能用来训练模型的“标准答案”(有标签数据)却少得可怜。
面对这个“数据饥渴”的困境,研究者们主要从两个方向找突破口。一条路是设计更“聪明”的模型,让它在只有少量标注样本的情况下也能学得好,这就是小样本学习的思路。另一条路是改变学习范式,不只用那一点点标注数据,而是把海量的、没标注的数据也利用起来,让模型从数据自身的结构中学习,这就是半监督学习。
我最近深入实践并验证了一种将这两条路优势结合的方法:基于张量嵌入的图半监督学习。简单来说,就是先用一个特别擅长从小样本中提取特征的张量神经网络,把原始的高维像素块转换成一组更精炼、信息更密集的“特征向量”(这就是张量嵌入)。然后,我们用这些高质量的特征向量来构建一个“关系网”(图),在这个网上,让已有的少数标签像水波一样扩散开来,去“感染”和预测那些没有标签的像素点。实测下来,这套组合拳在多个公开数据集上,只用极少的标注样本(比如每类只给10-30个),就能取得比单纯用张量网络好得多的分类效果,真正做到了“四两拨千斤”。
2. 核心思路拆解:为什么是“张量嵌入”+“图学习”?
2.1 张量嵌入:为高光谱数据“瘦身增肌”
高光谱图像天生就是个三维张量:长(空间)x 宽(空间)x 波段数(光谱)。传统方法往往粗暴地把它拉平成一个超长的一维向量,这就像把一本立体的书压成一张纸,很多宝贵的空间结构和光谱间的关联信息就在这个过程中丢失了。
张量方法的精髓在于,它尊重数据的原始结构。我们使用的核心模型叫做Rank-R 前馈神经网络。它的关键创新在于,其第一层(连接输入和隐藏层)的权重矩阵被强制约束为一种叫做CP分解的形式。你可以把它想象成,一个庞大的权重矩阵(连接所有输入特征和隐藏神经元)被拆解成了几个小得多的因子矩阵的乘积。
这么做的巨大优势是什么?
- 参数爆炸式减少:假设输入是一个 7x7x200 的像素块(共9800个原始特征),一个普通的全连接层如果有100个神经元,就需要近100万个参数。而通过低秩CP分解(例如秩R=5),参数数量可能骤降到几千个。这直接对抗了过拟合——模型不会去死记硬背那寥寥几个训练样本的噪声,而是去学习更本质、更泛化的特征。
- 保留结构信息:分解过程隐式地建模了空间维度和光谱维度之间的交互,让提取的特征嵌入(即隐藏层的输出)天然地融合了光谱-空间信息。
所以,张量嵌入扮演了“特征精炼师”的角色。它把原始高维、冗余、可能含有噪声的像素块,压缩成一个低维、紧凑、但信息密度更高的特征向量。这个向量,就是我们后续所有操作的基石。
2.2 图半监督学习:让标签在“关系网”中流动
有了好的特征表示,下一步就是利用无标签数据。图学习是半监督学习中非常直观有效的一派。它的核心思想是“物以类聚”。
- 建图:我们把每个像素点(现在用它的张量嵌入向量表示)看作图中的一个“节点”。然后,计算节点之间的相似度(比如用高斯核函数),如果两个节点特征向量很相似,我们就在它们之间连一条“边”,边的权重代表相似程度。这样,所有数据点(有标签+无标签)就构成了一张关系网。
- 标签传播:现在,少数有标签的节点就像几个染了色的水源。标签传播算法会让这些颜色(标签信息)沿着图的边,根据边的权重(相似度),逐渐扩散到整个网络。相似度高的节点之间,颜色更容易流动。经过迭代,最终每个无标签节点都会根据其邻居节点的标签和连接强度,获得一个预测标签。
这个过程的美妙之处在于,它完全是无监督地利用了整个数据集的内在几何结构。模型不再仅仅看孤立的样本,而是看样本在特征空间中的“邻里关系”来做出判断。
2.3 协同增效:1+1>2 的逻辑
那么,为什么要把这两者结合起来?单独用图学习,直接用原始像素特征建图不行吗?
实测下来,真的不行,或者说效果不好。高光谱原始数据维度太高、噪声大,直接计算相似度构建的图质量很差,会包含大量错误的连接(把不同类别的点连在一起)。垃圾进,垃圾出,在这样的图上做标签传播,效果自然不理想。
而张量嵌入,正是解决这个问题的钥匙。Rank-R FNN 通过小样本训练后,其产生的嵌入向量,是经过网络“理解”和“提纯”后的特征。在这个嵌入空间里:
- 同类样本会聚集得更紧密。
- 异类样本会分得更开。
- 数据的流形结构更加清晰。
用这样的高质量嵌入来构建相似度图,图的精度会大幅提升。一个更干净的图,意味着标签传播的路径更准确,最终分类性能的飞跃也就水到渠成。这就好比,你要在一个城市里找人(分类),如果给你一张标注了少数地标(有标签数据)的精确地图(高质量嵌入构建的图),你很容易推理出其他位置是什么;但如果地图本身画得歪歪扭扭、错误百出(低质量原始特征建的图),你就很可能迷路。
3. 实操全流程:从数据到结果
纸上谈兵终觉浅,下面我结合实验中的具体设置,拆解整个实现流程。你需要准备的“食材”包括:高光谱数据集、Python环境(如PyTorch/TensorFlow)、以及图学习库(如scikit-learn扩展或专门的图学习包)。
3.1 数据准备与预处理
我们以公开的Indian Pines数据集为例。它有 145 个光谱波段,16 种地物类别,共约1万个已标注像素。
第一步:构建空间-光谱样本块直接对单个像素分类会丢失空间上下文信息。我们采用一个滑动窗口,以目标像素为中心,截取一个s x s大小的空间邻域,形成一个s x s x 145的三维张量块X_x,y。这里s是奇数,如5或7。我们假设这个小块的中心像素标签代表了整个小块的标签。这是合理且常见的做法,能有效引入空间信息。
import numpy as np def extract_patches(data, labels, window_size=5): """ 从高光谱图像中提取以每个标注像素为中心的块。 data: 高光谱图像 (height, width, bands) labels: 标注图像 (height, width) window_size: 块的空间尺寸(奇数) """ half = window_size // 2 patches, patch_labels = [], [] # 获取所有有标注的像素坐标 rows, cols = np.where(labels > 0) for r, c in zip(rows, cols): # 提取块,处理边界(这里用0填充) r_start, r_end = r - half, r + half + 1 c_start, c_end = c - half, c + half + 1 patch = np.pad(data, ((half, half), (half, half), (0,0)), mode='constant')[r_start:r_end, c_start:c_end, :] patches.append(patch) patch_labels.append(labels[r, c]) return np.array(patches), np.array(patch_labels) - 1 # 标签通常从0开始第二步:数据集划分与极小样本设置这是模拟现实困境的关键。我们不能用太多标注数据。
- 将总数据按类别分层采样,分成6份(6-fold cross-validation)。
- 对于每一折的训练集,我们进行极度稀疏采样:从每类中随机抽取 α 个样本(例如 α = 10, 20, 30)作为有标签训练集。再额外抽10个/类作为验证集,用于控制张量网络训练(如早停)。
- 该折剩下的训练集样本和整个测试集,都作为无标签数据池。我们会从这个池子里按比例(如20%, 40%, 60%)抽取数据,加入到图学习的构建中。
注意:这里的“无标签”在评估时是有真实标签的,只是不用于初始训练。我们以此来模拟真实世界中大量无标签数据可用的场景。
第三步:数据归一化将每个样本块X_x,y在光谱维度上进行最小-最大归一化到 [0, 1] 区间,加速网络收敛。
def min_max_normalize(patches): """ 对每个样本块进行逐波段的归一化 """ # patches shape: (n_samples, height, width, bands) min_vals = patches.min(axis=(1,2), keepdims=True) max_vals = patches.max(axis=(1,2), keepdims=True) normalized = (patches - min_vals) / (max_vals - min_vals + 1e-10) return normalized3.2 阶段一:训练Rank-R FNN获取张量嵌入
这是整个流程的特征工程核心。我们需要实现或找到一个支持CP分解权重约束的神经网络层。
网络结构简述:
- 输入层:接收形状为
[batch_size, s, s, bands]的张量。 - CP分解层(自定义层):这是关键。该层不直接使用一个大权重矩阵
W,而是存储D个小的因子矩阵W_d(对于三维输入,D=3)。前向传播时,通过Khatri-Rao积实时计算vec(W),再与拉平的输入做内积。输出维度是隐藏神经元数量q。 - 隐藏层激活:对CP层的输出使用ReLU等激活函数。
- 输出层:一个普通的全连接层,接Softmax,输出类别概率。
训练要点:
- 目标:我们不是追求这个网络本身的分类精度达到极致,而是要它学到一个好的特征转换器。因此,验证集的损失或精度用于早停即可。
- 关键超参数:
- 分解秩 R:控制模型的容量和参数效率。R太小,模型欠拟合;R太大,可能过拟合且计算量增加。实验表明,对于高光谱数据,R=3或5通常是个不错的起点。
- 隐藏神经元数 q:即嵌入向量的维度。它决定了输出特征的尺寸。并非越大越好,需要平衡信息保留和后续图构建的计算复杂度。q=50或75是常见选择。
- 输出嵌入:训练完成后,我们丢弃最后的输出层。将预处理后的数据(包括所有有标签和无标签样本)输入网络,并提取CP层激活后的输出(即隐藏层的值)。这个
q维的向量,就是每个样本的“张量嵌入”。
# 伪代码示意:提取嵌入 def get_tensor_embeddings(model, dataloader): model.eval() embeddings, all_labels = [], [] with torch.no_grad(): for patches, labels in dataloader: # 前向传播至隐藏层 hidden_activation = model.cp_layer(patches) # 假设cp_layer是返回隐藏层输出的方法 embeddings.append(hidden_activation.cpu().numpy()) all_labels.append(labels.cpu().numpy()) return np.vstack(embeddings), np.concatenate(all_labels)3.3 阶段二:基于嵌入构建图并应用SSL算法
现在,我们有了所有样本(少量有标签L,大量无标签U)的q维嵌入向量。接下来进入图学习阶段。
第一步:构建相似度图我们采用最常用的k-最近邻(k-NN)图或ε-邻域图。这里以k-NN图为例:
- 计算所有样本嵌入向量两两之间的欧氏距离(或余弦相似度)。
- 对于每个节点
i,找到距离它最近的k个节点(不包括自己)。 - 在节点
i和这k个邻居之间建立无向边。 - 边的权重
W_ij通常用高斯热核函数计算:W_ij = exp(-||x_i - x_j||^2 / (2σ^2)),其中σ是尺度参数,可以取所有成对距离的中位数。
from sklearn.neighbors import kneighbors_graph import scipy.sparse as sp def build_knn_graph(embeddings, k=10, sigma=None): """ 构建k-NN相似度图 """ n_samples = embeddings.shape[0] # 计算k-NN连接性矩阵(二进制,表示是否邻居) connectivity = kneighbors_graph(embeddings, n_neighbors=k, mode='connectivity', include_self=False) # 计算距离矩阵(这里kneighbors_graph不直接返回距离,需另算) from sklearn.metrics.pairwise import euclidean_distances dist_matrix = euclidean_distances(embeddings) # 将距离转换为相似度权重(仅对k-NN连接应用) if sigma is None: # 启发式设置sigma:取所有距离的中位数 sigma = np.median(dist_matrix[dist_matrix > 0]) similarity = np.exp(-dist_matrix**2 / (2 * sigma**2)) # 只保留k-NN连接的权重 adjacency = connectivity.multiply(similarity) # 点乘,非连接处为0 # 通常使矩阵对称(因为k-NN可能不对称) adjacency = (adjacency + adjacency.T) / 2 return adjacency第二步:应用图半监督学习算法我们将构建好的图(邻接矩阵W)、有标签节点的索引及其真实标签、无标签节点的索引,输入到SSL算法中。实验对比了多种算法,其中基于拉普拉斯正则化的方法表现最为稳健。
以经典的Label Propagation (LP)算法为例,其核心思想是迭代地让每个节点的标签向其邻居加权平均更新,而有标签节点的标签在迭代过程中会被“钳制”回初始值。
# 伪代码示意:Label Propagation 核心迭代 def label_propagation(adjacency, labels, labeled_mask, max_iter=1000, tol=1e-3): """ adjacency: 相似度矩阵(已归一化,如对称归一化拉普拉斯相关) labels: 初始标签矩阵,one-hot形式,无标签处为0 labeled_mask: 布尔数组,True表示该节点有标签 """ n_nodes = adjacency.shape[0] # 初始化预测标签 Y = labels.copy() for it in range(max_iter): Y_new = adjacency @ Y # 标签传播步骤 # 钳制有标签节点的值 Y_new[labeled_mask] = labels[labeled_mask] # 检查收敛 if np.linalg.norm(Y_new - Y) < tol: break Y = Y_new # 对于多分类,取概率最大的类作为预测 predictions = np.argmax(Y, axis=1) return predictions在实际实验中,我们测试了多达11种图SSL算法,包括:
- 拉普拉斯相关:Lap, LapRePo, LapWNL
- 泊松方程:PoCG, PoSpe
- 随机游走:LaWa
- MBO方案:MMBO, VMBO
- 稀疏标签传播:SpLa
- p-拉普拉斯:p-Lap
3.4 性能评估与对比
评估采用分层6折交叉验证,重复多次以消除随机性。核心评估指标是F1-score(宏平均),因为它能更好地处理类别不平衡问题(高光谱数据中各类别像素数通常差异很大)。
对比基线:Rank-R FNN本身作为一个纯监督模型(仅使用那极少量的有标签数据训练和预测)。
核心发现:
- 嵌入质量至关重要:使用 Rank-R FNN 提取的嵌入,相比原始像素特征,能显著提升所有图SSL算法的性能。这证实了高质量嵌入能构建更优的图。
- 算法差异性:并非所有SSL算法都能同等受益。基于拉普拉斯正则化的方法(如Lap, LapWNL)和随机游走(LaWa)表现最稳定、最优。而一些更复杂的方法(如MBO变体)在不平衡数据上提升有限,甚至可能因参数敏感而表现不佳。
- 无标签数据量的收益递减:增加用于图构建的无标签数据比例(从20%到60%),初期能提升分类精度,但达到一定比例后(如40%),性能提升变得微乎其微,而计算成本(建图复杂度O(n²))却显著增加。这表明存在一个“性价比”最高的无标签数据用量阈值。
- 统计显著性:通过配对t检验,像Lap这样的方法相比纯监督的Rank-R FNN,在F1分数上的提升是统计显著的(p值远小于0.001)。
4. 关键参数、调优与避坑指南
在实际复现和应用这个方法时,以下几个点的把握直接决定了成败。
4.1 张量网络训练的关键
- Rank (R) 与隐藏层大小 (q) 的权衡:这不是越大越好。我们的实验表明,对于Indian Pines和Salinas数据集,R=5, q=50 与 R=5, q=75 效果接近,但后者参数更多。建议从较小的R和q开始(如R=3,q=50),如果欠拟合(训练集和验证集损失都高),再逐步增加。盲目增大不仅增加训练时间,还可能在小样本上导致过拟合。
- 样本块大小 (s):我们测试了5和7。
s=5在大多数情况下已经足够捕获局部空间纹理,且计算负担更小。s=7可能引入更多来自邻域不同类别的噪声,特别是对于边缘像素。除非地物目标非常大,否则优先选择s=5。 - 训练技巧:由于标签数据极少,务必使用早停(Early Stopping),耐心值(patience)设小一点(如5-10轮)。强烈建议使用权重衰减(L2正则化)和Dropout(即使在隐藏层后)来进一步抑制过拟合。学习率不宜过大,使用学习率调度器(如ReduceLROnPlateau)是稳妥的选择。
4.2 图构建的陷阱与技巧
- 相似度度量与尺度参数 σ:欧氏距离+高斯核是最常用的。σ 的选择非常关键。一个稳健的启发式方法是取所有成对距离的中位数。也可以尝试取第k近邻距离的平均值。如果 σ 太小,图变得稀疏,信息无法有效传播;σ 太大,图过于稠密,不同类别可能被错误连接。
- 近邻数 k:k 决定了图的局部连通性。k 太小,图不连通,标签无法传播到孤立区域;k 太大,图包含太多不相关连接,引入噪声。一个经验法则是从
k = log(n)或k=10左右开始尝试,观察性能变化。可以将其视为一个需要轻微调优的超参数。 - 图的归一化:在将邻接矩阵
W输入给许多SSL算法(如Label Propagation)前,需要进行归一化,例如对称归一化:D^{-1/2} W D^{-1/2},其中D是度矩阵。这能提高算法的数值稳定性和收敛速度。 - 处理大规模数据:当无标签数据量很大时(n > 10000),构建全连接k-NN图(O(n²))会内存爆炸。此时必须使用近似最近邻搜索(如Annoy, Faiss, HNSW)来高效构建稀疏图。或者,可以考虑锚点图方法,用远少于n的锚点来构建图,将复杂度从 O(n²) 降至 O(nm),其中m是锚点数量。
4.3 SSL算法选择与使用心得
- 首选拉普拉斯类方法:如果你的目标是稳定、可解释的提升,从标准的Label Propagation或基于拉普拉斯正则化的方法开始。它们实现简单,超参数少(主要就是传播的迭代次数和停止容差),且在我们的实验中表现最稳健。
- 警惕计算复杂度:MBO、p-Laplace等方法虽然数学上很优雅,但计算成本通常更高,且对超参数(如p-Laplace中的p值)更敏感。在类别严重不平衡的数据集上,它们可能表现不稳定。不建议在初次尝试或生产环境首选这些复杂方法。
- “无标签数据越多越好”是个误区:我们的实验清晰地表明,当无标签数据量达到总数据量的一个比例(例如40%-50%)后,性能增长曲线会极度平缓。盲目使用全部无标签数据只会徒增计算时间,而不会带来显著精度提升。建议进行一个简单的消融实验:绘制不同无标签数据比例下的性能曲线,找到那个“拐点”。
- 标签置信度与迭代:在标签传播中,有标签节点的标签是“硬钳制”的。但在一些改进算法中,可以考虑对有标签节点的置信度进行软性约束,或者引入“标签吸收”的概念,让传播过程更平滑。
5. 常见问题与排查实录
在实际跑通这个流程的过程中,你肯定会遇到各种问题。下面是我踩过的一些坑和解决方案。
Q1: 张量网络训练损失不下降,或者很快过拟合。
- 检查点:首先,确认你的数据预处理(归一化)是否正确。输入数据是否在合理范围(如[0,1])。
- 学习率:这是最常见的问题。对于小样本训练,学习率必须设得非常小(例如1e-4或1e-5)。尝试使用学习率查找器(LR Finder)或简单地以10倍为单位逐步调小。
- 正则化:你是否使用了足够的正则化?增加权重衰减系数(如1e-4),在CP层后或隐藏层后加入Dropout(如0.3-0.5)。
- Rank R是否太小:如果损失从一开始就很高且不降,可能是模型容量不足(欠拟合)。尝试将R从3增加到5。
- 验证集划分:确保验证集是从与训练集同分布的有标签数据中划分出来的,并且没有数据泄露。
Q2: 图SSL算法预测结果全是同一个类别。
- 检查图连通性:你的图可能是由几个不连通的子图组成的,而标签只存在于其中一个小子图里。计算图的连通分量。确保k值足够大,使得整个图是连通的,或者至少大多数节点在一个大连通分量内。
- 检查相似度权重:如果σ设置得极大,所有边的权重都接近1,会导致标签迅速均匀化。如果σ设置得过小,权重矩阵几乎为零,标签无法传播。打印出权重矩阵的统计信息(均值、方差),确保它是一个合理的稀疏矩阵,有明确的大小差异。
- 算法收敛问题:有些迭代算法可能不收敛。增加最大迭代次数,并打印每次迭代后预测值的变化范数,观察其是否趋于稳定。
Q3: 整体流程跑通了,但效果比论文里报告的差很多。
- 复现一致性:首先,确保你使用的数据集版本、训练/验证/测试集的划分比例、以及评价指标(是整体精度OA,还是每类F1的宏平均?)与论文完全一致。高光谱数据集的预处理(如去除水汽吸收波段)版本不同,结果差异会很大。
- 超参数差异:论文中可能经过了细致的超参数调优。请仔细核对:样本块大小
s、RankR、隐藏层大小q、图构建的k和σ、SSL算法的内部参数(如正则化系数)。 - 随机性:由于采样(选取有标签样本、划分数据)具有随机性,单次运行的结果可能有波动。务必进行多次运行(例如10次)并报告平均性能和标准差,这才是可靠的比较。
- 实现细节:张量网络的CP分解层实现是否正确?前向传播计算是否与公式对应?图构建时是否对邻接矩阵进行了正确的对称化和归一化?这些细节的偏差都会导致性能损失。
Q4: 面对新的高光谱数据集,如何确定一套可行的启动参数?这是一个非常实际的问题。我的建议是建立一个参数搜索基线:
- 张量网络:固定
s=5,尝试(R, q)组合:[(3,50), (5,50), (3,75), (5,75)]。使用极小的有标签数据(如每类5个)快速训练,看哪个组合在验证集上收敛最快且损失最低。 - 图构建:固定使用表现稳健的Lap算法。对于k,尝试
[5, 10, 15]。对于σ,先用“距离中位数”的启发式方法,然后在其0.5倍和2倍附近微调。 - 无标签数据量:从20%开始,增加到40%,观察性能增益。如果增益明显,再试60%;如果增益很小,则40%可能已足够。
这个方法的核心优势在于其模块化和可解释性。你可以清晰地看到,是张量嵌入的提升带来了图质量的提升,进而带来了最终分类性能的提升。它不像一个端到端的黑箱模型,其每个环节都有调整和优化的空间。虽然它涉及两个阶段,看起来比单一模型复杂,但在标注数据极其稀缺的现实约束下,这种“精心准备食材(嵌入),再巧妙烹饪(图学习)”的策略,往往是达成高精度结果的更务实、更可靠的路径。
