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

相关推荐
WWZZ20254 小时前
快速上手大模型:机器学习6(过拟合、正则化)
人工智能·算法·机器学习·计算机视觉·机器人·slam·具身感知
智启七月5 小时前
谷歌 Gemini 3.0 正式发布:一键生成 Web OS,编程能力碾压竞品
人工智能·python
Juchecar5 小时前
物质导光导电的微观原理与半导体
人工智能
2401_841495645 小时前
【强化学习】动态规划算法
人工智能·python·算法·动态规划·强化学习·策略迭代·价值迭代
WWZZ20255 小时前
快速上手大模型:机器学习5(逻辑回归及其代价函数)
人工智能·算法·机器学习·计算机视觉·机器人·slam·具身感知
FreeCode5 小时前
深度解析Agent Skills:为智能体构建专业特长
人工智能·agent
_一两风5 小时前
用 n8n 自动化生成「每日科技热点速递」:从 RSS 到 AI 写作全流程实战(小白必看)
人工智能·rss·deepseek
极昆仑智慧5 小时前
OpenAI推出了支持人工智能的浏览器ChatGPT Atlas
人工智能·chatgpt
hunteritself5 小时前
阿里千问上线记忆,Manus 1.5 全栈升级,ChatGPT 将推成人模式!| AI Weekly 10.13-10.19
大数据·人工智能·深度学习·机器学习·chatgpt