umap-learn 是做什么的?
概述
UMAP(均匀流形近似与投影)是一种用于可视化和一般非线性降维的技术。应用此技能可实现快速、可扩展的嵌入,同时保留局部和全局结构,适用于监督学习、半监督学习和聚类预处理。
快速开始
安装
当前稳定版本:umap-learn 0.5.12(发布于2026年4月)。需要Python 3.9+,依赖scikit-learn>=1.6、numba、pynndescent、numpy和scipy。请固定到已验证的版本:
uv pip install umap-learn==0.5.12
基本用法
UMAP遵循scikit-learn约定,可作为t-SNE或PCA的即插即用替代品。
import umap
from sklearn.preprocessing import StandardScaler
# 准备数据(标准化至关重要)
scaled_data = StandardScaler().fit_transform(data)
# 方法1:一步完成(拟合和变换)
embedding = umap.UMAP().fit_transform(scaled_data)
# 方法2:分步进行(用于复用已训练的模型)
reducer = umap.UMAP(random_state=42)
reducer.fit(scaled_data)
embedding = reducer.embedding_ # 访问训练好的嵌入
预处理要求: 预处理方式需要与度量标准匹配。对于数值型欧几里得风格指标,在拟合前缩放特征,以防高方差列主导结果。对于余弦、二元、预计算距离或混合特征工作流,请选择与指标匹配的预处理方法,而不是盲目地标准化每一列。
典型工作流
import umap
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
# 1. 预处理数据
scaler = StandardScaler()
scaled_data = scaler.fit_transform(raw_data)
# 2. 创建并拟合UMAP
reducer = umap.UMAP(
n_neighbors=15,
min_dist=0.1,
n_components=2,
metric='euclidean',
random_state=42
)
embedding = reducer.fit_transform(scaled_data)
# 3. 可视化
plt.scatter(embedding[:, 0], embedding[:, 1], c=labels, cmap='Spectral', s=5)
plt.colorbar()
plt.title('UMAP嵌入')
plt.show()
参数调优指南
UMAP有四个主要参数控制嵌入行为。理解这些参数对于有效使用至关重要。
n_neighbors(默认值:15)
作用: 平衡嵌入中的局部与全局结构。
工作原理: 控制UMAP在学习流形结构时检查的局部邻域大小。
不同值的效果:
- 低值(2-5): 强调精细的局部细节,但可能将数据分割成不连续的组件
- 中等值(15-20): 平衡局部结构和全局关系(推荐起点)
- 高值(50-200): 优先考虑整体拓扑结构,忽略细粒度细节
建议: 从15开始,根据结果调整。增加以获取更多全局结构,减少以获取更多局部细节。
min_dist(默认值:0.1)
作用: 控制低维空间中点的紧密程度。
工作原理: 设置输出表示中点之间允许的最小距离。
不同值的效果:
- 低值(0.0-0.1): 创建适用于聚类的聚集嵌入;揭示精细的拓扑细节
- 高值(0.5-0.99): 防止紧密堆积;强调整体拓扑保留而非局部结构
建议: 对于聚类应用使用0.0,可视化使用0.1-0.3,松散结构使用0.5以上。
n_components(默认值:2)
作用: 确定嵌入输出空间的维度。
关键特性: 与t-SNE不同,UMAP在嵌入维度上扩展良好,适用于可视化以外的场景。
常见用途:
- 2-3维: 可视化
- 5-10维: 聚类预处理(比2D更好地保留密度)
- 10-50维: 下游机器学习模型的特征工程
建议: 可视化使用2,聚类使用5-10,机器学习流水线使用更高维度。
metric(默认值:'euclidean')
作用: 指定输入数据点之间的距离计算方式。
支持的指标:
- Minkowski变体: euclidean、manhattan、chebyshev
- 空间指标: canberra、braycurtis、haversine
- 相关指标: cosine、correlation(适用于文本/文档嵌入)
- 二元数据指标: hamming、jaccard、dice、russellrao、kulsinski、rogerstanimoto、sokalmichener、sokalsneath、yule
- 自定义指标: 通过Numba用户定义的距离函数
建议: 数值数据使用euclidean,文本/文档向量使用cosine,二元数据使用hamming。
参数调优示例
# 用于强调局部结构的可视化
umap.UMAP(n_neighbors=15, min_dist=0.1, n_components=2, metric='euclidean')
# 用于聚类预处理
umap.UMAP(n_neighbors=30, min_dist=0.0, n_components=10, metric='euclidean')
# 用于文档嵌入
umap.UMAP(n_neighbors=15, min_dist=0.1, n_components=2, metric='cosine')
# 用于保留全局结构
umap.UMAP(n_neighbors=100, min_dist=0.5, n_components=2, metric='euclidean')
监督与半监督降维
UMAP支持利用标签信息指导嵌入过程,实现类别分离的同时保留内部结构。
监督UMAP
通过拟合时的y参数传递目标标签:
# 监督降维
embedding = umap.UMAP().fit_transform(data, y=labels)
关键优势:
- 实现清晰的类别分离
- 保留每个类别内的内部结构
- 保持类别之间的全局关系
半监督UMAP
对于部分标签,按照scikit-learn约定用-1标记未标记的点:
# 创建半监督标签
semi_labels = labels.copy()
semi_labels[unlabeled_indices] = -1
# 使用部分标签拟合
embedding = umap.UMAP().fit_transform(data, y=semi_labels)
何时使用: 当标注成本高或拥有比标签更多的数据时。
用于聚类的UMAP
UMAP可作为基于密度的聚类算法(如HDBSCAN)的高效预处理,克服维数灾难。
聚类最佳实践
关键原则: 为聚类配置UMAP时要与可视化区别对待。
推荐参数:
- n_neighbors: 增加到约30(默认值15过于局部,可能产生人为的细粒度聚类)
- min_dist: 设置为0.0(将点密集打包在聚类内以获得更清晰的边界)
- n_components: 使用5-10维(保持性能的同时,比2D更好地保留密度)
聚类工作流
单独安装HDBSCAN以进行基于密度的聚类:
uv pip install hdbscan
import umap
import hdbscan
from sklearn.preprocessing import StandardScaler
# 1. 预处理数据
scaled_data = StandardScaler().fit_transform(data)
# 2. 使用聚类优化参数的UMAP
reducer = umap.UMAP(
n_neighbors=30,
min_dist=0.0,
n_components=10, # 高于2以更好地保留密度
metric='euclidean',
random_state=42
)
embedding = reducer.fit_transform(scaled_data)
# 3. 应用HDBSCAN聚类
clusterer = hdbscan.HDBSCAN(
min_cluster_size=15,
min_samples=5,
metric='euclidean'
)
labels = clusterer.fit_predict(embedding)
# 4. 评估
from sklearn.metrics import adjusted_rand_score
score = adjusted_rand_score(true_labels, labels)
print(f"调整兰德指数:{score:.3f}")
print(f"聚类数量:{len(set(labels)) - (1 if -1 in labels else 0)}")
print(f"噪声点:{sum(labels == -1)}")
聚类后可视化
# 创建用于可视化的2D嵌入(与聚类分开)
vis_reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, n_components=2, random_state=42)
vis_embedding = vis_reducer.fit_transform(scaled_data)
# 使用聚类标签绘图
import matplotlib.pyplot as plt
plt.scatter(vis_embedding[:, 0], vis_embedding[:, 1], c=labels, cmap='Spectral', s=5)
plt.colorbar()
plt.title('UMAP可视化与HDBSCAN聚类')
plt.show()
重要警告: UMAP不能完全保留密度,可能会产生人为的聚类分割。务必验证并探索生成的聚类结果。
变换新数据
UMAP通过其transform()方法支持新数据的预处理,使训练好的模型能够将未见数据投影到学习到的嵌入空间中。
基本变换用法
# 在训练数据上训练
trans = umap.UMAP(n_neighbors=15, random_state=42).fit(X_train)
# 变换测试数据
test_embedding = trans.transform(X_test)
与机器学习流水线的集成
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import umap
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2)
# 预处理
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 训练UMAP
reducer = umap.UMAP(n_components=10, random_state=42)
X_train_embedded = reducer.fit_transform(X_train_scaled)
X_test_embedded = reducer.transform(X_test_scaled)
# 在嵌入上训练分类器
clf = SVC()
clf.fit(X_train_embedded, y_train)
accuracy = clf.score(X_test_embedded, y_test)
print(f"测试准确率:{accuracy:.3f}")
重要考虑因素
数据一致性: transform方法假设训练数据和测试数据在高维空间中的整体分布是一致的。当此假设不成立时,请考虑使用参数化UMAP。
性能: 变换操作非常高效(通常小于1秒),但由于Numba JIT编译,初始调用可能较慢。
Scikit-learn兼容性: UMAP遵循标准sklearn约定,可以在流水线中工作。最近的0.5.x版本还改进了特征名称支持以及与当前scikit-learn验证API的兼容性:
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('umap', umap.UMAP(n_components=10)),
('classifier', SVC())
])
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)
feature_names = pipeline.named_steps['umap'].get_feature_names_out()
高级特性
参数化UMAP
参数化UMAP使用学习到的神经网络映射函数替代直接嵌入优化。
与标准UMAP的主要区别:
- 使用TensorFlow/Keras训练编码器网络
- 支持新数据的高效变换
- 通过解码器网络支持重构(逆变换)
- 允许自定义架构(图像的CNN,序列的RNN)
安装:
uv pip install "umap-learn[parametric-umap]==0.5.12"
# 安装TensorFlow支持下的参数化UMAP扩展。
基本用法:
from umap.parametric_umap import ParametricUMAP
# 默认架构(3层100神经元全连接网络)
embedder = ParametricUMAP()
embedding = embedder.fit_transform(data)
# 高效变换新数据
new_embedding = embedder.transform(new_data)
自定义架构:
import tensorflow as tf
# 定义自定义编码器
encoder = tf.keras.Sequential([
tf.keras.layers.InputLayer(shape=(input_dim,)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(2) # 输出维度
])
embedder = ParametricUMAP(encoder=encoder, dims=(input_dim,))
embedding = embedder.fit_transform(data)
持久化: 使用内置的Keras感知方法保存参数化UMAP,而不是简单的pickle:
embedder.save("parametric_umap_model", exclude_raw_data=True)
from umap.parametric_umap import load_ParametricUMAP
loaded = load_ParametricUMAP("parametric_umap_model")
new_embedding = loaded.transform(new_data)
最近的0.5.12修复包括参数化UMAP重训练稳定性改进和度量梯度修复,因此对于神经网络工作流,建议固定到当前版本。
何时使用参数化UMAP:
- 训练后需要高效变换新数据
- 需要重构能力(逆变换)
- 希望将UMAP与自编码器结合
- 处理复杂数据类型(图像、序列),受益于专用架构
逆变换
逆变换支持从低维嵌入重构高维数据。
基本用法:
reducer = umap.UMAP()
embedding = reducer.fit_transform(data)
# 从嵌入坐标重构高维数据
reconstructed = reducer.inverse_transform(embedding)
重要限制:
- 计算成本高
- 在嵌入凸包之外表现不佳
- 在聚类间隙区域精度降低
示例:探索嵌入空间:
import numpy as np
# 在嵌入空间中创建点网格
x = np.linspace(embedding[:, 0].min(), embedding[:, 0].max(), 10)
y = np.linspace(embedding[:, 1].min(), embedding[:, 1].max(), 10)
xx, yy = np.meshgrid(x, y)
grid_points = np.c_[xx.ravel(), yy.ravel()]
# 从网格重构样本
reconstructed_samples = reducer.inverse_transform(grid_points)
AlignedUMAP
对于需要共享坐标系统的时间序列或相关数据集(时间序列实验、批次),请使用umap.AlignedUMAP().fit(datasets, relations=relations),其中relations映射连续数据集之间的样本索引,并且对于有意义的对齐是必需的。参数、方法和一个详细示例在references/api_reference.md的“AlignedUMAP Class”和“Usage Examples”部分。
可复现性
为确保结果可复现,请始终设置random_state参数:
reduc
...