k-means

K-Means算法详解

一、数学公式

K-Means的核心是最小化簇内平方误差和(Within-Cluster Sum of Squares, WCSS),目标函数定义如下:

设样本集为 X={x1,x2,...,xn}X = \{x_1, x_2, ..., x_n\}X={x1,x2,...,xn}(其中 xi∈Rdx_i \in \mathbb{R}^dxi∈Rd 为d维特征向量),聚类目标是将样本划分为 kkk 个簇 C1,C2,...,CkC_1, C_2, ..., C_kC1,C2,...,Ck,每个簇的中心为 μj∈Rd\mu_j \in \mathbb{R}^dμj∈Rd(j=1,2,...,kj=1,2,...,kj=1,2,...,k)。

目标函数:
J=∑j=1k∑xi∈Cj∥xi−μj∥2 J = \sum_{j=1}^k \sum_{x_i \in C_j} \| x_i - \mu_j \|^2 J=j=1∑kxi∈Cj∑∥xi−μj∥2

其中 ∥xi−μj∥2\| x_i - \mu_j \|^2∥xi−μj∥2 是样本 xix_ixi 与簇中心 μj\mu_jμj 的欧氏距离平方(简化计算,与欧氏距离最小化等价)。

二、推导过程

K-Means通过交替优化 (类似EM算法)求解目标函数 JJJ,分为两个核心步骤:

1. 分配步骤(E步:确定簇归属)

固定簇中心 μ1,...,μk\mu_1, ..., \mu_kμ1,...,μk,为每个样本 xix_ixi 分配到距离最近的簇 ,即:
Cj={xi∣∥xi−μj∥2≤∥xi−μl∥2,∀l≠j} C_j = \left\{ x_i \mid \| x_i - \mu_j \|^2 \leq \| x_i - \mu_l \|^2, \forall l \neq j \right\} Cj={xi∣∥xi−μj∥2≤∥xi−μl∥2,∀l=j}

合理性 :对固定的 μj\mu_jμj,目标函数 JJJ 是各样本距离平方的总和。为最小化 JJJ,每个 xix_ixi 必须属于能让其距离平方项最小的簇,因此上述分配是最优的。

2. 更新步骤(M步:更新簇中心)

固定簇分配 C1,...,CkC_1, ..., C_kC1,...,Ck,求解最优簇中心 μj\mu_jμj 使 JJJ 最小。

对 JJJ 关于 μj\mu_jμj 求导并令导数为0:

  • 展开目标函数:J=∑j=1k∑xi∈Cj(xi−μj)T(xi−μj)J = \sum_{j=1}^k \sum_{x_i \in C_j} (x_i - \mu_j)^T (x_i - \mu_j)J=∑j=1k∑xi∈Cj(xi−μj)T(xi−μj)
  • 展开平方项:(xi−μj)T(xi−μj)=∥xi∥2−2μjTxi+∥μj∥2(x_i - \mu_j)^T (x_i - \mu_j) = \| x_i \|^2 - 2\mu_j^T x_i + \| \mu_j \|^2(xi−μj)T(xi−μj)=∥xi∥2−2μjTxi+∥μj∥2
  • 对 μj\mu_jμj 求偏导:∂J∂μj=∑xi∈Cj(−2xi+2μj)\frac{\partial J}{\partial \mu_j} = \sum_{x_i \in C_j} (-2x_i + 2\mu_j)∂μj∂J=∑xi∈Cj(−2xi+2μj)
  • 令导数为0:∑xi∈Cj(−xi+μj)=0  ⟹  μj⋅∣Cj∣=∑xi∈Cjxi\sum_{x_i \in C_j} (-x_i + \mu_j) = 0 \implies \mu_j \cdot |C_j| = \sum_{x_i \in C_j} x_i∑xi∈Cj(−xi+μj)=0⟹μj⋅∣Cj∣=∑xi∈Cjxi

最终得最优簇中心为簇内样本的均值
μj=1∣Cj∣∑xi∈Cjxi \mu_j = \frac{1}{|C_j|} \sum_{x_i \in C_j} x_i μj=∣Cj∣1xi∈Cj∑xi

3. 收敛条件

重复"分配-更新"步骤,直到簇中心变化小于阈值(如 ∥μjnew−μjold∥2<ϵ\| \mu_j^{\text{new}} - \mu_j^{\text{old}} \|^2 < \epsilon∥μjnew−μjold∥2<ϵ)或迭代次数达到上限。

三、Python代码实现(手动实现)
python 复制代码
import numpy as np
import matplotlib.pyplot as plt

class KMeans:
    def __init__(self, n_clusters=2, max_iter=100, tol=1e-4):
        self.n_clusters = n_clusters  # 簇数量
        self.max_iter = max_iter      # 最大迭代次数
        self.tol = tol                # 收敛阈值
        self.centers = None           # 簇中心
        self.labels = None            # 样本标签

    def fit(self, X):
        n_samples, n_features = X.shape
        
        # 1. 初始化簇中心(随机选择k个样本)
        self.centers = X[np.random.choice(n_samples, self.n_clusters, replace=False)]
        
        for _ in range(self.max_iter):
            # 2. 分配步骤:计算每个样本到中心的距离,分配到最近簇
            # 距离平方:(n_samples, n_clusters)
            distances = np.sum((X[:, np.newaxis] - self.centers) **2, axis=2)
            self.labels = np.argmin(distances, axis=1)  # 每个样本的簇标签
            
            # 3. 更新步骤:计算新簇中心(均值)
            new_centers = np.array([X[self.labels == j].mean(axis=0) for j in range(self.n_clusters)])
            
            # 4. 检查收敛
            center_shift = np.sum(np.linalg.norm(new_centers - self.centers, axis=1)** 2)
            if center_shift < self.tol:
                break
            
            self.centers = new_centers

# 测试代码
if __name__ == "__main__":
    # 生成模拟数据
    np.random.seed(42)
    X1 = np.random.normal(loc=[2, 2], scale=0.5, size=(100, 2))
    X2 = np.random.normal(loc=[5, 5], scale=0.5, size=(100, 2))
    X3 = np.random.normal(loc=[8, 2], scale=0.5, size=(100, 2))
    X = np.vstack([X1, X2, X3])
    
    # 聚类
    kmeans = KMeans(n_clusters=3)
    kmeans.fit(X)
    
    # 可视化
    plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels, cmap='viridis', alpha=0.7)
    plt.scatter(kmeans.centers[:, 0], kmeans.centers[:, 1], c='red', s=200, marker='X', label='Centers')
    plt.legend()
    plt.show()
四、sklearn实现

sklearn的sklearn.cluster.KMeans封装了高效实现(支持并行计算、多种初始化方式等)。

python 复制代码
from sklearn.cluster import KMeans
import numpy as np
import matplotlib.pyplot as plt

# 生成模拟数据(同上)
np.random.seed(42)
X1 = np.random.normal(loc=[2, 2], scale=0.5, size=(100, 2))
X2 = np.random.normal(loc=[5, 5], scale=0.5, size=(100, 2))
X3 = np.random.normal(loc=[8, 2], scale=0.5, size=(100, 2))
X = np.vstack([X1, X2, X3])

# 初始化并训练模型
kmeans_sklearn = KMeans(n_clusters=3, random_state=42)  # random_state确保结果可复现
kmeans_sklearn.fit(X)

# 结果
labels = kmeans_sklearn.labels_       # 聚类标签
centers = kmeans_sklearn.cluster_centers_  # 簇中心
inertia = kmeans_sklearn.inertia_    # 最终WCSS值

# 可视化
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.7)
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, marker='X', label='Centers')
plt.legend()
plt.show()

总结

K-Means通过交替优化簇分配和簇中心,最小化簇内平方误差和,实现高效聚类。手动实现可理解核心逻辑,sklearn版本则适合工程应用(优化了初始化、空簇处理等细节)。

相关推荐
ElfBoard44 分钟前
ElfBoard技术贴|如何在【RK3588】ELF 2开发板实现GPIO功能复用
linux·人工智能·单片机·嵌入式硬件·物联网·机器人
SUPER52664 小时前
本地开发环境_spring-ai项目启动异常
java·人工智能·spring
上进小菜猪8 小时前
基于 YOLOv8 的智能车牌定位检测系统设计与实现—从模型训练到 PyQt 可视化落地的完整实战方案
人工智能
AI浩9 小时前
UNIV:红外与可见光模态的统一基础模型
人工智能·深度学习
GitCode官方9 小时前
SGLang AI 金融 π 对(杭州站)回顾:大模型推理的工程实践全景
人工智能·金融·sglang
木头左9 小时前
LSTM模型入参有效性验证基于量化交易策略回测的方法学实践
人工智能·rnn·lstm
找方案9 小时前
我的 all-in-rag 学习笔记:文本分块 ——RAG 系统的 “信息切菜术“
人工智能·笔记·all-in-rag
亚马逊云开发者10 小时前
让 AI 工作空间更智能:Amazon Quick Suite 集成博查搜索实践
人工智能
腾讯WeTest10 小时前
「低成本、高质高效」WeTest AI翻译限时免费
人工智能
Lucas5555555510 小时前
现代C++四十不惑:AI时代系统软件的基石与新征程
开发语言·c++·人工智能