谱聚类初探

Posted UQI-LIUWJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谱聚类初探相关的知识,希望对你有一定的参考价值。

1, 谱聚类引入

对于这样分布的样本,我们怎么进行聚类呢?我们可以使用核函数+K-means来聚类,当然也可以使用本节将要介绍的谱聚类。

2,模型介绍

谱聚类是一种基于图模型的聚类方法(一般图是带权重的无向图)。比起传统的K-Means算法,谱聚类对数据分布的适应性更强,聚类效果也很优秀,同时聚类的计算量也小很多。

谱聚类的主要思想是把所有的数据看做空间中的点,这些点之间可以用边连接起来。距离较远的两个点之间的边权重值较低,而距离较近的两个点之间的边权重值较高,通过对所有数据点组成的图进行切图,让切图后不同的子图间边权重和尽可能的低,而子图内的边权重和尽可能的高,从而达到聚类的目的。

2.1 几个图的概念

先定义几个概念:

图:G={V,E}

节点集合:V={1,2,……,N}

2.1.1 边集合与邻接矩阵

边集合: E:W=[wij] 1≤i,j≤N  ,这里我们用一个相似度矩阵W来表达节点与节点之间,即边的信息。其中相似度的定义有以下几种:

1、ϵ-邻近法

从上式可见,两点的权重要不就是ϵ, 要不就是0, 没有其他信息了。距离远近度量很不精确,因此在实际应用中,我们很少使用ϵ-邻近法。

2、K邻近法

利用KNN算法遍历所有的样本点,取每个样本最近的k个点作为近邻,只有和样本距离最近的k个点之间的边权重大于0。但是这种方法会造成重构之后的邻接矩阵W非对称,我们后面的算法需要对称邻接矩阵。为了解决这种问题,一般采取下面两种方法之一:

3、全连接法

相比前两种方法,第三种方法所有的点之间的权重值都大于0,因此称之为全连接法。在实际的应用中,使用第三种全连接法来建立邻接矩阵是最普遍的。

可以选择不同的核函数来定义边权重,常用的有多项式核函数,高斯核函数和Sigmoid核函数。最常用的是高斯核函数RBF。

2.1.2 度与度矩阵

度:度为该顶点与其他顶点连接权值之和。度矩阵是一个对角矩阵。

2.2 切割

2.2.1 两类的切割

我们先看切割成两类的情况

也就是两个切割A,B之间的度量是任意A中的点和任意B中的点之间的相似度。也就是“切口”两侧的点之间的相似度权重。

2.2.2 K类的切割

然后我们推广到K个类别

那么这个K类切割怎么度量呢?从在两类切割的情况我们知道,切割的度量只取决于“切口”两侧点之间的相似度权重。那么K类切割也是同理,只不过原来是切一刀,现在是切K刀,每一刀切下Ai和其他部分。

于是我们就有:

那么我们可以通过调整Ai的分配,获得最小的Cut。

2.3 Ncut

但是如果仅最优化这边的cut,会有一个不足。就是我们希望分割效果更好,希望每一个分割“紧凑”一点。也就是类间的相似度低(w小),类内的相似度大(w大)。那么Cut的定义就无法满足我们这个需求了。为了满足这个“分割紧凑”的需求,我们对Cut进行归一化操作:

这里的Δ定义为某一类内点的度(虽然我觉得可能是类内点之间的度更合理一点?欢迎讨论)

整合一下,Normalized Cut (NCut)的定义为

 

我们的目标是

3, Ncut模型的矩阵表示

通过上一小节,我们知道

为了更方便地求解这个优化问题,我们一般要将它转化为矩阵形式

3.1 指示向量

我们先来看一下等号左边,我们定义一个指示向量indicator vector,它类似于one-hot向量

 

3.2 Ncut

接下来我们要对Ncut进行矩阵表示

NCut是一个求和的数值,那么我们可以将其转化为对角矩阵求迹(trace)

然后把分子分母分离(对角矩阵求逆相当于对每一个对角线元素求逆):

整合一下,即:

但是,我们目前知道的是相似度矩阵W(N×N维)和指示向量Y(N×K维)【这里我们先给定一个特定的Y,然后将O,P用Y来表示。之后再用优化问题求解Y,所以此时Y暂定为已知的】

3.2.1 求P

我们先看一下 (K×K维):

 

 

对于每一个的结果是一个K×K的矩阵。如果中第j维是1(其他是0),那么中只有第(j,j)坐标元素为1,其余为0。

那么这样所有的求和的结果,元素值为1的部分全在对角线上.

对角线上的每个元素都是这一类的元素个数。这个已经和P(下图)很像了,唯一的区别就是求和的内容。

我们在前面又有

所以前面的P也可以写成

整合一下,有:

3.2.2. 求O

最后我们来看O

 

对于第一项,也就是组别k内所有点的度之和,即

而我们前面已经求得了

而对于第二项

我们先猜测一下,因为我们现在就只知道W和Y,我们先试试行不行.

因为我们只是需要求迹,而

是对角矩阵,所以这里我们也只需要验证的对角线部分即可。

我们取,什么时候元素会到对角线上呢?那就是yi和yj在一组里面(比如第k组)。此时第k行第k列的元素+

所以最后,只看对角线上的元素,那么这个矩阵就是

3.3 结论

所以

其中D-W我们称之为拉普拉斯矩阵

3.4 求最优方案的方法

在之后会涉及的RatioCut中,我们去求拉普拉斯矩阵L的最小的前k个特征值,然后求出对应的特征向量并标准化,然后对结果进行一次传统的聚类。

Ncut和RatioCut唯一的区别就是分母的D,所以仿RatioCut方法,我们这里进行特征分解的矩阵为标准化的拉普拉斯矩阵,即

4 拉普拉斯矩阵

拉普拉斯矩阵(Laplacian matrix)),也称为基尔霍夫矩阵, 是表示图的一种矩阵。给定一个有n个顶点的图,其拉普拉斯矩阵被定义为:L=D−W 其中D为图的度矩阵,W为图的邻接矩阵。
 

4.1 拉普拉斯矩阵举例

以下图为例:

邻接矩阵
度矩阵
拉普拉斯矩阵

 

 

 

 

 

 

 

 

 

 

 

 

4.2 拉普拉斯矩阵性质

证明:


5 RatioCut切图

大体思路和Ncut是一样的,不过分母除的不是某个分割的度之和,而是某个分割中点的个数

和Ncut一样,这边的指示向量也表示了第i个元素(i∈[1,n])在第j个分割中(j∈[1,k])

那么我们对每一个分割Ai有:

(展开的时候,都属于Ai的和都不属于Ai的相减为0,所以不显示出来)

               

(Ai分割边缘上所有点的权重之和就是cut(Ai,~Ai))

                

汇总到一块,总的分割的RatioCut为

通过找到L的最小的k个特征值,可以得到对应的k个特征向量,这k个特征向量组成一个nxk维度的矩阵,即为我们的H。

由于我们在使用维度规约的时候损失了少量信息,导致得到的优化后的指示向量h对应的H现在不能完全指示各样本的归属,因此一般在得到 n × k维度的矩阵H后还需要对每一行进行一次传统的聚类,比如使用K-Means聚类。

6 谱聚类算法流程汇总

以Ncut为例

输入:样本集D=(x1,x2,x3,......,xn)、相似矩阵的生成方式、 降维后的维度k1(需要分成几个分割)、聚类方法、聚类后的维度k2

输出: 簇划分C=(c1,c2,.....ck2) 

流程:

1)根据输入的相似矩阵的生成方式构建样本的相似矩阵S

2)根据相似矩阵S构建邻接矩阵W ,构建度矩阵D

3)计算出拉普拉斯矩阵L

4)构建标准化后的拉普拉斯矩阵

5)计算最小的k1个特征值所各自对应的特征向量f

6) 将各自对应的特征向量f组成的矩阵按行标准化,最终组成n × k1的特征矩阵F(第j个矩阵(j∈[1,n])属于第i个分割(i∈[1,k1]))

7)对F中的每一行作为一个k1维的样本,共n个样本,用输入的聚类方法进行聚类,聚类维数为k2

8)得到簇划分C=(c1,c2,.....ck2) 

7 谱聚类python实现

为了方便记忆,我按照流程1~8依次实现

7-0-1 导入库

import numpy as np
from sklearn import datasets

7-0-2 加载鸢尾花数据

#加载鸢尾花数据
iris=datasets.load_iris()
'''
print(iris['data'].shape)
(150, 4)
print(iris['data'][0])
array([5.1, 3.5, 1.4, 0.2])
'''

m=iris['data'].shape[0]
n=iris['data'].shape[1]

7-1 构建样本的相似矩阵S

#相似矩阵
AMatrix=np.zeros((m,m))

#求两条记录之间的距离(相似度),用高斯核函数
def AffMatrix(arrA,arrB):
    sigma=0.5 
    
    diff=arrA-arrB #两个ndarray逐元素相减
    
    diff=np.square(diff)#相减的结果逐元素平方
    
    a=np.sum(diff)#到这里就完成了||x-x'||^2这一部分了
    
    b=2*(np.square(sigma))#到这里完成了2*σ^2
    
    w=np.exp(-a/b) #高斯核函数
    #print(w)
    
    return w
    

7-2 根据相似矩阵S构建邻接矩阵W ,构建度矩阵D

7-2-1 邻接矩阵

#计算邻接矩阵
def M_Matrix(m,AMatrix):
    for i in range(m):
        for j in range(m):
            AMatrix[i][j]=AffMatrix(iris['data'][i],iris['data'][j])
            #计算两两的相似度
    
    AMatrix=AMatrix-np.identity(m)
    #我们认为自己到自己是没有边相连的,所以需要让对角线元素变为0
    #自己和自己的高斯核结果为0
    
    return AMatrix

w=M_Matrix(m,AMatrix)

'''
w.shape
(150, 150)
'''

7-2-2 度矩阵

D=np.diag(np.sum(w,axis=0))

这里复习一个知识,就是axis的事情

axis=0 第0个坐标的元素叠加,其他坐标的元素不变 sum([0,1,2,....,n][x][y])

axis=1 第1个坐标的元素叠加,其他坐标的元素不变 sum([x][0,1,2,....,n][y])

7.3 计算出拉普拉斯矩阵L=D−W 

L=D-w

7.4 构建标准化后的拉普拉斯矩阵

#np.power(np.sum(w,axis=0),-0.5)
#D的对角线元素开根号
Dn=np.diag(np.power(np.sum(w,axis=0),-0.5))
#Dn=D^(-1/2)
L=Dn.dot(L).dot(Dn)

7.5 计算最小的k1个特征值所各自对应的特征向量f

eigvals, eigvecs = np.linalg.eig(L)
# 规范化后的拉普拉斯矩阵 特征分解

k = 3
indices = eigvals.argsort()[:k]
#最小的三个特征值


k_max_eigvecs = eigvecs[:,indices]
#最小的三个特征值对应的特征向量
'''
k_max_eigvecs.shape
(150,3)
'''

7.7 对F中的每一行作为一个k1维的样本,共n个样本,用输入的聚类方法进行聚类,聚类维数为k2

#KNN进行聚类
from sklearn.cluster import KMeans

res = KMeans(n_clusters=k).fit_predict(k_max_eigvecs)

7.9 可视化

def draw():
    colorSet = ["black", "blue", "green", "yellow", "red", "purple", "orange", "brown"]
    for i in range(m):
        x = iris['data'][i][0]
        y = iris['data'][i][1]
        color = colorSet[res[i] % len(colorSet)]
        plt.scatter(x,y, marker="o", c=color, s=20)
    plt.xlabel("x")
    plt.ylabel("y")
    plt.show()
draw()

8 sklearn库中的谱聚类使用

在scikit-learn的类库中,sklearn.cluster.SpectralClustering实现了基于Ncut的谱聚类,没有实现基于RatioCut的切图聚类。同时,对于相似矩阵的建立,也只是实现了基于K邻近法和全连接法的方式,没有基于ϵ-邻近法的相似矩阵。

8.1 SpectralClustering中的参数

8.1.1 n_clusters

代表我们在对谱聚类切图时降维到的维数,同时也是最后一步聚类算法聚类到的维数。

8.1.2 affinity:

我们的相似矩阵的建立方式。可以选择的方式有三类,

第一类是 **‘nearest_neighbors’**即K邻近法。


第二类是**‘precomputed’**即自定义相似矩阵。选择自定义相似矩阵时,需要自己调用set_params来自己设置相似矩阵。


第三类是全连接法,可以使用各种核函数来定义相似矩阵,还可以自定义核函数。

最常用的是内置高斯核函数’rbf’。其他比较流行的核函数有‘linear’即线性核函数, ‘poly’即多项式核函数, ‘sigmoid’即sigmoid核函数。

如果选择了这些核函数, 对应的核函数参数在后面有单独的参数需要设置。

affinity默认是高斯核’rbf’。

8.1.3 核函数参数gamma:

如果我们在affinity参数使用了多项式核函数 ‘poly’,高斯核函数‘rbf’, 或者’sigmoid’核函数,那么我们就需要对这个参数进行调参。

8.1.4 核函数参数degree

如果我们在affinity参数使用了多项式核函数 ‘poly’,那么我们就需要对这个参数进行调参。这个参数对应中的d。默认是3。

8.1.5 核函数参数coef0:

如果我们在affinity参数使用了多项式核函数 ‘poly’,或者sigmoid核函数,那么我们就需要对这个参数进行调参。

8.1.6 kernel_params

如果affinity参数使用了自定义的核函数,则需要通过这个参数传入核函数的参数。

8.1.7 n_neighbors

 如果我们affinity参数指定为’nearest_neighbors’即K邻近法,则我们可以通过这个参数指定KNN算法的K的个数。默认是10

8.2 代码

from sklearn.cluster import SpectralClustering
from sklearn import metrics

iris=datasets.load_iris()
y_pred = SpectralClustering(gamma=0.1,n_clusters=3).fit_predict(iris['data'])

8.3 可视化

可以看到结果和我们之前自己实现的是差不多的

def draw():
    colorSet = ["black", "blue", "green", "yellow", "red", "purple", "orange", "brown"]
    for i in range(m):
        x = iris['data'][i][0]
        y = iris['data'][i][1]
        color = colorSet[y_pred[i] % len(colorSet)]
        plt.scatter(x,y, marker="o", c=color, s=20)
    plt.xlabel("x")
    plt.ylabel("y")
    plt.show()
draw()

以上是关于谱聚类初探的主要内容,如果未能解决你的问题,请参考以下文章

转:完整的最简单的谱聚类python代码

Spectral clustering(谱聚类)算法的实现

Spectral clustering(谱聚类)算法的实现

Spectral clustering(谱聚类)算法的实现

谱聚类算法总结

谱聚类(spectral clustering)原理总结