一、前述
譜聚類(spectral clustering)是一種基於圖論的聚類方法,主要思想是把所有的資料看做空間中的點,這些點之間可以用邊連線起來。距離較遠(或者相似度較低)的兩個點之間的邊權重值較低,而距離較近(或者相似度較高)的兩個點之間的邊權重值較高,通過對所有資料點組成的圖進行切圖,讓切圖後不同的子圖間邊權重和儘可能的低,而子圖內的邊權重和儘可能的高,從而達到聚類的目的。
二、具體原理
1、優點
譜聚類相較於前面講到的最最傳統的k-means聚類方法,譜聚類又具有許多的優點:
1.只需要待聚類點之間的相似度矩陣就可以做聚類了。
2.對於不規則的資料(或者說是離群點)不是那麼敏感。
3.k-means聚類演算法比較適合於凸資料集(資料集內的任意兩點之間的連線都在該資料集以內,簡單理解就是圓形,可能不準確),而譜聚類則比較通用。
2、相關概念
相似度矩陣S的構建
構建相似度的矩陣的過程中,可以使用歐氏距離、餘弦相似度、高斯相似度等來計算資料點之間的相似度,選用哪個要根據你自己的實際情況來。不過在譜聚類中推薦使用的是高斯相似度,但是我在我的工程中使用的是餘弦相似度。
拉普拉斯矩陣
它的定義很簡單,拉普拉斯矩陣。是度矩陣,也就是相似度矩陣的每一行(或者每一列)加和得到的一個對角矩陣。W就是圖的鄰接矩陣。
相似矩陣
鄰接矩陣,它是由任意兩點之間的權重值組成的矩陣。通常我們可以自己輸入權重,但是在譜聚類中,我們只有資料點的定義,並沒有直接給出這個鄰接矩陣,那麼怎麼得到這個鄰接矩陣呢?
基本思想是,距離較遠的兩個點之間的邊權重值較低,而距離較近的兩個點之間的邊權重值較高,不過這僅僅是定性,我們需要定量的權重值。一般來說,我們可以通過樣本點距離度量的相似矩陣來獲得鄰接矩陣。
構建鄰接矩陣的方法有三類。-鄰近法,K鄰近法和全連線法。
對於-鄰近法,它設定了一個距離閾值,然後用歐式距離度量任意兩點和的距離。即相似矩陣的, 然後根據和的大小關係,來定義鄰接矩陣如下:
從上式可見,兩點間的權重要不就是,要不就是0,沒有其他的資訊了。距離遠近度量很不精確,因此在實際應用中,我們很少使用-鄰近法。
第二種定義鄰接矩陣的方法是K鄰近法,利用KNN演算法遍歷所有的樣本點,取每個樣本最近的k個點作為近鄰,只有和樣本距離最近的k個點之間的。但是這種方法會造成重構之後的鄰接矩陣W非對稱,我們後面的演算法需要對稱鄰接矩陣。為了解決這種問題,一般採取下面兩種方法之一:
第一種K鄰近法是隻要一個點在另一個點的K近鄰中,則保留
第二種K鄰近法是必須兩個點互為K近鄰中,才能保留
第三種定義鄰接矩陣的方法是全連線法,相比前兩種方法,第三種方法所有的點之間的權重值都大於0,因此稱之為全連線法。可以選擇不同的核函式來定義邊權重,常用的有多項式核函式,高斯核函式和Sigmoid核函式。最常用的是高斯核函式RBF,此時相似矩陣和鄰接矩陣相同:
在實際的應用中,使用第三種全連線法來建立鄰接矩陣是最普遍的,而在全連線法中使用高斯徑向核RBF是最普遍的。
3、演算法流程:
輸入:樣本集D=,相似矩陣的生成方式, 降維後的維度, 聚類方法,聚類後的維度
輸出: 簇劃分
1) 根據輸入的相似矩陣的生成方式構建樣本的相似矩陣S
2)根據相似矩陣S構建鄰接矩陣W,構建度矩陣D
3)計算出拉普拉斯矩陣L
4)求L的最小的個特徵值所各自對應的特徵向量
6) 將特徵向量組成維的特徵矩陣F
7)對F中的每一行作為一個維的樣本,共n個樣本,用輸入的聚類方法進行聚類,聚類維數為。
8)得到簇劃分
4、總結
譜聚類演算法是一個使用起來簡單,但是講清楚卻不是那麼容易的演算法,它需要你有一定的數學基礎。如果你掌握了譜聚類,相信你會對矩陣分析,圖論有更深入的理解。同時對降維裡的主成分分析也會加深理解。
譜聚類演算法的主要優點有:
1)譜聚類只需要資料之間的相似度矩陣,因此對於處理稀疏資料的聚類很有效。這點傳統聚類演算法比如K-Means很難做到
2)由於使用了降維,因此在處理高維資料聚類時的複雜度比傳統聚類演算法好
譜聚類演算法的主要缺點有:
1)如果最終聚類的維度非常高,則由於降維的幅度不夠,譜聚類的執行速度和最後的聚類效果均不好。
2) 聚類效果依賴於相似矩陣,不同的相似矩陣得到的最終聚類效果可能很不同。
三、程式碼
# !/usr/bin/python # -*- coding:utf-8 -*- import numpy as np import matplotlib.pyplot as plt import matplotlib.colors from sklearn.cluster import spectral_clustering from sklearn.metrics import euclidean_distances def expand(a, b): d = (b - a) * 0.1 return a-d, b+d if __name__ == "__main__": matplotlib.rcParams['font.sans-serif'] = [u'SimHei'] matplotlib.rcParams['axes.unicode_minus'] = False t = np.arange(0, 2*np.pi, 0.1) data1 = np.vstack((np.cos(t), np.sin(t))).T data2 = np.vstack((2*np.cos(t), 2*np.sin(t))).T data3 = np.vstack((3*np.cos(t), 3*np.sin(t))).T data = np.vstack((data1, data2, data3)) n_clusters = 3 m = euclidean_distances(data, squared=True) sigma = np.median(m) plt.figure(figsize=(12, 8), facecolor='w') plt.suptitle(u'譜聚類', fontsize=20) clrs = plt.cm.Spectral(np.linspace(0, 0.8, n_clusters)) for i, s in enumerate(np.logspace(-2, 0, 6)): print(s) af = np.exp(-m ** 2 / (s ** 2)) + 1e-6 y_hat = spectral_clustering(af, n_clusters=n_clusters, assign_labels='kmeans', random_state=1) plt.subplot(2, 3, i+1) for k, clr in enumerate(clrs): cur = (y_hat == k) plt.scatter(data[cur, 0], data[cur, 1], s=40, c=clr, edgecolors='k') x1_min, x2_min = np.min(data, axis=0) x1_max, x2_max = np.max(data, axis=0) x1_min, x1_max = expand(x1_min, x1_max) x2_min, x2_max = expand(x2_min, x2_max) plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.title(u'sigma = %.2f' % s, fontsize=16) plt.tight_layout() plt.subplots_adjust(top=0.9) plt.show()