UMAP-Dense:从局部密度失真到密度保持的非线性降维

发布时间:2026/10/13 23:26:24
UMAP-Dense:从局部密度失真到密度保持的非线性降维 最近在整理非线性降维的方法笔记翻到 UMAP 相关的资料顺手把从 UMAP 到 UMAP-Dense 这条线重新梳理了一遍。我这个笔记的切入点其实很朴素很多项目里我都用 UMAP 做高维特征的可视化比如单细胞表达谱、图像特征向量、用户行为序列的 embedding但工作中陆续发现“降维效果好”和“可视化效果好”是两回事。尤其数据集本身密度不均匀的时候普通 UMAP 会把低密度区域压缩得特别严重聚类看起来清清楚楚但实质上把局部结构给破坏了。后来接触了 UMAP-Dense 这种密度保持型扩展才好了一些。这篇笔记适合两类人看一类是刚开始做高维数据可视化的同学想弄明白 UMAP 到底在算什么、参数怎么调另一类是已经跑熟 UMAP、但总觉得结果“好看但不可信”的从业者想搞清楚普通 UMAP 和密度保持版 UMAP-Dense 的差别以及各自该用在什么场景。笔记不会堆公式但会把原理和实操经验结合着写重点说清楚为什么 UMAP-Dense 能保留密度、又在什么情况下反而会添乱。1. 非线性降维需求与 UMAP 的定位先说一个很多人忽略的前提线性降维比如 PCA适合的是“数据本身大致在一个线性子空间里”的情况。但真实数据几乎不满足这个假设。拿基因表达数据来说不同细胞类型对应的是高维空间里弯弯曲曲的流形结构而不是一个平直的平面PCA 投影之后各类群很容易互相叠在一起。非线性降维要做的就是把这些弯曲的流形“展开”到二维或三维同时尽量保留点与点之间的局部邻接关系。在非线性降维这个领域UMAP 出现的时间比 t-SNE 晚但它几乎是目前最常用的方法。原因很实际速度快能处理几十万甚至上百万级别的样本初始化和随机种子对结果的影响相对可控参数少默认值往往就能出比较合理的结果而且它保留了相对更好的全局结构。很多做单细胞测序的团队已经默认把 UMAP 作为标准可视化工序了。我个人的体会是UMAP 最核心的设计思想可以概括成两步先在原始高维空间里构建一张加权近邻图再用优化的方式把这张图“尽量原样”地搬到低维空间里。第一步负责定义“什么是附近”第二步负责把“附近”变成平面上的距离。这里要特意提一下 UMAP-Dense。它并不是一个完全独立的新算法而是在 UMAP 框架基础上加入了“密度保持”约束的变体。市面上实现这套思想的库不算多最常见的实现方式是在损失函数里加一个密度相关性正则项让低维嵌入中的局部密度顺序与高维空间中的局部密度顺序尽量一致。社区里有人管它叫 densMAP也有人直接叫 UMAP-Dense我这里就用 UMAP-Dense 作为统称因为这篇笔记标题就是这么写的。适用场景大致可以这样划分可视化为主样本量几万到几十万特征维度几十到几千直接用 UMAP。数据来源混杂、不同群体密度差异明显比如某些细胞群体数量特别少、某些用户群体行为特别集中建议加密度保持机制。需要根据可视化结果判断“这个群体是不是足够紧凑”“那个边界是否真实”时UMAP-Dense 比普通 UMAP 更接近真实拓扑。2. UMAP 原理拆解图构建与低维布局2.1 高维图构建近邻搜索与模糊集表示UMAP 的第一步是找到每个点的 k 个近邻。这个“近邻”的定义会在很大程度上决定最终嵌入形态。如果样本量很大直接计算两两距离是 O(n²) 的根本跑不动所以实际实现里会用最近邻搜索树或者近似近邻算法来加速。拿到近邻之后UMAP 做的不是简单地把两个点连接起来而是把这段局部距离转换成一个区间化的相似度。这个转换和 t-SNE 里计算高斯核概率相似但 UMAP 用的是一个更宽松的指数函数并且不需要像 t-SNE 那样做严格的归一化。简单理解距离越近相似度越高但相似度不会因为某个局部区域密度太高而无限逼近 1。这一步里的关键参数是n_neighbors。它控制着每个点会“看见”多少个邻居本质上是在平衡局部细节和全局结构。n_neighbors越小每个点只跟距离最近的少数点建立连接结果会偏向于展现非常精细的局部结构但容易碎成很多孤立的小团n_neighbors越大连接关系越宏观局部细节会被平滑掉容易挤压掉一些小群体。另一个参数metric决定距离怎么算。默认是euclidean但如果特征是稀疏的高维向量比如 TF-IDF 向量或者某些 one-hot 编码特征欧氏距离的表现并不好换成cosine往往更稳定。我调参的时候第一步永远是看metric因为它决定了输入数据的“世界观”。2.2 低维布局从谱初始化到梯度优化图构建完成之后UMAP 会在低维空间里初始化一组点的位置然后通过优化把高维图中的连接关系“复刻”到低维坐标中。初始化方式通常有两种随机初始化和谱初始化。谱初始化是用高维图的拉普拉斯特征向量作为低维坐标相当于先用线性方法给一个大致布局再做非线性微调。这种方式的好处是可复现性更好起始状态不容易陷入局部最小值。默认情况下UMAP 会使用谱初始化只是当样本量特别大时会自动切换成随机初始化。优化目标是一个交叉熵损失。这个交叉熵可以粗略地理解成两部分一部分鼓励原本在高维中相邻的点在低维中也靠在一起另一部分鼓励原本不相邻的点不要被强行拉近。UMAP 从 t-SNE 的 KL 散度中演化而来但做了两点改动一是取消了归一化二是对非邻居点使用了不同的权重策略从而让优化过程更快也让全局结构保持得更好。低维空间中点与点之间的距离由一个名为min_dist的参数控制。min_dist越小点与点之间的最小间隔越小聚类内部的点会挤得越紧群体边界越分明min_dist越大嵌图越是松散的“云团”分布更适合看整体趋势。2.3 为什么普通 UMAP 会在密度不均时失真理解 UMAP 的图构建方式之后就不难解释密度失真的来源了。在均匀密度的流形上每个点的近邻距离分布是类似的最终嵌入能很好地反映结构。可当数据密度差异悬殊时就会出问题。设想有两个区域一个区域包含大量密集样本另一个区域只有少量稀疏样本。对稀疏区域的点来说它找到的 k 个近邻可能已经跨了很长的实际距离对密集区域的点来说k 个近邻可能只在很小的半径内。但 UMAP 的布局优化过程会“一视同仁”地要求所有近邻对在低维空间里靠得足够近于是出现一种常见后果稀疏区域的点被严重压缩和密集区域之间的空白被抹平看起来就像所有群体都挤在一起。这种现象在单细胞数据里尤其常见。某一类罕见细胞群体数量少内部表达差异也不大在普通 UMAP 图上往往会被压到主群体旁边几乎看不出独立分布。这不是算法有 bug而是损失函数压根没有把“密度”作为需要保持的信息。3. 从 UMAP 到 UMAP-Dense密度信息如何被保留3.1 UMAP-Dense 要解决的问题UMAP-Dense 这类密度保持型变体的目标可以一句话说清楚让低维空间中每个点的局部密度尽量保持与高维空间中该点的局部密度一致。什么是“局部密度”一个直观的计算方式是取每个点到它的第 k 个近邻的距离距离越小说明周围点越密集距离越大说明周围越稀疏。这个距离可以当作密度的反指标。普通 UMAP 在优化过程中完全不管这个指标而 UMAP-Dense 会在优化时增加一个衡量“高维密度序列”和“低维密度序列”相关性的损失项。损失函数把两部分加在一起一部分是原本的 UMAP 布局损失负责保持邻接关系另一部分是密度相关性损失负责让“拥挤的地方继续拥挤稀疏的地方继续稀疏”。两部分之间有一个权重系数默认值会尽量平衡两者但实际使用时如果数据密度差异极大可以把密度损失权重调高一点让密度保持优先。3.2 UMAP-Dense 的典型实现方式在常用 Python 库umap-learn中可以通过开启densmap参数来启用密度保持模式同时配合output_densTrue输出每个点的局部密度估计值。典型调用形式import umap # 普通 UMAP model umap.UMAP( n_neighbors15, min_dist0.1, n_components2, random_state42 ) embedding model.fit_transform(X) # UMAP-Dense密度保持模式 model_dense umap.UMAP( n_neighbors15, min_dist0.1, n_components2, densmapTrue, output_densTrue, random_state42 ) embedding_dense, dens_row, dens_col model_dense.fit_transform(X)需要注意densmap不能与所有参数组合同时使用。比如某些metric设置、某些版本的n_neighbors太小或者样本量过少的情况下密度估计会变得不稳定。具体可以看库的文档和报错信息我后面会单独说几个常见的坑。dens_row是每个点在原始高维空间中的局部密度估计dens_col是嵌入后在低维空间中的局部密度估计。拿到这两个值之后可以算它们的相关系数。这个系数是衡量 UMAP-Dense 是否真正保住了密度的最直接指标越接近 1说明低维图中的密度顺序越接近原始数据。3.3 什么时候该用 UMAP-Dense用不用 UMAP-Dense不是看“想不想保留密度”而是看数据里有没有值得保留的密度差异。第一种典型场景是稀有群体的识别。比如混合样本里有一类样本只占 1%但它们内部表达模式稳定具备独立的生物学意义。普通 UMAP 会因为近邻图构建的天然偏向把这些样本揉进大群体的边缘。UMAP-Dense 能在损失函数层面强制低维空间的局部密度与高维空间对齐给这类稀有群体更多“空间”。第二种典型场景是多批次数据的可比性分析。不同批次的数据采集条件不同常常会引入密度上的系统性偏差。普通 UMAP 做出来的图可能某个批次因为整体特征更集中在图上占据的面积特别小看起来像被压缩了。UMAP-Dense 至少能保留“这个批次在原始空间里更稀疏还是更密集”的宏观信息降低批次效应对布局的干扰。第三种场景是异常检测前的探索性分析。如果数据里天然存在少数孤立点普通 UMAP 很可能把它们随机分配到某个聚类的边缘导致你根本看不出来。UMAP-Dense 的密度项会让孤立点保持在稀疏区域可视化时更容易通过密度着色发现问题。反过来说如果数据本身密度比较均匀或者你只关心“分成几簇、每簇是否分得开”那 UMAP-Dense 带来的收益不大反而会因为多了一个约束项需要额外调参和忍受更长的运行时间。4. 实操案例同一份数据跑 UMAP 与 UMAP-Dense4.1 实验数据准备为了把两类算法放一起对比我构造了一个带有密度差异的模拟数据集三群点一群密度很高两群密度较低然后在部分区域加一点噪声点。这个场景和很多真实业务数据的感觉类似——大多数样本聚集在少数几个典型模式里剩下的样本分布在边缘。数据生成大致是import numpy as np from sklearn.datasets import make_blobs # 高密度群体 X1, _ make_blobs(n_samples3000, centers[(0, 0)], cluster_std0.5, random_state1) # 低密度群体 X2, _ make_blobs(n_samples300, centers[(5, 5)], cluster_std1.2, random_state1) X3, _ make_blobs(n_samples300, centers[(-4, 4)], cluster_std1.5, random_state1) X np.vstack([X1, X2, X3])真实项目中很少直接对二维数据降维所以我把它映射到更高维空间再通过 PCA 稍微加上线性变换制造一些相关性避免低维可视化直接“就是原始图”的错觉。实际操作时也会故意混入一些不是明显聚类的背景点模拟噪声。4.2 大规模调参思路我不建议一开始就用默认参数直接跑。先跑一个快速版本观察是否出现以下几类现象点全部挤成一团、某个群体被拉伸成细线、不同群体边界处颜色交错明显。根据现象反推需要调整的参数。我常用的调参顺序是先定metric。特征连续且标准化较好用euclidean稀疏高维特征用cosine。再调n_neighbors。小数据集里从 5 到 50 都试一下画几张图出来对比。n_neighbors15是我比较常用的起点。然后调min_dist。想看清晰的聚类从 0.1 往下降想看连续流形结构从 0.1 往上升。最后才调整densmap相关的参数。开启密度保持后需要重新观察嵌入的整体形态是否发生太大改变如果改变太大说明密度损失权重对当前数据过重。这个顺序的核心逻辑是先保证“邻接关系”被正确学习再考虑“密度信息”的保持。顺序反过来的话很容易把噪声当成结构。4.3 结果对照与密度相关性评估跑完两份嵌入后我一般会同时看三样东西整体散点图、局部密度相关性、群体分离度。散点图上最直观的变化是普通 UMAP 中低密度群体和高密度群体之间的距离被压缩了高密度群体占据的面积很大UMAP-Dense 的结果里低密度群体的分布范围更宽边界更清晰群体之间的空白也更明显。用np.corrcoef(dens_row, dens_col)计算密度相关普通 UMAP 的相关系数通常在 0.5 到 0.7 之间UMAP-Dense 能到 0.85 以上。这个数字是评估密度保持最直接的量化指标比单纯看图片“顺不顺眼”靠谱得多。还有一个细节值得说如果只做“看起来漂亮”的降维普通 UMAP 也能达到目的。但一旦你后续要根据图的距离做定量分析比如算群体之间的最近邻距离、统计边界密度普通 UMAP 已经无法保证这些数字反映真实高维结构。UMAP-Dense 在这类定量场景里优势明显。5. 常见问题与排查技巧实录5.1 UMAP-Dense 报错与参数冲突densmapTrue和某些参数组合会直接报兼容性错误。我个人踩过的坑主要有这几个densmapTrue不支持部分metric。解决方法是保持固定metric优先使用euclidean或cosine。output_densTrue只对开启密度保持模式有效不开densmap时不要指望拿到密度估计。样本量太小比如只有几百个点n_neighbors15时密度估计很不稳定跑出来的结果怪怪的。此时可以把n_neighbors降到 5 到 10。如果报错信息里明确说某个参数冲突我的建议是不要强行绕过换一组参数往往更省时间。密度保持本身就是额外约束参数空间天然比普通 UMAP 小这也是“用功能换约束”的代价。5.2 嵌入结果的稳定性问题UMAP 的另一个常见问题是随机性。即便random_state固定使用不同的初始化也会导致最终嵌图有或大或小的差异。如果你只是做一次探索性可视化这没什么问题但如果要把 UMAP 结果作为后续分析的特征输入就需要谨慎。我通常会跑 5 次计算嵌入坐标之间的平均距离以及密度相关的波动范围。如果多次运行结果差异很大大概率是数据本身的流形结构不够清晰或者n_neighbors太小导致图结构对初始化过于敏感。这种情况下增加n_neighbors、使用谱初始化或者先做 PCA 降维去噪都会让结果更稳定。有一点经验不要在一开始就对原始几千维特征跑 UMAP。先做 PCA 取前几十个主成分效果会好很多。这不仅能加速近邻搜索还能减少噪声对图构建的干扰。UMAP-Dense 对这种“先线性去噪再非线性降维”的流程同样适用。5.3 密度相关系数不够高密度相关系数偏低通常不是因为算法能力问题而是数据本身就不适合这种密度保持方式。常见原因有两个一是特征尺度没有统一某些维度的方差远大于其他维度导致距离计算几乎只被少数特征主导二是噪声点太多干扰了局部密度估计。解决办法是先把特征标准化过滤掉明显离群点再跑 UMAP-Dense。还有一个容易被忽略的点局部密度估计对n_neighbors极其敏感。n_neighbors5时每个点的局部密度只取决于最近 5 个点估计方差大n_neighbors50时密度估计更平滑但会牺牲局部精度。我一般会在n_neighbors15的基础上做对比如果密度相关始终不高就尝试把n_neighbors提到 30 或 50看趋势如何。5.4 运行速度优化技巧UMAP-Dense 比普通 UMAP 慢这是必然的因为密度损失项的计算需要额外维护每个点的局部密度信息。样本量到十万级别时跑一次 UMAP-Dense 可能要等很长时间。优化手段有几个先把维度压到 30 维左右再进 UMAP减少n_neighbors用precomputed距离矩阵避免重复计算或者用更小规模的随机子样先跑一遍参数验证再全量跑。千万别一上来就全量数据加全参数跑光是等结果的时间就够浪费的。6. 我对 UMAP-Dense 的实操体会笔记写到这里我想补充一点偏个人经验的内容。过去很长一段时间里我都是 UMAP 的忠实用户因为它的可视化效果实在太容易给人“洞见”的感觉点簇分明、颜色清爽。但后来在做聚类评估时我意识到一个问题很多人把 UMAP 的可视化结果直接当成了“真实的高维几何”来解读这其实是危险的。UMAP 的输出只是低维投影投影必然有扭曲扭曲方式由参数和损失函数决定。普通 UMAP 的扭曲主要集中在密度不均匀处这恰恰是数据分析中最不该被扭曲的地方。UMAP-Dense 的好处不是“更漂亮”而是更诚实。它让嵌入空间里的密度分布与原始空间里的密度分布保持可验证的相关性给了我一个量化指标去判断降维结果的可信度。在实际项目中我会同时跑普通 UMAP 和 UMAP-Dense把两张图放在一起对比。如果两者的整体模式差异不大说明数据本身结构稳健如果差异巨大那就说明密度信息起了决定性作用后续分析需要格外小心。最后再分享一个小技巧做完了 UMAP-Dense可以用dens_row和dens_col的差距来识别“视觉上聚集但实际上稀疏”的点。这些点在图上可能被周围的密集点拽过去了密度相关性却不匹配。把这些点单独标出来看经常能发现有意思的异常样本或者边缘群体。这个方法是我偶然发现的但对混合群体数据特别有用建议你们试一下。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询