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 維度: 下游 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
對於部分標籤,按照 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"調整後的 Rand 指數:{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() 方法實現新數據的預處理,允許訓練好的模型將未見過的數據投影到已學習的嵌入空間中。
基本轉換用法
# 在訓練數據上進行訓練
reducer = umap.UMAP(n_neighbors=15, random_state=42).fit(X_train)
# 轉換測試數據
test_embedding = reducer.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 類別」和「使用範例」。
可重複性
為確保可重複的結果,始終設定 random_state 參數:
reducer = umap.UMAP(random_state=42)
使用 numpy 隨機狀態管理以獲得完全可重複性:
import numpy as np
np.random.seed(42)
常見問題排解
常見錯誤與解決方案
1. 記憶體不足錯誤
原因: 大型數據集上的預設距離計算可能消耗過多記憶體。
解決方案:
- 使用
umap.UMAP(low_memory=True)(預設值) - 在小批次中使用近似最近鄰搜尋
- 處理非常大(>100k 行)的數據集時,考慮對數據進行子採樣
2. 分離或破碎的嵌入
原因: n_neighbors 設定過低,強制分離的連接組件。
解決方案: 增加 n_neighbors 以包含更多全局上下文。
3. 聚類間無結構的團塊
原因: n_neighbors 設定過高,平滑了局部結構。
解決方案: 減少 n_neighbors 以揭示局部細節。
4. 不同運行之間的結果不一致
原因: 未設定 random_state 參數。
解決方案: 始終設定 random_state 以獲得可重複的結果。