支持距离矩阵输入且可预测新样本簇的Python聚类算法咨询
结论
存在满足需求的聚类算法。你当前使用的AgglomerativeClustering属于转导式聚类,设计目标仅为输出输入距离矩阵对应样本的簇标签,没有训练出可泛化的簇表征,因此要给新样本打标签必须把新样本加入数据集重跑全量聚类、重算包含新样本的全量距离矩阵,天生不支持增量预测。
只要选择归纳式、具备显式簇原型、支持预计算距离输入的聚类算法,就能完全满足“训练后无需重跑全量聚类、无需重算全量距离矩阵,直接预测新样本簇归属”的要求。
最优适配方案:K-Medoids聚类
K-Medoids(围绕中心点聚类)是最适配你场景的算法,核心特性完全匹配需求:
- 支持
precomputed预计算距离矩阵输入,可以直接用你基于随机森林邻近度生成的距离矩阵训练; - 训练过程会为每个簇选出一个实际存在的训练样本作为簇中心点(Medoid),仅保存这k个中心点的信息(k为簇数量,通常远小于训练集总样本量);
- 预测新样本时,仅需要计算新样本到这k个中心点的距离,不需要触碰其他训练样本、不需要重算训练集的n×n全量距离矩阵、不需要重跑聚类流程,直接把新样本分配给距离最近的中心点所属簇即可。
scikit-learn生态中可以通过scikit-learn-extra包的KMedoids实现使用,安装命令为pip install scikit-learn-extra。
适配你随机森林邻近度场景的可运行代码
复用你已有代码的逻辑,仅替换聚类模型、补充预测函数即可:
from sklearn_extra.cluster import KMedoids import numpy as np import pandas as pd # ---- 以下部分完全复用你原有代码的逻辑,加载数据、训练随机森林、生成训练集距离矩阵 ---- from sklearn.ensemble import RandomForestRegressor from sklearn import datasets def distanceMatrix(model, X, normalize=True): terminals = model.apply(X) nTrees = terminals.shape[1] a = terminals[:,0] proxMat = 1 * np.equal.outer(a, a) for i in range(1, nTrees): a = terminals[:,i] proxMat += 1*np.equal.outer(a, a) if normalize: proxMat = proxMat / nTrees return 1 - proxMat iris = datasets.load_iris() df = pd.DataFrame(iris['data'], columns = iris['feature_names']) df['target'] = pd.Series(iris['target'], name = 'target_values') df['target_name'] = df['target'].replace([0,1,2], ['iris-' + species for species in iris['target_names'].tolist()]) df['iris_setosa'] = (df['target_name'] == 'iris-setosa').astype(int) df['iris_versicolor'] = (df['target_name'] == 'iris-versicolor').astype(int) df['iris_virginica'] = (df['target_name'] == 'iris-virginica').astype(int) y = df['petal width (cm)'] X = df.drop(['target','target_name','petal width (cm)'], axis = 1) overfitted_model = RandomForestRegressor(n_estimators=250, min_samples_leaf=10, random_state=42) overfitted_model.fit(X, y) distance_matrix = distanceMatrix(overfitted_model, X, normalize=True) # ---- 替换原有层次聚类为K-Medoids ---- cluster_model = KMedoids(n_clusters=3, metric='precomputed', random_state=42) cluster_model.fit(distance_matrix) df['label'] = cluster_model.labels_ # 提取每个簇的medoid对应的训练样本,预测阶段仅需要这几个样本 medoid_indices = cluster_model.medoid_indices_ medoid_samples = X.iloc[medoid_indices] medoid_terminals = overfitted_model.apply(medoid_samples) # ---- 新样本预测函数,无全量重计算 ---- def predict_new(new_X): new_terminals = overfitted_model.apply(new_X) n_new = len(new_terminals) dist_to_medoids = np.zeros((n_new, len(medoid_indices))) for i in range(n_new): # 仅计算新样本到k个medoid的距离,计算量和训练集总样本量无关 same_leaf_ratio = np.mean(new_terminals[i] == medoid_terminals, axis=1) dist_to_medoids[i] = 1 - same_leaf_ratio return cluster_model.cluster_centers_[np.argmin(dist_to_medoids, axis=1)] # 测试预测(示例取前5个样本当新样本) test_new_samples = X.iloc[:5] print("新样本预测簇标签:", predict_new(test_new_samples))
其他可选方案
- HDBSCAN:支持预计算距离矩阵输入,训练后提供近似预测方法,不需要重跑全量聚类,但预测阶段需要计算新样本到所有训练集核心点的距离,训练集体量较大时预测成本高于K-Medoids,适合非凸簇分布的场景。
- 原型映射法:如果已经用转导式聚类(比如你之前用的层次聚类)拿到了训练集标签,可以训练一个
NearestCentroid分类器拟合训练集的簇标签,预测阶段仅需要计算新样本到每个簇质心的距离,但这种方法是二次拟合的近似方案,精度略低于原生支持预测的聚类模型。
注:对随机森林邻近度计算感兴趣的读者,可参考决策森林邻近度相关的公开教程内容拓展学习。
内容的提问来源于stack exchange,提问作者cs0815
相关产品推荐
相关产品推荐

