社區發現之譜聚類算法的實現


#譜聚類算法實現
#1、計算距離矩陣(歐氏距離,作爲相似度矩陣)
#2、利用KNN計算鄰接矩陣A
#3、由鄰接矩陣計算都矩陣D和拉普拉斯矩陣L
#4、標準化拉普拉斯矩陣
#5、對拉普拉斯矩陣進行特徵值分解得到特徵向量
#6、對特徵向量進行K-means聚類
#7、得到分類結果

import numpy as np
#距離矩陣的計算

def euclidDistance(x1, x2, sqrt_flag=False):
    res = np.sum((x1-x2)**2)
    if sqrt_flag:
        res = np.sqrt(res)
    return res

def calEuclidDistanceMatrix(X):
    #X是(500,2)的數據維度
    X = np.array(X)
    #初始化一個相似度矩陣[len(x),len(x)]
    S = np.zeros((len(X), len(X)))
    #計算相似度矩陣
    for i in range(len(X)):
        for j in range(i+1, len(X)):
            S[i][j] = 1.0 * euclidDistance(X[i], X[j])
            S[j][i] = S[i][j]
    return S



#鄰接矩陣
#傳入的參數是相似度矩陣Similarity和K
def myKNN(S, k, sigma=1.0):
    N = len(S)
    A = np.zeros((N,N))
    # print(S[0,:15])
    for i in range(N):
        #S[i]爲相似都矩陣中的第i行的所有數據  S[i]長度爲500
        dist_with_index = zip(S[i], range(15))#每一個值賦予一個編號,然後打包,相當於給節點加上編號
        #由小到大排序,按照x[0],也就是zip中的第一個值,即相似度值(相似度值,編號)
        dist_with_index = sorted(dist_with_index, key=lambda x:x[0])
        #選出K個與節點i最相關的節點,相似度矩陣中值越小,表示兩點之間距離越近,相似度越大
        #所以是由小到大排序,再選擇
        #得到外循環節點i的鄰居節點集合
        neighbours_id = [dist_with_index[m][1] for m in range(k+1)] # xi's k nearest neighbours
        # print(neighbours_id)
        # print(dist_with_index)
        # print(len(dist_with_index))
        # if i==0:
        #     break
        #用高斯核函數計算鄰接矩陣的值
        for j in neighbours_id: # xj is xi's neighbour
            A[i][j] = np.exp(-S[i][j]/2/sigma/sigma)
            A[j][i] = A[i][j] # mutually

    return A

#數據加載

from sklearn import datasets

def genTwoCircles(n_samples=1000):
    #數據和標籤  (500,2)和500的維度
    X, y = datasets.make_circles(n_samples, factor=0.5, noise=0.05)
    return X, y

#拉普拉斯矩陣標準化
def calLaplacianMatrix(adjacentMatrix):

    # 計算鄰接矩陣的度
    degreeMatrix = np.sum(adjacentMatrix, axis=1)#axis=1逐行相加  axis=0逐列相加


    # 將鄰接矩陣轉化爲對角矩陣,再減去鄰接矩陣就是拉普拉斯矩陣  L=D-A
    laplacianMatrix = np.diag(degreeMatrix) - adjacentMatrix

    # normailze拉普拉斯矩陣歸一化操作
    # D^(-1/2) L D^(-1/2)
    sqrtDegreeMatrix = np.diag(1.0 / (degreeMatrix ** (0.5)))
    #三部分做矩陣乘法得到歸一化的拉普拉斯矩陣
    return np.dot(np.dot(sqrtDegreeMatrix, laplacianMatrix), sqrtDegreeMatrix)

#畫圖
from matplotlib import pyplot as plt
from itertools import cycle, islice

#參數data,y_sp爲特徵向量聚類的標籤,y_km爲原始數據聚類的標籤
def plot(X, y_sp, y_km):
    colors = np.array(list(islice(cycle(['#377eb8', '#ff7f00', '#4daf4a',
                                                '#f781bf', '#a65628', '#984ea3',
                                                '#999999', '#e41a1c', '#dede00']),
                                        int(max(y_km) + 1))))
    plt.subplot(121)
    plt.scatter(X[:,0], X[:,1], s=10, color=colors[y_sp])
    plt.title("Spectral Clustering")
    plt.subplot(122)
    plt.scatter(X[:,0], X[:,1], s=10, color=colors[y_km])
    plt.title("Kmeans Clustering")
    plt.show()


def main():
    import sys

    sys.path.append("..")

    from sklearn.cluster import KMeans

    np.random.seed(1)
    #data維度是(500,2),500個樣本,本個樣本特徵維度是2
    #label是(500),表示每一個樣本的標籤
    data, label = genTwoCircles(n_samples=500)
    #計算相似度矩陣  (500,500)的維度
    Similarity = calEuclidDistanceMatrix(data)
    #計算鄰接矩陣 k=10爲每個節點選擇最相似的10個鄰居
    Adjacent = myKNN(Similarity, k=10)#維度是(500,500)
    #計算歸一化的拉普拉斯矩陣
    Laplacian = calLaplacianMatrix(Adjacent)
    #計算拉普拉斯矩陣的特徵值和特徵向量 x:500  V(500,500)500個節點,每個節點特徵爲500維
    x, V = np.linalg.eig(Laplacian)
    #將特徵值由小到大排序  對應特徵向量
    x = zip(x, range(len(x)))
    x = sorted(x, key=lambda x: x[0])
    #取出特徵向量來,因爲特徵向量是按列排的,所以要轉置,用vstack推疊起來
    H = np.vstack([V[:, i] for (v, i) in x[:500]]).T
    #送入kmeans中聚類 傳入的是分解後的特徵向量
    sp_kmeans = KMeans(n_clusters=2).fit(H)
    #原始數據進行kmeans聚類
    pure_kmeans = KMeans(n_clusters=2).fit(data)
    #傳入plot函數作圖,原始數據,兩種聚類的標籤結果
    plot(data, sp_kmeans.labels_, pure_kmeans.labels_)

if __name__ == '__main__':
    main()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章