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

相关推荐
NAGNIP7 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab8 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab8 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP12 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年12 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼12 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS12 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区13 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈13 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang14 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx