譜聚類是基於譜圖理論基礎上的一種聚類方法,與傳統的聚類方法相比:具有在任意形狀的樣本空間上聚類並且收斂於全局最優解的優點。(但效率不高,實際工作中用的比較少)
譜聚類
通過對樣本數據的拉普拉斯矩陣的特徵向量進行聚類,從而達到對樣本數據進行聚類的目的;其本質是將聚類問題轉換爲圖的最優劃分問題,是一種點對聚類算法。
譜聚類算法將數據集中的每個對象看做圖的頂點 V,將頂點間的相似度量化爲相應頂點連接邊E的權值 w,這樣就構成了一個基於相似度的無向加權圖 G(V,E),於是聚類問題就轉換爲圖的劃分問題。基於圖的最優劃分規則就是子圖內的相似度最大,子圖間的相似度最小。
步驟
譜聚類的構建過程主要包含以下幾個步驟
- 構建表示對象相似度的矩陣 W
- 構建度矩陣 D(對角矩陣)
- 構建拉普拉斯矩陣 L(有個特點:行累加=0)
- 計算矩陣L的前 k 個特徵值的特徵向量(k 個列向量)
- 將k個列向量組成矩陣 U
- 對矩陣 U 中的 n 行數據利用 K-means 或其它經典聚類算法進行聚類得出最終結果
拉普拉斯矩陣及變形
- 拉普拉斯矩陣(算法默認使用的是拉普拉斯矩陣)
- 對稱拉普拉斯矩陣
- 隨機遊走拉普拉斯矩陣
應用場景
圖形聚類、計算機視覺、非凸球形數據聚類等
面臨的問題
- 相似度矩陣的構建問題(比較難):業界一般使用高斯相似函數或者k近鄰來作爲相似度量,一般建議 使用k近鄰的方式來計算相似度權值
- 聚類數目的給定
- 如何選擇特徵向量(矩陣一大,求特徵向量的難度會比較高)
- 如何提高譜聚類的執行效率
代碼實現
使用scikit的相關API創建模擬數據,然後使用譜聚類算法進行數據聚類操作,並比較算法在不同參數情況下的聚類效果。
API
sklearn.cluster.spectral_clustering(affinity, n_clusters=8, n_components=None, eigen_solver=None, random_state=None, n_init=10, eigen_tol=0.0, assign_labels=‘kmeans’)
參數
- affinity:相似度矩陣構建方式
- assign_labels:上面說的U的構建方式
代碼
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import sklearn.datasets as ds
import matplotlib.colors
import warnings
from sklearn.cluster import spectral_clustering#引入譜聚類
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import euclidean_distances
## 設置屬性防止中文亂碼及攔截異常信息
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False
warnings.filterwarnings('ignore', category=FutureWarning)
### 創建模擬數據
N = 1000
centers = [[1, 2], [-1, -1], [1, -1], [-1, 1]]
#符合高斯分佈的數據集
data1, y1 = ds.make_blobs(N, n_features=2, centers=centers, cluster_std=(0.75,0.5,0.3,0.25), random_state=0)
data1 = StandardScaler().fit_transform(data1)
dist1 = euclidean_distances(data1, squared=True)
# 權重計算公式
affinity_params1 = map(lambda x: (x,np.exp(-dist1 ** 2 / (x ** 2)) + 1e-6), np.logspace(-2,0,6))
# 數據2
#圓形數據集
t = np.arange(0, 2 * np.pi, 0.1)
data2_1 = np.vstack((np.cos(t), np.sin(t))).T
data2_2 = np.vstack((2*np.cos(t), 2*np.sin(t))).T
data2_3 = np.vstack((3*np.cos(t), 3*np.sin(t))).T
data2 = np.vstack((data2_1, data2_2, data2_3))
y2 = np.vstack(([0] * len(data2_1), [1] * len(data2_2), [2] * len(data2_3)))
## 數據2的參數
dist2 = euclidean_distances(data2, squared=True)
affinity_params2 = map(lambda x: (x, np.exp(-dist2 ** 2 / (x ** 2)) + 1e-6), np.logspace(-2,0,6))
datasets = [(data1, y1, affinity_params1), (data2, y2, affinity_params2)]
def expandBorder(a, b):
d = (b - a) * 0.1
return a-d, b+d
colors = ['r', 'g', 'b', 'y']
cm = mpl.colors.ListedColormap(colors)
for i,(X, y, params) in enumerate(datasets):
x1_min, x2_min = np.min(X, axis=0)
x1_max, x2_max = np.max(X, axis=0)
x1_min, x1_max = expandBorder(x1_min, x1_max)
x2_min, x2_max = expandBorder(x2_min, x2_max)
n_clusters = len(np.unique(y))
plt.figure(figsize=(12, 8), facecolor='w')
plt.suptitle(u'譜聚類--數據%d' % (i+1), fontsize=20)
plt.subplots_adjust(top=0.9,hspace=0.35)
for j,param in enumerate(params):
sigma,af = param
#譜聚類的建模
## af: 指定相似度矩陣構造方式(就是相似度矩陣)
y_hat = spectral_clustering(af, n_clusters=n_clusters, assign_labels='kmeans', random_state=28)
unique_y_hat = np.unique(y_hat)
n_clusters = len(unique_y_hat) - (1 if -1 in y_hat else 0)
print ("類別:",unique_y_hat,";聚類簇數目:",n_clusters)
## 開始畫圖
plt.subplot(3,3,j+1)
for k, col in zip(unique_y_hat, colors):
cur = (y_hat == k)
plt.scatter(X[cur, 0], X[cur, 1], s=40, c=col, edgecolors='k')
plt.xlim((x1_min, x1_max))
plt.ylim((x2_min, x2_max))
plt.grid(True)
plt.title('$\sigma$ = %.2f ,聚類簇數目:%d' % (sigma, n_clusters), fontsize=16)
plt.subplot(3,3,7)
plt.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=cm, edgecolors='none')
plt.xlim((x1_min, x1_max))
plt.ylim((x2_min, x2_max))
plt.title('原始數據,聚類簇數目:%d' % len(np.unique(y)))
plt.grid(True)
plt.show()
類別: [0 1 2 3] ;聚類簇數目: 4
類別: [0 1 2 3] ;聚類簇數目: 4
類別: [0 1 2 3] ;聚類簇數目: 4
類別: [0 1 2 3] ;聚類簇數目: 4
類別: [0 1 2 3] ;聚類簇數目: 4
類別: [0 1 2 3] ;聚類簇數目: 4
類別: [0 1 2] ;聚類簇數目: 3
類別: [0 1 2] ;聚類簇數目: 3
類別: [0 1 2] ;聚類簇數目: 3
類別: [0 1 2] ;聚類簇數目: 3
類別: [0 1 2] ;聚類簇數目: 3
類別: [0 1 2] ;聚類簇數目: 3
上面提到“affinity:相似度矩陣構建方式”,可以使用高斯(相似度矩陣的構建問題,業界一般使用高斯相似函數或者k近鄰來作爲相似度量。)σ是高斯相似函數中的值