聚類算法---kmeans以及DBSCAN算法


一、聚類分析又稱群分析,它是研究(樣品或指標)分類問題的一種統計分析方法,同時也是數據挖掘的一個重要算法。
聚類(Cluster)分析是由若干模式(Pattern)組成的,通常,模式是一個度量(Measurement)的向量,或者是 多維空間中的一個點。
聚類分析以相似性為基礎,在一個聚類中的模式之間比不在同一聚類中的模式之間具有更多的相似性。
 

二、聚類算法分類
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在此數據集上效果較差。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM