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版本则适合工程应用(优化了初始化、空簇处理等细节)。

相关推荐
海边夕阳20064 小时前
【每天一个AI小知识】:什么是生成对抗网络?
人工智能·经验分享·深度学习·神经网络·机器学习·生成对抗网络
Wise玩转AI4 小时前
Day 27|智能体的 UI 与用户交互层
人工智能·python·ui·ai·chatgpt·ai智能体
youcans_5 小时前
【youcans论文精读】VM-UNet:面向医学图像分割的视觉 Mamba UNet 架构
论文阅读·人工智能·计算机视觉·图像分割·状态空间模型
铮铭5 小时前
扩散模型简介:The Annotated Diffusion Model
人工智能·机器人·强化学习·世界模型
轻竹办公PPT5 小时前
轻竹论文:毕业论文AI写作教程
人工智能·ai·ai写作
呵呵哒( ̄▽ ̄)"5 小时前
专项智能练习(课程类型)
人工智能
2501_918126916 小时前
如何用ai把特定领域的生活成本归零
人工智能·生活·个人开发
Brianna Home6 小时前
[鸿蒙2025领航者闯关] 鸿蒙 6.0 星盾安全架构 + AI 防窥:金融级支付安全实战与深度踩坑实录
人工智能·安全·harmonyos·安全架构
CoderYanger6 小时前
递归、搜索与回溯-穷举vs暴搜vs深搜vs回溯vs剪枝:12.全排列
java·算法·leetcode·机器学习·深度优先·剪枝·1024程序员节