平衡谱特征选择:无监督降维中如何兼顾流形结构与判别信息 📅 发布时间:2026/9/9 17:29:36 👁 浏览次数: 搞无监督特征选择的人应该都听过“谱方法”这个词。但大多数项目里大家默认用的还是方差过滤、相关性分析、卡方检验这些最朴素的方案总觉得谱特征选择门槛高、调参麻烦、收益不透明。直到我认真看了TCYB-2022上的这篇《Balanced Spectral Feature Selection》才意识到问题不在于谱方法不行而在于很多经典谱方法只解决了一半问题——它们在努力拟合局部图结构却牺牲了对判别信息的保留。所谓“Balanced”就是要把这两件事摆到同一个目标函数里去平衡而不是让某一方主导。这篇文章想解决的场景很明确当你要从几百上千个特征里挑出一部分用来做聚类、可视化、或者给下游模型降维时传统的谱特征选择方法往往选出的特征高度相似甚至把无用但平滑的特征排在前面。Balanced Spectral Feature Selection的思路是在谱嵌入框架里同时约束局部流形结构和全局判别能力用行稀疏化的方式让最终选出的特征既保留图结构又不过度冗余。对做数据预处理、特征工程、无监督学习的同学来说这个方法是一个值得认真落地的备选方案。下面我会先从原理层面拆解它的动机再给出一条我自己复现过、跑通了的完整链路最后重点讲实验里那些文章不会写但实际一定会踩的坑。1. 为什么“平衡”两个字值得单独拿出来聊1.1 无监督特征选择里的“谱”到底在谱什么要理解Balanced Spectral Feature Selection得先回到谱特征选择的基本逻辑。我们手里有一批样本每个样本有高维特征。没有标签的情况下怎么判断哪些特征重要一个经典假设是数据在低维空间里是有结构的相近的样本在原始特征空间里也应该相近。谱方法做的事情就是先根据样本之间的相似度构建一个图然后用图拉普拉斯去刻画特征在这个图上的光滑程度。具体来说我们会为每个特征计算它在图上的“拉普拉斯分数”分数越低说明这个特征在相邻样本之间变化越平缓也就是越符合流形假设。Laplacian Score是这个思路的代表也是入门必看的方法。它很直观但局限也很明显它只看单个特征与图的一致性完全不考虑特征之间的冗余更不考虑多特征组合起来是否能区分不同的簇。于是后续有了SPEC、UDFS、NDFS等改进试图把谱信息嵌入和稀疏学习结合起来。但这里有个裂缝很多方法在构造目标函数时把“拟合图嵌入”当成唯一目标结果就是选出来的特征在局部很平滑却可能在全局上毫无区分度。举个极端例子所有样本在某个特征上的取值都近似等于一个常数这个特征在图上的拉普拉斯分数会很低因为它均匀得离谱但它对聚类毫无价值。单纯追求图平滑就会选出这种“漂亮但没用”的特征。1.2 经典方法只做了一半我早期用Laplacian Score做过一个电商用户分群项目特征有四百多个里面有大量用户活跃天、访问频次这类分布极度偏斜的字段。跑完拉普拉斯分数排在最前面的几乎全是稀疏到接近常数的风控标记字段唯一的作用就是它们在图里特别“平整”。但真正能区分用户消费偏好的字段全都沉在榜单中间。这个例子让我很受触动——谱方法对图结构的尊重确实很强但“平滑”不代表“有用”。后来的UDFS在拉普拉斯项之外加入了Frobenius范数损失让特征选择矩阵同时学到判别投影NDFS则把聚类和特征选择放进同一个框架迭代更新聚类标签和特征选择矩阵。思路方向对了但代价是模型复杂度明显上升尤其是聚类标签初始化和特征选择矩阵迭代都容易掉进局部最优。而且这些方法在目标函数里图拟合项和判别项通常是加性组合权重完全靠经验调结果要么还是偏向图结构要么判别项喧宾夺主把流形信息毁了。1.3 把“平衡”拆开看结构保持、判别能力、冗余控制Balanced Spectral Feature Selection说的“平衡”我理解下来至少包含三个层面。第一是局部与全局的平衡。局部看样本邻居关系全局看簇间分离程度。目标函数里不能只有图拉普拉斯平方和也要有嵌入空间里类间散度的约束。第二是相关性与互补性的平衡。特征选择切忌只看单特征得分两个特征单独看都很强放一起几乎是同一个信息那选一个就够了。目标函数需要用特征选择矩阵的行范数惩罚来控制冗余因为行范数大意味着这个特征对多个维度都有贡献自然更可能承载多样性信息。第三是结构复杂度和可解释性的平衡。方法不能为了效果把图嵌入维度搞得和特征数一样高那样计算量大到工程上不可用而且选出来的特征缺乏稳定性。这三层平衡恰恰是TCYB-2022这篇论文最核心的贡献。它不是第一个提出谱特征选择的论文但它把“平衡”这个容易被忽视的细节拿到了台面上用可求解的优化形式把三个目标拧在一起。2. 从TCYB论文中提取的核心建模逻辑2.1 图构建与邻域阈值我把论文思路落地时首先处理的是图构建。这一步听起来简单实际有很大操作空间。经典做法是用k近邻或ε近邻构建无向加权图权重用热核函数计算( S_{ij} \exp(-||x_i - x_j||^2 / \sigma) )但这里有个关键细节特征选择时用的样本距离是在原始高维空间里算的可高维空间的距离有意义的前提是特征尺度一致。如果特征量纲不统一欧氏距离会被量纲大的特征主导整个图就废了。所以实际工程里要先做标准化再做距离计算再去构建图。文本场景还要考虑用余弦距离图像特征则常用欧氏距离配合白化预处理。邻域大小k的选择也直接影响后续平衡效果。k太小图可能断成多个连通分量拉普拉斯矩阵出现多个零特征值优化时特征向量不稳定k太大图的局部性消失流形结构被过度平滑Balanced方法里“局部”这一项就形同虚设。我在不同数据集上的经验样本量在几千到一万时k取5到15之间比较合理样本量很大时k可以适当放大但不要超过ln(n)的2到3倍。2.2 目标函数如何同时容纳局部与全局信息论文的目标函数整体上可以看成这样一个优化问题给定特征矩阵 (X \in \mathbb{R}^{n \times d})和图拉普拉斯矩阵 (L \in \mathbb{R}^{n \times n})我们想找一个特征选择矩阵 (W \in \mathbb{R}^{d \times c})以及一个嵌入表示 (Y \in \mathbb{R}^{n \times c})然后最小化[ \mathcal{L} \alpha \cdot Tr(Y^T L Y) ||Y - X W||F^2 \beta \cdot ||W||{2,1} \gamma \cdot \text{Disc}(Y) ]几个项的作用分别是(Tr(Y^T L Y))让嵌入表示Y在图上尽量平滑。Y是样本的低维表示如果两个样本在图中是邻居它们的Y表示差异应该小。这一项把流形结构带进了学习。(||Y - X W||_F^2)要求低维嵌入Y可以被原始特征X的线性组合近似。这一步很关键它逼迫特征选择矩阵W去重建能被图结构认可的表示而不是单纯去拟合图。(||W||_{2,1})行稀疏正则。W的每一行对应一个特征的全部维度。行范数小意味着这个特征对重建嵌入贡献微弱最终会被淘汰行范数大的特征被保留。这样就把“选哪些特征”变成了“哪些行比较大”的自然结果。(\text{Disc}(Y))判别项鼓励样本的嵌入表示在不同簇之间更加分离。实际实现中可以用谱聚类得到的簇标签来构造类间散度矩阵或者用核化类间距离。这里的关键设计是Y和W是交替优化的。初始化Y时通常用拉普拉斯矩阵的广义特征分解求出前c个特征向量作为嵌入的初值。后面固定Y对W求解一个带L2,1正则的线性回归再用更新过的W去构造新的相似度或更新Y。如此反复直到收敛。2.3 求解过程中的数值稳定性处理直接套用这个目标函数数值上是很容易出问题的。一是拉普拉斯矩阵的特征分解L可能是半正定的大规模稀疏矩阵求前c个广义特征向量时如果直接拿numpy求全部特征值内存直接爆炸。正确做法是用ARPACK或LOBPCG这类稀疏特征求解器只求最小的k个特征值及对应向量。二是W的L2,1范数在原点不可导单纯用梯度下降会遇到数值抖动工程上更稳妥的方式是写成迭代重加权最小二乘每次更新时给W的每一行一个权重权重等于该行范数的倒数加一个小常数然后转成加权的岭回归求解。虽然多几次迭代但每次都是线性闭式解整体非常稳。我有一段时间直接用现成ADMM库去解PyADMM性能不太行大矩阵上迭代几百轮时间完全不可接受。后来改回“重加权最小二乘稀疏特征分解”的组合拳效果和速度都立刻对味了。这也印证了一个经验做特征选择这种底层模块尽量别把自己的效率绑架在通用优化库上针对问题结构手写迭代往往更实用。3. 一条能自己动手复现的算法链路3.1 从零开始的核心代码骨架下面是我整理的一份最小可复现的代码骨架去掉了论文里一些花哨的变体保留了平衡谱特征选择的骨架。用协方差矩阵和邻接图模拟方便大家直接跑通。import numpy as np from scipy.sparse.csgraph import laplacian from sklearn.neighbors import kneighbors_graph from scipy.sparse.linalg import eigsh def balanced_spectral_selection(X, k5, c2, alpha1.0, beta1.0, max_iter30, tol1e-4): n, d X.shape # 标准化提到最前面 X (X - X.mean(axis0)) / (X.std(axis0) 1e-8) # 1. 构建k近邻图 热核权重 graph kneighbors_graph(X, n_neighborsk, modeconnectivity, include_selfFalse) graph 0.5 * (graph graph.T) # 对称化 # 简单热核 from sklearn.metrics import pairwise_distances dist pairwise_distances(X) knn graph.toarray() S np.exp(-dist**2 / (np.median(dist[dist 0])**2)) * (knn 0) S np.maximum(S, S.T) L laplacian(S, normedTrue) # 2. 初始化Y拉普拉斯前c个特征向量 evals, evecs eigsh(L, kc, whichSM, maxiter1000) Y evecs[:, :c] W np.zeros((d, c)) # 3. 交替迭代 for it in range(max_iter): # 固定Y解带L2,1正则的线性回归 # 先计算行权重矩阵D_w row_norms np.linalg.norm(W, axis1) 1e-6 D_w np.diag(1.0 / (2.0 * row_norms)) # 岭回归闭式解 W_new np.linalg.solve(X.T X beta * D_w 1e-4 * np.eye(d), X.T Y) # 固定W更新Y这里用谱回归近似 Y_new X W_new # 可选Y做一次图平滑 Y_new np.linalg.solve(np.eye(n) alpha * L, Y_new) if np.linalg.norm(Y_new - Y, fro) tol * np.linalg.norm(Y, fro): W W_new Y Y_new break W, Y W_new, Y_new feature_score np.linalg.norm(W, axis1) rank np.argsort(feature_score)[::-1] return rank, feature_score这个骨架省略了判别项但已经比单纯Laplacian Score多了一个联合优化环节。如果你想加入判别项可以在Y更新之后对Y做一次k-means得到伪标签再计算类间散度叠加到Y的更新里。注意迭代次数别太多否则容易过拟合到初始化标签上。3.2 数据集与基线选择我在复现时用了三类数据。一类是UCI的Wine、Ionosphere样本量小特征中等适合快速验证逻辑正确性一类是MNIST的784维像素特征用来测高维时的稳定性还有一类是20Newsgroups的文本TF-IDF矩阵维度破万用来检验行稀疏正则在大规模场景下是否还靠谱。基线一定要放Laplacian Score和无监督判别特征选择。因为Balanced方法本质上是对这两类的整合如果跑不过这两个基线说明你的实现平衡没调好而不是方法本身有问题。另外方差过滤和完全随机选择也值得放进来它们能给你一个“地板”参考。我在多个数据集上都发现一个现象特征维度特别高时很多谱方法的上限只比随机选择高几个点Balanced方法能看到明显的提升但这个提升需要用可靠的评价指标来度量。3.3 聚类效果怎么衡量才公平特征选择没有标签怎么判断好坏最常用的是选出一批特征后在这个特征子集上做聚类然后和真实标签比较。但这里有个大坑聚类算法本身有随机性k-means的初始点不同跑出来的NMI完全不一样。我一开始只跑一次k-means某次实验中Balanced方法的NMI忽高忽低差点得出错误结论。后来我改成固定随机种子重复20次聚类取中位数才看到相对稳定的提升。更有说服力的做法是结合下游任务。比如用选出的特征训练一个监督分类器虽然特征选择时没用标签但最终评价可以用分类准确率来判断“这组特征有没有保留判别信息”。文本场景我习惯用SVM图像场景用简单的逻辑回归。这个“无监督选择监督验证”的组合在跟业务方沟通时特别好使因为NMI这种指标很难讲清业务价值分类准确率却谁都能听懂。4. 实验里最容易翻车的几个细节4.1 图的尺度参数到底怎么定这是最让我头疼的环节。热核参数σ如果设置太大所有权重都趋近于1图太平滑设置太小只有距离非常近的点才有连接流形结构碎成一地。论文里通常会说“根据经验调参”但这句经验对不同数据集根本不好使。我后面的做法是先计算样本间的成对距离取所有非零距离的中位数作为σ的基准再用α倍中位数做网格搜索α从0.2到3.0。更重要的是观察选出来特征的稳定性如果相邻α值对应的特征重合度只有百分之五六十说明σ这个位置太敏感需要重新预处理数据如果特征重合度稳定在八成以上那这个σ就是可信的。另一个容易忽略的点是构建图之前一定要对特征做标准化但如果你做的是文本TF-IDF标准化又可能破坏稀疏结构。文本场景我更倾向于先做L2行归一化再用余弦距离转成相似度。一句话总结图构建的预处理方法对结果的影响比目标函数里α、β的影响大得多务必花时间先定它。4.2 特征选择比例与嵌入维度的耦合问题Bravo这里有个很少有人讲清楚的关系你再怎么调平衡项特征选择比例m和嵌入维度c如果配比不对结果照样崩。我的经验是嵌入维度c代表你希望保留多少个“结构方向”而选择的特征数量m至少要能撑起这几个方向。如果c10却只选5个特征那么总会有方向找不到特征支撑等于那些结构信息在特征空间里被硬生生抹掉了。我常用的配置是c取聚类预期簇数的2到3倍m取特征总数的20%到50%。如果你的项目提前知道要聚类成5簇那c设10左右比较稳妥如果对簇数没概念用谱聚类里的特征值gap先估一个范围再定c。不要迷信“嵌入维度越大越好”c太大时Y的每一维可能只是在拟合噪声目标函数里的判别项反而会放大随机簇的差异。4.3 归一化顺序错了结果差一个档次我踩过一个特别低级的坑先算相似度矩阵再对特征做标准化。结果就是全数据集的大幅值特征在原始距离中占据了绝对主导标准化之后图结构已经固定你再怎么改目标函数也救不回来。正确顺序一定是先特征标准化/归一化再构建图再进行谱分解。虽然这听起来显然是先处理后建图但实际代码里很容易把顺序写反因为数据集读取时你可能天然觉得“先算特征矩阵然后建图”而标准化模块是后加的一不小心就加到了建图之后。类似的问题还有特征矩阵是否中心化。拉普拉斯图本质上对特征的均值偏移不敏感因为热核距离考虑了均值但L2,1回归项对均值敏感因为XW的拟合会受到均值影响。因此我建议在进模型之前对所有特征做一遍零均值标准化。若每个特征方差差异很大而不处理最后的行范数会偏向高方差特征导致选出来的特征和方差过滤差不多完全丧失谱结构的意义。4.4 收敛判据与初始化敏感度迭代优化最常见的问题是卡在局部最优。我试过把W初始化为全零矩阵结果第一轮迭代里行权重全部变成同一个常数后续更新慢得要命。更稳的做法是用Laplacian Score排序的前c个特征对应的方向来初始化W或者直接用随机高斯矩阵但固定随机种子。迭代终止条件也别只看目标函数变化量最好同时看W的排序变化如果两轮迭代之间特征排名重合度超过99%说明已经稳定没必要继续烧算力。另外因为目标函数里有L2,1范数它的行权重在原点附近特别敏感。某些特征的行范数会在优化过程中反复横跳这通常不是平衡权重的问题而是线性回归部分的特征共线性太强。解决办法是在X.T X加上一个小的岭项我习惯加1e-4到1e-3的系数能显著提升稳定性而且不会改变特征选择的整体排序。5. 方法边界与我的默认取舍5.1 什么时候效果明显什么时候并不值得Balanced Spectral Feature Selection不是万能的。在我实测过的场景里它对手写数字、图像HOG特征、文本TF-IDF这类本身具有局部流形结构的数据效果明显往往比Laplacian Score高3到5个NMI点比方差过滤高更多。但如果你处理的是高维稀疏的one-hot类别特征基本没有局部结构可言图方法就无从发力。这种场景下老老实实用基于频次和方差的方法反而更可靠。另一个不值得用的场景是特征数量极其庞大但样本量很小比如基因表达数据n只有几十d有上万。此时光构建一个k近邻图就已经充满噪声更别提后面的特征分解和回归。我试过在n50d5000的模拟数据上跑结果特征选择结果比随机选择还差。不是说论文方法有问题而是方法的适用前提——可靠的距离度量——在此被样本量限制死了。5.2 与谱聚类嵌入、监督信息结合的可能这个方法是纯无监督的但我在项目里经常把它当特征预处理层而不是最终结果。比如先用Balanced方法选出一批候选特征再结合业务定义的标签做一次监督过滤效果比只用单边方法要好。原因也简单Balanced方法把流形结构的信息压缩进特征子集监督过滤把业务关心的目标压缩进去两者叠加信息更完整。我还在一个推荐系统项目里把选出的特征当成排序模型的输入对比直接用全量特征模型训练速度大幅提升AUC只掉了0.001左右。这其实是个很实用的定位——它不追求在某个指标上超越全量特征而是用少量特征去逼近全量特征的效果这在高维业务场景中反而很有价值。5.3 我做个人项目时的默认参数组合如果你不想从零调参可以参考我最后固定下来的一套参数组合它在我手头多个数据集上都拿到了能接受的结果标准化z-score图构建k5近邻边权采用热核σ取中位距离嵌入维度c聚类预期簇数的2倍通常设4到10目标函数权重α0.1β10γ0.5特征选择比例30%到40%迭代次数30轮以内配合收敛判据需要注意的是β取10看起来很大但L2,1范数对行范数的绝对值贡献往往远小于重构误差权重太小等于没加特征选择就退化成线性回归的保序结果。这个参数在我项目中几乎是设置后就不用再动的反而α需要根据数据噪声适当调整。数据噪声大时α降低一些避免过度强调图平滑把噪声点也当成了流形结构。另外如果你不是在做学术对比而是业务落地我建议最后一定要看一眼选出来的特征列表到底是不是业务上有意义的字段。谱方法最大的缺点就是它只看结构不看业务含义。哪怕NMI提升了如果选出的特征在业务上完全无法解释那它只能作为离线参考不能直接上生产。我通常会把选出的特征再做一次业务映射保留三到五个最强字段再配合人工规则决定最终上线名单。复现这个思路最大的收获不是拿到了一个多强的特征选择器而是彻底理解了一个道理在真实数据里“平滑”和“有用”之间永远存在一条细线你说不清它在哪里但你可以用一个平衡的目标函数去逼近它。以后再遇到高维特征选择的活我不再急着套方差或卡方而会先问一句这份数据的流形结构是什么如果答不上来那再先进的谱方法也只是在给噪声画皮而已。