本文主要讨论k-means 聚类算法背后的基础知识和数学原理,以及如何在 scikit-learn 中实现它。
聚类(或聚类分析)是一种技术,允许我们找到相似对象的群组,这些对象彼此之间的关系比与其他群组中的对象更密切。聚类的商业应用示例包括根据不同主题对文档、音乐和电影进行分组,或者基于共同购买行为找到共享类似兴趣的客户,作为推荐引擎的基础。
在本文中,我们将学习关于最流行的聚类算法之一:k-means 的知识,这在学术界和工业界都被广泛使用。我们将涵盖以下内容:
正如我们将看到的那样,与其他聚类算法相比,k-means 算法极其易于实现并且在计算上非常高效,这可能解释了它的流行。k-means 算法属于原型聚类的一种。
原型聚类意味着每个聚类由一个原型表示,该原型可以是具有连续特征的相似点的质心(平均值),或者在分类特征的情况下是最具代表性或最频繁出现的点(中心点)。
虽然 k-means 在识别球形状的簇方面非常出色,但该聚类算法的一个缺点是我们必须事先指定簇的数量 k。对 k 进行不恰当的选择可能导致聚类性能不佳 —— 我们将在本教程后面讨论如何选择 k。
虽然 k-means 聚类可以应用于高维数据,但为了可视化的目的,我们将使用一个简单的二维数据集来演示以下示例。
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# create dataset
X, y = make_blobs(
n_samples=150, n_features=2,
centers=3, cluster_std=0.5,
shuffle=True, random_state=0
)
# plot
plt.scatter(
X[:, 0], X[:, 1],
c='white', marker='o',
edgecolor='black', s=50
)
plt.show()
image-20240718221214619
我们刚刚创建的数据集包含了150个随机生成的点,大致分成了三个密度较高的区域,这通过二维散点图进行了可视化。
在聚类的实际应用中,我们没有关于这些样本的任何真实分类信息(即作为经验证据而非推断提供的信息);否则,它将属于监督学习的范畴。因此,我们的目标是根据它们的特征相似性将样本分组,这可以通过 k-means 算法来实现,其主要步骤如下四步:
可视化上述步骤:
kmeans现在,下一个问题是我们如何衡量对象之间的相似性?我们可以将相似性定义为距离的相反,对于具有连续特征的样本聚类,常用的距离是在 m 维空间中两点 x 和 y 之间的平方欧氏距离。
索引 j 指的是样本点 x 和 y 的第 j 维(特征列)。
下述的方程中,我们将使用上标 i 和 j 分别表示样本索引和簇索引。
基于这个欧氏距离度量,我们可以将 k-means 算法描述为一个简单的优化问题,即通过迭代的方式最小化簇内平方误差和(Sum of Squared Errors, SSE),有时也称为簇的惯性。
这里,
是第个聚类的中心以及,
请注意,当我们使用欧氏距离度量将 k-means 应用于真实世界的数据时,我们希望确保特征在相同的尺度上进行测量,必要时可以应用 z-score 标准化或 min-max 缩放。
现在我们已经学习了 k-means 算法的工作原理,让我们使用 scikit-learn 的 cluster 模块中的 KMeans 类将其应用于我们的示例数据集。
from sklearn.cluster import KMeans
km = KMeans(
n_clusters=3, init='random',
n_init=10, max_iter=300,
tol=1e-04, random_state=0
)
y_km = km.fit_predict(X)
上述代码,我们将所需的簇数设置为 3。我们设置 n_init=10
,以便独立运行 k-means 聚类算法 10 次,每次使用不同的随机中心点选择最终模型为 SSE 最低的模型。通过 max_iter 参数,我们指定每次单独运行的最大迭代次数(这里是 300)。
请注意,scikit-learn 中的 k-means 实现在达到最大迭代次数之前,如果收敛了会提前停止。然而,如果选择了相对较大的 max_iter 值,可能 k-means 在特定运行中无法达到收敛,这可能会导致计算开销较大。
处理收敛问题的一种方法是选择较大的 tol 值,该参数控制在簇内平方误差变化方面的容差以宣布收敛。在前面的代码中,我们选择了容差为 1e-04(等于 0.0001)。
k-means 的一个问题是可能会出现一个或多个空簇。然而,在当前的 scikit-learn 实现中已经考虑了这个问题。如果一个簇为空,算法会搜索距离空簇质心最远的样本点。然后,它将重新分配质心为这个最远的点。
现在们已经预测了聚类标签 y_km
,现在让我们可视化 k-means 算法在数据集中识别的聚类以及这些聚类的中心点。这些聚类的中心点存储在拟合后的 KMeans 对象的 cluster_centers_
属性下:
# 聚类以及聚类中心可视化
plt.scatter(
X[y_km == 0, 0], X[y_km == 0, 1],
s=50, c='lightgreen',
marker='s', edgecolor='black',
label='cluster 1'
)
plt.scatter(
X[y_km == 1, 0], X[y_km == 1, 1],
s=50, c='orange',
marker='o', edgecolor='black',
label='cluster 2'
)
plt.scatter(
X[y_km == 2, 0], X[y_km == 2, 1],
s=50, c='lightblue',
marker='v', edgecolor='black',
label='cluster 3'
)
# plot the centroids
plt.scatter(
km.cluster_centers_[:, 0], km.cluster_centers_[:, 1],
s=250, marker='*',
c='red', edgecolor='black',
label='centroids'
)
plt.legend(scatterpoints=1)
plt.grid()
plt.show()
image-20240719161042482
在生成的散点图中,我们可以看到 k-means 算法将三个中心点放置在了每个“球体”的中心位置,考虑到这个数据集,这样的分组看起来是合理的。
尽管 k-means 在这个数据集上表现良好,但其中一个缺点是,在知道最优的 k 值之前,我们必须指定聚类的数量 k。在现实世界的应用中,尤其是在处理无法可视化的高维数据集时,选择聚类数量可能并不总是那么显而易见。
手肘法(Elbow Method)是一种有用的图形工具,用于估计给定任务的最优聚类数量 k。直观地说,如果 k 增加,那么聚类内的 SSE(Sum of Squared Errors,平方误差和,也常被称为“畸变”)将会减少。这是因为样本将更接近它们被分配到的中心点。然而,随着 k 的增加,SSE 减少的幅度会逐渐减小。手肘法通过绘制 SSE 关于 k 的曲线,并寻找曲线的“手肘点”(即 SSE 开始急剧减少后趋于平缓的点)来确定最优的 k 值。这个点通常被认为是 SSE 下降速率开始显著减缓的位置,因此被视为最佳聚类数量的一个合理估计。
手肘法的核心思想是识别出 k 值,在该值处畸变(即数据点与聚类中心之间的某种度量,如平方误差和)开始迅速减少。如果我们为不同的 k 值绘制畸变图,这一点将会变得更加清晰。
# 计算不同簇类的度量
distortions = []
for i in range(1, 11):
km = KMeans(
n_clusters=i, init='random',
n_init=10, max_iter=300,
tol=1e-04, random_state=0
)
km.fit(X)
distortions.append(km.inertia_)
# plot
plt.plot(range(1, 11), distortions, marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.show()
image-20240719162015835
从生成的图中我们可以看到,手肘位于 k = 3 的位置(在k=3处值迅速减少),这证明了 k = 3 确实是对于这个数据集的一个好选择。手肘法的这一结论与我们先前通过直接观察数据集分布或运行 k-means 算法得到的聚类结果相一致,进一步验证了选择 k = 3 的合理性。
我希望你喜欢这个关于 k-means 算法的教程!我们探讨了 k-means 算法背后的基本概念和数学原理,学习了如何实现 k-means,以及如何选择最优的聚类数量 k。希望这些内容能够帮助你更好地理解和应用 k-means 算法。如果你有任何问题或想要进一步了解的内容,请随时告诉我!
欢迎扫码关注: