ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

数据降维全解析:从PCA到UMAP的方法选型与实战避坑

数据降维全解析:从PCA到UMAP的方法选型与实战避坑 我刚入行那会儿接了一个用户画像项目特征工程做完表里躺着一千多列。模型倒是能跑但特征之间互相纠缠业务方追问这个指标为什么重要的时候我完全答不上来。后来才想明白我当时缺的不是更多特征而是一套系统的数据降维思路。数据降维简单说就是在尽量保留原始信息的前提下把高维数据压缩到低维空间。它在实际项目里几乎无处不在做可视化要看二维/三维投影训练模型要缓解维度灾难业务分析要剔除冗余特征。这篇文章不打算堆公式而是把数据降维方法按类别从头到尾梳理一遍——不同方法解决什么问题、底层逻辑是什么、什么场景下选哪种以及我实际项目中踩过的坑。无论你是刚接触特征工程的初学者还是已经在调参路上摸爬滚打了一阵的工程师这套分类框架都能帮你少走弯路。1. 先回答一个前置问题你到底为什么要降维很多人一上来就打开 sklearn 调 PCA但降维之前最该问的一句话是我到底为了解决什么问题目的不同选的方法可以完全不一样。1.1 维度灾难特征一多数据反而变稀特征数量上去之后数据在高维空间里会变得异常稀疏。举个例子100 个样本在一维数轴上能把区间铺得很满但到了 10 维空间里每个样本周围几乎是空的样本之间的距离全都变得差不多。这就直接导致基于距离的算法KNN、K-Means、基于核的模型失效因为最近邻和最远邻之间的距离差异小到可以忽略。模型的复杂度也会随维度爆炸式增长。想要在一个高维空间里把函数拟合好需要的样本量是指数级上升的。特征从 10 个加到 100 个样本量还是原来的几千条模型很快就过拟合到噪声上去了。1.2 共线性、噪声与计算压力第二个常见场景是特征之间高度相关。你做特征工程时新构造的特征往往和原始特征高度冗余比如消费金额和消费次数×平均客单价。这些共线性特征会让线性模型的系数估计变得极不稳定今天参数是这个值明天换一批数据就变另一个值解释起来非常尴尬。还有很现实的计算压力。一位热编码、文本 TF-IDF、用户行为交叉特征动不动就是几万维。很多算法的时间复杂度是随特征数平方甚至立方增长的不降维训练一轮就要等到天荒地老。光谱上高维数据里还混着大量无关噪声降维本身也是一种去噪手段。1.3 不同目标指向不同方法我习惯把降维的目的归纳成四类可视化把数据压到 2D 或 3D用眼睛看聚类结构。这类需求首选 t-SNE、UMAPPCA 也能当快速预览。建模预处理缓解维度灾难、消除共线性、压缩特征维度。常用 PCA、LDA、SVD大样本复杂结构上会用自编码器。压缩存储用低维表示代替原始高维向量。自编码器、PCA 的低秩近似都行。业务解释要跟业务方讲清楚为什么是这些特征。这时候老老实实做特征选择别用什么主成分。把这四个目的记在脑子里再看下面所有方法就不会觉得是一团乱麻了。2. 降维的第一道分水岭特征选择与特征抽取数据降维方法如果要画一张总分类图最顶层的分叉一定是特征选择和特征抽取。这两派的思路本质上是相反的。2.1 特征选择从原始特征里挑特征选择做的是一件很朴素的事原始特征一个都不改只是把里面没用的、冗余的删掉留下一个有代表性的子集。它的核心是定义什么算有用。按评价方式可以分成三类过滤式Filter不依赖任何模型直接算统计量。比如方差过滤方差太小的特征基本是常数、相关系数过滤去掉跟目标弱相关或彼此高度相关的特征、卡方检验、互信息。优点是快缺点是完全不顾特征组合起来的效果。包裹式Wrapper以模型效果为评价标准反复尝试不同的特征子集。经典的有递归特征消除RFE、前向选择、后向剔除。效果通常更好但计算开销很大特征上千时基本跑不动。嵌入式Embedded把特征选择融进模型训练过程。最有代表性的就是 Lasso 的 L1 正则它会把一堆特征的系数压成 0树模型的特征重要性也可以归到这一类。速度和效果比较均衡是我在实际项目里用得最多的一类。特征选择最大的优点是可解释性极强——留下的还是原来的字段客户年龄就是客户年龄。缺点是它只能在已有特征里挑无法组合出新的、信息密度更高的表示。2.2 特征抽取从原始特征里造特征抽取相反它不保留原始特征而是把原始特征通过某种变换组合成一组全新的、维度更低的变量。PCA 的主成分、LDA 的判别方向、自编码器的瓶颈向量都是造出来的新特征。这类方法的优势是压缩能力强能最大限度去除特征间的冗余结构把信息集中到少数几个新维度里。代价是新的变量几乎没有直观含义——主成分是几十个原始特征的线性组合你很难跟业务方解释第三主成分到底代表什么。2.3 两者的边界由任务说了算很多初学者纠结到底该用特征选择还是特征抽取我的答案很简单看你要不要跟人解释。在金融风控、医疗诊断这类强监管、强解释需求的场景我几乎只用特征选择因为模型上线后要回答监管的质疑主成分说不清楚。但在图像、文本、推荐系统这类只看最终效果的场景特征抽取是绝对主流没人关心第 17 个主成分是什么。对比维度特征选择特征抽取降维后特征原始特征子集新构造的组合特征可解释性高保留业务语义低难解释信息利用只能丢弃不能组合可以组合出高密度信息典型方法Filter / Wrapper / EmbeddedPCA、LDA、t-SNE、UMAP、自编码器典型场景风控、医疗、业务解释视觉、文本、推荐、建模压缩实践中也可以两者叠加先用嵌入式特征选择砍掉明显没用的特征再用 PCA 压到几十维喂给模型。我自己经常这么干效果比单用一类好不少。3. 线性降维三件套PCA、SVD、LDA 的原理与边界特征抽取这棵树下按是否假设线性关系又分成线性派和非线性派。线性派是入门门槛最低、应用最广的一类核心就三个名字PCA、SVD、LDA。3.1 PCA在方差最大的方向上投影主成分分析Principal Component Analysis的思想非常朴素找一组正交方向把数据投影上去让投影后的方差尽可能大。方差大意味着信息保留得多丢掉的方向方差小丢掉也不心疼。具体做法是先把数据中心化每个特征减均值然后计算协方差矩阵对这个矩阵做特征值分解。特征值大的方向就是主成分方向特征值本身代表该方向上的方差。投影后第 k 个主成分解释的方差比例就是第 k 个特征值 / 所有特征值之和把特征值从大到小排列前 k 个累计解释比例达到 80% 或 90% 时你就可以取 k 个主成分。画一个特征值-序号的肘部图看拐点在哪里是最常用的选 k 方式。这里有个前提必须强调特征必须做标准化。否则量纲大的特征方差天然就大PCA 会优先保它结果全被消费金额万元这种大数特征带跑了年龄、频次这类小数特征直接被忽略。3.2 SVDPCA 背后的计算引擎奇异值分解Singular Value Decomposition很多人把它当成和 PCA 并列的另一种方法这是个常见的误解。更准确的说法是SVD 是 PCA 最稳定的计算方法。对中心化后的数据矩阵 X 做 SVD得到 X UΣVᵀ其中 V 的列向量恰好就是 PCA 要求的主成分方向Σ 里的奇异值与特征值之间只差一个与样本量相关的常数。也就是说PCA 的本质计算步骤可以直接用 SVD 完成而且 SVD 不需要先算协方差矩阵数值稳定性更好尤其适合特征数远大于样本数p n的情况。实际工具里更常用的是 TruncatedSVD也就是截断 SVD——只算前 k 个奇异值和对应的向量不把完整分解算出来。它最大的好处是能直接处理稀疏矩阵。文本 TF-IDF 出来动辄几万维还是稀疏的用普通 PCA 得先稠密化内存直接爆掉而 TruncatedSVD 可以直接上手这就是经典的自然语言处理技术 LSA潜在语义分析的底层实现。3.3 LDA有监督降维的代表线性判别分析Linear Discriminant Analysis和 PCA 最大的不同是它用到了标签。PCA 只关心方差LDA 关心的是投影后类间距离尽量大、类内距离尽量小。它的优化目标是一个比值类间散度除以类内散度。让这个比值最大找到的方向就能把不同类别尽量分开。LDA 有一个硬性限制降维后的维度最多不超过类别数减一C-1。二分类问题LDA 最多只能降到 1 维。很多做分类的同事喜欢先用 LDA 降维再喂给其他分类器因为它在压缩的同时还考虑了类别信息通常比无监督的 PCA 更适合分类任务。但它的假设比较强——要求各类别近似服从高斯分布且协方差矩阵相近数据分布严重偏离时效果会打折。3.4 三件套怎么选方法是否有监督优化目标降维后维度上限典型场景PCA无投影方差最大min(n, p)探索分析、去相关、特征压缩SVD / TruncatedSVD无最小化重建误差低秩近似min(n, p)文本稀疏矩阵、推荐系统、LSALDA有类间散度 / 类内散度比值最大C-1有标签的分类任务前降维我的经验是拿到新数据无脑先跑一遍标准化 PCA 看结构永远是最快建立直觉的方式。如果标签信息非常重要且数据量不大再叠加 LDA 对比效果。SVD 则是在数据稀疏、内存受限时的救命稻草。4. 当线性假设失效核技巧、流形学习与 t-SNE线性方法虽然好用但它的天花板很清楚数据在原始空间里必须是线性可压缩的。现实中的数据往往没这么乖巧。4.1 一条卷起来的瑞士卷流形学习的教科书里最爱举的例子是瑞士卷三维空间里一张卷起来的薄片数据其实躺在一个二维流形上但它在三维空间里是弯弯曲曲的。PCA 在这个数据上表现很差因为它只会找全局方差最大的直线方向强行投影会把瑞士卷压成一团乱麻本来分离的两种点的投影会重叠在一起。这种现象在真实数据里很常见手写数字的像素空间、人脸图像的原始像素、传感器波形本质都嵌在高度弯曲的低维流形上。线性假设一旦不成立就该请出非线性方法。4.2 核主成分分析与经典流形学习非线性降维的第一波尝试是核方法。核主成分分析KPCA的思路是原始空间里数据是弯的但如果用一个核函数把数据隐式映射到更高维的空间高维空间里数据反而可能是线性的再做一次普通 PCA 就行。RBF 核高斯核是最常用的选择。KPCA 的问题是新维度非常难解释而且核矩阵的计算量随样本量平方增长上万条样本就开始吃力。紧接着是真正的流形学习家族Isomap核心是把欧氏距离换成测地距离。两点之间不是直线距离而是沿数据流形表面走的最短路径。先构建近邻图再算图上的最短路径最后用多维缩放MDS在低维空间里还原这些距离。它对邻域参数敏感噪声大时容易在图上连出捷径结果崩掉。LLE局部线性嵌入假设每个点可以由它的 k 个近邻线性重构重构权重保留局部几何结构再在低维空间里让这些权重尽量不变。它比 Isomap 快但对噪声和 k 的选择同样敏感。这两兄弟在 scikit-learn 里都还在但我这几年已经很少在实际项目里首推它们了因为它们对超参数太敏感且稳定性和速度都被后来的 UMAP 碾压。4.3 t-SNE为可视化而生的非线性降维t-SNEt-distributed Stochastic Neighbor Embedding是可视化界的扛把子。它的思路很有意思在高维空间里把点之间的距离转成概率分布——近的点概率高远的点概率低然后在低维空间里也构造一个概率分布让这两个分布尽量一致用 KL 散度衡量差异不断迭代优化。它和 PCA 有个非常大的区别t-SNE 优化的是局部结构——邻居关系保持住了但全局的簇间距离没有意义。簇之间的远近、大小、密度都不能拿来解读。另一个特点是每次运行结果都可能不同因为它的优化是带随机性的。实操中几个参数值得注意perplexity困惑度可以理解为每个点眼里有多少个邻居一般取 5~50。太小局部噪声被放大太大全局结构虽然显现但计算很慢。我常用 30 作为起点。n_iter迭代次数太少了还没收敛图是花的5000 起步比较稳。样本量几万条以内还好几十万条就很慢通常先抽样再做 t-SNE。还有一条铁律我放在这里后面还会再讲t-SNE 的结果只用来看不要拿它当特征喂给模型。5. 深度时代的降维UMAP 与自编码器前面讲的方法都有十几年甚至几十年的历史了但降维这个领域并没有原地踏步。近几年项目里我明显感受到两个新选择在快速普及UMAP 和自编码器。5.1 UMAP又快又能保留结构的后起之秀UMAPUniform Manifold Approximation and Projection是 2018 年前后火起来的理念上受流形学习和拓扑数据分析启发。它在高维空间里构造一个带权重的近邻图用来刻画数据的局部流形结构然后在低维空间里用类似的办法构造另一个图让两个图尽量一致。相比 t-SNEUMAP 的几个优势非常实际速度快百万级样本也能跑t-SNE 到十万条就让人抓狂。全局结构保留得更好簇和簇之间的距离有一定含义不像 t-SNE 那样完全失真。可以对新样本做映射训练好之后能用 transform 方法把新数据投到同一个低维空间这在工程上太重要了。经典 t-SNE 没有这个能力新数据来了只能和旧数据混在一起重新跑。UMAP 有两个核心超参数n_neighbors默认 15越小越关注局部越大越倾向全局和min_dist默认 0.1控制低维空间里点能挨多近值越小簇越紧凑。我实际用下来的感受是它作为可视化和探索工具表现优异即使不用来最终建模也常能帮你在几分钟内发现数据里的异常簇和离群点。5.2 自编码器让网络自己学一套压缩表示自编码器Autoencoder是深度学习在降维领域的代表。结构上就三块编码器把高维输入压到瓶颈层这就是降维后的表示解码器再从瓶颈层把原始输入重建出来。损失函数是重建误差比如均方误差训练目标就是让压扁-还原这个过程尽量不丢信息。自编码器最漂亮的地方是瓶颈层的宽度就是降维后的维度你想要多少维就设多少个神经元。而且因为中间层是非线性的激活函数它能捕捉数据里非常复杂的非线性结构这点 PCA 做不到。我在项目里用过几种变体普通自编码器做特征压缩和预训练瓶颈向量喂给下游分类器。去噪自编码器DAE输入加噪声目标是还原干净数据学出来的表示鲁棒性更好。变分自编码器VAE瓶颈层学的是分布的参数降维之外还附带生成能力图像生成、异常检测里很常见。自编码器的短板也很明显训练需要更多数据、调参成本高结果受随机种子影响而且它学到的表示完全不可解释。数据量只有几千条时老实说它多半打不过 PCA 好特征工程。5.3 什么时候值得上深度学习方案我的判断标准大约是这样样本量低于几千、特征维度几千到几万经典方法足够别折腾深度学习。样本量十万级以上、数据是高维复杂结构图像、文本、行为序列自编码器或 UMAP 上场压缩效果和下游任务收益通常远超线性方法。有预训练模型可用的场景取倒数第二层或倒数第一层的向量作为降维后特征往往比从零训自编码器更快更好本质上这也是一种降维。6. 拿着数据怎么选一套可落地的决策流程讲了这么多方法落到实际项目里很多人还是会在屏幕前愣住。我这里整理了一套我自己的决策流程基本就是连续问五个问题。6.1 五个问题定位方法问题一降维后给谁用给眼睛看选 t-SNE 或 UMAP给模型做特征选 PCA、LDA、自编码器给业务解释选特征选择。这一步直接砍掉一半候选。问题二有没有标签有标签且目标是分类LDA 和嵌入式特征选择优先考虑无标签PCA、SVD、UMAP、自编码器之间选。问题三数据是什么形态稠密数值表PCA 最顺手高维稀疏文本或计数矩阵TruncatedSVD 保命图像、语音这种原生高维信号直接考虑卷积自编码器或预训练模型特征。问题四数据是线性可分的吗不确定就先跑 PCA看前几个主成分的累计解释率。如果前 10 个主成分连 50% 方差都解释不了说明线性结构的假设很弱应该上非线性方法。问题五老板或业务方要解释吗要解释回到特征选择不要解释特征抽取随便用。把这五个问题的答案组合起来就有了一张很实用的速查表数据情况推荐起点理由稠密表格、量纲差异大、无标签标准化 PCA最快建立全局结构认知高维稀疏文本 / 行为计数TruncatedSVD不需要稠密化内存友好有标签、目标分类建模LDA 或嵌入式特征选择利用类别信息兼顾解释性数据高度非线性、样本量大UMAP / 自编码器处理复杂弯曲流形需要给业务方解释为什么Filter Embedded 特征选择保留原始特征语义纯探索性看聚类t-SNE / UMAP局部结构清晰聚类直观6.2 四个真实场景的选型示范场景 A保险用户画像2000 特征需要向业务解释。我的做法是先用 LightGBM 的重要性或 Lasso 做嵌入式筛选砍到 50 个原始特征再从中做相关性去重最后留 30 个左右。全程没有用一个主成分业务方看得懂模型效果也稳定。场景 B文本分类TF-IDF 出来五万维。直接用 TruncatedSVD 降到 200 维这就是 LSA再喂给逻辑回归。五万维稀疏矩阵直接训练逻辑回归也能跑但降维之后训练速度快一个量级效果还经常更好因为去掉了大量稀疏带来的噪声。场景 C商品图像检索用预训练 CNN 的 1024 维特征。检索对延迟敏感1024 维向量算余弦相似度在大规模库上很贵。我试过直接 PCA 降到 64 维召回损失很小也试过 UMAP 降到 64 维保留的语义结构更好。最后还是选了 PCA——因为上线时 sklearn 的 PCA 是确定性变换维护简单UMAP 每次预测都要保证和训练时同一套参数和状态工程上麻烦不少。场景 D单细胞测序或传感器波形探索。这类数据你就是想看看有没有隐含的分群结构首选 UMAP。先 UMAP 降到 2D 画图发现明显分群后再回头找标签或聚类效率极高。7. 实战踩坑记录标准化、数据泄漏与结果解释最后这部分是我在这几年项目里踩过、也看同事踩过的坑。每一条都对应着真实的调参到半夜的经历。7.1 不标准化就做 PCA量纲大的特征会霸屏我第一次独立做 PCA 时就没标准化结果第一主成分几乎等于消费金额一个特征其他几百个特征加起来只占不到 10% 的贡献。原因是 PCA 找的是方差最大的方向而消费金额的单位是万元数值天然比年龄消费频次大得多。所有基于方差和距离的降维方法PCA、LDA、t-SNE、UMAP、K-Means 前置步骤都默认特征在同一尺度下才有意义。所以第一步永远是 StandardScaler 或 MinMaxScaler没有例外。7.2 把 t-SNE 结果喂给下游模型典型的误用t-SNE 只保留局部邻居结构全局距离是失真的。我曾经见过一个同事把 t-SNE 降到 3 维的结果直接当特征喂给 XGBoost模型在验证集上看着还行一换真实环境效果崩了——因为模型学到的是 t-SNE 优化出来的伪结构而不是数据的真实分布。t-SNE 只能用来探索和可视化。如果你确实需要非线性降维后的特征来做建模用 UMAP 的 transform或者用自编码器的瓶颈向量两者都保留了可迁移的结构信息。7.3 先在全量数据上 fit再切训练集数据泄漏这是所有降维坑里最隐蔽、后果最严重的一个。PCA、特征选择、标准化这些步骤如果先在包含测试集的全量数据上计算测试集的信息就通过转换参数泄漏进了训练过程最后验证结果会偏乐观上线就现原形。正确的顺序是先切分训练集测试集再在训练集上 fit然后用同一组参数 transform 测试集。代码应该是这样from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler scaler StandardScaler() scaler.fit(X_train) X_train_scaled scaler.transform(X_train) X_test_scaled scaler.transform(X_test) pca PCA(n_components50) pca.fit(X_train_scaled) X_train_pca pca.transform(X_train_scaled) X_test_pca pca.transform(X_test_scaled)用 sklearn 的 Pipeline 把标准化和 PCA 包起来然后对 Pipeline 统一 fit 训练集是防止手误最可靠的方式。特征选择也一样——同样只准看训练集。7.4 主成分方向的正负号没有业务含义PCA 的载荷向量loading里某个特征的系数是正还是负数学上没有确定含义。翻转整个方向方差解释率一点不变主成分还是那个主成分。但人看到主成分 1 中年龄的系数是 -0.8很容易脑补出年龄越大这个主成分越低的业务解释这是在过度解读。主成分只适合用来做投影和压缩不适合逐项解释系数的业务含义。真要谈哪个特征重要用特征选择的排序或者等模型训完做 permutation importance。7.5 随机性与超参数降维结果不可复现怎么办t-SNE、UMAP、自编码器都有随机初始化不固定随机种子的话结果每次跑都不一样这在团队协作里非常致命。第一次跑出一个聚类图第二次重跑发现簇的形状变了不少然后三个人对着三个版本讨论业务结论纯属浪费时间。所有这类方法都要设置random_stateUMAP 里是random_statet-SNE 里也是自编码器则要固定 PyTorch/TensorFlow 的全局种子。另外t-SNE 的困惑度、UMAP 的n_neighbors对图的影响非常大不要默认参数一路跑到底至少各试几个值对比一下选一个符合业务直觉的设置。我自己现在拿到高维数据第一步永远是标准化之后跑一个 PCA 先看结构至于后面上不上非线性方法完全看 PCA 的解释率结果和项目的时间成本说话。降维这件事本质上就是拿可解释性换信息密度想清楚这个交换值不值比背下所有算法公式重要得多。
返回列表