基于图挖掘与马尔可夫链的无监督特征选择方法解析与实践

发布时间:2026/5/31 12:42:26

基于图挖掘与马尔可夫链的无监督特征选择方法解析与实践 1. 项目概述与核心挑战在数据科学和机器学习领域我们常常会面对一个令人头疼的“维度灾难”问题。想象一下你手头有一份数据集它可能来自基因测序、图像像素分析或者用户行为日志动辄就有成千上万个特征变量。这些特征里有的确实能帮助我们区分不同类别比如区分猫和狗的图片但更多的可能是冗余的、不相关的甚至是噪声。直接把这些“臃肿”的数据喂给模型不仅会让训练过程慢如蜗牛消耗巨大的计算资源还极易导致模型“过拟合”——也就是模型只记住了训练数据中的噪声和无关细节而在新数据上表现糟糕。特征选择就是解决这个问题的“瘦身”手术。它的目标是从原始的高维特征空间中挑选出一个精简的、信息量最大的特征子集。传统的方法比如基于统计检验的过滤法Filter Methods或者基于模型迭代的包装法Wrapper Methods虽然各有千秋但都存在一个根本性的局限它们大多默认特征之间是相互独立的。这就像在分析一个团队时只评估每个成员的个人能力却完全忽略了成员之间的协作关系和沟通网络。在现实中特征之间往往存在复杂的依赖关系。例如在文本分析中“电脑”和“计算机”是强相关的同义词在生物信息学中某些基因会协同工作形成一个功能通路。忽略这些关系选出的“最优”特征子集可能无法真正代表数据的底层结构导致信息丢失。我最近在复现和深入研究一篇2019年发表于《Big Data Mining and Analytics》的论文其核心思想让我眼前一亮将图挖掘和马尔可夫链结合起来用于高维数据的特征选择。这个方法不再把特征看作孤立的点而是将它们视为一个网络中的节点特征之间的相似性或依赖关系就是连接这些节点的边。通过在这个“特征关系网”上进行随机游走马尔可夫过程我们可以找出那些处于网络核心位置、连接紧密的“枢纽”特征。这些特征往往蕴含着更强的判别力。更妙的是这个方法在无监督没有数据标签的情况下也能工作这在实际应用中价值巨大因为给海量数据打标签通常是昂贵且困难的。接下来我将结合自己的实践和理解为你彻底拆解这个方法从原理到实现再到避坑指南。2. 方法原理深度解析为什么是“图”“马尔可夫链”2.1 从特征表到特征关系图传统的数据视图是一个矩阵行是样本列是特征。本文方法的第一个关键转变是视角的转换——我们从关注“样本-特征”矩阵转向构建“特征-特征”关系图。构建图的逻辑节点Vertices每一个原始特征比如图像的一个像素强度、文本中的一个词频都成为图中的一个节点。假设我们有m个特征图G就有m个节点。边Edges边的存在与否以及边的权重由特征之间的“相似性”或“依赖程度”决定。计算任意两个特征fi和fj的相似度。常用的度量包括欧氏距离取倒数或负值转化为相似度、皮尔逊相关系数、互信息等。阈值化为了得到一个稀疏的、更有意义的图我们通常设定一个阈值τ。只有当两个特征间的相似度大于τ时才在它们之间建立一条边。这一步至关重要它过滤掉了微弱或偶然的关联让图的结构更能反映强依赖关系。实操心得相似度度量与阈值选择这是整个方法的第一个“调参点”直接影响图的质量。对于连续数值型特征如图像像素欧氏距离的倒数或高斯核函数是常见选择。对于类别型特征可以考虑互信息。阈值τ的选择没有银弹我的经验是可以尝试将相似度矩阵的所有值排序取中位数或某个分位数如70%分位作为初始阈值。也可以通过观察图的连通性来调整如果图太稀疏很多孤立节点可能阈值太高如果图太稠密几乎全连接则阈值太低失去了筛选意义。一个实用的技巧是先用一个较小的阈值构建图然后逐步增加观察核心特征子集的变化是否趋于稳定。这样我们就把一个高维的、难以直观理解的特征空间转化成了一个可视化的、结构化的网络。在这个网络中联系紧密的特征群可能代表了数据中某个潜在的、共同作用的主题或模式。2.2 马尔可夫链在图上的随机游走与稳态分布图构建好了如何评价每个特征节点的重要性呢这里引入了第二个核心工具马尔可夫链。我们可以把图上的一个“随机游走者”想象成一个探索特征空间的智能体。它的行为规则由转移概率矩阵 P定义这个游走者当前位于节点i。它下一步走到邻居节点j的概率P_{i-j}与连接i和j的边的权重即特征相似度正相关。论文中给出了一种简化的计算方式P_{i-j} r_i / (节点i的度)。其中r_i是特征i与类别标签的相关性如有监督信息在无监督情况下可以初始化为1或根据节点度赋予先验重要性。更通用的做法是直接使用边的归一化权重P_{i-j} w_{ij} / Σ_k w_{ik}即从i出发到各邻居的概率之和为1。关键过程收敛到稳态让这个游走者在图上漫游足够长的时间进行多次状态转移。数学上可以证明对于一个满足一定条件不可约、非周期的马尔可夫链无论从哪个节点开始经过长时间游走后游走者位于每个节点上的概率分布会收敛到一个固定的向量称为稳态分布 π。这个稳态分布π的物理意义非常深刻π_i的值高表示从长期来看游走者访问节点i的概率大。什么样的节点容易被频繁访问呢是那些“交通枢纽”——即本身重要性高如果引入了r_i、且被其他重要节点紧密包围的节点。在图论中这常常对应着节点的特征向量中心性。因此稳态概率π_i就成了我们对特征i进行排序的分数。分数越高该特征越可能处于特征依赖网络的核心在保留数据原始结构和判别力方面就越重要。2.3 与经典方法的理论联系拉普拉斯分數为了论证其合理性论文将本方法与经典的拉普拉斯分數Laplacian Score进行了理论关联。拉普拉斯分數是一种无监督特征选择方法其目标是选择一个特征子集使得数据样本在选定特征上的局部近邻关系由样本相似图定义得以最大程度保持。其目标函数可以简化为最小化f_r^T L f_r其中f_r是第r个特征在所有样本上的取值向量L是样本相似图的拉普拉斯矩阵。这本质上是在要求一个好的特征其在相似样本上的取值应该相近。本文方法的巧妙之处在于它从“特征相似图”而非“样本相似图”出发。论文通过推导证明当我们在特征图上进行随机游走并计算稳态分布时所选出的那些具有高稳态概率的特征同样能够满足“保持数据局部结构”的准则。这意味着通过挖掘特征间的关联来筛选特征间接地达到了保留样本间结构信息的目的。这为该方法在无监督学习中的有效性提供了理论支撑。3. 算法实现与实操步骤详解理论可能有些抽象我们直接上干货看看如何用代码一步步实现这个特征选择算法。我将以Python为例结合常用的科学计算库进行说明。3.1 环境准备与数据加载首先确保你的环境中有以下核心库pip install numpy scipy scikit-learn networkx我们使用论文中提到的COIL20图像数据集进行演示。这个数据集包含20个物体的1440张图片每张图片是32x32的灰度图所以拉平后就是1024维的特征向量。import numpy as np from sklearn.datasets import fetch_olivetti_faces, load_digits from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import networkx as nx # 由于COIL20不是sklearn内置数据集我们用Olivetti Faces或Digits数据集替代演示它们同样是高维图像数据。 # 这里以Digits数据集8x8图像64维为例原理完全相同。 data load_digits() X data.data # 特征矩阵形状 (n_samples, n_features) y data.target # 标签用于后续验证 print(f“数据集形状: {X.shape}”) # 输出: (1797, 64) # 标准化特征使相似度计算更公平 scaler StandardScaler() X_scaled scaler.fit_transform(X)3.2 核心算法实现步骤接下来我们实现论文中描述的算法。我将过程分解为几个清晰的函数。步骤1计算特征相似度矩阵并构建图def build_feature_graph(X, similarity_threshold0.7, metriccorrelation): 构建特征相似度图。 参数: X: 标准化后的数据矩阵形状 (n_samples, n_features) similarity_threshold: 相似度阈值大于此值则建边 metric: 相似度度量correlation相关系数或euclidean欧氏距离需转换 返回: G: networkx图对象 similarity_matrix: 完整的相似度矩阵 n_features X.shape[1] # 计算特征间的相似度矩阵 if metric correlation: # 使用皮尔逊相关系数值域[-1,1]我们取绝对值并映射到[0,1] corr_matrix np.corrcoef(X, rowvarFalse) # rowvarFalse表示每列是一个特征 similarity_matrix np.abs(corr_matrix) # 取绝对值负相关也被视为一种强关系 elif metric euclidean: from scipy.spatial.distance import pdist, squareform # 计算欧氏距离距离越小越相似 dist_matrix squareform(pdist(X.T, euclidean)) # X.T 使计算在特征间进行 # 将距离转换为相似度相似度 1 / (1 距离)。加1防止除零。 similarity_matrix 1 / (1 dist_matrix) else: raise ValueError(“不支持的相似度度量方式”) # 创建无向图 G nx.Graph() # 添加节点 G.add_nodes_from(range(n_features)) # 添加边基于阈值 for i in range(n_features): for j in range(i1, n_features): # 避免重复和自环 if similarity_matrix[i, j] similarity_threshold: G.add_edge(i, j, weightsimilarity_matrix[i, j]) print(f“图构建完成。节点数: {G.number_of_nodes()} 边数: {G.number_of_edges()}”) # 检查图是否连通如果不连通可能需要调整阈值或处理 if not nx.is_connected(G): print(“警告图不是连通的。这可能导致马尔可夫链不收敛。考虑降低阈值或使用最大连通子图。”) # 一个处理策略取最大连通分量 largest_cc max(nx.connected_components(G), keylen) G G.subgraph(largest_cc).copy() print(f“已提取最大连通分量包含 {G.number_of_nodes()} 个节点。”) # 需要重新映射特征索引此处为简化演示我们假设图是连通的。 return G, similarity_matrix步骤2计算转移概率矩阵与稳态分布这是算法的核心。我们使用迭代法求解稳态分布。def compute_stationary_distribution(G, max_iter1000, tol1e-8): 通过幂迭代法计算随机游走在图G上的稳态分布。 参数: G: networkx图 max_iter: 最大迭代次数 tol: 收敛容忍度 返回: pi: 稳态概率向量排序后的特征得分 feature_ranking: 按得分从高到低排序的特征索引 n G.number_of_nodes() # 获取邻接矩阵带权重 A nx.adjacency_matrix(G, weightweight).toarray().astype(float) # 计算转移概率矩阵PP_{ij} A_{ij} / sum_k A_{ik} # 即从i到j的概率等于边权重除以节点i的所有出边权重和 row_sums A.sum(axis1, keepdimsTrue) # 避免除零对于孤立节点理论上已被移除将其行和设为1 row_sums[row_sums 0] 1 P A / row_sums # 初始化随机分布向量均匀分布 pi_t np.ones(n) / n # 幂迭代pi^{t1} pi^{t} * P 注意是左乘即pi是行向量 # 更常见的写法是计算P的转置的右特征向量这里我们迭代计算。 # 稳态分布满足 pi pi * P即pi是P的左特征值1对应的左特征向量。 for iteration in range(max_iter): pi_next pi_t P # 行向量左乘转移矩阵 # 检查收敛 if np.linalg.norm(pi_next - pi_t, ord1) tol: print(f“迭代 {iteration} 次后收敛。”) pi_t pi_next break pi_t pi_next else: print(f“警告在 {max_iter} 次迭代后未完全收敛。”) # 归一化确保和为1 pi_t pi_t / pi_t.sum() # 按稳态概率从高到低排序特征 feature_ranking np.argsort(pi_t)[::-1] # argsort默认升序[::-1]反转得到降序 feature_scores pi_t[feature_ranking] return pi_t, feature_ranking, feature_scores步骤3特征选择与评估得到特征排序后我们可以选择Top-K个特征进行后续建模。def select_features_by_ranking(X, feature_ranking, k): 根据特征排序选择前k个特征。 参数: X: 原始数据矩阵 feature_ranking: 特征索引排序数组从最重要到最不重要 k: 要选择的特征数量 返回: X_reduced: 降维后的数据矩阵 selected_indices: 被选中的特征索引 selected_indices feature_ranking[:k] X_reduced X[:, selected_indices] return X_reduced, selected_indices # 主流程 # 1. 构建特征图 print(“步骤1: 构建特征相似度图”) G, sim_mat build_feature_graph(X_scaled, similarity_threshold0.5, metriccorrelation) # 阈值需要根据数据调整 # 2. 计算稳态分布特征得分 print(“\n步骤2: 计算马尔可夫链稳态分布”) stationary_dist, ranking, scores compute_stationary_distribution(G) # 3. 选择Top-K个特征例如K20 k 20 print(f“\n步骤3: 选择Top-{k}个特征”) X_reduced, selected_feat_idx select_features_by_ranking(X_scaled, ranking, k) print(f“原始特征数: {X_scaled.shape[1]} 选择后特征数: {X_reduced.shape[1]}”) print(f“选中的特征索引按重要性排序: {selected_feat_idx}”)3.3 效果验证与对比实验为了验证我们选出的特征是否有效最直接的方式就是用它们去训练一个分类器看看性能如何。同时我们与经典的无监督方法如方差选择、拉普拉斯分數进行对比。from sklearn.feature_selection import VarianceThreshold, SelectKBest from sklearn.svm import SVC from sklearn.model_selection import cross_val_score import matplotlib.pyplot as plt # 定义评估函数 def evaluate_feature_set(X_selected, y, estimatorSVC(kernellinear, random_state42)): “”“使用交叉验证评估特征子集的分类精度”“” scores cross_val_score(estimator, X_selected, y, cv5, scoringaccuracy) return scores.mean(), scores.std() # 对比方法1方差选择最简单的无监督方法 selector_var VarianceThreshold() X_var selector_var.fit_transform(X_scaled) # 方差选择不会排序我们假设方差大的特征好取前k个 variances np.var(X_scaled, axis0) ranking_var np.argsort(variances)[::-1] X_reduced_var, _ select_features_by_ranking(X_scaled, ranking_var, k) # 对比方法2拉普拉斯分數需要样本相似图这里用sklearn的近似实现 # 注意拉普拉斯分數是基于样本图的与我们的方法基于特征图不同。 from sklearn.neighbors import kneighbors_graph from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import chi2 # 这里需要一个打分函数拉普拉斯分數需要自定义 # 由于sklearn没有内置拉普拉斯分數我们简化处理用基于卡方检验的有监督方法作为另一个对比参照。 selector_chi2 SelectKBest(score_funcchi2, kk) X_reduced_chi2 selector_chi2.fit_transform(X_scaled, y) # 这是有监督的仅作参考 # 评估我们的方法GraphMarkov acc_our, std_our evaluate_feature_set(X_reduced, y) print(f“我们的方法 (GraphMarkov) Top-{k} 特征 平均准确率: {acc_our:.4f} (±{std_our:.4f})”) # 评估方差选择 acc_var, std_var evaluate_feature_set(X_reduced_var, y) print(f“方差选择 (Variance) Top-{k} 特征 平均准确率: {acc_var:.4f} (±{std_var:.4f})”) # 评估有监督的卡方检验作为性能上限的粗略参考 acc_chi2, std_chi2 evaluate_feature_set(X_reduced_chi2, y) print(f“有监督卡方检验 (Chi2) Top-{k} 特征 平均准确率: {acc_chi2:.4f} (±{std_chi2:.4f})”) # 绘制不同K值下的性能曲线可选 accuracies_our [] accuracies_var [] k_values range(10, min(50, X_scaled.shape[1]), 5) for k_val in k_values: X_red_our, _ select_features_by_ranking(X_scaled, ranking, k_val) X_red_var, _ select_features_by_ranking(X_scaled, ranking_var, k_val) acc_our, _ evaluate_feature_set(X_red_our, y) acc_var, _ evaluate_feature_set(X_red_var, y) accuracies_our.append(acc_our) accuracies_var.append(acc_var) plt.figure(figsize(10, 6)) plt.plot(k_values, accuracies_our, b-o, labelOur Method (GraphMarkov)) plt.plot(k_values, accuracies_var, r--s, labelVariance Threshold) plt.xlabel(Number of Selected Features (K)) plt.ylabel(Cross-Validation Accuracy) plt.title(Feature Selection Performance Comparison) plt.legend() plt.grid(True) plt.show()4. 关键参数调优与常见问题排查在实际应用中算法有几个关键参数需要仔细调整也会遇到一些典型问题。以下是我在复现过程中总结的经验。4.1 核心参数解析与调优指南相似度度量与阈值 (similarity_metric threshold)问题这是影响图结构的首要因素。阈值设高了图太稀疏可能丢失重要连接设低了图太稠密噪声边过多导致稳态分布区分度不高。调优建议可视化绘制相似度矩阵的直方图观察相似度值的分布。阈值可以设在分布的双峰谷底或某个百分位如70%。网格搜索在一个合理的范围内如0.3到0.8以0.05或0.1为步长尝试不同的阈值。对于每个阈值运行算法得到特征排序然后用一个简单的分类器如逻辑回归在验证集上评估Top-K特征的性能选择性能最好的阈值。领域知识对于图像数据像素在空间上相邻通常更相关可以考虑使用基于空间距离的高斯核。对于基因数据可以使用基于生物学通路知识的先验网络来构建边。随机游走中的“重启概率” (Teleportation / Damping Factor)问题基础的PageRank或随机游走模型可能会遇到“死胡同”出度为0的节点或“陷阱”强连通子图但无法跳出。这会导致稳态分布不理想或收敛问题。解决方案引入“重启随机游走”或“阻尼因子”α。在每个时间步游走者以概率α按照转移矩阵P行走以概率(1-α)随机跳转到图中的任意一个节点均匀分布或按某个偏好分布。这保证了马尔可夫链的遍历性和稳态解的唯一性。修正后的迭代公式为π^{t1} α * π^t * P (1-α) * v其中v是重启分布向量常设为均匀向量。α通常取0.85。代码修改在compute_stationary_distribution函数中迭代步骤修改为alpha 0.85 v np.ones(n) / n # 均匀重启分布 for iteration in range(max_iter): pi_next alpha * (pi_t P) (1 - alpha) * v # ... 收敛判断特征先验重要性 (r_i)问题在无监督场景下论文中r_i被忽略或设为1。但在有监督或半监督场景我们可以利用标签信息来初始化节点的重要性。应用方法r_i可以设置为特征i与目标标签y的互信息、相关系数绝对值或F统计量。在构建转移概率时可以将r_i作为节点自身的“自环”权重或影响转移到邻居的概率。例如一种改进的转移概率可以定义为P_{i-j} (w_{ij} * r_j) / Σ_k (w_{ik} * r_k)。这会使游走更倾向于走向与标签相关性高的特征节点。4.2 常见问题与解决方案实录图不连通导致收敛问题现象算法运行时警告图不连通或者稳态分布计算不收敛/收敛极慢。诊断使用nx.is_connected(G)检查。如果图由多个连通分量组成转移矩阵P将是可约的导致多个稳态分布迭代可能不收敛到唯一解。解决降低相似度阈值这是最直接的方法让更多的边出现连接不同的分量。使用最大连通子图如代码示例所示提取最大的连通分量进行计算。但要注意这相当于丢弃了其他分量中的特征可能丢失信息。一个更温和的策略是对每个连通分量单独计算稳态分布然后根据分量大小或其他指标进行加权合并。引入重启机制如上所述加入随机跳转阻尼因子可以强制使整个图变得“强连通”从而保证唯一稳态解。计算复杂度高现象当特征数m很大时例如上万维计算m x m的相似度矩阵和存储图会消耗大量内存和时间。优化策略稀疏化使用较小的阈值确保相似度矩阵和图的邻接矩阵是稀疏的。使用scipy.sparse格式存储矩阵。近似最近邻对于大规模数据精确计算所有特征对之间的相似度不可行。可以使用近似最近邻算法如Annoy, Faiss, HNSW来为每个特征快速找出最相似的K个邻居只在这些邻居间建边。分块计算将特征分组分别计算组内和组间的相似度。迭代求解优化对于超大图幂迭代可能较慢。可以使用ARPACK等库来高效计算主特征向量。选择的特征“冗余”现象选出的Top-K个特征得分都很高但它们之间可能高度相关信息重叠严重。分析这正是本方法希望解决的问题——通过图结构高度相关的特征会形成团clique而随机游走倾向于访问团中的节点。但有时我们可能希望从每个团中只选一个代表。后处理策略在得到特征排序后可以加入一个“去冗余”步骤。例如按得分从高到低遍历特征如果当前特征与已选特征集合的平均相似度超过某个阈值则跳过它。这可以在保证判别力的同时增加特征多样性。在非图像数据上效果不佳现象如论文实验结果所示该方法在图像数据COIL20, ORL上表现优异但在文本BASEHOCK和生物微阵列数据上可能不如有监督的Fisher Score。根本原因论文中使用欧氏距离计算特征相似度这对数值型、空间结构明显的图像特征很有效。但对于文本的TF-IDF向量或基因表达数据欧氏距离可能不是最佳度量。改进方向定制相似度度量。这是该方法适应不同领域的关键。文本数据使用余弦相似度或Jaccard相似度来衡量词向量之间的相似性。生物数据使用基于基因共表达网络或蛋白质互作网络PPI的先验知识来定义边而不是单纯基于表达值的统计相似度。通用策略可以尝试多种相似度/距离度量如曼哈顿距离、余弦距离、互信息并通过下游任务性能来选择最优者。5. 方法优势总结与扩展思考经过上面的拆解和实践我们可以清晰地看到这种基于图挖掘与马尔可夫链的特征选择方法的独特优势突破独立性假设它明确地建模并利用了特征之间的依赖关系更符合真实数据的特性。物理意义可解释选出的特征子集对应着特征关系图中的“核心节点”或“枢纽”这些特征往往在领域内有明确的解释如图像中的关键轮廓区域、文本中的核心主题词。天然适用于无监督学习不依赖数据标签这在标注成本高昂的现实场景中极具吸引力。与下游模型解耦作为一种过滤式方法它的计算独立于任何具体的分类或回归算法计算一次特征得分可以用于后续任何机器学习任务。灵活性高图构建和随机游走模型可以融入领域知识如定制相似度、添加节点先验具有很强的扩展性。扩展思考与未来方向 在我自己的实验和思考中我觉得这个方法还有不少可以深挖和优化的点动态图与迭代优化是否可以不止步于构建一次静态图也许可以先用初始选出的特征训练一个弱模型根据模型反馈调整特征间的边权重例如增加对分类贡献协同的特征之间的权重进行迭代式特征选择。融合多源信息对于多模态数据如图像文本可以构建多层图或异构图在不同类型的特征节点间定义边从而进行跨模态的联合特征选择。处理超大规模特征需要与更高效的近似图学习、稀疏编码技术结合以应对互联网级别的高维特征选择问题。理论深入稳态分布PageRank值与特征判别力之间的理论联系是否可以进一步严格化能否推导出基于图结构的特征重要性理论下界总而言之将特征选择问题转化为图上的节点重要性排序问题是一个非常有洞察力的视角。它借助了成熟复杂的网络科学工具来解决机器学习中的经典难题。虽然实现过程中有不少细节需要打磨但这条路径为处理高维、复杂关联的数据提供了新的、有力的工具。在实际项目中尤其是当你面对没有标签的数据或者怀疑特征间存在复杂关联时不妨尝试一下这个思路它可能会给你带来意想不到的收获。

相关新闻