二、聚類算法分類
1.基於划分
給定一個有N個元組或者紀錄的數據集,分裂法將構造K個分組,每一個分組就代表一個聚類,K<N。
特點:計算量大。很適合發現中小規模的數據庫中小規模的數據庫中的球狀簇。
算法:K-MEANS算法、K-MEDOIDS算法、CLARANS算法
2.基於層次
對給定的數據集進行層次似的分解,直到某種條件滿足為止。具體又可分為“自底向上”和“自頂向下”兩種方案。
特點:較小的計算開銷。然而這種技術不能更正錯誤的決定。
算法:BIRCH算法、CURE算法、CHAMELEON算法
3.基於密度
只要一個區域中的點的密度大過某個閾值,就把它加到與之相近的聚類中去。
特點:能克服基於距離的算法只能發現“類圓形”的聚類的缺點。
算法:DBSCAN算法、OPTICS算法、DENCLUE算法
4.基於網格
將數據空間划分成為有限個單元(cell)的網格結構,所有的處理都是以單個的單元為對象的。
特點:處理速度很快,通常這是與目標數據庫中記錄的個數無關的,只與把數據空間分為多少個單元有關。
算法:STING算法、CLIQUE算法、WAVE-CLUSTER算法
---------------------
三、度量指標
算法步驟及代碼實現
1. K-Means(K均值)聚類
算法步驟:
(1) 首先我們選擇一些類/組,並隨機初始化它們各自的中心點。中心點是與每個數據點向量長度相同的位置。這需要我們提前預知類的數量(即中心點的數量)。
(2) 計算每個數據點到中心點的距離,數據點距離哪個中心點最近就划分到哪一類中。
(3) 計算每一類中中心點作為新的中心點。
(4) 重復以上步驟,直到每一類中心在每次迭代后變化不大為止。也可以多次隨機初始化中心點,然后選擇運行結果最好的一個。
下圖演示了K-Means進行分類的過程:
---------------------
優點:
速度快,計算簡便
缺點:
我們必須提前知道數據有多少類/組。
K-Medians是K-Means的一種變體,是用數據集的中位數而不是均值來計算數據的中心點。
K-Medians的優勢是使用中位數來計算中心點不受異常值的影響;缺點是計算中位數時需要對數據集中的數據進行排序,速度相對於K-Means較慢。
---------------------
肘部法則
如果問題中沒有指定k的值,可以通過肘部法則這一技術來估計聚類數量。肘部法則會把不同k值的成本函數值畫出來。隨着kk值的增大,平均畸變程度會減小;每個類包含的樣本數會減少,於是樣本離其重心會更近。但是,隨着k值繼續增大,平均畸變程度的改善效果會不斷減低。k值增大過程中,畸變程度的改善效果下降幅度最大的位置對應的k值就是肘部。為了讓讀者看的更加明白,下面讓我們通過一張圖用肘部法則來確定最佳的kk值。下圖數據明顯可分成兩類:
1 import random 2 from sklearn import datasets 3 import numpy as np 4 import matplotlib.pyplot as plt 5 from mpl_toolkits.mplot3d import Axes3D 6 %matplotlib inline 7 8 9 # 正規化數據集 X 10 def normalize(X, axis=-1, p=2): 11 lp_norm = np.atleast_1d(np.linalg.norm(X, p, axis)) 12 lp_norm[lp_norm == 0] = 1 13 return X / np.expand_dims(lp_norm, axis) 14 15 16 # 計算一個樣本與數據集中所有樣本的歐氏距離的平方 17 def euclidean_distance(one_sample, X): 18 one_sample = one_sample.reshape(1, -1) 19 X = X.reshape(X.shape[0], -1) 20 distances = np.power(np.tile(one_sample, (X.shape[0], 1)) - X, 2).sum(axis=1) 21 return distances 22 23 24 25 class Kmeans(): 26 """Kmeans聚類算法. 27 28 Parameters: 29 ----------- 30 k: int 31 聚類的數目. 32 max_iterations: int 33 最大迭代次數. 34 varepsilon: float 35 判斷是否收斂, 如果上一次的所有k個聚類中心與本次的所有k個聚類中心的差都小於varepsilon, 36 則說明算法已經收斂 37 """ 38 def __init__(self, k=2, max_iterations=500, varepsilon=0.0001): 39 self.k = k 40 self.max_iterations = max_iterations 41 self.varepsilon = varepsilon 42 43 # 從所有樣本中隨機選取self.k樣本作為初始的聚類中心 44 def init_random_centroids(self, X): 45 n_samples, n_features = np.shape(X) 46 centroids = np.zeros((self.k, n_features)) 47 for i in range(self.k): 48 centroid = X[np.random.choice(range(n_samples))] 49 centroids[i] = centroid 50 return centroids 51 52 # 返回距離該樣本最近的一個中心索引[0, self.k) 53 def _closest_centroid(self, sample, centroids): 54 distances = euclidean_distance(sample, centroids) 55 closest_i = np.argmin(distances) 56 return closest_i 57 58 # 將所有樣本進行歸類,歸類規則就是將該樣本歸類到與其最近的中心 59 def create_clusters(self, centroids, X): 60 n_samples = np.shape(X)[0] 61 clusters = [[] for _ in range(self.k)] 62 for sample_i, sample in enumerate(X): 63 centroid_i = self._closest_centroid(sample, centroids) 64 clusters[centroid_i].append(sample_i) 65 return clusters 66 67 # 對中心進行更新 68 def update_centroids(self, clusters, X): 69 n_features = np.shape(X)[1] 70 centroids = np.zeros((self.k, n_features)) 71 for i, cluster in enumerate(clusters): 72 centroid = np.mean(X[cluster], axis=0) 73 centroids[i] = centroid 74 return centroids 75 76 # 將所有樣本進行歸類,其所在的類別的索引就是其類別標簽 77 def get_cluster_labels(self, clusters, X): 78 y_pred = np.zeros(np.shape(X)[0]) 79 for cluster_i, cluster in enumerate(clusters): 80 for sample_i in cluster: 81 y_pred[sample_i] = cluster_i 82 return y_pred 83 84 # 對整個數據集X進行Kmeans聚類,返回其聚類的標簽 85 def predict(self, X): 86 # 從所有樣本中隨機選取self.k樣本作為初始的聚類中心 87 centroids = self.init_random_centroids(X) 88 89 # 迭代,直到算法收斂(上一次的聚類中心和這一次的聚類中心幾乎重合)或者達到最大迭代次數 90 for _ in range(self.max_iterations): 91 # 將所有進行歸類,歸類規則就是將該樣本歸類到與其最近的中心 92 clusters = self.create_clusters(centroids, X) 93 former_centroids = centroids 94 95 # 計算新的聚類中心 96 centroids = self.update_centroids(clusters, X) 97 98 # 如果聚類中心幾乎沒有變化,說明算法已經收斂,退出迭代 99 diff = centroids - former_centroids 100 if diff.any() < self.varepsilon: 101 break 102 103 return self.get_cluster_labels(clusters, X) 104 105 106 def main(): 107 # Load the dataset 108 X, y = datasets.make_blobs(n_samples=10000, 109 n_features=3, 110 centers=[[3,3, 3], [0,0,0], [1,1,1], [2,2,2]], 111 cluster_std=[0.2, 0.1, 0.2, 0.2], 112 random_state =9) 113 114 # 用Kmeans算法進行聚類 115 clf = Kmeans(k=4) 116 y_pred = clf.predict(X) 117 118 119 # 可視化聚類效果 120 fig = plt.figure(figsize=(12, 8)) 121 ax = Axes3D(fig, rect=[0, 0, 1, 1], elev=30, azim=20) 122 plt.scatter(X[y==0][:, 0], X[y==0][:, 1], X[y==0][:, 2]) 123 plt.scatter(X[y==1][:, 0], X[y==1][:, 1], X[y==1][:, 2]) 124 plt.scatter(X[y==2][:, 0], X[y==2][:, 1], X[y==2][:, 2]) 125 plt.scatter(X[y==3][:, 0], X[y==3][:, 1], X[y==3][:, 2]) 126 plt.show() 127 128 129 if __name__ == "__main__": 130 main()
2.DBSCAN也是基於密度的聚類算法,與均值漂移聚類類似
具體步驟:
1. 首先確定半徑r和minPoints(數目). 從一個沒有被訪問過的任意數據點開始,以這個點為中心,r為半徑的圓內包含的點的數量是否大於或等於minPoints,如果大於或等於minPoints則改點被標記為central point,反之則會被標記為noise point。
2. 重復1的步驟,如果一個noise point存在於某個central point為半徑的圓內,則這個點被標記為邊緣點,反之仍為noise point。重復步驟1,知道所有的點都被訪問過。
在DBSCAN算法中將數據點分為一下三類:
核心點:在半徑Eps內含有超過MinPts數目的點
邊界點:在半徑Eps內點的數量小於MinPts,但是落在核心點的鄰域內
噪音點:既不是核心點也不是邊界點的點
優點:不需要知道簇的數量
缺點:需要確定距離r和minPoints
1 import numpy as np 2 3 from sklearn.cluster import DBSCAN 4 from sklearn import metrics 5 from sklearn.datasets.samples_generator import make_blobs 6 from sklearn.preprocessing import StandardScaler 7 8 9 ############################################################################## 10 # Generate sample data 11 centers = [[1, 1], [-1, -1], [1, -1]] 12 X, labels_true = make_blobs(n_samples=750, centers=centers, cluster_std=0.4, 13 random_state=0) 14 15 X = StandardScaler().fit_transform(X) 16 17 ############################################################################## 18 # Compute DBSCAN 19 db = DBSCAN(eps=0.3, min_samples=10).fit(X) 20 core_samples_mask = np.zeros_like(db.labels_, dtype=bool) 21 core_samples_mask[db.core_sample_indices_] = True 22 labels = db.labels_ 23 24 # Number of clusters in labels, ignoring noise if present. 25 n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) 26 27 print('Estimated number of clusters: %d' % n_clusters_) 28 print("Homogeneity: %0.3f" % metrics.homogeneity_score(labels_true, labels)) 29 print("Completeness: %0.3f" % metrics.completeness_score(labels_true, labels)) 30 print("V-measure: %0.3f" % metrics.v_measure_score(labels_true, labels)) 31 print("Adjusted Rand Index: %0.3f" 32 % metrics.adjusted_rand_score(labels_true, labels)) 33 print("Adjusted Mutual Information: %0.3f" 34 % metrics.adjusted_mutual_info_score(labels_true, labels)) 35 print("Silhouette Coefficient: %0.3f" 36 % metrics.silhouette_score(X, labels)) 37 38 ############################################################################## 39 # Plot result 40 import matplotlib.pyplot as plt 41 42 # Black removed and is used for noise instead. 43 unique_labels = set(labels) 44 colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels))) 45 for k, col in zip(unique_labels, colors): 46 if k == -1: 47 # Black used for noise. 48 col = 'k' 49 50 class_member_mask = (labels == k) 51 52 xy = X[class_member_mask & core_samples_mask] 53 plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col, 54 markeredgecolor='k', markersize=14) 55 56 xy = X[class_member_mask & ~core_samples_mask] 57 plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col, 58 markeredgecolor='k', markersize=6) 59 60 plt.title('Estimated number of clusters: %d' % n_clusters_) 61 plt.show()
黑色的點代表離群點或者叫噪聲點
3.層次聚類
層次聚類算法分為兩類:自上而下和自下而上。凝聚層級聚類(HAC)是自下而上的一種聚類算法。HAC首先將每個數據點視為一個單一的簇,然后計算所有簇之間的距離來合並簇,知道所有的簇聚合成為一個簇為止。
度量方法:
算法步驟:
1. 首先我們將每個數據點視為一個單一的簇,然后選擇一個測量兩個簇之間距離的度量標准。例如我們使用average linkage作為標准,它將兩個簇之間的距離定義為第一個簇中的數據點與第二個簇中的數據點之間的平均距離。
2. 在每次迭代中,我們將兩個具有最小average linkage的簇合並成為一個簇。
3. 重復步驟2知道所有的數據點合並成一個簇,然后選擇我們需要多少個簇。
層次聚類優點:(1)不需要知道有多少個簇
(2)對於距離度量標准的選擇並不敏感
缺點:效率低
---------------------
1 import numpy as np 2 import pandas as pd 3 from sklearn.cluster import AgglomerativeClustering 4 import matplotlib.pyplot as plt 5 import numpy as np 6 from scipy import ndimage 7 from matplotlib import pyplot as plt 8 from sklearn import manifold, datasets 9 10 11 # In[2]: 12 #1797個樣本,每個樣本包括8*8像素的圖像和一個[0, 9]整數的標簽 13 digits = datasets.load_digits(n_class=10)#手寫字體數據集, 14 X = digits.data 15 y = digits.target 16 n_samples, n_features = X.shape 17 print(digits.keys()) 18 print X[:5,:] 19 print n_samples,n_features 20 21 22 # In[3]: 23 24 # Visualize the clustering 25 def plot_clustering(X_red, X, labels, title=None): 26 x_min, x_max = np.min(X_red, axis=0), np.max(X_red, axis=0) 27 X_red = (X_red - x_min) / (x_max - x_min) 28 29 plt.figure(figsize=(6, 4)) 30 for i in range(X_red.shape[0]): 31 plt.text(X_red[i, 0], X_red[i, 1], str(y[i]), 32 color=plt.cm.spectral(labels[i] / 10.), 33 fontdict={'weight': 'bold', 'size': 9}) 34 35 plt.xticks([]) 36 plt.yticks([]) 37 if title is not None: 38 plt.title(title, size=17) 39 plt.axis('off') 40 plt.tight_layout() 41 42 43 # In[ ]: 44 45 # 2D embedding of the digits dataset 46 print("Computing embedding") 47 X_red = manifold.SpectralEmbedding(n_components=2).fit_transform(X) 48 print("Done.") 49 50 from sklearn.cluster import AgglomerativeClustering 51 52 for linkage in ('ward', 'average', 'complete'): 53 clustering = AgglomerativeClustering(linkage=linkage, n_clusters=10) 54 clustering.fit(X_red) 55 plot_clustering(X_red, X, clustering.labels_, "%s linkage" % linkage) 56 57 58 plt.show()
上圖顯示層次聚類采用不同距離度量方法的效果,complete在此數據集上效果較差。