名称: umap-learn 描述: “UMAP降维。用于2D/3D可视化、聚类预处理(HDBSCAN)、监督/参数化UMAP的高维数据快速非线性流形学习。”
UMAP降维库
概述
UMAP(Uniform Manifold Approximation and Projection)是一种用于可视化和通用非线性降维的维度降低技术。应用此技能进行快速、可扩展的嵌入,保留局部和全局结构,支持监督学习和聚类预处理。
快速入门
安装
# 通过conda
conda install -c conda-forge umap-learn
# 通过pip
pip install umap-learn
基本用法
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_ # 访问训练后的嵌入
关键预处理要求: 应用UMAP前,始终标准化特征以确保各维度权重相等。
典型工作流程
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维: 下游ML模型的特征工程
推荐: 可视化使用2,聚类使用5-10,ML管道使用更高维。
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
对于部分标签,用-1标记未标记点,遵循scikit-learn约定:
# 创建半监督标签
semi_labels = labels.copy()
semi_labels[unlabeled_indices] = -1
# 用部分标签拟合
embedding = umap.UMAP().fit_transform(data, y=semi_labels)
何时使用: 当标记成本高或您有比标签更多的数据时。
UMAP度量学习
在有标记数据上训练监督嵌入,然后应用到新未标记数据:
# 在有标记数据上训练
mapper = umap.UMAP().fit(train_data, train_labels)
# 转换未标记测试数据
test_embedding = mapper.transform(test_data)
# 用作下游分类器的特征工程
from sklearn.svm import SVC
clf = SVC().fit(mapper.embedding_, train_labels)
predictions = clf.predict(test_embedding)
何时使用: 用于机器学习管道中的监督特征工程。
UMAP用于聚类
UMAP可作为密度基聚类算法(如HDBSCAN)的有效预处理,克服维度灾难。
聚类最佳实践
关键原则: 为聚类配置UMAP不同于可视化。
推荐参数:
- n_neighbors: 增加至~30(默认15太局部,可能创建人为细粒度聚类)
- min_dist: 设置为0.0(在聚类内密集排列点以获得更清晰边界)
- n_components: 使用5-10维(与2D相比,保持性能同时改善密度保留)
聚类工作流程
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"Adjusted Rand Score: {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('带HDBSCAN聚类的UMAP可视化')
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}")
重要考虑
数据一致性: 转换方法假设高维空间中的总体分布在训练和测试数据之间一致。当此假设失败时,考虑使用参数化UMAP。
性能: 转换操作高效(通常<1秒),但初始调用可能因Numba JIT编译而较慢。
Scikit-learn兼容性: UMAP遵循标准sklearn约定,在管道中无缝工作:
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)
高级特性
参数化UMAP
参数化UMAP用学习到的神经网络映射函数替换直接嵌入优化。
与标准UMAP的关键区别:
- 使用TensorFlow/Keras训练编码器网络
- 支持高效转换新数据
- 支持通过解码器网络重建(逆转换)
- 允许自定义架构(用于图像的CNN,用于序列的RNN)
安装:
pip install umap-learn[parametric_umap]
# 需要TensorFlow 2.x
基本用法:
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(input_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)
何时使用参数化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
用于分析时间或相关数据集(如时间序列实验、批次数据):
from umap import AlignedUMAP
# 相关数据集列表
datasets = [day1_data, day2_data, day3_data]
# 创建对齐嵌入
mapper = AlignedUMAP().fit(datasets)
aligned_embeddings = mapper.embeddings_ # 嵌入列表
何时使用: 比较相关数据集的嵌入,同时保持一致的坐标系。
可重复性
为确保可重复结果,始终设置random_state参数:
reducer = umap.UMAP(random_state=42)
UMAP使用随机优化,因此没有固定随机状态时,运行之间结果会略有不同。
常见问题与解决方案
问题: 不连通组件或分裂聚类
- 解决方案: 增加
n_neighbors以强调更多全局结构
问题: 聚类过于分散或分离不佳
- 解决方案: 减少
min_dist以允许更紧密堆积
问题: 聚类结果差
- 解决方案: 使用聚类特定参数(n_neighbors=30, min_dist=0.0, n_components=5-10)
问题: 转换结果与训练显著不同
- 解决方案: 确保测试数据分布匹配训练,或使用参数化UMAP
问题: 在大数据集上性能慢
- 解决方案: 设置
low_memory=True(默认),或考虑先用PCA降维
问题: 所有点塌缩到单个聚类
- 解决方案: 检查数据预处理(确保适当缩放),增加
min_dist
资源
参考资料/
包含详细API文档:
api_reference.md:完整UMAP类参数和方法
当需要详细参数信息或高级方法使用时加载这些参考资料。