聚類算法(上):8個常見的無監督聚類方法介紹和比較

deephub 發佈 2024-05-05T02:05:59.539398+00:00

無監督聚類方法的評價指標必須依賴於數據和聚類結果的內在屬性,例如聚類的緊湊性和分離性,與外部知識的一致性,以及同一算法不同運行結果的穩定性。本文將全面概述Scikit-Learn庫中用於的聚類技術以及各種評估方法。

無監督聚類方法的評價指標必須依賴於數據和聚類結果的內在屬性,例如聚類的緊湊性和分離性,與外部知識的一致性,以及同一算法不同運行結果的穩定性。

本文將全面概述Scikit-Learn庫中用於的聚類技術以及各種評估方法。

本文將分為2個部分,1、常見算法比較 2、聚類技術的各種評估方法

本文作為第一部分將介紹和比較各種聚類算法:

  • K-Means
  • Affinity Propagation
  • Agglomerative Clustering
  • Mean Shift clustering
  • Bisecting K-Means
  • DBSCAN
  • OPTICS
  • BIRCH

首先我們生成一些數據,後面將使用這些數據作為聚類技術的輸入。

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
#Set the number of samples and features
n_samples = 1000
n_features = 4
#Create an empty array to store the data
data = np.empty((n_samples, n_features))
#Generate random data for each feature
for i in range(n_features):
data[:, i] = np.random.normal(size=n_samples)
#Create 5 clusters with different densities and centroids
cluster1 = data[:200, :] + np.random.normal(size=(200, n_features), scale=0.5)
cluster2 = data[200:400, :] + np.random.normal(size=(200, n_features), scale=1) + np.array([5,5,5,5])
cluster3 = data[400:600, :] + np.random.normal(size=(200, n_features), scale=1.5) + np.array([-5,-5,-5,-5])
cluster4 = data[600:800, :] + np.random.normal(size=(200, n_features), scale=2) + np.array([5,-5,5,-5])
cluster5 = data[800:, :] + np.random.normal(size=(200, n_features), scale=2.5) + np.array([-5,5,-5,5])
#Combine the clusters into one dataset
X = np.concatenate((cluster1, cluster2, cluster3, cluster4, cluster5))
# Plot the data
plt.scatter(X[:, 0], X[:, 1])
plt.show()

結果如下:

我們將用特徵值和簇ID創建一個DF。稍後在模型性能時將使用這些數據。

df = pd.DataFrame(X, columns=["feature_1", "feature_2", "feature_3", "feature_4"])
cluster_id = np.concatenate((np.zeros(200), np.ones(200), np.full(200, 2), np.full(200, 3), np.full(200, 4)))
df["cluster_id"] = cluster_id
df

現在我們將構建和可視化8個不同的聚類模型:

1、K-Means

K-Means聚類算法是一種常用的聚類算法,它將數據點分為K個簇,每個簇的中心點是其所有成員的平均值。K-Means算法的核心是疊代尋找最優的簇心位置,直到達到收斂狀態。

K-Means算法的優點是簡單易懂,計算速度較快,適用於大規模數據集。但是它也存在一些缺點,例如對於非球形簇的處理能力較差,容易受到初始簇心的選擇影響,需要預先指定簇的數量K等。此外,當數據點之間存在噪聲或者離群點時,K-Means算法可能會將它們分配到錯誤的簇中。

#K-Means
from sklearn.cluster import KMeans
#Define function:
kmeans = KMeans(n_clusters=5)
#Fit the model:
km = kmeans.fit(X)
km_labels = km.labels_
#Print results:
#print(kmeans.labels_)
#Visualise results:
plt.scatter(X[:, 0], X[:, 1], 
c=kmeans.labels_, 
s=70, cmap='Paired')
plt.scatter(kmeans.cluster_centers_[:, 0],
kmeans.cluster_centers_[:, 1],
marker='^', s=100, linewidth=2, 
c=[0, 1, 2, 3, 4])

2、Affinity Propagation

Affinity Propagation是一種基於圖論的聚類算法,旨在識別數據中的"exemplars"(代表點)和"clusters"(簇)。與K-Means等傳統聚類算法不同,Affinity Propagation不需要事先指定聚類數目,也不需要隨機初始化簇心,而是通過計算數據點之間的相似性得出最終的聚類結果。

Affinity Propagation算法的優點是不需要預先指定聚類數目,且能夠處理非凸形狀的簇。但是該算法的計算複雜度較高,需要大量的存儲空間和計算資源,並且對於噪聲點和離群點的處理能力較弱。

from sklearn.cluster import AffinityPropagation
#Fit the model:
af = AffinityPropagation(preference=-563, random_state=0).fit(X)
cluster_centers_indices = af.cluster_centers_indices_
af_labels = af.labels_
n_clusters_ = len(cluster_centers_indices)
#Print number of clusters:
print(n_clusters_)
import matplotlib.pyplot as plt
from itertools import cycle
plt.close("all")
plt.figure(1)
plt.clf()
colors = cycle("bgrcmykbgrcmykbgrcmykbgrcmyk")
for k, col in zip(range(n_clusters_), colors):
class_members = af_labels == k
cluster_center = X[cluster_centers_indices[k]]
plt.plot(X[class_members, 0], X[class_members, 1], col + ".")
plt.plot(
cluster_center[0],
cluster_center[1],
"o",
markerfacecolor=col,
markeredgecolor="k",
markersize=14,
)
for x in X[class_members]:
plt.plot([cluster_center[0], x[0]], [cluster_center[1], x[1]], col)
plt.title("Estimated number of clusters: %d" % n_clusters_)
plt.show()

3、Agglomerative Clustering

凝聚層次聚類(Agglomerative Clustering)是一種自底向上的聚類算法,它將每個數據點視為一個初始簇,並將它們逐步合併成更大的簇,直到達到停止條件為止。在該算法中,每個數據點最初被視為一個單獨的簇,然後逐步合併簇,直到所有數據點被合併為一個大簇。

Agglomerative Clustering算法的優點是適用於不同形狀和大小的簇,且不需要事先指定聚類數目。此外,該算法也可以輸出聚類層次結構,便於分析和可視化。缺點是計算複雜度較高,尤其是在處理大規模數據集時,需要消耗大量的計算資源和存儲空間。此外,該算法對初始簇的選擇也比較敏感,可能會導致不同的聚類結果。

from sklearn.cluster import AgglomerativeClustering
#Fit the model:
clustering = AgglomerativeClustering(n_clusters=5).fit(X)
AC_labels= clustering.labels_
n_clusters = clustering.n_clusters_
print("number of estimated clusters : %d" % clustering.n_clusters_)
# Plot clustering results
colors = ['purple', 'orange', 'green', 'blue', 'red']
for index, metric in enumerate([#"cosine", 
"euclidean", 
#"cityblock"
]):
model = AgglomerativeClustering(
n_clusters=5, linkage="ward", affinity=metric
)
model.fit(X)
plt.figure()
plt.axes([0, 0, 1, 1])
for l, c in zip(np.arange(model.n_clusters), colors):
plt.plot(X[model.labels_ == l].T, c=c, alpha=0.5)
plt.axis("tight")
plt.axis("off")
plt.suptitle("AgglomerativeClustering(affinity=%s)" % metric, size=20)
plt.show()

4、Mean Shift Clustering

Mean Shift Clustering是一種基於密度的非參數聚類算法,其基本思想是通過尋找數據點密度最大的位置(稱為"局部最大值"或"高峰"),來識別數據中的簇。算法的核心是通過對每個數據點進行局部密度估計,並將密度估計的結果用於計算數據點移動的方向和距離。算法的核心是通過對每個數據點進行局部密度估計,並將密度估計的結果用於計算數據點移動的方向和距離。

Mean Shift Clustering算法的優點是不需要指定簇的數目,且對於形狀複雜的簇也有很好的效果。算法還能夠有效地處理噪聲數據。他的缺點也是計算複雜度較高,尤其是在處理大規模數據集時,需要消耗大量的計算資源和存儲空間,該算法還對初始參數的選擇比較敏感,需要進行參數調整和優化。

from sklearn.cluster import MeanShift, estimate_bandwidth
# The following bandwidth can be automatically detected using
bandwidth = estimate_bandwidth(X, quantile=0.2, n_samples=100)
#Fit the model:
ms = MeanShift(bandwidth=bandwidth)
ms.fit(X)
MS_labels = ms.labels_
cluster_centers = ms.cluster_centers_
labels_unique = np.unique(labels)
n_clusters_ = len(labels_unique)
print("number of estimated clusters : %d" % n_clusters_)
from itertools import cycle
plt.figure(1)
plt.clf()
colors = cycle("bgrcmykbgrcmykbgrcmykbgrcmyk")
for k, col in zip(range(n_clusters_), colors):
my_members = labels == k
cluster_center = cluster_centers[k]
plt.plot(X[my_members, 0], X[my_members, 1], col + ".")
plt.plot(
cluster_center[0],
cluster_center[1],
"o",
markerfacecolor=col,
markeredgecolor="k",
markersize=14,
)
plt.title("Estimated number of clusters: %d" % n_clusters_)
plt.show()

5、Bisecting K-Means

Bisecting K-Means是一種基於K-Means算法的層次聚類算法,其基本思想是將所有數據點劃分為一個簇,然後將該簇分成兩個子簇,並對每個子簇分別應用K-Means算法,重複執行這個過程,直到達到預定的聚類數目為止。

算法首先將所有數據點視為一個初始簇,然後對該簇應用K-Means算法,將該簇分成兩個子簇,並計算每個子簇的誤差平方和(SSE)。然後,選擇誤差平方和最大的子簇,並將其再次分成兩個子簇,重複執行這個過程,直到達到預定的聚類數目為止。

Bisecting K-Means算法的優點是具有較高的準確性和穩定性,能夠有效地處理大規模數據集,並且不需要指定初始聚類數目。該算法還能夠輸出聚類層次結構,便於分析和可視化。缺點是計算複雜度較高,尤其是在處理大規模數據集時,需要消耗大量的計算資源和存儲空間。此外該算法對初始簇的選擇也比較敏感,可能會導致不同的聚類結果。

from sklearn.cluster import BisectingKMeans
#Build and fit model:
bisect_means = BisectingKMeans(n_clusters=5).fit(X)
BKM_labels = bisect_means.labels_
#Print model attributes:
#print('Labels: ', bisect_means.labels_)
print('Number of clusters: ', bisect_means.n_clusters)
#Define varaibles to be included in scatterdot:
y= bisect_means.labels_
#print(y)
centers = bisect_means.cluster_centers_
# Visualize the results using a scatter plot
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.scatter(centers[:, 0], centers[:, 1], c='r', s=100)
plt.show()

6、DBSCAN

DBSCAN (Density-Based Spatial Clustering of Applications with Noise)是一種基於密度的聚類算法,其可以有效地發現任意形狀的簇,並能夠處理噪聲數據。DBSCAN算法的核心思想是:對於一個給定的數據點,如果它的密度達到一定的閾值,則它屬於一個簇中;否則,它被視為噪聲點。

DBSCAN算法的優點是能夠自動識別簇的數目,並且對於任意形狀的簇都有較好的效果。並且還能夠有效地處理噪聲數據,不需要預先指定簇的數目。缺點是對於密度差異較大的數據集,可能會導致聚類效果不佳,需要進行參數調整和優化。另外該算法對於高維數據集的效果也不如其他算法

from sklearn.cluster import DBSCAN
db = DBSCAN(eps=3, min_samples=10).fit(X)
DBSCAN_labels = db.labels_
# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
n_noise_ = list(labels).count(-1)
print("Estimated number of clusters: %d" % n_clusters_)
print("Estimated number of noise points: %d" % n_noise_)
unique_labels = set(labels)
core_samples_mask = np.zeros_like(labels, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unique_labels))]
for k, col in zip(unique_labels, colors):
if k == -1:
# Black used for noise.
col = [0, 0, 0, 1]
class_member_mask = labels == k
xy = X[class_member_mask & core_samples_mask]
plt.plot(
xy[:, 0],
xy[:, 1],
"o",
markerfacecolor=tuple(col),
markeredgecolor="k",
markersize=14,
)
xy = X[class_member_mask & ~core_samples_mask]
plt.plot(
xy[:, -1],
xy[:, 1],
"o",
markerfacecolor=tuple(col),
markeredgecolor="k",
markersize=6,
)
plt.title(f"Estimated number of clusters: {n_clusters_}")
plt.show()

7、OPTICS

OPTICS(Ordering Points To Identify the Clustering Structure)是一種基於密度的聚類算法,其能夠自動確定簇的數量,同時也可以發現任意形狀的簇,並能夠處理噪聲數據。OPTICS算法的核心思想是:對於一個給定的數據點,通過計算它到其它點的距離,確定其在密度上的可達性,從而構建一個基於密度的距離圖。然後,通過掃描該距離圖,自動確定簇的數量,並對每個簇進行劃分。

OPTICS算法的優點是能夠自動確定簇的數量,並能夠處理任意形狀的簇,並能夠有效地處理噪聲數據。該算法還能夠輸出聚類層次結構,便於分析和可視化。缺點是計算複雜度較高,尤其是在處理大規模數據集時,需要消耗大量的計算資源和存儲空間。另外就是該算法對於密度差異較大的數據集,可能會導致聚類效果不佳。

from sklearn.cluster import OPTICS
import matplotlib.gridspec as gridspec
#Build OPTICS model:
clust = OPTICS(min_samples=3, min_cluster_size=100, metric='euclidean')
# Run the fit
clust.fit(X)
space = np.arange(len(X))
reachability = clust.reachability_[clust.ordering_]
OPTICS_labels = clust.labels_[clust.ordering_]
labels = clust.labels_[clust.ordering_]
plt.figure(figsize=(10, 7))
G = gridspec.GridSpec(2, 3)
ax1 = plt.subplot(G[0, 0])
ax2 = plt.subplot(G[1, 0])
# Reachability plot
colors = ["g.", "r.", "b.", "y.", "c."]
for klass, color in zip(range(0, 5), colors):
Xk = space[labels == klass]
Rk = reachability[labels == klass]
ax1.plot(Xk, Rk, color, alpha=0.3)
ax1.plot(space[labels == -1], reachability[labels == -1], "k.", alpha=0.3)
ax1.set_ylabel("Reachability (epsilon distance)")
ax1.set_title("Reachability Plot")
# OPTICS
colors = ["g.", "r.", "b.", "y.", "c."]
for klass, color in zip(range(0, 5), colors):
Xk = X[clust.labels_ == klass]
ax2.plot(Xk[:, 0], Xk[:, 1], color, alpha=0.3)
ax2.plot(X[clust.labels_ == -1, 0], X[clust.labels_ == -1, 1], "k+", alpha=0.1)
ax2.set_title("Automatic Clustering\nOPTICS")
plt.tight_layout()
plt.show()

8、BIRCH

BIRCH(Balanced Iterative Reducing and Clustering using Hierarchies)是一種基於層次聚類的聚類算法,其可以快速地處理大規模數據集,並且對於任意形狀的簇都有較好的效果。BIRCH算法的核心思想是:通過對數據集進行分級聚類,逐步減小數據規模,最終得到簇結構。BIRCH算法採用一種類似於B樹的結構,稱為CF樹,它可以快速地插入和刪除子簇,並且可以自動平衡,從而確保簇的質量和效率。

BIRCH算法的優點是能夠快速處理大規模數據集,並且對於任意形狀的簇都有較好的效果。該算法對於噪聲數據和離群點也有較好的容錯性。缺點是對於密度差異較大的數據集,可能會導致聚類效果不佳,對於高維數據集的效果也不如其他算法。

import matplotlib.colors as colors
from sklearn.cluster import Birch, MiniBatchKMeans
from time import time
from itertools import cycle
# Use all colors that matplotlib provides by default.
colors_ = cycle(colors.cnames.keys())
fig = plt.figure(figsize=(12, 4))
fig.subplots_adjust(left=0.04, right=0.98, bottom=0.1, top=0.9)
# Compute clustering with BIRCH with and without the final clustering step
# and plot.
birch_models = [
Birch(threshold=1.7, n_clusters=None),
Birch(threshold=1.7, n_clusters=5),
]
final_step = ["without global clustering", "with global clustering"]
for ind, (birch_model, info) in enumerate(zip(birch_models, final_step)):
t = time()
birch_model.fit(X)
print("BIRCH %s as the final step took %0.2f seconds" % (info, (time() - t)))
# Plot result
labels = birch_model.labels_
centroids = birch_model.subcluster_centers_
n_clusters = np.unique(labels).size
print("n_clusters : %d" % n_clusters)
ax = fig.add_subplot(1, 3, ind + 1)
for this_centroid, k, col in zip(centroids, range(n_clusters), colors_):
mask = labels == k
ax.scatter(X[mask, 0], X[mask, 1], c="w", edgecolor=col, marker=".", alpha=0.5)
if birch_model.n_clusters is None:
ax.scatter(this_centroid[0], this_centroid[1], marker="+", c="k", s=25)
ax.set_ylim([-12, 12])
ax.set_xlim([-12, 12])
ax.set_autoscaley_on(False)
ax.set_title("BIRCH %s" % info)
plt.show()

總結

上面就是我們常見的8個聚類算法,我們對他們進行了簡單的說明和比較,並且用sklearn演示了如何使用,在下一篇文章中我們將介紹聚類模型評價方法。

關鍵字: