机器学习之K-means算法

引言:

在机器学习的江湖里,分类算法(如逻辑回归、SVM)总是自带“主角光环”——它们有明确的标签指引,像跟着导航的旅行者一样目标清晰。但现实中,我们常常遇到另一类问题:数据本身没有标签,却需要我们发现隐藏的分组规律。比如:

  • 电商想知道“哪些用户可能购买高端产品”(无预设标签)
  • 医院需要将相似病症的患者分组(无明确疾病类别)
  • 图像识别中提取“天空”“草地”等区域(无预先标记的区域)

这时候,聚类算法就成为了数据探索的“秘密武器”。而在聚类家族中,K-means凭借其简单高效的特点,自1957年被提出以来,始终是最经典的算法之一。今天,我们就来彻底搞懂它。


一、K-means到底在做什么?

1.1 一句话定义

K-means是一种无监督学习算法,目标是将无标签的数据集分成K个簇(Cluster),使得同一簇内的数据点“相似度高”,不同簇间的数据点“相似度低”。

1.2 关键概念:簇与质心(Centroid)

  • 簇(Cluster):数据的子集,簇内数据点具有高度相似性(通常用欧氏距离衡量)。
  • 质心(Centroid):每个簇的“中心”,数学上是簇内所有点的坐标平均值(类似均值点)。例如,二维空间中一个簇的质心是该簇所有点的(x均值, y均值)。

1.3 核心目标:最小化“簇内误差”

K-means的本质是一个优化问题,它的目标是让所有数据点到其所属簇质心的**距离平方和(SSE,Sum of Squared Errors)**最小。公式如下:

SSE=∑i=1K∑x∈Ci∥x−μi∥2 SSE = \sum_{i=1}^{K} \sum_{x \in C_i} \|x - \mu_i\|^2 SSE=i=1KxCixμi2

其中:

  • KKK 是簇的数量;
  • CiC_iCi 是第i个簇;
  • μi\mu_iμi 是第i个簇的质心;
  • ∥x−μi∥\|x - \mu_i\|xμi 是数据点x到质心μi\mu_iμi的欧氏距离。

简单说,K-means希望每个簇内的点都尽可能“紧凑”地围绕在质心周围。


二、K-means算法步骤:四步搞定聚类

理解算法步骤最好的方式是“可视化想象”。假设我们有一组二维数据点(比如用户的“消费金额”和“购买频率”),现在要分成3个簇(K=3)。以下是K-means的核心流程:

2.1 第一步:随机初始化K个质心

从数据集中随机选择K个点作为初始质心(记为μ1,μ2,...,μK\mu_1, \mu_2, ..., \mu_Kμ1,μ2,...,μK)。
注意:初始质心的选择会影响最终结果(后面会讲如何优化)。

2.2 第二步:分配数据点到最近的簇

对于每个数据点xxx,计算它到所有K个质心的距离(常用欧氏距离),将xxx分配到距离最近的质心对应的簇中。
公式:Ci={x∥arg⁡min⁡j∥x−μj∥2}C_i = \{x \| \arg\min_j \|x - \mu_j\|^2\}Ci={xargminjxμj2}(即x属于使其到质心距离最小的簇j)。

2.3 第三步:重新计算簇的质心

对每个簇CiC_iCi,用簇内所有点的坐标平均值计算新的质心μi′\mu_i'μi
μi′=1∣Ci∣∑x∈Cix \mu_i' = \frac{1}{|C_i|} \sum_{x \in C_i} x μi=Ci1xCix
其中∣Ci∣|C_i|Ci是簇CiC_iCi中的数据点数量。

2.4 第四步:重复直到收敛

重复步骤2和步骤3,直到满足以下条件之一:

  • 质心不再变化(或变化极小);
  • SSE(簇内误差)不再显著下降;
  • 达到预设的最大迭代次数。

最终,所有数据点被稳定分配到K个簇中,任务完成!


三、K值怎么选?两个实用方法

K-means有一个“先天缺陷”:需要人为指定K的值(簇的数量)。选大了会过拟合(簇太细),选小了会欠拟合(簇太粗)。如何科学确定K?

3.1 肘部法(Elbow Method):最常用的“拐点检测”

肘部法的核心思想是:随着K增大,SSE(簇内误差)会逐渐减小;但当K超过某个值后,SSE的下降速度会突然变缓,这个转折点就是“肘部”,对应的K就是最优值。

具体操作

  1. 尝试K=1到K=10(根据数据量调整);
  2. 对每个K运行K-means,计算对应的SSE;
  3. 绘制“K vs SSE”曲线,找到拐点。

举个例子:如果K=3时SSE从1000降到500(大幅下降),K=4时降到450(小幅下降),K=5时降到430(几乎不变),那么肘部在K=3或K=4,通常选K=3。

3.2 轮廓系数(Silhouette Coefficient):更全面的评估

轮廓系数从“簇内紧密度”和“簇间分离度”两个维度评估聚类效果,取值范围[-1, 1]:

  • 接近1:数据点与自身簇的质心很近,与其他簇的质心很远(理想情况);
  • 接近0:数据点在两个簇的边界上;
  • 接近-1:数据点可能被错误分配到其他簇。

计算方式
对每个数据点xxx

  1. 计算a(x)a(x)a(x)xxx到同簇其他所有点的平均距离(簇内紧密度,越小越好);
  2. 计算b(x)b(x)b(x)xxx到最近的其他簇中所有点的平均距离(簇间分离度,越大越好);
  3. 轮廓系数s(x)=b(x)−a(x)max⁡(a(x),b(x))s(x) = \frac{b(x) - a(x)}{\max(a(x), b(x))}s(x)=max(a(x),b(x))b(x)a(x)

最终取所有数据点s(x)s(x)s(x)的平均值作为整体轮廓系数,值越大,聚类效果越好。


四、K-means的优缺点:没有完美的算法,但有最适合的场景

4.1 优点

  • 简单高效:时间复杂度为O(nKIT)O(nKIT)O(nKIT)(n是样本数,I是迭代次数,T是特征数),适合大规模数据;
  • 容易实现:逻辑清晰,代码复杂度低(scikit-learn一行代码搞定);
  • 可解释性强:簇的结果直观,便于业务理解(比如用户分群后的营销策略)。

4.2 缺点

  • 依赖初始质心:随机初始化可能导致“局部最优”(比如陷入一个较差的聚类结果);
  • 需要预设K值:无法自动确定簇的数量(需结合肘部法、轮廓系数等辅助);
  • 对噪声和离群点敏感:一个离群点可能大幅拉偏质心位置;
  • 不适用于非凸形状的簇:如果数据是环形、链式等非凸分布,K-means效果会很差。

五、K-means的“进化版”:解决经典缺陷

针对原始K-means的不足,研究者提出了多个改进版本,实际应用中可以根据需求选择:

5.1 K-means++

原始K-means随机选质心,可能导致初始质心太接近。K-means++的改进方法是:

  1. 第一个质心随机选;
  2. 后续质心选择概率与到已选质心的距离平方成正比(离已选质心越远的点,被选中的概率越高);
  3. 重复直到选满K个质心。
    这样可以避免初始质心“扎堆”,减少陷入局部最优的概率。

5.2 Mini-Batch K-means

传统K-means每次迭代都要计算所有数据点到质心的距离,当数据量极大(比如亿级样本)时,计算成本很高。Mini-Batch K-means的改进是:
每次迭代只使用随机采样的一个小批量(Batch)数据来更新质心,大幅降低计算量,同时保持较好的聚类效果(适合实时或大规模数据场景)。

5.3 模糊C均值(FCM)

原始K-means要求每个数据点严格属于一个簇(硬分配),但现实中很多数据可能“同时属于多个簇”(比如一个用户既喜欢科技产品又喜欢时尚单品)。FCM通过引入“隶属度”概念(每个点对每个簇的归属概率),允许模糊分配,更符合复杂场景的需求。


六、实战:用Python实现K-means聚类

理论看懂了,接下来我们用Python的scikit-learn库实战一下。假设我们有一个二维数据集(模拟用户的“收入”和“消费”),需要分成3个簇。

6.1 步骤1:安装依赖库

pip install numpy pandas matplotlib scikit-learn

6.2 步骤2:生成模拟数据

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

# 生成3个簇的二维数据(n_samples=300,n_features=2,centers=3)
X, y_true = make_blobs(n_samples=300, n_features=2, centers=3, cluster_std=0.8, random_state=42)

# 可视化原始数据
plt.scatter(X[:, 0], X[:, 1], s=30, c='blue', label='原始数据')
plt.title("原始数据分布")
plt.legend()
plt.show()

6.3 步骤3:运行K-means并可视化结果

from sklearn.cluster import KMeans

# 初始化K-means(K=3)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300, random_state=42)
# 训练模型
kmeans.fit(X)
# 预测每个点的簇标签
y_pred = kmeans.labels_
# 获取质心坐标
centroids = kmeans.cluster_centers_

# 可视化聚类结果
plt.scatter(X[:, 0], X[:, 1], s=30, c=y_pred, cmap='viridis', label='聚类结果')
plt.scatter(centroids[:, 0], centroids[:, 1], s=200, c='red', marker='*', label='质心')
plt.title("K-means聚类结果(K=3)")
plt.legend()
plt.show()

6.4 步骤4:用肘部法确定最优K值

# 计算不同K值的SSE
sse = []
for k in range(1, 10):
    kmeans = KMeans(n_clusters=k, init='k-means++', max_iter=300, random_state=42)
    kmeans.fit(X)
    sse.append(kmeans.inertia_)  # inertia_属性存储SSE

# 绘制肘部图
plt.plot(range(1, 10), sse, 'bo-')
plt.xlabel('K(簇的数量)')
plt.ylabel('SSE(簇内误差平方和)')
plt.title("肘部法确定最优K值")
plt.show()

运行后,你会看到一条“先陡后缓”的曲线,拐点对应的K就是最优值(本例中明显是K=3)。


七、K-means的应用场景:从商业到科研

K-means的简单高效让它成为各领域的“通用工具”,常见应用包括:

  • 用户分群:电商根据“消费金额”“购买频率”“浏览时长”将用户分成高价值、潜力、流失等群体,针对性运营;
  • 图像分割:将图像像素的颜色值聚类,提取主要颜色(比如压缩图片时减少颜色数量);
  • 文本聚类:对新闻标题进行TF-IDF向量化后聚类,自动分类新闻主题(如体育、科技);
  • 生物信息学:根据基因表达数据将细胞分成不同类型(如干细胞、免疫细胞);
  • 异常检测:将正常数据聚类后,离群的小簇或孤立点可能是异常值(如信用卡欺诈交易)。

总结:K-means的“正确打开方式”

K-means不是完美的算法,但它是最易理解和使用的聚类工具。掌握它的关键在于:

  1. 明确目标:聚类是无监督任务,需提前想清楚“我希望数据如何分组”;
  2. 合理选K:用肘部法、轮廓系数辅助确定簇的数量;
  3. 预处理数据:标准化(Z-score)或归一化(Min-Max)特征,避免量纲影响距离计算;
  4. 迭代优化:尝试K-means++初始化,多次运行取最优结果(避免随机初始化的偶然性)。

最后,记住:聚类的结果需要业务验证——如果数学上的“最优聚类”不符合实际业务逻辑(比如用户分群后无法制定营销策略),那么需要调整特征或算法。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值