6928 字
35 分钟
K-means 聚类算法

K-means 聚类算法#

前言#

今天开始学习无监督学习,K-means 是最基础也是最重要的一个算法。和之前学的分类算法不同,聚类是在没有标签的情况下,让算法自己去发现数据中的结构,把相似的数据点“物以类聚”。感觉就像是在整理一堆混在一起的乐高积木,要把相同颜色和形状的放在一起。这篇笔记记录了我对 K-means 的学习和实践。

核心思想:物以类聚,人以群分#

K-means 的目标非常明确:把一堆数据点分成 KK 个簇 (Cluster),并且让簇内的数据点尽可能相似(距离近),簇间的数据点尽可能不同(距离远)

为了实现这个目标,算法会迭代地做两件事:

  1. 分配:把每个数据点分给离它最近的那个簇的中心。
  2. 更新:把每个簇的中心移动到这个簇里所有数据点的平均位置。

不断重复这两步,直到簇的中心不再变化,就找到了最终的聚类结果。

深入数学原理#

目标函数:我们要优化什么?#

K-means 优化的目标函数是所有数据点到其所属簇中心的距离平方和 (Sum of Squared Errors, SSE),也称为簇内平方和 (Within-Cluster Sum of Squares, WCSS)惯性 (Inertia)。我们的目标是最小化这个值。

J=i=1nk=1Krikxiμk2J = \sum_{i=1}^n \sum_{k=1}^K r_{ik} \|\mathbf{x}_i - \boldsymbol{\mu}_k\|^2

其中:

  • nn 是样本数量,KK 是要分的簇的数量。
  • xi\mathbf{x}_i 是第 ii 个数据点。
  • μk\boldsymbol{\mu}_k 是第 kk 个簇的中心点(质心)。
  • rikr_{ik} 是一个指示变量:如果 xi\mathbf{x}_i 属于簇 kk,则 rik=1r_{ik}=1,否则为0。

算法步骤:E-M思想的体现#

K-means 的迭代过程完美地体现了 EM (Expectation-Maximization) 算法的思想。

  1. 初始化: 随机选择 KK 个数据点作为初始的簇中心 μ1,μ2,,μK\boldsymbol{\mu}_1, \boldsymbol{\mu}_2, \dots, \boldsymbol{\mu}_K

  2. 重复以下步骤直到收敛:

    • E-Step (Expectation/分配步): 对于每个数据点 xi\mathbf{x}_i,计算它到所有 KK 个簇中心的距离,并把它分配给最近的那个簇。这一步是在固定簇中心 μk\boldsymbol{\mu}_k 的情况下,优化分配 rikr_{ik} 来最小化目标函数 JJrik={1if k=argminjxiμj20otherwiser_{ik} = \begin{cases} 1 & \text{if } k = \arg\min_j \|\mathbf{x}_i - \boldsymbol{\mu}_j\|^2 \\ 0 & \text{otherwise} \end{cases}
    • M-Step (Maximization/更新步): 对于每个簇 kk,重新计算它的中心点 μk\boldsymbol{\mu}_k,使其成为该簇内所有数据点的均值。这一步是在固定分配 rikr_{ik} 的情况下,优化簇中心 μk\boldsymbol{\mu}_k 来最小化目标函数 JJμk=i=1nrikxii=1nrik\boldsymbol{\mu}_k = \frac{\sum_{i=1}^n r_{ik} \mathbf{x}_i}{\sum_{i=1}^n r_{ik}}

收敛性证明:

  • 在E-step,我们把点分给最近的中心,这必然会使总的距离平方和 JJ 减小或不变。
  • 在M-step,把中心更新为均值,根据均值的性质,这也会使该簇内的距离平方和达到最小,从而使总的 JJ 减小或不变。
  • 因为 JJ 是一个非负值,它有下界。一个单调递减且有下界的序列必然会收敛。
  • 注意:K-means 只能保证收敛到局部最优解,而不是全局最优解。最终结果对初始点的选择很敏感。

如何选择合适的 K 值?#

这是 K-means 的一个核心问题。KK 值选得太大或太小,都得不到有意义的结果。有几种常用的方法来帮助我们选择。

  1. 肘部法则 (Elbow Method):

    • 方法:计算不同 KK 值对应的目标函数 JJ (即 WCSS)。
    • 原理:随着 KK 的增加,WCSS 会不断减小。我们寻找曲线下降速率由快变缓的那个点,就像人的“手肘”一样。这个拐点通常被认为是比较合适的 KK 值。
    • 缺点:“手肘”有时不明显,主观性较强。
  2. 轮廓系数 (Silhouette Coefficient):

    • 方法:对每个样本 ii,计算其轮廓系数 s(i)s(i)s(i)=b(i)a(i)max{a(i),b(i)}s(i) = \frac{b(i) - a(i)}{\max\{a(i), b(i)\}}
      • a(i)a(i): 样本 ii同簇其他所有点的平均距离(衡量簇内凝聚度)。
      • b(i)b(i): 样本 ii最近的异簇所有点的平均距离(衡量簇间分离度)。
    • 原理s(i)s(i) 的取值范围是 [1,1][-1, 1]
      • 值越接近1,说明聚类效果越好。
      • 值越接近-1,说明可能分到了错误的簇。
      • 值在0附近,说明样本在两个簇的边界上。
    • 选择:计算所有样本的平均轮廓系数,选择使平均轮廓系数最大的那个 KK 值。
  3. Gap 统计量 (Gap Statistic):

    • 方法:将真实数据的 WCSS 与在数据边界内随机生成的“无结构”数据的 WCSS 进行比较。
    • 原理:寻找一个 KK 值,使得真实数据的 WCSS 下降程度,远大于随机数据的 WCSS 下降程度。这个“Gap”最大的点就是最优的 KK
    • 优点:比肘部法则更自动化,但计算量更大。

实战演练:客户分群分析#

理论学完了,我们来模拟一个真实的商业场景:对客户进行分群,以便实施精准营销。

0. 准备工作#

首先,导入需要的库并生成模拟数据。在真实项目中,这一步是加载和清洗你的客户数据。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Scikit-learn 工具集
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

1. 客户数据预处理和特征工程#

我们生成一些模拟数据,代表客户的两个特征:年收入消费分数

# 生成模拟客户数据
X, y_true = make_blobs(n_samples=300, centers=5, cluster_std=0.8, random_state=42)
# 为了更贴近真实场景,我们对数据进行一些变换
rng = np.random.RandomState(42)
X[:, 0] = X[:, 0] * 20000 + 80000 # 年收入
X[:, 1] = X[:, 1] * 30 + 50 # 消费分数 (1-100)
# 将数据转换为DataFrame,便于分析
customer_df = pd.DataFrame(X, columns=['Annual Income (k$)', 'Spending Score (1-100)'])
# --- 数据标准化 ---
# 年收入和消费分数的量纲差异很大,必须进行标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

2. 最优K值选择#

我们同时使用肘部法则轮廓系数来确定最佳的 KK 值。

wcss = []
silhouette_scores = []
K_range = range(2, 11) # K值至少为2
for k in K_range:
# 训练K-means模型
kmeans = KMeans(n_clusters=k, init='k-means++', n_init=10, random_state=42)
kmeans.fit(X_scaled)
# 记录WCSS (惯性)
wcss.append(kmeans.inertia_)
# 记录轮廓系数
silhouette_scores.append(silhouette_score(X_scaled, kmeans.labels_))
# 绘制K值选择图
plt.figure(figsize=(12, 5))
# 肘部法则
plt.subplot(1, 2, 1)
plt.plot(K_range, wcss, 'bo-')
plt.xlabel('簇数量 K')
plt.ylabel('簇内平方和 (WCSS)')
plt.title('肘部法则')
plt.grid(True)
# 轮廓系数
plt.subplot(1, 2, 2)
plt.plot(K_range, silhouette_scores, 'ro-')
plt.xlabel('簇数量 K')
plt.ylabel('平均轮廓系数')
plt.title('轮廓系数法')
plt.grid(True)
plt.tight_layout()
plt.show()
# 根据图像,我们选择 K=5
optimal_k = 5

从图中我们可以清晰地看到,肘部法则在 K=5 处有一个明显的拐点,而轮廓系数也在 K=5 时达到最大值。因此,我们确定最佳的簇数量是5。

3. 客户分群和特征分析#

现在,我们使用找到的最佳 KK 值来训练最终模型,并对分群结果进行分析。

# 使用最佳K值训练最终模型
final_kmeans = KMeans(n_clusters=optimal_k, init='k-means++', n_init=10, random_state=42)
customer_df['Cluster'] = final_kmeans.fit_predict(X_scaled)
# 可视化分群结果
plt.figure(figsize=(10, 8))
sns.scatterplot(data=customer_df, x='Annual Income (k$)', y='Spending Score (1-100)',
hue='Cluster', palette='viridis', s=100, alpha=0.8, legend='full')
centroids = scaler.inverse_transform(final_kmeans.cluster_centers_)
plt.scatter(centroids[:, 0], centroids[:, 1], s=300, c='red', marker='X', label='Centroids')
plt.title('客户分群结果')
plt.xlabel('年收入 (k$)')
plt.ylabel('消费分数 (1-100)')
plt.legend()
plt.grid(True)
plt.show()
# 分析每个客户群的特征
cluster_analysis = customer_df.groupby('Cluster').agg({
'Annual Income (k$)': ['mean', 'median'],
'Spending Score (1-100)': ['mean', 'median'],
'Cluster': 'count'
}).rename(columns={'count': 'Num Customers'})
print("\n--- 各客户群特征分析 ---")
print(cluster_analysis)

4. 业务洞察和建议#

最后,也是最重要的一步,是根据分群结果给出有价值的业务建议。

  • 簇 0: 谨慎型

    • 特征: 收入低,消费分数低。
    • 洞察: 可能是学生或刚入职的年轻人,对价格敏感。
    • 建议: 推送折扣、优惠券等促销信息,进行低价商品营销。
  • 簇 1: 挥霍型

    • 特征: 收入低,但消费分数高。
    • 洞察: “月光族”,追求时尚和潮流,但购买力有限。
    • 建议: 推送新品、潮流单品,提供分期付款等服务。
  • 簇 2: 普通型

    • 特征: 收入和消费分数都处于中等水平。
    • 洞察: 这是最主流的客户群体,构成了消费的基本盘。
    • 建议: 维持客户关系,通过会员制度、积分兑换等方式提高客户忠诚度。
  • 簇 3: 目标型 (高价值)

    • 特征: 收入高,消费分数高。
    • 洞察: 核心高价值客户,有钱又愿意花钱。
    • 建议: 提供VIP服务、专属定制、高端新品发布会等,重点维护,提升品牌忠诚度。
  • 簇 4: 理性型

    • 特征: 收入高,但消费分数低。
    • 洞察: 有钱但消费谨慎,可能对品质和实用性要求高。
    • 建议: 强调产品的质量、耐用性和性价比,避免过度营销,提供详细的产品信息。

学习总结#

容易踩的坑#

  • 忘记数据标准化:K-means 基于距离计算,如果特征量纲不一致,量纲大的特征会主导聚类结果。这是最致命的错误!
  • K 值选择不当:业务理解和多种技术方法结合来确定 KK 值非常重要。
  • 初始点敏感性:传统的随机初始化可能导致很差的局部最优解。使用 k-means++ 初始化(sklearn 的默认方法)能极大地改善这个问题。
  • 对非球形簇和异常值敏感:K-means 假设簇是凸的、球形的,对不规则形状的簇和异常值处理得不好。

学习感悟#

K-means 是一个非常直观且高效的聚类算法,完美地展示了无监督学习的魅力——在没有答案的情况下发现数据中隐藏的模式。从理论推导的 E-M 步骤,到如何选择 KK 值的各种权衡,再到最终如何将聚类结果转化为有价值的商业洞察,整个过程构成了一个完整的、从数据到决策的闭环。虽然它有局限性,但作为探索性数据分析的第一步,K-means 无疑是强大且不可或缺的工具。


第二部分:K-means算法的数学理论深入#

2.1 距离度量与相似性函数#

2.1.1 欧几里得距离的数学性质#

K-means算法默认使用欧几里得距离作为相似性度量,其数学定义为:

d(xi,xj)=xixj2=k=1d(xikxjk)2d(\mathbf{x}_i, \mathbf{x}_j) = \|\mathbf{x}_i - \mathbf{x}_j\|_2 = \sqrt{\sum_{k=1}^{d}(x_{ik} - x_{jk})^2}

欧几里得距离的重要性质:

  1. 非负性d(xi,xj)0d(\mathbf{x}_i, \mathbf{x}_j) \geq 0,当且仅当 xi=xj\mathbf{x}_i = \mathbf{x}_j 时等于0
  2. 对称性d(xi,xj)=d(xj,xi)d(\mathbf{x}_i, \mathbf{x}_j) = d(\mathbf{x}_j, \mathbf{x}_i)
  3. 三角不等式d(xi,xk)d(xi,xj)+d(xj,xk)d(\mathbf{x}_i, \mathbf{x}_k) \leq d(\mathbf{x}_i, \mathbf{x}_j) + d(\mathbf{x}_j, \mathbf{x}_k)

2.1.2 其他距离度量#

虽然K-means通常使用欧几里得距离,但在特定应用中可以考虑其他距离度量:

曼哈顿距离(L1范数): d1(xi,xj)=k=1dxikxjkd_1(\mathbf{x}_i, \mathbf{x}_j) = \sum_{k=1}^{d}|x_{ik} - x_{jk}|

闵可夫斯基距离(Lp范数): dp(xi,xj)=(k=1dxikxjkp)1/pd_p(\mathbf{x}_i, \mathbf{x}_j) = \left(\sum_{k=1}^{d}|x_{ik} - x_{jk}|^p\right)^{1/p}

马哈拉诺比斯距离: dM(xi,xj)=(xixj)TS1(xixj)d_M(\mathbf{x}_i, \mathbf{x}_j) = \sqrt{(\mathbf{x}_i - \mathbf{x}_j)^T\mathbf{S}^{-1}(\mathbf{x}_i - \mathbf{x}_j)}

其中 S\mathbf{S} 是协方差矩阵,该距离考虑了特征间的相关性。

2.2 目标函数的数学分析#

2.2.1 目标函数的凸性分析#

K-means的目标函数可以重写为:

J(C,M)=i=1nmink=1,,Kxiμk2J(\mathbf{C}, \mathbf{M}) = \sum_{i=1}^{n}\min_{k=1,\ldots,K}\|\mathbf{x}_i - \boldsymbol{\mu}_k\|^2

其中 C={C1,C2,,CK}\mathbf{C} = \{C_1, C_2, \ldots, C_K\} 表示簇的划分,M={μ1,μ2,,μK}\mathbf{M} = \{\boldsymbol{\mu}_1, \boldsymbol{\mu}_2, \ldots, \boldsymbol{\mu}_K\} 表示簇中心。

重要性质:

  1. 关于簇中心的凸性:固定簇划分 C\mathbf{C} 时,目标函数关于簇中心 M\mathbf{M} 是凸函数
  2. 关于簇划分的非凸性:固定簇中心 M\mathbf{M} 时,目标函数关于簇划分 C\mathbf{C} 是非凸的
  3. 整体非凸性:联合优化问题是非凸的,存在多个局部最优解

2.2.2 最优性条件#

簇中心的最优性条件: 对于固定的簇划分,最优簇中心满足: Jμk=2iCk(xiμk)=0\frac{\partial J}{\partial \boldsymbol{\mu}_k} = -2\sum_{i \in C_k}(\mathbf{x}_i - \boldsymbol{\mu}_k) = 0

解得: μk=1CkiCkxi\boldsymbol{\mu}_k^* = \frac{1}{|C_k|}\sum_{i \in C_k}\mathbf{x}_i

这证明了最优簇中心就是簇内所有点的质心(均值)。

簇划分的最优性条件: 对于固定的簇中心,最优簇划分满足: Ck={i:xiμk2xiμj2,jk}C_k^* = \{i : \|\mathbf{x}_i - \boldsymbol{\mu}_k\|^2 \leq \|\mathbf{x}_i - \boldsymbol{\mu}_j\|^2, \forall j \neq k\}

即每个点应该分配给距离最近的簇中心。

2.3 收敛性理论分析#

2.3.1 收敛性证明#

定理:K-means算法在有限步内收敛到局部最优解。

证明思路:

  1. 目标函数单调性:每次迭代后,目标函数值 JJ 单调递减或保持不变

    • E步:固定簇中心,重新分配点到最近簇,必然使 JJ 减小或不变
    • M步:固定簇划分,更新簇中心为质心,必然使 JJ 减小或不变
  2. 有界性:目标函数 J0J \geq 0,有下界

  3. 有限状态空间:对于有限数据集,可能的簇划分数量有限

  4. 严格递减性:除非已达到局部最优,否则每次迭代 JJ 严格递减

因此,算法必在有限步内收敛到某个局部最优解。

2.3.2 收敛速度分析#

线性收敛率:在一般情况下,K-means具有线性收敛速度: J(t+1)Jρ(J(t)J)J^{(t+1)} - J^* \leq \rho(J^{(t)} - J^*)

其中 0<ρ<10 < \rho < 1 是收敛因子,JJ^* 是局部最优值。

影响收敛速度的因素:

  1. 初始化质量:好的初始化可以显著减少迭代次数
  2. 数据分布:簇间分离度越高,收敛越快
  3. 维度诅咒:高维数据可能导致收敛变慢
  4. 簇的数量:K值过大可能影响收敛稳定性

2.4 K-means++初始化算法#

2.4.1 算法动机#

标准K-means对初始化敏感,随机初始化可能导致:

  • 收敛到较差的局部最优解
  • 需要更多迭代次数
  • 结果不稳定

K-means++通过智能初始化策略解决这些问题。

2.4.2 算法描述#

K-means++初始化步骤:

  1. 选择第一个中心:从数据点中均匀随机选择第一个簇中心 μ1\boldsymbol{\mu}_1

  2. 迭代选择后续中心:对于 i=2,3,,Ki = 2, 3, \ldots, K

    • 计算每个点 xj\mathbf{x}_j 到最近已选中心的距离: D(xj)=minl=1,,i1xjμl2D(\mathbf{x}_j) = \min_{l=1,\ldots,i-1}\|\mathbf{x}_j - \boldsymbol{\mu}_l\|^2
    • 以概率正比于 D(xj)2D(\mathbf{x}_j)^2 选择下一个中心: P(xj)=D(xj)2k=1nD(xk)2P(\mathbf{x}_j) = \frac{D(\mathbf{x}_j)^2}{\sum_{k=1}^{n}D(\mathbf{x}_k)^2}

2.4.3 理论保证#

近似比定理:K-means++初始化保证期望目标函数值不超过最优解的 O(logK)O(\log K) 倍:

E[JK-means++]8(logK+2)JOPT\mathbb{E}[J_{\text{K-means++}}] \leq 8(\log K + 2) \cdot J_{\text{OPT}}

这个理论保证使K-means++成为实际应用中的标准选择。

2.5 算法复杂度分析#

2.5.1 时间复杂度#

单次迭代复杂度:

  • E步(分配):O(nKd)O(nKd),需要计算每个点到每个中心的距离
  • M步(更新):O(nd)O(nd),计算新的簇中心

总时间复杂度: O(nKdt)O(nKdt)

其中:

  • nn:样本数量
  • KK:簇的数量
  • dd:特征维度
  • tt:迭代次数

2.5.2 空间复杂度#

存储需求:

  • 数据矩阵:O(nd)O(nd)
  • 簇中心:O(Kd)O(Kd)
  • 簇分配:O(n)O(n)

总空间复杂度:O(nd+Kd)=O((n+K)d)O(nd + Kd) = O((n+K)d)

2.5.3 优化策略#

加速技术:

  1. 三角不等式加速:利用三角不等式减少距离计算
  2. KD树加速:在低维空间中使用KD树加速最近邻搜索
  3. Mini-batch K-means:使用小批量数据进行更新,适合大规模数据
  4. 并行化:E步和M步都可以并行化处理

第三部分:K-means算法的变种与扩展#

3.1 K-medoids算法(PAM)#

3.1.1 算法动机#

K-means使用均值作为簇中心,存在以下问题:

  • 对异常值敏感
  • 簇中心可能不是实际数据点
  • 只适用于数值型数据

K-medoids(也称PAM,Partitioning Around Medoids)使用实际数据点作为簇中心。

3.1.2 算法描述#

目标函数: J=i=1nmink=1,,Kd(xi,mk)J = \sum_{i=1}^{n}\min_{k=1,\ldots,K}d(\mathbf{x}_i, \mathbf{m}_k)

其中 mk\mathbf{m}_k 是第k个簇的medoid(中位点),必须是数据集中的实际点。

算法步骤:

  1. 初始化:随机选择K个数据点作为初始medoids
  2. 分配步:将每个点分配给最近的medoid
  3. 更新步:对每个簇,选择使簇内总距离最小的点作为新medoid: mk=argminxjCkxiCkd(xi,xj)\mathbf{m}_k = \arg\min_{\mathbf{x}_j \in C_k}\sum_{\mathbf{x}_i \in C_k}d(\mathbf{x}_i, \mathbf{x}_j)

3.1.3 优缺点分析#

优点:

  • 对异常值更鲁棒
  • 簇中心是实际数据点,更有解释性
  • 可以使用任意距离度量

缺点:

  • 时间复杂度更高:O(n2Kt)O(n^2Kt)
  • 在大数据集上计算成本昂贵

3.2 Fuzzy C-means算法#

3.2.1 软聚类的概念#

传统K-means进行硬聚类,每个点只属于一个簇。Fuzzy C-means引入模糊集合理论,允许每个点以不同程度属于多个簇。

3.2.2 数学模型#

隶属度矩阵:定义隶属度矩阵 U=[uik]n×K\mathbf{U} = [u_{ik}]_{n \times K},其中 uiku_{ik} 表示点 xi\mathbf{x}_i 属于簇 kk 的程度。

约束条件:

  1. 0uik10 \leq u_{ik} \leq 1i,k\forall i, k
  2. k=1Kuik=1\sum_{k=1}^{K} u_{ik} = 1i\forall i
  3. 0<i=1nuik<n0 < \sum_{i=1}^{n} u_{ik} < nk\forall k

目标函数: Jm=i=1nk=1Kuikmxiμk2J_m = \sum_{i=1}^{n}\sum_{k=1}^{K} u_{ik}^m \|\mathbf{x}_i - \boldsymbol{\mu}_k\|^2

其中 m>1m > 1 是模糊化参数,控制聚类的”软”程度。

3.2.3 优化算法#

使用拉格朗日乘数法,可以得到更新公式:

隶属度更新: uik=1j=1K(xiμkxiμj)2m1u_{ik} = \frac{1}{\sum_{j=1}^{K}\left(\frac{\|\mathbf{x}_i - \boldsymbol{\mu}_k\|}{\|\mathbf{x}_i - \boldsymbol{\mu}_j\|}\right)^{\frac{2}{m-1}}}

簇中心更新: μk=i=1nuikmxii=1nuikm\boldsymbol{\mu}_k = \frac{\sum_{i=1}^{n} u_{ik}^m \mathbf{x}_i}{\sum_{i=1}^{n} u_{ik}^m}

3.3 Mini-batch K-means#

3.3.1 大数据挑战#

标准K-means在处理大规模数据时面临挑战:

  • 内存需求:需要将所有数据加载到内存
  • 计算成本:每次迭代需要遍历所有数据点
  • 收敛速度:大数据集可能需要更多迭代

3.3.2 Mini-batch策略#

核心思想:每次迭代只使用数据的一个小批量(mini-batch)进行更新。

算法步骤:

  1. 初始化:选择初始簇中心
  2. 采样:随机采样大小为 bb 的mini-batch
  3. 分配:将mini-batch中的点分配给最近的簇中心
  4. 更新:使用学习率 η\eta 更新簇中心: μk(t+1)=(1η)μk(t)+η1CkxiCkxi\boldsymbol{\mu}_k^{(t+1)} = (1-\eta)\boldsymbol{\mu}_k^{(t)} + \eta \cdot \frac{1}{|C_k|}\sum_{\mathbf{x}_i \in C_k}\mathbf{x}_i

3.3.3 学习率设计#

自适应学习率ηk=CkCk+countk\eta_k = \frac{|C_k|}{|C_k| + \text{count}_k}

其中 countk\text{count}_k 是簇 kk 在历史中被更新的次数。

优势分析:

  • 时间复杂度:O(bKdt)O(bKdt),其中 bnb \ll n
  • 内存复杂度:O(bd+Kd)O(bd + Kd)
  • 适合在线学习和流数据处理

3.4 核K-means算法#

3.4.1 非线性聚类需求#

标准K-means假设簇是球形的,无法处理复杂的非线性结构。核K-means通过核技巧将数据映射到高维特征空间。

3.4.2 核函数与特征映射#

核函数定义κ(xi,xj)=ϕ(xi),ϕ(xj)\kappa(\mathbf{x}_i, \mathbf{x}_j) = \langle\phi(\mathbf{x}_i), \phi(\mathbf{x}_j)\rangle

常用核函数:

  1. 多项式核κ(xi,xj)=(xiTxj+c)d\kappa(\mathbf{x}_i, \mathbf{x}_j) = (\mathbf{x}_i^T\mathbf{x}_j + c)^d
  2. RBF核κ(xi,xj)=exp(γxixj2)\kappa(\mathbf{x}_i, \mathbf{x}_j) = \exp(-\gamma\|\mathbf{x}_i - \mathbf{x}_j\|^2)
  3. Sigmoid核κ(xi,xj)=tanh(αxiTxj+c)\kappa(\mathbf{x}_i, \mathbf{x}_j) = \tanh(\alpha\mathbf{x}_i^T\mathbf{x}_j + c)

3.4.3 核K-means目标函数#

在特征空间中,目标函数变为: J=i=1nmink=1,,Kϕ(xi)μkϕ2J = \sum_{i=1}^{n}\min_{k=1,\ldots,K}\|\phi(\mathbf{x}_i) - \boldsymbol{\mu}_k^{\phi}\|^2

其中 μkϕ=1CkiCkϕ(xi)\boldsymbol{\mu}_k^{\phi} = \frac{1}{|C_k|}\sum_{i \in C_k}\phi(\mathbf{x}_i) 是特征空间中的簇中心。

距离计算ϕ(xi)μkϕ2=κ(xi,xi)2CkjCkκ(xi,xj)+1Ck2j,lCkκ(xj,xl)\|\phi(\mathbf{x}_i) - \boldsymbol{\mu}_k^{\phi}\|^2 = \kappa(\mathbf{x}_i, \mathbf{x}_i) - \frac{2}{|C_k|}\sum_{j \in C_k}\kappa(\mathbf{x}_i, \mathbf{x}_j) + \frac{1}{|C_k|^2}\sum_{j,l \in C_k}\kappa(\mathbf{x}_j, \mathbf{x}_l)

第四部分:聚类评估与验证#

4.1 内部评估指标#

4.1.1 轮廓系数(Silhouette Coefficient)#

定义:对于点 xi\mathbf{x}_i,其轮廓系数为: si=biaimax(ai,bi)s_i = \frac{b_i - a_i}{\max(a_i, b_i)}

其中:

  • aia_i:点 ii 到同簇其他点的平均距离
  • bib_i:点 ii 到最近异簇所有点的平均距离

数学表达: ai=1Ck1jCk,jid(xi,xj)a_i = \frac{1}{|C_k|-1}\sum_{j \in C_k, j \neq i}d(\mathbf{x}_i, \mathbf{x}_j) bi=minlk1CljCld(xi,xj)b_i = \min_{l \neq k}\frac{1}{|C_l|}\sum_{j \in C_l}d(\mathbf{x}_i, \mathbf{x}_j)

整体轮廓系数S=1ni=1nsiS = \frac{1}{n}\sum_{i=1}^{n}s_i

取值范围si[1,1]s_i \in [-1, 1]

  • sis_i 接近1:聚类效果好
  • sis_i 接近0:点在簇边界上
  • sis_i 接近-1:点可能被分配到错误的簇

4.1.2 Calinski-Harabasz指数#

定义CH=tr(BK)tr(WK)nKK1CH = \frac{\text{tr}(B_K)}{\text{tr}(W_K)} \cdot \frac{n-K}{K-1}

其中:

  • BKB_K:簇间散布矩阵
  • WKW_K:簇内散布矩阵

数学表达: BK=k=1KCk(μkμ)(μkμ)TB_K = \sum_{k=1}^{K}|C_k|(\boldsymbol{\mu}_k - \boldsymbol{\mu})(\boldsymbol{\mu}_k - \boldsymbol{\mu})^T WK=k=1KiCk(xiμk)(xiμk)TW_K = \sum_{k=1}^{K}\sum_{i \in C_k}(\mathbf{x}_i - \boldsymbol{\mu}_k)(\mathbf{x}_i - \boldsymbol{\mu}_k)^T

其中 μ=1ni=1nxi\boldsymbol{\mu} = \frac{1}{n}\sum_{i=1}^{n}\mathbf{x}_i 是全局均值。

解释:CH指数越大,表示簇间分离度越高,簇内紧密度越高。

4.1.3 Davies-Bouldin指数#

定义DB=1Kk=1Kmaxlk(σk+σld(μk,μl))DB = \frac{1}{K}\sum_{k=1}^{K}\max_{l \neq k}\left(\frac{\sigma_k + \sigma_l}{d(\boldsymbol{\mu}_k, \boldsymbol{\mu}_l)}\right)

其中:

  • σk=1CkiCkxiμk\sigma_k = \frac{1}{|C_k|}\sum_{i \in C_k}\|\mathbf{x}_i - \boldsymbol{\mu}_k\|:簇内平均距离
  • d(μk,μl)d(\boldsymbol{\mu}_k, \boldsymbol{\mu}_l):簇中心间距离

解释:DB指数越小,表示聚类效果越好。

4.2 外部评估指标#

4.2.1 调整兰德指数(Adjusted Rand Index, ARI)#

当有真实标签时,可以使用外部指标评估聚类效果。

兰德指数RI=TP+TNTP+FP+FN+TNRI = \frac{TP + TN}{TP + FP + FN + TN}

其中:

  • TP:同簇且同类的点对数
  • TN:异簇且异类的点对数
  • FP:同簇但异类的点对数
  • FN:异簇但同类的点对数

调整兰德指数ARI=RIE[RI]max(RI)E[RI]ARI = \frac{RI - E[RI]}{\max(RI) - E[RI]}

ARI的取值范围为[-1, 1],值越大表示聚类效果越好。

4.2.2 归一化互信息(Normalized Mutual Information, NMI)#

互信息MI(C,T)=k=1Kl=1LP(k,l)logP(k,l)P(k)P(l)MI(C, T) = \sum_{k=1}^{K}\sum_{l=1}^{L}P(k,l)\log\frac{P(k,l)}{P(k)P(l)}

其中C是聚类结果,T是真实标签。

归一化互信息NMI=MI(C,T)H(C)H(T)NMI = \frac{MI(C, T)}{\sqrt{H(C) \cdot H(T)}}

NMI的取值范围为[0, 1],值越大表示聚类效果越好。

第五部分:实际应用技巧与最佳实践#

5.1 数据预处理策略#

5.1.1 特征标准化#

问题:不同特征的量纲和数值范围差异很大时,欧几里得距离会被大数值特征主导。

解决方案

  1. Z-score标准化xi(j)=xi(j)μ(j)σ(j)\mathbf{x}_i^{(j)} = \frac{x_i^{(j)} - \mu^{(j)}}{\sigma^{(j)}}

  2. Min-Max标准化xi(j)=xi(j)min(j)max(j)min(j)\mathbf{x}_i^{(j)} = \frac{x_i^{(j)} - \min^{(j)}}{\max^{(j)} - \min^{(j)}}

  3. 鲁棒标准化xi(j)=xi(j)median(j)IQR(j)\mathbf{x}_i^{(j)} = \frac{x_i^{(j)} - \text{median}^{(j)}}{\text{IQR}^{(j)}}

5.1.2 异常值处理#

检测方法

  1. 统计方法:3σ原则,IQR方法
  2. 基于距离:局部异常因子(LOF)
  3. 基于密度:DBSCAN预处理

处理策略

  • 删除异常值
  • 异常值截断(Winsorization)
  • 使用鲁棒的聚类算法(如K-medoids)

5.1.3 维度降维#

高维数据的挑战

  • 维度诅咒:高维空间中距离失去区分性
  • 计算复杂度增加
  • 噪声特征影响

降维方法

  1. 主成分分析(PCA)Y=XW\mathbf{Y} = \mathbf{X}\mathbf{W} 其中W是主成分矩阵

  2. t-SNE:适合可视化,保持局部结构

  3. UMAP:保持全局和局部结构

  4. 特征选择:基于方差、相关性或模型的特征选择

5.2 K值选择的高级方法#

5.2.1 信息准则方法#

贝叶斯信息准则(BIC)BIC(K)=2ln(L)+Kln(n)BIC(K) = -2\ln(L) + K \cdot \ln(n)

其中L是似然函数,第二项是复杂度惩罚。

赤池信息准则(AIC)AIC(K)=2ln(L)+2KAIC(K) = -2\ln(L) + 2K

选择使BIC或AIC最小的K值。

5.2.2 Gap统计量#

定义Gap(K)=E[log(WK)]log(WK)\text{Gap}(K) = \mathbb{E}[\log(W_K^*)] - \log(W_K)

其中:

  • WKW_K:实际数据的簇内平方和
  • WKW_K^*:参考分布(如均匀分布)的簇内平方和

算法步骤

  1. 对每个K,计算实际数据的WKW_K
  2. 生成B个参考数据集,计算WK(b)W_K^{*(b)}
  3. 计算Gap统计量和标准误差
  4. 选择满足Gap(K) ≥ Gap(K+1) - s_{K+1}的最小K

5.2.3 X-means算法#

核心思想:自动确定K值,通过BIC准则决定是否分裂簇。

算法流程

  1. 从K=1开始运行K-means
  2. 对每个簇,尝试分裂为两个子簇
  3. 使用BIC准则判断分裂是否改善模型
  4. 重复直到没有簇需要分裂

5.3 处理特殊数据类型#

5.3.1 类别型数据聚类#

K-modes算法

距离度量:使用汉明距离 d(xi,xj)=l=1dδ(xil,xjl)d(\mathbf{x}_i, \mathbf{x}_j) = \sum_{l=1}^{d}\delta(x_{il}, x_{jl})

其中δ(a,b)=0\delta(a,b) = 0如果a=ba=b,否则为1。

簇中心:使用众数而非均值 modek(l)=argmaxviCk1(xi(l)=v)\text{mode}_k^{(l)} = \arg\max_{v}\sum_{i \in C_k}\mathbf{1}(x_i^{(l)} = v)

5.3.2 混合数据类型#

K-prototypes算法:结合K-means和K-modes

距离度量d(xi,xj)=l=1dn(xil(n)xjl(n))2+γl=1dcδ(xil(c),xjl(c))d(\mathbf{x}_i, \mathbf{x}_j) = \sum_{l=1}^{d_n}(x_{il}^{(n)} - x_{jl}^{(n)})^2 + \gamma\sum_{l=1}^{d_c}\delta(x_{il}^{(c)}, x_{jl}^{(c)})

其中γ\gamma是权重参数,平衡数值型和类别型特征的贡献。

5.4 大规模数据处理#

5.4.1 分布式K-means#

MapReduce框架

Map阶段

  • 输入:数据分片和当前簇中心
  • 输出:(簇ID, 点的坐标和计数)

Reduce阶段

  • 输入:同一簇的所有点
  • 输出:新的簇中心

Spark实现

# 伪代码
def distributed_kmeans(data_rdd, k, max_iter):
centers = initialize_centers(k)
for iteration in range(max_iter):
# 广播簇中心
broadcast_centers = spark.broadcast(centers)
# 分配点到最近簇
assignments = data_rdd.map(lambda x: assign_to_cluster(x, broadcast_centers.value))
# 计算新簇中心
new_centers = assignments.reduceByKey(lambda a, b: combine_points(a, b)) \
.map(lambda x: compute_center(x))
centers = new_centers.collect()

5.4.2 在线K-means#

流数据处理

算法特点

  • 数据逐个到达,无法存储所有历史数据
  • 需要实时更新簇中心
  • 内存使用固定

更新策略μk(t+1)=nk(t)μk(t)+xnewnk(t)+1\boldsymbol{\mu}_k^{(t+1)} = \frac{n_k^{(t)}\boldsymbol{\mu}_k^{(t)} + \mathbf{x}_{new}}{n_k^{(t)} + 1}

其中nk(t)n_k^{(t)}是簇k在时刻t的点数。

第六部分:实际项目案例分析#

6.1 客户细分案例#

6.1.1 业务背景#

目标:基于客户行为数据进行市场细分,制定差异化营销策略。

数据特征

  • 人口统计学特征:年龄、性别、收入、教育水平
  • 行为特征:购买频率、平均订单金额、品类偏好
  • 时间特征:客户生命周期、最近购买时间

6.1.2 技术实现#

特征工程

  1. RFM分析

    • Recency:最近购买时间
    • Frequency:购买频率
    • Monetary:购买金额
  2. 特征变换

    # 对数变换处理偏态分布
    df['log_monetary'] = np.log1p(df['monetary'])
    # 标准化处理
    scaler = StandardScaler()
    features_scaled = scaler.fit_transform(features)

聚类分析

# K值选择
inertias = []
silhouette_scores = []
K_range = range(2, 11)
for k in K_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
kmeans.fit(features_scaled)
inertias.append(kmeans.inertia_)
silhouette_scores.append(silhouette_score(features_scaled, kmeans.labels_))
# 最终聚类
optimal_k = 5
kmeans = KMeans(n_clusters=optimal_k, random_state=42)
cluster_labels = kmeans.fit_predict(features_scaled)

6.1.3 结果解释#

簇特征分析

簇ID客户类型特征描述营销策略
0高价值客户高频高额购买VIP服务,个性化推荐
1潜力客户中频中额购买促销活动,品类扩展
2新客户低频低额购买欢迎礼包,引导购买
3流失风险客户长时间未购买召回活动,优惠券
4价格敏感客户促销时购买定向折扣,限时优惠

6.2 图像分割案例#

6.2.1 技术原理#

颜色空间聚类:将图像像素在颜色空间中进行聚类,实现图像分割。

算法流程

  1. 将图像从RGB转换到Lab颜色空间
  2. 提取像素的颜色特征
  3. 使用K-means进行聚类
  4. 将聚类结果映射回图像

6.2.2 代码实现#

import cv2
import numpy as np
from sklearn.cluster import KMeans
def image_segmentation(image_path, k=3):
# 读取图像
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
# 重塑为二维数组
pixel_values = image.reshape((-1, 3))
pixel_values = np.float32(pixel_values)
# K-means聚类
kmeans = KMeans(n_clusters=k, random_state=42)
labels = kmeans.fit_predict(pixel_values)
centers = kmeans.cluster_centers_
# 将聚类结果映射回图像
centers = np.uint8(centers)
segmented_data = centers[labels.flatten()]
segmented_image = segmented_data.reshape(image.shape)
return segmented_image

6.3 文档聚类案例#

6.3.1 文本预处理#

TF-IDF特征提取TF-IDF(t,d)=TF(t,d)×IDF(t)\text{TF-IDF}(t,d) = \text{TF}(t,d) \times \text{IDF}(t)

其中: TF(t,d)=count(t,d)tdcount(t,d)\text{TF}(t,d) = \frac{\text{count}(t,d)}{\sum_{t' \in d}\text{count}(t',d)} IDF(t)=logN{d:td}\text{IDF}(t) = \log\frac{N}{|\{d : t \in d\}|}

降维处理: 使用截断SVD(LSA)降维: XUkΣkVkT\mathbf{X} \approx \mathbf{U}_k\mathbf{\Sigma}_k\mathbf{V}_k^T

6.3.2 聚类效果评估#

主题一致性:使用困惑度和主题连贯性评估聚类质量。

可视化分析:使用t-SNE将高维文档向量投影到2D空间进行可视化。

学习总结与思考#

算法优势与局限性#

K-means的优势:

  1. 简单高效:算法原理直观,实现简单,计算复杂度低
  2. 可扩展性:适合大规模数据处理,有多种优化变种
  3. 广泛适用:在多个领域都有成功应用案例
  4. 理论基础:有完整的数学理论支撑

主要局限性:

  1. K值选择:需要预先指定簇数,缺乏自适应性
  2. 形状假设:假设簇是球形的,无法处理复杂形状
  3. 初始化敏感:结果依赖于初始化,可能陷入局部最优
  4. 异常值敏感:均值计算容易受异常值影响

实际应用建议#

  1. 数据预处理至关重要:标准化、异常值处理、降维等步骤直接影响聚类效果
  2. 多种方法结合:使用多个指标选择K值,结合领域知识验证结果
  3. 算法选择:根据数据特点选择合适的变种算法
  4. 结果解释:聚类结果需要结合业务背景进行解释和验证

未来发展方向#

  1. 深度聚类:结合深度学习的端到端聚类方法
  2. 自适应聚类:能够自动确定簇数和形状的算法
  3. 多模态聚类:处理图像、文本、音频等多种数据类型
  4. 增量学习:支持在线学习和概念漂移的聚类算法

K-means作为聚类分析的基石算法,虽然有其局限性,但其简单性和有效性使其在实际应用中仍然占据重要地位。通过深入理解其数学原理、掌握各种变种算法、熟练运用评估方法,我们能够在实际项目中更好地发挥其价值。

K-means 聚类算法
https://kyc001.github.io/posts/k-means-clustering-algorithm/
作者
kyc001
发布于
2025-07-27
许可协议
CC BY-NC-SA 4.0