本文主要講解的聚類算法有:k均值算法、均值漂移算法、凝聚層次算法、DBSCAN密度聚類算法,還介紹了聚類算法性能指標——輪廓系數。
聚類(cluster)與分類(class)不同,分類是有監督學習模型,聚類屬於無監督學習模型。聚類講究使用一些算法把樣本划分為n個群落。一般情況下,這種算法都需要計算歐幾里得距離。
$$P(x_1) - Q(x_2): |x_1-x_2| = \sqrt{(x_1-x_2)^2} \\
P(x_1,y_1) - Q(x_2,y_2): \sqrt{(x_1-x_2)^2+(y_1-y_2)^2} \\
P(x_1,y_1,z_1) - Q(x_2,y_2,z_2): \sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2} \\$$
歐氏距離:用兩個樣本對應特征值之差的平方和的平方根,來表示這兩個樣本的相似性。
K均值算法
第一步:隨機選擇k個樣本作為k個聚類的中心,計算每個樣本到各個聚類中心的歐氏距離,將該樣本分配到與之距離最近的聚類中心所在的類別中。
第二步:根據第一步所得到的聚類划分,分別計算每個聚類的幾何中心,將幾何中心作為新的聚類中心,重復第一步,直到計算所得幾何中心與聚類中心重合或接近重合為止。
注意:
-
聚類數$k$必須事先已知。借助某些評估指標(歐式距離),優選最好的聚類數。
-
聚類中心的初始選擇會影響到最終聚類划分的結果。初始中心盡量選擇距離較遠的樣本。
K均值算法相關API:
import sklearn.cluster as sc model = sc.KMeans(n_clusters=4) # n_clusters: 聚類數 # 訓練模型,不斷調整聚類中心,直到最終聚類中心穩定則聚類完成 model.fit(x) centers = model.cluster_centers_ # 獲取訓練結果的聚類中心 labels = model.labels_ # 每個樣本的聚類的類別標簽
案例:加載multiple3.txt數據集,基於K均值算法完成樣本的聚類。
import numpy as np import sklearn.cluster as sc import matplotlib.pyplot as plt x = np.loadtxt('../machine_learning_date/multiple3.txt', delimiter=',') model = sc.KMeans(n_clusters=4) # 構建質心數量為4的聚類模型 model.fit(x) # 訓練模型 pred_y = model.labels_ # 返回每個樣本的聚類的類別標簽: 0/1/2/3 centers = model.cluster_centers_ # 返回所有的聚類中心樣本 print(centers) # 繪制分類邊界線 n = 500 l, r = x[:, 0].min() - 1, x[:, 0].max() + 1 b, t = x[:, 1].min() - 1, x[:, 1].max() + 1 # 生成網格坐標矩陣 grid_x = np.meshgrid(np.linspace(l, r, n), np.linspace(b, t, n)) # 按列拼接數組 flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel())) flat_y = model.predict(flat_x) grid_y = flat_y.reshape(grid_x[0].shape) plt.figure('K-Means Cluster', facecolor='lightgray') plt.title('K-Means Cluster', fontsize=14) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray') plt.scatter(x[:, 0], x[:, 1], c=pred_y, cmap='brg', s=80) plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='+', s=500) plt.show()
圖像量化
K-Means聚類算法可以應用於圖像量化領域。通過K-Means算法可以把一張圖像所包含的顏色值進行聚類划分,求每一類別的平均值后再重新生成新的圖像。可以達到圖像降維的目的。這個過程稱為圖像量化。圖像量化可以更好的保留圖像的輪廓,降低機器識別圖像輪廓的難度。
import sklearn.cluster as sc import matplotlib.pyplot as plt import scipy.misc as sm # 通過K均值聚類量化 圖像中的"顏色" def quant(image, n_clusters): # x代表所有圖片像素點的亮度值數組 x = image.reshape(-1, 1) model = sc.KMeans(n_clusters=n_clusters) model.fit(x) # 當n_clusters=2時,label含:0/1 # 當n_clusters=3時,label含:0/1/2 # 當n_clusters=4時,label含:0/1/2/3 y = model.labels_ # 獲取聚類中心 [30, 70, 110, 190] centers = model.cluster_centers_.ravel() # 獲取質心並變為一維數組 return centers[y].reshape(image.shape) # 讀取灰度圖 original = sm.imread('../machine_learning_date/lily.jpg', True) quant2 = quant(original, 2) quant3 = quant(original, 3) quant4 = quant(original, 4) plt.figure('Image Quant', facecolor='lightgray') plt.subplot(221) plt.title('Original', fontsize=14) plt.axis('off') plt.imshow(original, cmap='gray') plt.subplot(222) plt.title('Quant-2', fontsize=14) plt.axis('off') plt.imshow(quant2, cmap='gray') plt.subplot(223) plt.title('Quant-3', fontsize=14) plt.axis('off') plt.imshow(quant3, cmap='gray') plt.subplot(224) plt.title('Quant-4', fontsize=14) plt.axis('off') plt.imshow(quant4, cmap='gray') plt.tight_layout() plt.show()
均值漂移算法
假設在一個有N個樣本點的特征空間,初始一個中心點,計算在設置的半徑圓形空間內的所有點與中心點的向量,計算整個圓形空間內所有向量的平均值,得到一個偏移均值,將中心點移動到偏移值位置,重復移動,直到滿足一定條件結束,最終移動的中心點就是聚類的中心,再根據每個樣本距離各個中心的距離,選擇最近聚類中心所屬的類別作為該樣本的類別。
均值漂移算法的特點:
- 聚類數不必事先已知,算法會自動識別出統計直方圖的中心數量。
- 聚類中心不依據於最初假定,聚類划分的結果相對穩定。
- 樣本空間應該服從某種概率分布規則,否則算法的准確性會大打折扣。
均值漂移算法相關API:
# n_samples:樣本數量 # quantile:量化寬度 # 計算出半徑(帶寬) bw = sc.estimate_bandwidth(x, n_samples=len(x), quantile=0.1) # 均值漂移聚類器 model = sc.MeanShift(bandwidth=bw, bin_seeding=True) model.fit(x)
案例:加載multiple3.txt,使用均值漂移算法對樣本完成聚類划分。
"""均值漂移""" import numpy as np import sklearn.cluster as sc import matplotlib.pyplot as plt x = np.loadtxt('../machine_learning_date/multiple3.txt', delimiter=',') # 構建聚類模型 bw = sc.estimate_bandwidth(x, n_samples=len(x), quantile=0.2) # 計算出半徑(帶寬) model = sc.MeanShift(bandwidth=bw, bin_seeding=True) model.fit(x) # 返回每個樣本的聚類的類別標簽: 0/1/2/3 pred_y = model.labels_ # 返回所有的聚類中心樣本 centers = model.cluster_centers_ print(centers) # 繪制分類邊界線 n = 500 l, r = x[:, 0].min() - 1, x[:, 0].max() + 1 b, t = x[:, 1].min() - 1, x[:, 1].max() + 1 grid_x = np.meshgrid(np.linspace(l, r, n), np.linspace(b, t, n)) flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel())) flat_y = model.predict(flat_x) grid_y = flat_y.reshape(grid_x[0].shape) plt.figure('K-Means Cluster') plt.title('K-Means Cluster', fontsize=14) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray') plt.scatter(x[:, 0], x[:, 1], c=pred_y, cmap='brg', s=80) plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='+', s=500) plt.show()
凝聚層次算法
首先假定每個樣本都是一個獨立的聚類,如果統計出來的聚類數大於期望的聚類數,則從每個樣本出發尋找離自己最近的另一個樣本,與之聚集,形成更大的聚類,同時令總聚類數減少,不斷重復以上過程,直到統計出來的聚類數達到期望值為止。
凝聚層次算法的特點:
-
聚類數必須事先已知。借助某些評估指標,優選最好的聚類數。
-
沒有聚類中心的概念,因此只能在訓練集中划分聚類,但不能對訓練集以外的未知樣本確定其聚類歸屬。
-
在確定被凝聚的樣本時,除了以距離作為條件以外,還可以根據連續性來確定被聚集的樣本。
凝聚層次算法相關API:
# 凝聚層次聚類器 model = sc.AgglomerativeClustering(n_clusters=4) pred_y = model.fit_predict(x)
案例:重新加載multiple3.txt,使用凝聚層次算法進行聚類划分。
import numpy as np import sklearn.cluster as sc import matplotlib.pyplot as plt x = np.loadtxt('../machine_learning_date/multiple3.txt', delimiter=',') # 凝聚層次聚類器 model = sc.AgglomerativeClustering(n_clusters=4) pred_y = model.fit_predict(x) # pred_y為x的類別 plt.figure('Agglomerative Cluster', facecolor='lightgray') plt.title('Agglomerative Cluster', fontsize=14) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.scatter(x[:, 0], x[:, 1], c=pred_y, cmap='brg', s=80) plt.show()
在確定被凝聚的樣本時,除了以距離作為條件以外,還可以根據連續性來確定被聚集的樣本。
""" 基於連續性近鄰篩選器的凝聚層次算法 """ import numpy as np import sklearn.cluster as sc import sklearn.neighbors as nb import matplotlib.pyplot as plt n_samples = 500 x = np.linspace(-1, 1, n_samples) y = np.sin(x * 2 * np.pi) n = 0.3 * np.random.rand(n_samples, 2) x = np.column_stack((x, y)) + n # 按列拼接 # 無連續性的凝聚層次聚類器 model_nonc = sc.AgglomerativeClustering(linkage='average', n_clusters=3) pred_y_nonc = model_nonc.fit_predict(x) # 近鄰篩選器 conn = nb.kneighbors_graph(x, 10, include_self=False) # 有連續性的凝聚層次聚類器 model_conn = sc.AgglomerativeClustering( linkage='average', n_clusters=3, connectivity=conn) pred_y_conn = model_conn.fit_predict(x) plt.figure('Nonconnectivity', facecolor='lightgray') plt.title('Nonconnectivity', fontsize=14) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.scatter(x[:, 0], x[:, 1], c=pred_y_nonc, cmap='brg', alpha=0.5, s=30) plt.figure('Connectivity', facecolor='lightgray') plt.title('Connectivity', fontsize=14) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.scatter(x[:, 0], x[:, 1], c=pred_y_conn, cmap='brg', alpha=0.5, s=30) plt.show()
輪廓系數
聚類性能的指標:同一個聚類內部的樣本要足夠密集,不同聚類之間樣本要足夠疏遠。
輪廓系數計算規則:針對樣本空間中的一個特定樣本,計算它與所在聚類其它樣本的平均距離a,以及該樣本與距離最近的另一個聚類中所有樣本的平均距離b,該樣本的輪廓系數為(b-a)/max(a, b),將整個樣本空間中所有樣本的輪廓系數取算數平均值,作為聚類划分的性能指標。
輪廓系數的區間為:[-1, 1]。 -1代表分類效果差,1代表分類效果好。0代表聚類重疊,沒有很好的划分聚類。
輪廓系數相關API:
import sklearn.metrics as sm # v:平均輪廓系數 # metric:距離算法:使用歐幾里得距離(euclidean) v = sm.silhouette_score(輸入集, 輸出集, sample_size=樣本數, metric=距離算法)
案例:輸出KMeans算法聚類划分后的輪廓系數。
# 打印平均輪廓系數 print(sm.silhouette_score( x, pred_y, sample_size=len(x), metric='euclidean'))
DBSCAN密度聚類算法
DBSCAN是基於密度的聚類算法,通過將緊密相連的樣本划為一類,這樣就得到了一個聚類類別。通過將所有各組緊密相連的樣本划為各個不同的類別,則我們就得到了最終的所有聚類類別結果。把具有足夠高密度的區域划分為鏃(同一類別的樣本他們之間緊密相連,也就是說在該類別任意樣本周圍不遠處一定有同類別的樣本存在)。並可在噪聲的空間數據庫中發現形狀的聚類。
DBSCAN算法的特點:
- 密度聚類算法一般假定類別可以通過樣本分布的緊密程度決定。
-
事先給定的半徑會影響最后的聚類效果,可以借助輪廓系數選擇較優的方案。
-
根據聚類的形成過程,把樣本細分為以下三類:
外周樣本:被其它樣本聚集到某個聚類中,但無法再引入新樣本的樣本。
孤立樣本:聚類中的樣本數低於所設定的下限,則不稱其為聚類,反之稱其為孤立樣本。
核心樣本:除了外周樣本和孤立樣本以外的樣本。
DBSCAN聚類算法相關API:
# DBSCAN聚類器 # eps:半徑 # min_samples:聚類樣本數的下限,若低於該數值,則稱為孤立樣本 model = sc.DBSCAN(eps=epsilon, min_samples=5) model.fit(x) # 獲取訓練后所有聚類的核心樣本的索引數組 # 可以以此作為掩碼,獲取所有的核心樣本 indices = model.core_sample_indices_ # 每個樣本的聚類類別標簽 [0,1,2,3,0,1,2,3,0,1,2,3,-1] labels = model.labels_ # 會出現-1的情況,label=-1的樣本就是孤立樣本
案例:修改凝聚層次聚類案例,基於DBSCAN聚類算法進行聚類划分,選擇最優半徑。
import numpy as np import sklearn.cluster as sc import sklearn.metrics as sm import matplotlib.pyplot as plt x = np.loadtxt('../machine_learning_date/perf.txt', delimiter=',') epsilons, scores, models = np.linspace(0.3, 1.2, 10), [], [] for epsilon in epsilons: # DBSCAN聚類器 model = sc.DBSCAN(eps=epsilon, min_samples=5) model.fit(x) # 計算輪廓系數 score = sm.silhouette_score( x, model.labels_, sample_size=len(x), metric='euclidean') scores.append(score) models.append(model) scores = np.array(scores) best_index = scores.argmax() # 求出最大值的索引 best_epsilon = epsilons[best_index] # 最優半徑 best_score = scores[best_index] # 最優輪廓系數 best_model = models[best_index] print("最優半徑", best_epsilon) # 最優半徑 0.7999999999999999 print("最優輪廓系數", best_score) # 最優輪廓系數 0.6366395861050828
獲取核心樣本、外周樣本、孤立樣本。並且使用不同的點型繪圖。
core_mask = np.zeros(len(x), dtype=bool) core_mask[best_model.core_sample_indices_] = True offset_mask = best_model.labels_ == -1 periphery_mask = ~(core_mask | offset_mask) plt.figure('DBSCAN Cluster', facecolor='lightgray') plt.title('DBSCAN Cluster', fontsize=14) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) labels = best_model.labels_ # 核心樣本 plt.scatter(x[core_mask][:, 0], x[core_mask][:, 1], c=labels[core_mask], cmap='brg', s=80, label='Core') # 外周樣本 plt.scatter(x[periphery_mask][:, 0], x[periphery_mask][:, 1], alpha=0.5, c=labels[periphery_mask], cmap='brg', marker='s', s=80, label='Periphery') # 孤立樣本 plt.scatter(x[offset_mask][:, 0], x[offset_mask][:, 1], c=labels[offset_mask], cmap='brg', marker='x', s=80, label='Offset') plt.legend() plt.show()