用相似度邻接矩阵进行谱聚类
scikit-learn 将样本间的相似度保存为加权邻接矩阵,计算图 Laplacian 的谱嵌入,再据此划分样本簇。
工程问题#
样本簇可能沿弯曲形状分布,直接按原始坐标到中心的距离划分并不总合适。谱聚类先把样本看作图上的顶点,用成对相似度表达连接强度,再利用图结构寻找分组。
实际实现#
在默认 RBF affinity 路径中,SpectralClustering.fit 通过 pairwise_kernels 构造 n×n 的 affinity_matrix_,矩阵的第 i、j 项是样本 i 与 j 的相似度边权。_spectral_embedding 将其作为 adjacency 传给 SciPy,计算归一化 Laplacian 和特征向量;之后默认用 k-means 给嵌入分配标签。
处理流程#
- 以样本为顶点,用 RBF 核生成成对相似度邻接矩阵
- 核对矩阵对称性并检查图的连通性
- 根据邻接矩阵的度计算归一化 Laplacian,求谱嵌入
- 在嵌入上分配聚类标签并返回结果
源码片段#
固定版本 · 原文摘录721 elif self.affinity == "precomputed":722 self.affinity_matrix_ = X723 else:724 params = self.kernel_params725 if params is None:726 params = {}727 if not callable(self.affinity):728 params["gamma"] = self.gamma729 params["degree"] = self.degree730 params["coef0"] = self.coef0731 self.affinity_matrix_ = pairwise_kernels(732 X, metric=self.affinity, filter_params=True, **params733 )precomputed 模式接收已有相似度矩阵;默认 RBF 路径通过 pairwise_kernels 生成成对边权。矩阵两维都对应样本编号。
745 maps = _spectral_embedding(746 self.affinity_matrix_,747 n_components=n_components,748 eigen_solver=self.eigen_solver,749 random_state=random_state,750 eigen_tol=self.eigen_tol,751 drop_first=False,752 )753 if self.verbose:754 print(f"Computing label assignment using {self.assign_labels}")755756 if self.assign_labels == "kmeans":757 _, self.labels_, _ = k_means(758 maps,759 self.n_clusters,760 random_state=random_state,761 n_init=self.n_init,762 verbose=self.verbose,763 )将相似度矩阵传入谱嵌入,随后默认对嵌入调用 k-means。邻接矩阵实际参与了聚类训练,而非仅被导出供展示。
323 n_nodes = adjacency.shape[0]324 # Whether to drop the first eigenvector325 if drop_first:326 n_components = n_components + 1327328 if not _graph_is_connected(adjacency):329 warnings.warn(330 "Graph is not fully connected, spectral embedding may not work as expected."331 )332333 laplacian, dd = csgraph_laplacian(334 adjacency, normed=norm_laplacian, return_diag=True335 )从 adjacency 的形状取得顶点数,检查连通性,并交给 SciPy 计算 Laplacian;默认 norm_laplacian 为真。
550 np.fill_diagonal(m, 0)551 w = m.sum(axis=axis)552 if normed:553 isolated_node_mask = (w == 0)554 w = np.where(isolated_node_mask, 1, np.sqrt(w))555 m /= w556 m /= w[:, np.newaxis]557 m *= -1558 _setdiag_dense(m, 1 - isolated_node_mask)559 else:560 m *= -1561 _setdiag_dense(m, w)562563 return m.astype(dtype, copy=False), w.astype(dtype, copy=False)稠密路径先清除对角项,再沿矩阵一维求度并归一化。这里按行列处理图的边权,生成用于特征分解的 Laplacian。
四段源码连接相似度矩阵构造、谱嵌入及标签分配、Laplacian 调用和稠密矩阵处理。特征求解器内部及核函数的数值实现没有包含在摘录中。
收益与取舍#
工程分析带来的收益
- 矩阵项直接对应一对样本的连接强度,能交给图 Laplacian 与线性代数例程处理
- 支持把外部已经计算好的相似度矩阵作为图输入,沿用相同的后续流程
付出的代价
- 稠密 n×n 相似度矩阵需要 O(n²) 存储,逐行或逐列求度需扫描矩阵;特征求解和标签分配还有各自的成本
- 核参数与相似度定义会改变图结构和聚类结果;自定义 affinity 不能把距离数值未经转换直接当作相似度
- 近邻图模式可以使用稀疏矩阵,但其存储、查边和遍历成本与此处的稠密表示不同
可比较的选择
近邻图的稀疏邻接矩阵只保留局部连接以减少图存储和计算,但邻域大小会影响连通性;它是不同的建图选择,不保证与全连接 RBF 图得到相同结果。
直接对原始特征做 k-means无需构造全样本相似度矩阵和求图特征向量,但使用的几何表示与优化目标不同。
版本与适用范围#
核验 scikit-learn 1.6.1 与 SciPy v1.15.2,重点为默认 RBF affinity 的稠密表示。这里保存的是图的边权,不是 Isomap 算出的全点对最短距离。SciPy 构造 Laplacian 时忽略邻接矩阵的对角项;稀疏近邻图和其他特征求解器走各自分支。
参考来源#
源码、官方文档与原始论文sklearn/manifold/_spectral_embedding.py · _spectral_embedding / csgraph_laplacian