引言:在机器学习领域,聚类算法作为无监督学习的核心技术之一,广泛应用于用户分群、图像分割、文本聚类、异常检测等场景。其中Kmeans算法以其简单高效、易于实现的特点,成为最受欢迎的聚类算法之一。本文将从基础概念出发,逐步深入剖析Kmeans的核心原理、算法步骤、数学推导,再通过Python代码实现(手动实现+sklearn库调用)帮助大家理解,最后探讨其参数调优、优缺点及实际应用场景,适合机器学习入门者及需要深入掌握Kmeans的开发者阅读。
一、Kmeans核心概念与核心思想
1.1 什么是聚类?
聚类是无监督学习的一种,其核心目标是:将数据集中的样本按照“相似度”划分为不同的簇(Cluster),使得同一簇内的样本相似度尽可能高(簇内紧凑),不同簇间的样本相似度尽可能低(簇间分离)。与监督学习不同,聚类不需要预先标注的标签数据,完全依靠数据自身的分布特征完成分类。
1.2 Kmeans的定义
Kmeans算法全称为K-均值聚类(K-means Clustering),是一种基于划分的聚类算法。其中“K”代表聚类的簇数(需要预先指定),“均值”代表每个簇的中心是该簇内所有样本的均值向量(聚类中心)。算法通过迭代优化的方式,将数据划分为K个互不重叠的簇,最终得到稳定的聚类结果。
1.3 核心思想
Kmeans的核心思想可以概括为“划分-更新-收敛”三步循环:
划分:根据当前的K个聚类中心,计算每个样本到各个中心的距离,将样本分配到距离最近的簇中;
更新:针对每个簇,重新计算该簇内所有样本的均值向量,将其作为新的聚类中心;
收敛:重复“划分-更新”步骤,直到聚类中心不再发生明显变化(或变化小于预设阈值),或达到最大迭代次数,算法停止。
二、Kmeans算法原理与详细步骤
2.1 距离度量
Kmeans算法的核心是通过“距离”判断样本与聚类中心的相似度,最常用的距离度量方式是欧氏距离(Euclidean Distance),适用于连续型数据。对于两个d维向量,欧氏距离公式为:
除了欧氏距离,还可根据数据类型选择其他距离(如曼哈顿距离、余弦相似度等),但欧氏距离是Kmeans的默认选择。
2.2 目标函数
Kmeans的目标是最小化簇内样本的离散程度,常用的目标函数为簇内平方和(Within-Cluster Sum of Square, WCSS),也称为惯性(Inertia)。其公式定义为:
其中:
K为预设的簇数;
表示第i个簇;
表示第i个簇的聚类中心(均值向量);
表示样本x到聚类中心
的欧氏距离。
Kmeans算法的迭代过程,本质上就是不断最小化目标函数J的过程。
2.3 完整算法步骤
结合目标函数和核心思想,Kmeans的完整算法步骤如下:
数据预处理:对数据进行标准化(如Z-score标准化),消除量纲影响(这是Kmeans的关键预处理步骤,否则数值范围大的特征会主导距离计算);
预设参数:确定簇数K、最大迭代次数max_iter、收敛阈值tol(聚类中心变化小于tol则停止);
初始化聚类中心:从数据集中随机选择K个样本作为初始聚类中心(传统Kmeans方式,存在局限性,后续会讲改进方案);
簇分配(划分):计算每个样本到K个聚类中心的欧氏距离,将样本分配到距离最近的簇;
更新聚类中心:对每个簇,计算簇内所有样本的均值向量,作为新的聚类中心;
判断收敛:计算新聚类中心与旧中心的距离,若所有中心的距离都小于tol,或达到最大迭代次数max_iter,则停止迭代;否则返回步骤4继续循环;
输出结果:得到K个簇及对应的聚类中心,目标函数J达到最小值(局部最优)。
2.4 数学推导:为什么聚类中心是簇内均值?
这里通过对目标函数J求导,证明“聚类中心为簇内样本均值”是最小化J的最优解。
对目标函数J中的第i个簇的聚类中心
求偏导
是d维向量,对其每个维度分别求导):
对单个维度k求导(链式法则):
令偏导数为0(极值条件),解得:
其中是第i个簇的样本数量。这表明,当聚类中心的每个维度都是簇内样本对应维度的均值时,目标函数J取得极小值。因此,“更新聚类中心为簇内均值”是Kmeans的最优更新策略。
三、Kmeans的初始化问题与改进:K-means++
3.1 传统Kmeans的局限性
传统Kmeans通过“随机选择K个样本作为初始中心”,存在两个严重问题:
对初始中心敏感:不同的初始中心可能导致最终聚类结果差异很大,甚至得到局部最优解(而非全局最优);
聚类效果不稳定:多次运行算法可能得到不同的结果,需要多次运行取最优。
3.2 K-means++的核心思想与步骤
K-means++是对传统Kmeans初始化步骤的改进,核心思想是:让初始聚类中心尽可能远离彼此,从而提升聚类效果的稳定性和最优性。其初始化步骤如下:
从数据集中随机选择1个样本作为第一个初始聚类中心
;
对于每个样本x,计算x到已选初始中心的最短距离
(即距离最近的初始中心的距离);
根据
的概率分布,随机选择下一个初始中心
越大的样本,被选中的概率越高);
重复步骤2-3,直到选够K个初始聚类中心;
后续的簇分配、中心更新步骤与传统Kmeans一致。
注:K-means++仅改进了初始化步骤,后续迭代过程与传统Kmeans完全相同。目前主流的Kmeans实现(如sklearn的KMeans类)默认使用K-means++初始化。
四、Python实现Kmeans(手动实现+sklearn库实现)
下面通过代码实践帮助大家理解Kmeans的实现逻辑,使用鸢尾花数据集(简化为2维,方便可视化)。
4.1 环境准备
需要安装的库:numpy、pandas、matplotlib、scikit-learn
pip install numpy pandas matplotlib scikit-learn4.2 手动实现Kmeans(含K-means++初始化)
import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.preprocessing import StandardScaler # 1. 数据加载与预处理 iris = load_iris() X = iris.data[:, :2] # 取前2个特征,方便可视化 y = iris.target # 标准化(关键步骤) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 2. 实现K-means++初始化 def kmeans_plus_plus_init(X, k): n_samples, n_features = X.shape centers = [] # 第一步:随机选择第一个中心 centers.append(X[np.random.choice(n_samples)]) # 第二步:迭代选择剩余k-1个中心 for _ in range(1, k): # 计算每个样本到最近已选中心的距离 distances = [] for x in X: min_dist = min([np.linalg.norm(x - center) for center in centers]) distances.append(min_dist ** 2) # 按距离平方概率选择 # 按距离平方的概率分布选择下一个中心 distances = np.array(distances) prob = distances / np.sum(distances) next_center_idx = np.random.choice(n_samples, p=prob) centers.append(X[next_center_idx]) return np.array(centers) # 3. 手动实现Kmeans def my_kmeans(X, k, max_iter=100, tol=1e-4): n_samples, n_features = X.shape # 初始化聚类中心(K-means++) centers = kmeans_plus_plus_init(X, k) for _ in range(max_iter): # 4. 簇分配:计算每个样本到中心的距离,分配到最近的簇 clusters = [[] for _ in range(k)] for x in X: distances = [np.linalg.norm(x - center) for center in centers] cluster_idx = np.argmin(distances) clusters[cluster_idx].append(x) # 5. 保存旧中心,更新新中心 old_centers = centers.copy() for i in range(k): if clusters[i]: # 避免簇为空(极端情况) centers[i] = np.mean(clusters[i], axis=0) # 6. 判断收敛:所有中心的变化小于tol center_changes = [np.linalg.norm(centers[i] - old_centers[i]) for i in range(k)] if max(center_changes) < tol: break # 生成最终的聚类标签 labels = np.zeros(n_samples) for i in range(k): for x in clusters[i]: idx = np.where(np.all(X == x, axis=1))[0][0] labels[idx] = i return labels, centers # 4. 运行手动实现的Kmeans k = 3 # 鸢尾花数据集实际有3类 labels, centers = my_kmeans(X_scaled, k) # 5. 可视化结果 plt.figure(figsize=(8, 6)) plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='viridis', edgecolors='black') plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='*', s=200, label='Cluster Centers') plt.xlabel('Sepal Length (Standardized)') plt.ylabel('Sepal Width (Standardized)') plt.title('Kmeans Clustering Result (Manual Implementation)') plt.legend() plt.show()4.3 sklearn库实现Kmeans(简洁高效)
sklearn的KMeans类默认使用K-means++初始化,支持并行计算,实际项目中推荐使用。
from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score # 1. 数据预处理(同上,已完成标准化) # 2. 初始化并训练Kmeans模型 kmeans = KMeans( n_clusters=3, # 簇数K init='k-means++', # 初始化方式(默认就是k-means++) max_iter=100, # 最大迭代次数 tol=1e-4, # 收敛阈值 random_state=42 # 随机种子,保证结果可重复 ) labels_sklearn = kmeans.fit_predict(X_scaled) # 3. 获取聚类中心 centers_sklearn = kmeans.cluster_centers_ # 4. 评估聚类效果(轮廓系数,取值[-1,1],越接近1越好) sil_score = silhouette_score(X_scaled, labels_sklearn) print(f"Silhouette Score: {sil_score:.4f}") # 5. 可视化结果 plt.figure(figsize=(8, 6)) plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels_sklearn, cmap='viridis', edgecolors='black') plt.scatter(centers_sklearn[:, 0], centers_sklearn[:, 1], c='red', marker='*', s=200, label='Cluster Centers') plt.xlabel('Sepal Length (Standardized)') plt.ylabel('Sepal Width (Standardized)') plt.title('Kmeans Clustering Result (sklearn Implementation)') plt.legend() plt.show()运行结果说明:轮廓系数(Silhouette Score)约为0.55,说明聚类效果较好(大于0.5属于可接受范围)。可视化图中,红色星号为聚类中心,不同颜色的点为不同簇的样本。
五、Kmeans参数调优:如何选择最优K值?
Kmeans的核心参数是簇数K,但K是预设的,无法从数据中直接得到。选择合适的K值是Kmeans调优的关键,常用的方法有两种:肘部法则(Elbow Method)和轮廓系数法(Silhouette Analysis)。
5.1 肘部法则
核心思想:随着K值增加,簇内平方和(WCSS/Inertia)会不断减小(因为簇数越多,每个簇的样本越集中);当K增加到某个值后,WCSS的下降速度会明显变慢,这个“拐点”对应的K值就是最优K值(类似肘部的形状)。
# 肘部法则选择最优K wcss = [] k_range = range(1, 10) # 测试K从1到9 for k in k_range: kmeans = KMeans(n_clusters=k, init='k-means++', random_state=42) kmeans.fit(X_scaled) wcss.append(kmeans.inertia_) # inertia_就是WCSS # 可视化肘部图 plt.figure(figsize=(8, 6)) plt.plot(k_range, wcss, 'bo-') plt.xlabel('Number of Clusters (K)') plt.ylabel('Within-Cluster Sum of Square (WCSS)') plt.title('Elbow Method for Optimal K') plt.grid(True) plt.show()结果解读:当K=3时,WCSS的下降速度明显变缓,因此最优K值为3(与鸢尾花数据集的实际类别数一致)。
5.2 轮廓系数法
核心思想:轮廓系数综合考虑了“簇内紧凑度”和“簇间分离度”,对于每个样本,计算其轮廓系数:
其中:
:样本i到其所在簇内其他样本的平均距离(簇内紧凑度,越小越好);
:样本i到其他簇内样本的平均距离的最小值(簇间分离度,越大越好)。
所有样本的轮廓系数的平均值即为整个数据集的轮廓系数,取值范围[-1,1]:
:聚类效果优秀;
0.5 < s ≤ 0.7:聚类效果良好;
0.25 < s ≤ 0.5:聚类效果一般;
s ≤ 0.25:聚类效果较差。
# 轮廓系数法选择最优K sil_scores = [] k_range = range(2, 10) # K=1时无法计算轮廓系数 for k in k_range: kmeans = KMeans(n_clusters=k, init='k-means++', random_state=42) labels = kmeans.fit_predict(X_scaled) sil_score = silhouette_score(X_scaled, labels) sil_scores.append(sil_score) # 可视化轮廓系数 plt.figure(figsize=(8, 6)) plt.plot(k_range, sil_scores, 'ro-') plt.xlabel('Number of Clusters (K)') plt.ylabel('Silhouette Score') plt.title('Silhouette Analysis for Optimal K') plt.grid(True) plt.show()结果解读:当K=3时,轮廓系数达到最大值(约0.55),因此最优K值为3,与肘部法则的结果一致。
六、Kmeans的优缺点
6.1 优点
简单易懂,实现难度低(无论是手动实现还是调用库);
计算效率高,时间复杂度为
(n为样本数,d为特征数,I为迭代次数),适合处理大规模数据集;
聚类结果直观,易于解释;
对连续型数据适应性好,应用范围广。
6.2 缺点
需要预先指定K值,最优K值的选择依赖经验和调优方法(肘部法则、轮廓系数法);
对初始聚类中心敏感(传统Kmeans),即使是K-means++也无法保证得到全局最优解;
对异常值和噪声数据敏感(异常值会严重影响聚类中心的计算);
仅适用于球形簇(簇内样本呈圆形/椭圆形分布),无法处理非凸簇、不规则形状的簇;
对数据量纲敏感,必须进行标准化预处理;
无法处理类别不平衡的数据(少数类簇可能被合并)。
七、Kmeans的应用场景
尽管存在局限性,但Kmeans凭借其高效性,在多个领域得到广泛应用:
用户分群:电商平台根据用户的购买历史、浏览行为、消费金额等数据,将用户划分为不同群体(如高价值用户、潜力用户、流失风险用户),用于精准营销、个性化推荐;
图像分割:将图像像素按照颜色、灰度值等特征聚类,实现目标区域提取(如人脸识别中的人脸区域分割、医学图像中的病灶分割);
文本聚类:对新闻、评论、文档等文本数据,通过TF-IDF等方法将文本转化为向量后,用Kmeans聚类,实现主题分类(如新闻分类为政治、娱乐、体育等);
异常检测:通过Kmeans聚类后,距离所有聚类中心都很远的样本,可判定为异常值(如信用卡欺诈交易检测、服务器异常访问检测);
数据降维:将聚类后的簇中心作为新的特征,实现数据降维(如高维数据聚类后,用簇标签替代原始特征)。
八、总结与展望
本文详细讲解了Kmeans聚类算法的核心概念、原理、算法步骤、数学推导,通过Python手动实现和sklearn库实现验证了算法效果,介绍了K-means++改进方案和最优K值的选择方法,最后分析了其优缺点和应用场景。
Kmeans是聚类算法的入门经典,但存在诸多局限性。针对这些局限性,后续可学习其改进算法:
Mini Batch Kmeans:适用于超大规模数据集,通过随机抽取批量样本迭代更新中心,提升计算效率;
DBSCAN:基于密度的聚类算法,无需预设K值,可处理非凸簇和异常值;
层次聚类:通过树状结构展示聚类过程,无需预设K值,适合小样本数据;
GMM(高斯混合模型):基于概率的聚类算法,可处理非球形簇,支持样本属于多个簇的概率输出。
希望本文能帮助大家深入理解Kmeans算法,在实际项目中灵活运用并根据数据特点选择合适的聚类算法。如果有疑问或补充,欢迎在评论区交流!