#譜聚類算法實現
#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()