博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

什么是k-means算法?

Posted on 2026-01-29 11:15  steve.z  阅读(25)  评论(0)    收藏  举报

K-Means聚类算法完全教程

1. 算法概述

1.1 什么是K-Means算法?

K-Means是一种无监督学习的聚类算法,用于将未标记的数据集划分为K个不同的簇。它的核心思想是"物以类聚" - 相似的数据点应该被分配到同一个簇中。

1.2 基本概念

  • 簇(Cluster):相似数据点的集合
  • 质心(Centroid):簇的中心点(所有点的均值)
  • 惯性(Inertia):每个点到其质心的距离平方和,衡量簇内紧密度

2. 算法原理

2.1 数学目标

最小化损失函数(惯性/簇内平方和):

\[J = \sum_{i=1}^{K} \sum_{x \in C_i} ||x - \mu_i||^2 \]

其中:

  • \(K\):簇的数量
  • \(C_i\):第i个簇
  • \(\mu_i\):第i个簇的质心
  • \(x\):数据点

2.2 算法步骤

步骤1:初始化

随机选择K个数据点作为初始质心

输入:数据集X,簇数K
输出:K个簇及其质心

1. 从X中随机选择K个点作为初始质心 μ₁, μ₂, ..., μₖ

步骤2:分配数据点

将每个数据点分配到最近的质心所在的簇

for 每个数据点x in X:
    计算x到每个质心μᵢ的距离
    将x分配到距离最近的质心对应的簇

步骤3:更新质心

重新计算每个簇的质心(取均值)

for 每个簇Cᵢ:
    计算新的质心 μᵢ = 均值(Cᵢ)

步骤4:迭代

重复步骤2-3,直到满足停止条件:

  1. 质心变化小于阈值(收敛)
  2. 达到最大迭代次数
  3. 簇的分配不再改变

3. 关键细节与参数选择

3.1 距离度量

常用的距离度量方法:

  1. 欧氏距离(最常用):

    \[d(x, y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2} \]

  2. 曼哈顿距离

    \[d(x, y) = \sum_{i=1}^{n}|x_i - y_i| \]

  3. 余弦相似度:适用于文本数据

3.2 如何选择K值?

方法1:肘部法则(Elbow Method)

计算不同K值对应的惯性值(SSE)
绘制K-SSE曲线,选择"肘部"拐点

方法2:轮廓系数(Silhouette Score)

\[s(i) = \frac{b(i) - a(i)}{\max(a(i), b(i))} \]

其中:

  • \(a(i)\):点i到同簇其他点的平均距离
  • \(b(i)\):点i到最近其他簇所有点的平均距离
    取值范围:[-1, 1],越接近1效果越好

方法3:间隙统计量(Gap Statistic)

比较实际数据与随机均匀分布的聚类效果差异

3.3 初始质心选择问题

问题:随机初始化可能导致:

  1. 收敛到局部最优
  2. 收敛速度慢
  3. 结果不稳定

解决方案:K-Means++初始化

1. 随机选择一个点作为第一个质心
2. 对于每个点x,计算其与最近质心的距离D(x)
3. 按照D(x)²的概率选择下一个质心
4. 重复直到选出K个质心

4. 算法变体与改进

4.1 K-Means++

  • 优点:更好的初始质心选择,加速收敛
  • 缺点:计算初始质心时需额外计算距离

4.2 Mini-Batch K-Means

使用数据子集(小批量)更新质心:

适用于大数据集
牺牲精度换取速度
支持在线学习

4.3 K-Medoids(PAM算法)

  • 使用实际数据点作为中心点(medoid)
  • 对异常值更鲁棒
  • 计算成本更高(O(n²))

4.4 二分K-Means(Bisecting K-Means)

1. 将所有点作为一个簇
2. 重复直到有K个簇:
   a. 选择SSE最大的簇进行二分
   b. 使用K-Means(K=2)划分该簇

5. 算法优缺点

5.1 优点

  1. 简单高效:时间复杂度O(n×K×I×d),适合大数据
  2. 可解释性强:每个簇有明确的中心点
  3. 收敛快:通常少量迭代即可收敛
  4. 可扩展性好:易于并行化实现

5.2 缺点

  1. 需要预先指定K值
  2. 对初始质心敏感(可用K-Means++缓解)
  3. 对异常值敏感
  4. 假设簇为凸形、各向同性,对非球形簇效果差
  5. 不适合处理类别型数据

6. 实践应用

6.1 应用场景

  1. 客户细分:基于购买行为对客户分组
  2. 图像压缩:减少颜色数量(颜色量化)
  3. 文档聚类:新闻分类、主题发现
  4. 异常检测:离群点检测
  5. 推荐系统:用户分群进行个性化推荐
  6. 社交网络分析:社区发现

6.2 Python实现示例

基础版本

import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

# 生成示例数据
np.random.seed(42)
X = np.random.randn(300, 2)
X[:100] += 5
X[100:200] += 10

# K-Means聚类
kmeans = KMeans(n_clusters=3, init='k-means++', n_init=10, max_iter=300)
kmeans.fit(X)

# 结果可视化
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap='viridis')
plt.scatter(kmeans.cluster_centers_[:, 0], 
           kmeans.cluster_centers_[:, 1], 
           s=200, marker='X', c='red')
plt.title('K-Means聚类结果')
plt.show()

完整实践:客户分群

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score

# 1. 加载数据
data = pd.read_csv('customer_data.csv')

# 2. 特征选择与预处理
features = ['Annual_Income', 'Spending_Score']
X = data[features].values

# 3. 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 4. 确定最佳K值(肘部法则)
inertia = []
K_range = range(2, 11)

for k in K_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X_scaled)
    inertia.append(kmeans.inertia_)

# 可视化肘部曲线
plt.plot(K_range, inertia, 'bo-')
plt.xlabel('K值')
plt.ylabel('惯性值')
plt.title('肘部法则')
plt.show()

# 5. 使用最佳K值进行聚类
optimal_k = 5  # 根据肘部法则确定
kmeans = KMeans(n_clusters=optimal_k, init='k-means++', random_state=42)
clusters = kmeans.fit_predict(X_scaled)

# 6. 评估聚类效果
silhouette_avg = silhouette_score(X_scaled, clusters)
print(f'轮廓系数: {silhouette_avg:.3f}')

# 7. 分析聚类结果
data['Cluster'] = clusters
cluster_summary = data.groupby('Cluster').mean()
print(cluster_summary)

7. 高级技巧与注意事项

7.1 数据预处理

  1. 标准化:确保不同特征具有相同尺度
    from sklearn.preprocessing import StandardScaler
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
  2. 处理缺失值
  3. 特征选择:去除无关特征

7.2 评估聚类质量

  1. 内部指标:轮廓系数、Davies-Bouldin指数
  2. 外部指标(需要真实标签):调整兰德指数、互信息
  3. 业务评估:结合实际业务意义

7.3 处理非凸簇

当数据不是球形分布时:

  1. 考虑使用DBSCAN谱聚类
  2. 使用核K-Means将数据映射到高维空间

7.4 大数据集处理

# 使用MiniBatchKMeans
from sklearn.cluster import MiniBatchKMeans
mbk = MiniBatchKMeans(n_clusters=10, batch_size=100)
mbk.fit(X_large)

8. 常见问题与解决方案

Q1: K-Means收敛到局部最优怎么办?

解决方案

  • 多次运行,选择最佳结果(n_init参数)
  • 使用K-Means++初始化
  • 增加迭代次数

Q2: 如何处理不同大小的簇?

解决方案

  • 调整样本权重
  • 使用层次聚类作为预处理
  • 考虑使用GMM(高斯混合模型)

Q3: 如何解释聚类结果?

方案

  1. 分析每个簇的特征统计量
  2. 可视化降维后的结果(PCA/t-SNE)
  3. 结合业务知识进行标签定义

9. 算法扩展与相关算法

9.1 模糊C-Means

  • 允许数据点以不同概率属于多个簇
  • 适用于边界模糊的数据

9.2 层次聚类

  • 不需要预先指定K值
  • 生成树状聚类结构

9.3 DBSCAN

  • 基于密度的聚类
  • 可发现任意形状的簇
  • 自动处理异常值

9.4 GMM(高斯混合模型)

  • 基于概率模型的软聚类
  • 假设每个簇服从高斯分布

10. 总结

K-Means是最经典、最常用的聚类算法之一,它的优势在于简单高效,特别适合处理大规模数据集。但在实际应用中需要注意:

  1. 数据预处理是关键步骤
  2. 合理选择K值直接影响结果
  3. 多次运行避免局部最优
  4. 结合业务理解解释聚类结果

虽然K-Means有局限性,但通过合适的改进(如K-Means++)和参数调优,它仍然是解决实际聚类问题的有力工具。

附录:数学推导

质心更新公式推导

对于第i个簇\(C_i\),质心\(\mu_i\)的最优解为:

\[\mu_i = \frac{1}{|C_i|} \sum_{x \in C_i} x \]

这个结果通过最小化损失函数\(J\)\(\mu_i\)求导得到:

\[\frac{\partial J}{\partial \mu_i} = -2 \sum_{x \in C_i} (x - \mu_i) = 0 \]

解得:

\[\mu_i = \frac{1}{|C_i|} \sum_{x \in C_i} x \]