在机器学习领域,聚类是一种无监督学习方法,旨在将数据集中的样本划分为若干个互不重叠的簇(Cluster)。而 KMeans 算法,作为最经典、最常用的聚类算法之一,凭借其简洁高效的特性,被广泛应用于数据分析、模式识别等领域。然而,在实际应用中,如果对 KMeans 算法的原理和局限性理解不够深入,很容易陷入各种坑中。本文将深入剖析 KMeans 算法的底层原理,并结合实际案例,分享一些实战经验和避坑技巧,助力大家更好地掌握和运用这一 powerful 的算法。
KMeans 算法原理深度剖析
KMeans 算法的核心思想非常简单:
- 初始化:随机选择 k 个样本点作为初始的簇中心(Centroid)。这里的 k 是一个超参数,需要我们事先指定。
- 分配:对于数据集中的每个样本点,计算其与各个簇中心的距离(通常使用欧氏距离),并将该样本点划分到距离最近的簇中。
- 更新:对于每个簇,重新计算该簇中所有样本点的均值,并将该均值作为新的簇中心。
- 迭代:重复步骤 2 和步骤 3,直到簇中心不再发生变化,或者达到预设的最大迭代次数。
这个过程可以用下图简单表示(Markdown 不支持直接插入图片,此处省略)。
距离度量
KMeans 算法中使用最广泛的距离度量方式是欧氏距离,公式如下:
d(x, y) = sqrt(sum((xi - yi)^2))
其中,x 和 y 分别表示两个样本点,xi 和 yi 表示 x 和 y 的第 i 个特征。
除了欧氏距离,还可以使用曼哈顿距离、切比雪夫距离等其他距离度量方式。选择哪种距离度量方式,取决于数据的具体情况和应用场景。
K 值的选择
KMeans 算法的性能很大程度上取决于 k 值的选择。如果 k 值选择不当,可能会导致聚类结果不理想。
常用的 K 值选择方法有:
肘部法则(Elbow Method):通过绘制 K 值与簇内误差平方和(SSE)的折线图,找到 SSE 下降速度明显变缓的“肘部”对应的 K 值。这个方法依赖于数据分布,如果数据分布没有明显的“肘部”,则效果不佳。

轮廓系数(Silhouette Coefficient):对于每个样本点,计算其轮廓系数,轮廓系数的取值范围为 [-1, 1],值越大,表示聚类效果越好。选择使得所有样本点的平均轮廓系数最大的 K 值。
Gap 统计量(Gap Statistic):将聚类结果与随机生成的数据集进行比较,选择使得 Gap 统计量最大的 K 值。
KMeans 算法的优缺点
优点:
- 算法简单易懂,容易实现。
- 计算复杂度低,适合处理大规模数据集。
缺点:
- 需要事先指定 K 值,对 K 值的选择比较敏感。
- 对初始簇中心的选择比较敏感,不同的初始簇中心可能会导致不同的聚类结果。
- 容易收敛到局部最优解。
- 对噪声和异常值比较敏感。
- 假设簇是凸形的,且簇的密度是均匀的,这与实际情况可能不符。
KMeans 算法的代码实现 (Python)
import numpy as np
class KMeans:
def __init__(self, n_clusters=3, max_iter=300):
self.n_clusters = n_clusters # 簇的数量
self.max_iter = max_iter # 最大迭代次数
self.centroids = None # 簇中心
def fit(self, X):
# 1. 随机初始化簇中心
self.centroids = X[np.random.choice(X.shape[0], self.n_clusters, replace=False)]
for _ in range(self.max_iter):
# 2. 分配样本到最近的簇
clusters = [[] for _ in range(self.n_clusters)]
for i, x in enumerate(X):
distances = [np.linalg.norm(x - centroid) for centroid in self.centroids]
cluster_idx = np.argmin(distances)
clusters[cluster_idx].append(i)
# 3. 更新簇中心
new_centroids = np.zeros_like(self.centroids)
for cluster_idx, cluster in enumerate(clusters):
if cluster:
new_centroids[cluster_idx] = np.mean(X[cluster], axis=0)
else:
# 如果簇为空,则随机选择一个新的簇中心 (处理空簇)
new_centroids[cluster_idx] = X[np.random.choice(X.shape[0])]
# 4. 检查簇中心是否发生变化
if np.allclose(self.centroids, new_centroids):
break
self.centroids = new_centroids
def predict(self, X):
# 预测样本所属的簇
predictions = []
for x in X:
distances = [np.linalg.norm(x - centroid) for centroid in self.centroids]
cluster_idx = np.argmin(distances)
predictions.append(cluster_idx)
return np.array(predictions)
# 示例
if __name__ == '__main__':
# 生成一些随机数据
X = np.random.rand(100, 2)
# 创建 KMeans 对象
kmeans = KMeans(n_clusters=3)
# 训练模型
kmeans.fit(X)
# 预测
predictions = kmeans.predict(X)
print(predictions)
KMeans 算法实战避坑经验
数据预处理:在使用 KMeans 算法之前,一定要对数据进行预处理,包括数据清洗、缺失值处理、异常值处理、特征缩放等。 特征缩放可以避免某些特征的数值过大,从而影响距离的计算。
K 值的选择: 使用肘部法则、轮廓系数等方法,尽可能选择合适的 K 值。可以尝试多个 K 值,并比较聚类结果。
初始簇中心的选择: KMeans 算法对初始簇中心的选择比较敏感。可以多次运行 KMeans 算法,每次使用不同的初始簇中心,然后选择聚类效果最好的结果。 scikit-learn 库中的 KMeans 类提供了
init参数,可以指定初始簇中心的选择方法,例如k-means++方法可以有效地选择初始簇中心。处理空簇: 在迭代过程中,可能会出现空簇的情况,即某个簇中没有样本点。 此时,需要重新选择该簇的簇中心。 常见的做法是从其他簇中随机选择一个样本点作为新的簇中心,或者随机生成一个新的簇中心。

使用 KMeans 的变体: 为了克服 KMeans 算法的一些缺点,研究人员提出了许多 KMeans 算法的变体,例如 KMeans++、Mini Batch KMeans、Bisecting K-Means 等。 可以根据实际情况选择合适的 KMeans 算法变体。
结合业务场景: 聚类结果是否合理,最终还是要结合具体的业务场景来判断。例如,在用户画像场景下,聚类结果的解释性非常重要。需要根据聚类结果分析不同用户群体的特征,并验证这些特征是否符合业务逻辑。可以使用诸如漏斗分析等方法辅助分析。
考虑使用其他聚类算法: KMeans 并非万能。在面对非凸形状簇、密度不均匀的数据集时,DBSCAN、层次聚类等算法可能更加有效。例如,在处理地理位置信息时,DBSCAN 可以根据密度自动识别出不同的商圈。
总而言之,KMeans 算法虽然简单易用,但在实际应用中需要注意很多细节。只有深入理解其原理,并结合实际情况,才能真正发挥 KMeans 算法的威力。
冠军资讯
代码一只喵