谱聚类
1. 基本原理
它的主要思想:把所有数据看成空间中的点,这些点之间可以用变连接起来,距离较远的两个点之间的边权重较低,而距离较近的两个点之间的权重较高,通过对所有数据点组成的图进行切图,让切图后的不同的子图间边权重和尽可能小(即距离远),而子图内的边权重和尽可能高(即距离近)。
难点:
- 如何构建图?
- 如何切分图?
2. 谱聚类基础
2.1 无向权重图
对于一个图,我们一般用点集合和边集合来描述,即。我们定义权重为点之间的权重,由于是无向图,故。
对于有边连接的两个点,;对于没有边连接的两个点,。
对于图中的任意一个点,它的度定义为和它相连的所有边权重之和,即
利用每个点度的定义,我们可以得到一个的度矩阵,它是一个对角阵,只有主对角有值,对应第行为第个点的度;利用所有点之间的权重,我们可以得到图的邻接矩阵,它也是一个矩阵,第行的第个值对应权重
除此之外,对于点集的一个子集,我们定义:
2.2 拉普拉斯矩阵
拉普拉斯矩阵,其性质如下:
- 对称矩阵,由于都为对称矩阵
- 由于是对称矩阵,它的所有特征值都是实数
- 对于任意向量,有
- 由于拉普拉斯矩阵是半正定的,其对应的个特征值都大于等于0。
3. 构建图——构建邻接矩阵
3.1 邻近法
通过设置一个阈值,然后利用欧氏距离度量任意两点的距离,即,然后根据的大小关系,来定义邻接矩阵:
从上式可知,两点间的权重要么,要么0,就没有其他信息了,距离远近度量很不明确,因此在实际应用中,很少采用。
3.2 近邻法
利用KNN
算法遍历所有的样本点,取每个样本最近的个点作为近邻,只有和样本距离最近的个点之间的。但是这种方法会造成重构之后的邻接矩阵非对称,我们后面的算法需要邻接矩阵对称。为了解决这种问题,一般采取下面两种方法之一:
- 只要一个点在另一个点的K近邻中,就保留
- 必须两个点互为近邻中,才能保留
3.3 全连接法
比前两种方法,第三种方法所有的点之间的权重值都大于0,因此称之为全连接法。可以选择不同的核函数来定义边权重,常用的有多项式核函数,高斯核函数和Sigmoid
核函数。最常用的是高斯核函数RBF
在实际的应用中,使用第三种全连接法来建立邻接矩阵是最普遍的,而在全连接法中使用高斯径向核RBF
是最普遍的。
4. 图的切分
对于无向图的切分,我们的目标是将图切成相互没有连接的个子图,每个子图集合为:,它们满足
对于任意两个子图点的集合,我们定义之间的切图权重为:
那么对于我们个子图点的集合:,我们定义切图为:
其中为的补集
那么如何切图可以让子图内的点权重和高,子图间的点权重和低呢?
一个自然的想法就是最小化, 但是可以发现,这种极小化的切图存在问题,如下图:
为了避免最小切图导致的切图效果不佳,我们需要对每个子图的规模做出限定,一般来说,有两种切图方式,第一种是,第二种是。
4.1 切图
对于每个切图,不仅要考虑最小化,还要考虑最大化每个子图样本的个数,即最小化函数:
我们引入指示向量,对于任意一个向量,它是一个维向量(为样本数),我们定义为:
对于有:
由上式可知,函数表达式可改写为:
其中为矩阵的迹,即我们的切图,实际上是最小化迹。注意到,则我们的优化目标为:
注意观察的每一个优化子目标,其中是单位正交基,是对称矩阵,此时是矩阵的一个特征值。对于,我们的目标是找到矩阵的最小特征值,而对于,我们的目标就是找到矩阵的个最小特征值。
4.2 切图
切图与切图类似,只是将的分母换成。由于子图样本的个数多不一定权重就大,我们切图时基于权重也更符合我们的目标,因此一般来说优于,定义如下:
对应的,切图对指示向量做了改进,定义如下:
我们的优化目标依然是:(推导与完全一致)
但是此时我们的,而是。推导如下:
也就是说,我们的优化目标最终为:
此时我们的中的指示向量不是单位正交基,所以我们令,则,也就是优化目标变成了:
可以发现这个式子和基本一致,只是中间的变成了。这样,我们可以按照的思想,求出的个最小特征值
一般来说,相当于对拉普拉斯矩阵做了一次标准化,即
5. 谱聚类算法流程
- 根据邻接矩阵生成方式构建邻接矩阵,构建度矩阵
- 计算出拉普拉斯矩阵
- 构建标准化后的拉普拉斯矩阵
- 计算最小的个特征值所各自对应的特征向量
- 将各自对应的特征向量组成的矩阵按行标准化,最终组成维矩阵
- 对中的每一行作为一个维样本,共个样本,用输入的聚类方法进行聚类,聚类维数为
- 得到簇划分
6. 实例演示
import numpy as np
import matplotlib.pyplot as plt
from sklearn import cluster, datasets
from sklearn.preprocessing import StandardScaler
np.random.seed(0)
# 构建数据
n_samples = 1500
noisy_circles = datasets.make_circles(n_samples=n_samples, factor=0.5, noise=0.05)
noisy_moons = datasets.make_moons(n_samples=n_samples, noise=0.05)
blobs = datasets.make_blobs(n_samples=n_samples, random_state=8)
data_sets = [
(
noisy_circles,
{
"n_clusters": 2
}
),
(
noisy_moons,
{
"n_clusters": 2
}
),
(
blobs,
{
"n_clusters": 3
}
)
]
colors = ["#377eb8", "#ff7f00", "#4daf4a"]
affinity_list = ['rbf', 'nearest_neighbors']
plt.figure(figsize=(17, 10))
for i_dataset, (dataset, algo_params) in enumerate(data_sets):
# 模型参数
params = algo_params
# 数据
X, y = dataset
X = StandardScaler().fit_transform(X)
for i_affinity, affinity_strategy in enumerate(affinity_list):
# 创建SpectralCluster
spectral = cluster.SpectralClustering(
n_clusters=params['n_clusters'],
eigen_solver='arpack',
affinity=affinity_strategy
)
# 训练
spectral.fit(X)
# 预测
y_pred = spectral.labels_.astype(int)
y_pred_colors = []
for i in y_pred:
y_pred_colors.append(colors[i])
plt.subplot(3, 4, 4*i_dataset+i_affinity+1)
plt.title(affinity_strategy)
plt.scatter(X[:, 0], X[:, 1], color=y_pred_colors)
plt.show()
7. 谱聚类算法小结
优点:
- 谱聚类只需要数据之间的邻接矩阵,因此对于处理稀疏数据的聚类很有效。这点传统聚类算法比如
K-Means
很难做到 - 由于使用了降维,因此在处理高维数据聚类时的复杂度比传统聚类算法好
缺点:
- 如果最终聚类的维度非常高,则由于降维的幅度不够,谱聚类的运行速度和最后的聚类效果均不好
- 聚类效果依赖于邻接矩阵,不同的邻接矩阵得到的最终聚类效果可能很不同