Fermat主动拉普拉斯学习:低标注成本高光谱图像分类方法
高光谱图像分类一直是遥感图像处理中的核心任务,但一个现实问题始终困扰着研究者:像素级标注成本太高。每张高光谱影像包含大量波段,人工对每个像素打标签往往需要专家知识,且工作量非常大。因此,如何用少量标注样本结合大量未标注数据完成可靠分类,就成为一个非常有价值的研究方向。
本文将围绕Fermat Active Laplace Learning for Semi-Supervised Hyperspectral Image Classification这个学术主题,从概念到代码实现进行系统拆解。我们会先讲清楚半监督学习、主动学习、拉普拉斯学习、Fermat 距离这几个关键概念,再给出一个可运行的最小实验框架。无论你是刚接触高光谱图像分类的研究生,还是希望在遥感项目中降低标注成本的同学,都可以从这篇文章中获得一套完整的思路和可复用代码。
1. 背景与核心概念
1.1 高光谱图像分类面临的现实挑战
高光谱图像与普通 RGB 图像最大的区别在于波段数。RGB 图像通常只有 3 个波段,而高光谱传感器可以在可见光到近红外乃至短波红外范围内获取几十到几百个连续波段。每个像素因此对应一条光谱曲线,包含丰富的光谱信息。
理论上,这种丰富的光谱信息可以帮助我们更精细地区分地物类别,比如不同作物类型、矿物成分或者水体污染情况。但随之而来的问题是:
- 特征维度高,容易引发“维度灾难”;
- 标注样本稀缺,人工标注成本高;
- 光谱之间可能存在大量冗余信息;
- 地物分布往往呈现复杂的空间相关性和非线性的流形结构。
在这样的背景下,仅仅依靠经典的监督学习方法,比如支持向量机或随机森林,在小样本场景下很容易出现过拟合或泛化能力不足的问题。
1.2 半监督学习:利用未标注数据
半监督学习(Semi-Supervised Learning, SSL)的核心思想非常直观:既然标注样本贵,而未标注样本便宜,那为什么不把大量未标注数据也利用起来?
常见的半监督学习思路包括:
- 一致性正则化:要求模型对输入的小扰动保持稳定的输出;
- 伪标签法:先用少量标注样本训练初始模型,再对未标注样本打上伪标签并加入训练;
- 基于图的半监督学习:把样本看成图上的节点,用边表示样本之间的相似度,通过图结构进行标签传播或拉普拉斯正则化。
在高光谱图像分类中,基于图的方法尤其有吸引力。因为高光谱图像本身具有天然的空间邻域关系,相邻像素通常属于同一地物类型。这种空间先验可以被自然地编码到图结构中,从而帮助未标注像素获得合理的标签。
1.3 主动学习:把标注预算花在“最有价值”的样本上
主动学习(Active Learning, AL)则是从另一个角度解决标注成本问题:与其随机挑选样本去标注,不如让算法主动选出“对模型提升最大”的样本交给标注者。
常见主动学习策略包括:
- 不确定性采样:选择当前模型最不确定的样本;
- 多样性采样:选择能代表不同区域的样本;
- 期望模型变化:选择让模型参数变化最大或损失下降最多的样本。
主动学习与半监督学习经常结合使用,形成主动半监督学习。先用少量标注样本启动模型,再通过主动学习挑选一批最有价值的未标注样本送入人工标注,更新模型,如此反复迭代。
1.4 什么是 Fermat Active Laplace Learning
从标题可以看出,Fermat Active Laplace Learning是三个技术要素的组合:
Fermat:费马距离,一种基于图最短路径的测地距离度量;Laplace Learning:拉普拉斯学习,一种基于图拉普拉斯正则化的半监督学习方法;Active Learning:主动样本选择策略。
它的整体思路可以概括为:在高光谱图像上,先利用 Fermat 距离构造更能反映样本流形结构的图,再在图上执行拉普拉斯学习,最后通过主动学习策略反复挑选最有标注价值的像素,从而在极小标注预算下获得较好的分类性能。
这个思路不仅可以用于高光谱图像分类,也适用于其他特征维度高、标注稀缺、样本间存在流形结构的数据集。
2. 相关技术原理拆解
2.1 图拉普拉斯与标签传播
基于图的半监督学习,首先要构建一个图 ( G = (V, E) ),其中节点 ( V ) 表示所有样本(包括标注和未标注样本),边 ( E ) 表示样本之间的相似关系。
假设图的邻接矩阵为 ( W ),那么度矩阵 ( D ) 是一个对角矩阵,对角线上的元素为:
[ D_{ii} = \sum_{j} W_{ij} ]
图拉普拉斯矩阵定义为:
[ L = D - W ]
拉普拉斯矩阵有一个非常重要的性质:对于任意向量 ( f ),都有:
[ f^T L f = \frac{1}{2} \sum_{i,j} W_{ij} (f_i - f_j)^2 ]
也就是说,如果两个节点在图中边权很大,但它们的标签 ( f_i ) 和 ( f_j ) 差异很大,那么惩罚项就会很大。这正是“相邻样本应该具有相似标签”这一先验的数学表达。
2.2 拉普拉斯学习的优化目标
拉普拉斯学习通常被建模为如下优化问题:
[ \min_{F} ; \sum_{i \in L} | F_i - Y_i |^2 + \mu \cdot \mathrm{tr}(F^T L F) ]
其中:
- ( F ) 是模型为所有样本预测的标签矩阵;
- ( Y ) 是标注样本的真实标签矩阵;
- ( L ) 是图拉普拉斯矩阵;
- ( \mu ) 是正则化系数,控制平滑项的重要性。
这个目标函数有两层含义:
- 标注样本的预测结果要尽量接近真实标签;
- 图上有强连接的样本之间预测结果要尽量平滑。
由于目标函数是二次的,因此存在闭式解。对 ( F ) 求导并令导数为零,可以得到一个线性方程组,直接求解即可。这也是拉普拉斯学习相比深度半监督方法的一大优势:参数少、实现简单、可解释性强。
2.3 Fermat 距离:考虑流形结构的距离度量
在构建图时,最直接的方式是用欧氏距离衡量样本之间的相似度。但欧氏距离是直线距离,它没有考虑样本所处的数据流形。
举个例子:两个像素之间光谱特征看起来有一定差异,但如果它们可以通过一系列光谱差异很小的像素连接起来,那么在流形意义上,它们的“真实距离”其实很近。反之,如果两个像素之间不存在平滑的路径,那么即使欧氏距离很小,它们也可能属于完全不同的地物。
Fermat 距离正是为了解决这个问题而提出的。它的定义可以理解为加权图上的最短路径距离:
[ d_F(i,j) = \min_{P} \sum_{(u,v) \in P} c(u,v) ]
其中 ( P ) 是节点 ( i ) 到节点 ( j ) 的一条路径,( c(u,v) ) 是相邻节点的连接代价。通常这个代价可以取为样本特征之间的欧氏距离的 ( p ) 次方,也可以取为基于相似度的某种权重。
使用 Fermat 距离构造图的优势在于:
- 能够捕捉数据空间的非线性流形结构;
- 对噪声和异常点具有更强的鲁棒性;
- 在高光谱图像中,有研究表明基于 Fermat 距离构造的图能更好地区分光谱相似但属于不同类别的地物。
当然,计算 Fermat 距离需要运行最短路径算法,比如 Dijkstra 算法,因此在大型数据集上的计算开销会明显高于直接计算欧氏距离。后面我们会讨论如何缓解这个问题。
2.4 主动拉普拉斯学习:两个模块如何协作
主动拉普拉斯学习可以看成是一个迭代过程:
- 当前有个标注样本集合 ( L ) 和未标注样本集合 ( U );
- 在图上执行拉普拉斯学习,得到所有节点的预测标签;
- 根据预测结果,计算每个未标注样本的信息量;
- 选择信息量最大的若干个样本,交给人工标注;
- 把新标注的样本加入 ( L ),更新图标签,然后回到第 2 步。
在每轮迭代中,拉普拉斯学习负责“从已知到未知”的传播,主动学习负责“从最不确定到最确定”的探索。两者结合,可以在少量标注预算下逐步逼近理想分类边界。
3. Fermat Active Laplace Learning 算法框架
3.1 整体流程
下面这张图展示了算法的整体流程,用文字描述如下:
- 读入高光谱图像数据,并按像素提取光谱特征;
- 随机或按特定策略选择少量像素作为初始标注集;
- 基于特征计算样本之间的 Fermat 距离;
- 利用 Fermat 距离构建 KNN 图或 Gaussian 核图;
- 计算图拉普拉斯矩阵;
- 执行拉普拉斯学习,得到所有像素的预测标签;
- 如果尚未达到预算,则执行主动学习策略,选择新的像素;
- 把新像素加入标注集,返回第 6 步;
- 达到预算后,输出最终分类结果并计算评价指标。
3.2 基于 Fermat 距离的图构造
这一步是整个方法的关键。与常规 KNN 图不同,这里不使用原始特征空间的欧氏距离,而是使用 Fermat 距离。
具体步骤如下:
- 初始化一个图,节点为所有像素,边权为相邻像素之间的欧氏距离;
- 在图上运行最短路径算法,求得任意两点之间的 Fermat 距离;
- 基于 Fermat 距离,用 KNN 或 (\epsilon)-ball 方法得到新的邻接关系;
- 对邻接矩阵进行对称化,并设置自环权重为 1;
- 使用 Gaussian 核或其他方式将距离转换为相似度权重。
由于高光谱图像中像素数量可能很大,直接计算全图 Fermat 距离矩阵是不现实的。常见做法是:
- 先对像素进行块状采样或使用超像素分割;
- 或者只计算局部区域内的像素之间的 Fermat 距离;
- 或者使用锚点图(Anchor Graph)思想,把部分代表点作为锚点。
3.3 拉普拉斯学习的求解细节
在得到图拉普拉斯矩阵 ( L ) 和标注矩阵 ( Y ) 后,我们需要求解如下方程:
[ (C + \mu L) F = C Y ]
其中 ( C ) 是对角矩阵,对角线元素为 1 表示该样本已标注,0 表示未标注。
由于 ( C + \mu L ) 是一个正定稀疏矩阵,可以使用共轭梯度法或者稀疏 Cholesky 分解来求解。在 Python 中,scipy.sparse.linalg.spsolve是一个不错的选择。
3.4 主动学习策略选择
在拉普拉斯学习框架下,主动学习不仅要考虑样本的不确定性,还要考虑样本在图中的传播影响力。这里给出两种常用策略:
- 最小置信度(Least Confidence):选择当前预测概率中最大类概率最小的样本。这个策略比较直观,适合作为基线方法。
- 期望风险减少(Expected Error Reduction):模拟为某个未标注样本添加标签后,模型在全部未标注样本上的损失变化。这种策略理论上更优,但计算成本较高。
- 影响最大化(Influence-based):在拉普拉斯学习中,可以通过扰动分析估计某个样本被标注后对整体预测结果的影响,从而选择影响力最大的样本。
在本文的代码示例中,我们先实现最小置信度策略,因为它的计算快,适合快速验证整体流程。后续可以替换为更复杂的策略。
4. 实验环境与数据准备
4.1 环境依赖
本文代码基于 Python 3.8+ 编写,核心依赖库如下:
numpy:数值计算;scipy:稀疏矩阵运算、最短路径算法;scikit-learn:KNN 搜索、数据预处理、评价指标;matplotlib:结果可视化。
以下是一个可直接安装依赖的命令:
pip install numpy scipy scikit-learn matplotlib版本方面,作者使用的常见组合是numpy 1.24、scipy 1.10、scikit-learn 1.2,这些版本相互兼容。如果你使用更新的版本,只要接口没有大的变化,代码仍然可以正常运行。
4.2 高光谱数据集
高光谱图像分类领域常用的公开数据集包括:
Indian Pines:印第安纳州西北部的一块农业区,包含 200 个波段,共 16 类地物;Pavia University:意大利帕维亚大学周边城区,包含 103 个波段,共 9 类地物;Salinas:美国加州萨利纳斯山谷,包含 224 个波段,共 16 类地物。
这些数据集通常以.mat格式发布,包含一个三维高光谱数据立方体data和一个二维标签矩阵gt。你可以使用scipy.io.loadmat读取。
注意,使用这些数据集时应遵守数据源的许可协议,在论文或项目中引用原始出处。
下面给出一个读取.mat数据的通用函数示例:
from scipy.io import loadmat def load_hsi_data(mat_path, data_key='data', label_key='gt'): """ 读取高光谱 .mat 数据。 :param mat_path: .mat 文件路径 :param data_key: 高光谱数据立方体的键名 :param label_key: 真实标签矩阵的键名 :return: data 三维数组 (H, W, C), labels 二维数组 (H, W) """ mat = loadmat(mat_path) data = mat[data_key] labels = mat[label_key] return data, labels4.3 特征矩阵构建
高光谱图像原始数据是一个三维立方体,例如形状为(height, width, bands)。为了输入到图模型,我们需要把每个像素的光谱向量展平成一行,得到特征矩阵X,同时把标签矩阵展平成标签向量y。
如果考虑空间邻域信息,还可以提取每个像素周围窗口内的局部特征,比如求窗口内的光谱均值或主成分特征。这一步可以显著提升分类效果,但也会增加特征维度。
5. 核心代码实现
本章节给出一个完整的Fermat Active Laplace Learning最小实现。代码分为四部分:
- Fermat 距离计算;
- 图拉普拉斯构造与求解;
- 主动学习采样策略;
- 主流程整合。
5.1 计算 Fermat 距离
我们用scipy.sparse.csgraph.dijkstra来计算加权图上的最短路径代价。
import numpy as np from scipy.sparse import csr_matrix from scipy.sparse.csgraph import dijkstra from sklearn.neighbors import NearestNeighbors def compute_fermat_matrix(X, k=10, p=2): """ 计算样本之间的 Fermat 距离矩阵。 参数: X : ndarray, shape (n_samples, n_features) 样本特征矩阵。 k : int 局部近邻数量,用于构建初始图。 p : float 边权指数,边权 = 欧氏距离 ** p。 返回: fermat_dist : ndarray, shape (n_samples, n_samples) Fermat 距离矩阵,仅对有限近邻有效,其他位置填充 np.inf。 """ n = X.shape[0] # 1. 使用 KNN 找到近邻 nn = NearestNeighbors(n_neighbors=k + 1, metric='euclidean') nn.fit(X) dists, indices = nn.kneighbors(X) # 2. 构建初始邻接矩阵 row_list, col_list, data_list = [], [], [] for i in range(n): for j, d in zip(indices[i], dists[i]): if i == j: continue cost = d ** p row_list.append(i) col_list.append(j) data_list.append(cost) adj = csr_matrix((data_list, (row_list, col_list)), shape=(n, n)) # 3. 用 Dijkstra 求最短路径距离作为 Fermat 距离 fermat_dist = dijkstra(adj, directed=False) return fermat_dist说明:
- 这里把局部欧氏距离作为边的代价;
- Dijkstra 会得到有边可达的节点间最短路径距离;
- 由于高光谱图像相对密集采样,同一个连通分量内的像素都可得到有限距离值。
5.2 用 Fermat 距离构造相似度图
得到 Fermat 距离矩阵后,我们通过 Gaussian 核函数把它转换为相似度矩阵:
def build_graph_from_fermat(fermat_dist, k=10, sigma=1.0): """ 根据 Fermat 距离矩阵构建 KNN 加权图。 返回邻接矩阵 W 和图拉普拉斯矩阵 L。 """ n = fermat_dist.shape[0] W = np.zeros((n, n)) for i in range(n): # 对每个样本取距离最近的 k 个非自身样本 row = fermat_dist[i].copy() row[i] = np.inf idx = np.argpartition(row, k)[:k] for j in idx: dist = fermat_dist[i, j] weight = np.exp(-dist ** 2 / (2 * sigma ** 2)) W[i, j] = weight W[j, i] = weight # 度矩阵 D = np.diag(W.sum(axis=1)) L = D - W return W, L注意,这里我们没有限制 neighbors 的连通性。如果某些样本在图上是孤立的,需要额外处理,否则拉普拉斯学习可能出现异常。
5.3 拉普拉斯学习求解
在得到图拉普拉斯矩阵 ( L ) 后,求解优化问题:
[ \min_{F} \sum_{i \in L} |F_i - Y_i|^2 + \mu \cdot \mathrm{tr}(F^T L F) ]
对应的线性系统为:
[ (C + \mu L) F = C Y ]
其中 ( C ) 是对角矩阵,已标注样本的位置为 1,其余为 0。
def laplace_learning(W, y, labeled_idx, n_classes, mu=1.0): """ 拉普拉斯学习求解。 参数: W : ndarray, shape (n_samples, n_samples) 相似度矩阵。 y : ndarray, shape (n_samples,) 标签向量,未标注位置为 -1。 labeled_idx : list or ndarray 已标注样本下标。 n_classes : int 类别数。 mu : float 平滑正则化参数。 返回: pred : ndarray, shape (n_samples,) 每个样本的预测类别。 F : ndarray, shape (n_samples, n_classes) 预测软标签矩阵。 """ n = W.shape[0] D = np.diag(W.sum(axis=1)) L = D - W # 构造 C 矩阵 C = np.zeros((n, n)) for idx in labeled_idx: C[idx, idx] = 1.0 # 构造 Y 矩阵 Y = np.zeros((n, n_classes)) for i, idx in enumerate(labeled_idx): label = int(y[idx]) Y[idx, label] = 1.0 A = C + mu * L B = C @ Y F = np.linalg.solve(A, B) pred = np.argmax(F, axis=1) return pred, F5.4 主动学习采样策略
这里实现一个最小置信度策略,选择当前预测最大概率最小的未标注样本:
def active_learning_query(F, unlabeled_idx, n_query=5): """ 主动采样:选择最不确定的未标注样本。 参数: F : ndarray, shape (n_samples, n_classes) 预测软标签矩阵。 unlabeled_idx : ndarray 当前未标注样本下标。 n_query : int 本轮要挑选的样本数量。 返回: query_idx : ndarray 被选中需要人工标注的样本下标。 """ confidence = F[unlabeled_idx].max(axis=1) sorted_idx = np.argsort(confidence) return unlabeled_idx[sorted_idx[:n_query]]也可以扩展到熵策略:
def active_learning_entropy(F, unlabeled_idx, n_query=5): """ 熵采样:选择预测熵最大的未标注样本。 """ probs = np.clip(F[unlabeled_idx], 1e-12, 1.0) entropy = -np.sum(probs * np.log(probs), axis=1) sorted_idx = np.argsort(entropy)[::-1] return unlabeled_idx[sorted_idx[:n_query]]5.5 主流程整合
下面把以上模块整合成一个完整的实验流程,方便你在自己的数据集上运行。
import numpy as np from sklearn.metrics import accuracy_score, cohen_kappa_score def active_fl_loop(X, y, n_classes, init_num=10, query_num=5, max_iters=20, k=10, sigma=1.0, mu=1.0): """ Fermat Active Laplace Learning 主流程。 参数: X : ndarray, shape (n_samples, n_features) 样本特征。 y : ndarray, shape (n_samples,) 真实标签。调用方需要保证只提供少量已标注样本。 n_classes : int 类别数。 init_num : int 初始随机标注样本数。 query_num : int 每轮主动采样的样本数。 max_iters : int 最大迭代轮数。 k : int Fermat 距离 KNN 近邻数。 sigma : float Gaussian 核带宽。 mu : float 拉普拉斯正则化系数。 返回: history : list of dict 每轮的预测准确率和已标注数量。 """ n = len(y) # 第一步:计算 Fermat 距离矩阵 print("Computing Fermat distance matrix ...") fermat_dist = compute_fermat_matrix(X, k=k, p=2) # 第二步:构建图 print("Building graph ...") W, L = build_graph_from_fermat(fermat_dist, k=k, sigma=sigma) # 初始化标签,-1 表示未标注 y_work = np.full(n, -1, dtype=int) # 随机选择初始标注样本 all_idx = np.arange(n) labeled_idx = np.random.choice(all_idx, size=init_num, replace=False) y_work[labeled_idx] = y[labeled_idx] history = [] for it in range(max_iters): # 拉普拉斯学习 pred, F = laplace_learning(W, y_work, labeled_idx, n_classes, mu=mu) # 记录在未标注样本上的准确率 unlabeled_idx = all_idx[y_work == -1] if len(unlabeled_idx) > 0: acc = accuracy_score(y[unlabeled_idx], pred[unlabeled_idx]) else: acc = accuracy_score(y, pred) print(f"Iter {it+1}: labeled = {len(labeled_idx)}, acc = {acc:.4f}") history.append({ 'iter': it, 'labeled_num': len(labeled_idx), 'acc': acc }) # 若已经没有未标注样本,则退出 if len(unlabeled_idx) == 0: break # 主动学习选择下一批样本 query_idx = active_learning_query(F, unlabeled_idx, n_query=query_num) # 模拟人工标注 y_work[query_idx] = y[query_idx] labeled_idx = np.concatenate([labeled_idx, query_idx]) return history, pred调用示例:
# 假设 X 是特征矩阵,y 是真实标签 # history, pred = active_fl_loop(X, y, n_classes=16, # init_num=20, query_num=10, max_iters=10)5.6 直接基于欧氏距离的对比实现
为了验证 Fermat 距离的效果,通常还要实现一个基于普通欧氏距离的对比实验。只需要把compute_fermat_matrix替换为直接计算欧氏距离即可。由于我们使用的是sklearn,可以直接用pairwise_distances函数:
from sklearn.metrics import pairwise_distances def compute_euclidean_matrix(X): return pairwise_distances(X, metric='euclidean')后面用同一个build_graph_from_fermat函数构建图,保持其他设置不变。
6. 运行与验证
6.1 验证目标
在实验部分,你至少需要关注以下几个问题:
- 在固定标注预算下,基于 Fermat 距离的图是否比基于欧氏距离的图分类准确率更高?
- 主动学习策略是否优于随机采样?
- 迭代过程中准确率曲线如何变化?是否收敛?
- 不同参数(近邻数 ( k )、带宽 ( \sigma )、正则化系数 ( \mu ))对结果的影响如何?
6.2 结果可视化
使用matplotlib画出分类结果图,以及与随机采样对比的曲线。
import matplotlib.pyplot as plt def plot_history(hist_fermat, hist_euclidean): plt.figure(figsize=(8, 5)) plt.plot([h['labeled_num'] for h in hist_fermat], [h['acc'] for h in hist_fermat], marker='o', label='Fermat graph') plt.plot([h['labeled_num'] for h in hist_euclidean], [h['acc'] for h in hist_euclidean], marker='s', label='Euclidean graph') plt.xlabel('Number of labeled samples') plt.ylabel('Accuracy on unlabeled set') plt.legend() plt.grid(True) plt.show()这种图能直观展示不同图构造方式在相同标注预算下的性能差异。
6.3 指标说明
高光谱图像分类常用的评价指标包括:
- Overall Accuracy(OA):所有测试样本中被正确分类的比例;
- Average Accuracy(AA):每个类别准确率的平均值,能体现类别均衡性;
- Kappa Coefficient:一致性系数,消除了随机分类的影响。
在实验报告中,通常需要同时给出这三个指标。但由于不同实验设置(训练集大小、类别数、是否包含背景类)差异很大,本文不给出固定的精度数值。你应该在自己的数据划分下运行代码,记录并比较结果。
7. 常见问题与排查思路
7.1 常见问题表格
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
程序报错,Fermat 距离矩阵包含inf或NaN | 图不连通,部分节点之间没有路径 | 增大近邻数 ( k ),或添加图连通性检查 |
| 拉普拉斯学习求解时矩阵奇异 | 图连通性差,或正则化系数 ( \mu ) 过小 | 增大 ( \mu ),或改为np.linalg.lstsq求解 |
| 主动学习不断选到同一个类别样本 | 采样策略过于简单,未考虑类别平衡 | 引入类别均衡约束,或改用 Batch 主动学习 |
| 迭代多轮后准确率反而下降 | 主动学习选入错误标注样本,或拉普拉斯平滑过度 | 检查标注噪声,减小 ( \mu ),增加查询批量 |
| 内存不足 | 全图 Fermat 距离矩阵过大 | 使用采样、锚点图,或近似最短路径算法 |
scipy.sparse.csgraph.dijkstra运行缓慢 | 图节点数过多,边数过多 | 降低近邻数 ( k ),或先使用超像素降维 |
7.2 深入排查建议
问题一:图不连通。
高光谱图像中如果包含强噪声像素,KNN 图可能出现孤立小连通分量。建议:
- 计算连通分量数量;
- 如果分量大于 1,在构建图后添加一个虚拟节点,把所有分量连接到该虚拟节点,或者增大 ( k )。
问题二:拉普拉斯学习预测结果全部为同一类。
这种情况通常发生在类别不平衡严重、初始标注很少的时候。建议:
- 增加初始标注样本数量;
- 在主动学习策略中加入类别多样性约束;
- 或者对拉普拉斯学习的解加入类别先验正则化。
问题三:Fermat 距离计算时间过长。
对于上百万像素的高光谱图像,全图 Dijkstra 不可行。可选的优化方案包括:
- 只对“代表点”计算 Fermat 距离,再通过标签传播扩展到全图;
- 使用超像素分割,以超像素为单位建图;
- 使用分块策略,在空间局部窗口内计算最短路径。
8. 最佳实践与工程建议
8.1 数据预处理
高光谱数据往往包含噪声波段和冗余波段。在构建图之前,建议先做以下处理:
- 去掉吸水波段:很多高光谱数据集中存在受大气水汽影响的波段,这些波段噪声较大;
- 归一化:对每个样本的光谱向量做零均值、单位方差归一化,或对全部波段做 Min-Max 归一化;
- 降维:使用 PCA 降到 30 到 60 维,不仅减少计算量,还能去除部分噪声。
降维代码示例:
from sklearn.decomposition import PCA pca = PCA(n_components=30) X_pca = pca.fit_transform(X)8.2 图构造参数调优
图构造是半监督学习中最敏感的部分。建议在实验中控制变量,逐一调参:
- 近邻数 ( k ):太小导致图不连通,太大则图结构趋于全局,失去局部流形信息。常见取值为 5 到 20;
- 路径代价指数 ( p ):( p ) 越大,长路径的代价越高,图会更偏向局部结构;( p = 2 ) 在不少实验中表现较好,但也可以尝试 ( p = 1 ) 或 ( p = 4 );
- Gaussian 核带宽 ( \sigma ):可设为所有距离值的中位数,这是一种常见且稳定的启发式策略;
- 正则化系数 ( \mu ):控制平滑项强度。( \mu ) 过大,所有预测都趋于平滑;过小,则标注样本过拟合。建议在 ([0.1, 10]) 范围内进行网格搜索。
8.3 主动学习策略设计
不要一上来就用复杂的采样策略。建议先跑通最小置信度或者熵采样,再逐步提升:
- 在每轮主动学习中,如果数据类别不平衡,可以按类别进行分组选择;
- 如果一次性需要选择多批样本,使用 Batch Active Learning 时应注意样本之间的多样性,避免重复选择信息冗余样本;
- 主动学习的每次迭代都会更新模型,建议记录每轮的选中样本和预测分布,便于后续分析。
8.4 工程化与可维护性
如果你要把这个方法应用到实际项目中,建议把实验代码按模块拆分:
project/ ├── data/ │ └── load_hsi.py ├── graph/ │ ├── fermat.py │ └── laplacian.py ├── active/ │ └── strategies.py ├── experiments/ │ └── run_experiment.py └── utils/ └── metrics.py在函数设计上,尽量让每个模块只做一件事。例如compute_fermat_matrix只负责计算距离,不负责建图;build_graph只负责从距离矩阵构造图,不负责分类;这样后期替换距离度量或主动学习策略都非常方便。
8.5 安全与合规提示
- 使用公开数据集时,注意遵循相应的数据许可协议;
- 如果涉及生产环境的遥感数据处理,确保数据来源和授权使用范围合法;
- 在发布实验代码时,不要附带可能包含敏感地理信息的大幅原始图像。
9. 总结与下一步方向
本文围绕Fermat Active Laplace Learning for Semi-Supervised Hyperspectral Image Classification主题,拆解了三个关键技术点:Fermat 距离、拉普拉斯学习、主动学习。并给出了一套 Python 最小实现,涵盖了图构造、半监督求解、主动采样和迭代训练全流程。
核心要点可以总结为以下几句话:
- 高光谱图像分类的瓶颈是标注成本高,半监督与主动学习是有效的降本方案;
- Fermat 距离比欧氏距离更适合捕捉高光谱数据的流形结构,但计算开销更大;
- 拉普拉斯学习实现简单、求解稳定,是一个非常好的基线方法;
- 主动学习在拉普拉斯学习框架下可以灵活迭代,每轮挑选最有价值的样本。
接下来你可以从以下几个方向继续深入:
- 在更大规模的数据集上验证 Fermat 距离的增益,并尝试与空间特征提取方法结合;
- 把主动学习策略从最小置信度升级为期望误差减少或影响最大化;
- 引入深度特征表示,用预训练卷积网络提取特征后再构建图;
- 尝试将 Fermat 图构造扩展到大规模数据场景,比如 Nyström 近似或锚点图方案。
如果你在实验过程中遇到问题,建议先从减小数据集、简化参数开始,一步步排查。代码跑通只是第一步,理解每一步为什么有效,才是真正提升实验水平的捷径。希望这篇文章对你有所启发。
