当前位置: 首页 > news >正文

K-means 聚类算法学习笔记

K-means 是一种非常流行且简单易懂的聚类算法,它的核心思想是“物以类聚,人以群分”。

K-means 是什么?

简单来说,K-means 的目标就是把一堆数据点分成 K 个“小团体”(也就是“簇”或“聚类”),让每个小团体内部的成员都尽可能地相似,而不同小团体之间的成员则尽可能地不同。

它判断“相似”的标准很简单:离得近就是一伙的!具体来说,就是离某个“中心点”(我们叫它“质心”)最近的数据点,就属于这个质心代表的团体。

一个好懂的比喻

想象一下,你是一家大型商场的负责人,现在想在商场里设置 K 个服务台,方便顾客咨询和休息。你会怎么做呢?

  1. 初步放置: 你可能会先随机选 K 个地方放上服务台。

  2. 顾客选择: 顾客来了之后,他们自然会选择离自己最近的那个服务台去咨询。

  3. 服务台调整: 过了一段时间,你发现每个服务台周围都聚集了一群顾客。为了让服务更高效,你会把每个服务台的位置调整到它所服务的这群顾客的“平均位置”(也就是这群顾客的中心点)。

  4. 重复调整: 顾客会根据新的服务台位置重新选择最近的服务台,你再根据新的顾客分布调整服务台位置……

这样几轮下来,每个服务台就会“守住”一片区域,这些区域就是我们说的“簇”,而服务台的最终位置就是每个簇的“质心”。

K-means 的“数学目标”

K-means 算法在背后做的事情,就是努力让一个叫做“目标函数”的值变得最小。这个目标函数长这样:

$$ \sum{i=1}^{n} \min{1 \le j \le K} |x_i - \mu_j|^2 $$

别被公式吓到,它的意思其实很简单:

  • x_i 代表每一个数据点。

  • μ_j 代表第 j 个簇的质心(也就是那个“中心点”)。

  • ||x_i - μ_j||^2 表示数据点 x_i 到它所属簇的质心 μ_j 之间的距离的平方。

  • min 表示每个点都选择离它最近的那个质心。

  • 表示把所有数据点到它们各自质心的距离平方加起来。

所以,整个公式的意思就是:所有数据点到它们各自所属簇的质心的距离的平方和,要尽可能地小。

为什么用平方距离?

  • 平方距离会放大那些离质心较远的点(“离群点”)的影响,让算法更关注这些点,努力把它们也拉进合适的簇里。

  • 它默认使用的是欧氏距离,这要求你的数据特征(比如身高、体重)是数值型的,并且它们的单位(量纲)最好是可比较的,或者经过了标准化处理。

算法步骤:两步走,直到稳定

K-means 算法是一个迭代过程,主要就两步,不断重复直到收敛(也就是结果不再有明显变化):

  1. 分配(Assignment Step): 对于每一个数据点,计算它到所有 K 个质心的距离,然后把它分配给距离最近的那个质心所在的簇。

  2. 更新(Update Step): 对于每一个簇,重新计算它的质心。新的质心就是这个簇里面所有数据点的平均值。

什么时候停止?

  • 质心的位置不再发生变化。

  • 目标函数(所有点到质心距离平方和)的下降幅度变得非常小,几乎不变了。

  • 达到了你预设的最大迭代次数。

算法复杂度: 大致是 O(n × K × d × iter)

  • n:数据点的数量。

  • K:你想要分的簇的数量。

  • d:每个数据点的特征维度(比如一个人的身高、体重、年龄,维度就是3)。

  • iter:算法迭代的次数。

为什么 K-means 会收敛?

这是一个很棒的问题!简单来说,K-means 每次迭代都不会让它的“数学目标”变差:

  • 分配步骤: 把每个点分给最近的质心,这肯定不会让总的距离平方和变大,只会变小或不变。

  • 更新步骤: 用簇内所有点的平均值作为新的质心,这是在当前簇分配下,能让簇内点到质心距离平方和最小化的最佳选择,所以也不会让总的距离平方和变大。

由于目标函数有一个下限(距离平方和不可能小于0),而且每次迭代都会让它变小或不变,所以算法最终一定会停下来,达到一个局部最优解(不一定是全局最优,但已经是一个不错的解了)。

初始化的重要性:K-means++

K-means 算法对最初选择的 K 个质心位置非常敏感。如果初始质心选得不好,算法可能会陷入一个“局部最优解”,导致聚类效果不理想。

为了解决这个问题,人们发明了 K-means++ 这种更好的初始化方法:

  1. 第一个质心: 随机从所有数据点中选择一个作为第一个质心。

  2. 后续质心: 接下来选择质心时,会“偏爱”那些离现有质心比较远的点。具体来说,一个点离现有质心越远,它被选为下一个质心的概率就越大。

这样做的好处是,初始质心会尽可能地分散开,覆盖到数据的不同区域,从而让算法更稳定,也更快地收敛到更好的结果。

小技巧: 除了 K-means++,实际应用中,我们还经常会多次随机运行 K-means 算法(每次用不同的初始质心),然后选择其中目标函数值最小(也就是聚类效果最好)的那次结果。

何时 K-means 好用?何时要慎用?

✅ 适合用 K-means 的场景:

  • 簇的形状: 你的数据簇大致是球形或凸形的,而且大小和密度都比较接近。

  • 特征类型: 你的数据特征是连续的数值型,并且各个特征的单位(量纲)相近,或者已经经过了标准化处理。

  • 聚类类型: 你需要一个快速、可扩展的“硬聚类”结果(每个数据点明确地只属于一个簇)。

❌ 慎用或不适合用 K-means 的场景:

  • 簇的形状: 簇的形状是拉长的、弯曲的、非球形的,或者簇之间有“桥梁”连接。K-means 会强行把它们切成球状,效果会很差。

  • 离群点: 数据中存在明显的“离群点”(异常值)。由于 K-means 使用平方距离,离群点会被“狠狠放大”,严重拉偏质心的位置,导致聚类不准确。

  • 特征类型: 数据中包含大量的类别型变量(比如“颜色:红/绿/蓝”)。欧氏距离不适合衡量这类特征的相似度。

  • 簇的差异: 不同簇之间的大小或密度差异非常大。

实战三件套:数据预处理、选 K、稳收敛

1. 数据预处理:让数据“听话”

  • 标准化: 这是最重要的一步!如果你的数据特征单位不同(比如身高是米,体重是公斤),数值大的特征会在距离计算中占据主导地位。通过标准化(比如 z-score 标准化,让数据变成均值为0,标准差为1),可以消除这种影响,让所有特征“一视同仁”。

  • 处理离群点: 识别并处理数据中的极端离群点。你可以选择删除它们,或者使用对离群点不那么敏感的替代算法(比如 K-medoids)。

2. 如何选择合适的 K 值?

选择 K(也就是你想分成多少个簇)是 K-means 聚类中最关键也最头疼的问题之一。以下是几种常用方法:

  • 肘部法则(Elbow Method):

    • 怎么做: 分别用 K=1, 2, 3... 等不同的 K 值运行 K-means 算法,并记录每次的总簇内平方和(SSE,也就是前面提到的目标函数值)。然后画一张图,横轴是 K 值,纵轴是 SSE。

    • 怎么看: 随着 K 的增加,SSE 会逐渐减小。你会发现曲线会有一个明显的“拐点”,就像人的肘部一样。这个拐点对应的 K 值通常被认为是比较合适的,因为它意味着再增加 K 值,SSE 的下降幅度就不那么明显了,收益不大了。

  • 轮廓系数(Silhouette Score):

    • 怎么做: 同样是尝试不同的 K 值。对于每个 K 值,计算所有数据点的平均轮廓系数。轮廓系数的取值范围是 [-1, 1]。

    • 怎么看: 轮廓系数越高越好。它衡量了一个点与它自己簇的相似度(应该高),以及与相邻簇的相异度(应该高)。平均轮廓系数最高的 K 值通常是最佳选择。

  • Gap Statistic:

    • 怎么做: 将你的数据集的 SSE 与随机生成的数据集的 SSE 进行比较。目标是找到一个 K 值,使得你的数据集的 SSE 与随机数据集的 SSE 之间的差距最大。

    • 怎么看: 差距越大,说明你的聚类结构越明显,这个 K 值越可能代表了数据的真实聚类数量。

3. 提升 K-means 的稳定性

  • 使用 K-means++ 初始化: 前面已经提到了,这是最常用的方法,能有效避免陷入糟糕的局部最优。

  • 多次重启取最优: 多次运行 K-means(每次随机初始化),然后选择目标函数值最小(SSE最小)的那次结果。

  • 设置最大迭代数与收敛阈值: 即使算法没有完全收敛,也可以在达到最大迭代次数后停止,或者当目标函数下降幅度小于某个很小的阈值时停止,防止无限循环。

  • 处理空簇: 偶尔会出现某个簇在迭代过程中变得没有数据点的情况。可以把这个空簇的质心重新放置到离当前所有质心最远的点上,或者放置到当前 SSE 最大的那个点上,以避免算法中断。

常见“坑”与应对策略

常见问题应对策略
特征尺度不一致标准化/归一化数据。
离群点拉偏质心去噪;使用 K-medoids(质心是真实样本点,更抗噪);或使用截断/鲁棒尺度(对距离计算进行调整,减少离群点影响)。
非球形簇考虑使用其他聚类算法,如 GMM(高斯混合模型,可拟合椭球形簇,是“软聚类”)、DBSCAN/HDBSCAN(基于密度,能发现任意形状的簇,且不用预设 K 值)、或谱聚类
K 值选择不当综合使用肘部法则、轮廓系数、Gap Statistic 进行评估。
数据维度太高先进行降维处理(如 PCA 主成分分析、UMAP),再进行聚类;或者只在关键的特征子空间进行聚类。

和“亲戚们”的对比

K-means 并不是唯一的聚类算法,它还有一些“亲戚”,各有优缺点:

  • K-means:

    • 特点: 硬分配(每个点明确属于一个簇)、倾向于发现球形簇、速度快、对离群点敏感。

    • 何时用: 数据量大,簇形状大致为球形,需要快速得到聚类结果。

  • K-medoids (PAM):

    • 特点: 质心是簇中真实存在的数据点(称为“代表点”),而不是平均值。因此,它对离群点不那么敏感,更鲁棒。

    • 何时用: 数据中可能存在离群点,但计算速度通常比 K-means 慢,不适合超大数据集。

  • GMM (高斯混合模型,基于 EM 算法):

    • 特点: 软分配(每个点以概率属于多个簇)、可以拟合不同形状(椭球形)和大小的簇。

    • 何时用: 簇的边界模糊,或者簇的形状不是简单的球形,需要更灵活的聚类。

  • DBSCAN / HDBSCAN:

    • 特点: 基于密度,不需要预先指定 K 值,能够发现任意形状的簇,并且能识别出噪声点(不属于任何簇的点)。

    • 何时用: 簇的形状不规则,数据中存在噪声,且不确定簇的数量。但参数选择可能比较敏感,对密度变化大的数据集效果不稳。

典型应用场景

K-means 在实际中应用非常广泛:

  • 图像量化/压缩: 把一张图片中成千上万种颜色聚类成 K 种主要颜色,从而实现图片压缩,生成“K 色图”。

  • 用户分群: 根据用户的购买行为、浏览历史、人口统计学特征等,将用户分成不同的群体(比如“高价值用户”、“新用户”、“流失风险用户”),以便进行精准营销或个性化推荐。

  • 文档/向量聚类: 将大量的文本(经过词向量或嵌入表示后)或任何高维数据向量进行聚类,可以快速将相似的文档或数据归类,加速信息检索或推荐系统中的“召回”环节。

迷你“心法”清单

  1. 数值特征: 在使用 K-means 之前,务必先对数据进行标准化(比如 z-scoreMin-Max 归一化)。

  2. 初始化: 总是使用 K-means++ 来初始化质心,并且最好多次运行 K-means 算法,选择其中最好的结果。

  3. 选择 K: 先尝试 K 值在 210 之间,然后结合肘部法则轮廓系数来选择 1-2 个最合适的候选 K 值,最后根据你的业务理解和可解释性来最终确定。

  4. 检查簇形状: 如果聚类结果中簇的形状明显不是球形,或者簇之间的大小/密度差异很大,那么请考虑使用 GMMDBSCAN 等更高级的算法。

  5. 大数据量: 如果你的数据量非常大(比如百万级甚至更大),可以考虑使用 Mini-Batch K-means,它是一种 K-means 的变体,通过使用数据子集来更新质心,从而大大加快计算速度,同时保持近似的聚类效果。

  6. 评估: 除了 SSE 和轮廓系数这些内部评估指标,更重要的是结合业务指标(比如用户转化率、留存率等)来评估聚类结果的实际价值。

http://www.dtcms.com/a/332804.html

相关文章:

  • 解锁PostgreSQL专家认证增强驱动引擎
  • 打靶日常-sql注入(手工+sqlmap)
  • 136-基于Spark的酒店数据分析系统
  • Python Sqlalchemy数据库连接
  • 紫金桥RealSCADA:国产工业大脑,智造安全基石
  • 【已解决】在Spring Boot工程中,若未识别到resources/db文件夹下的SQL文件
  • JavaScript 防抖(Debounce)与节流(Throttle)
  • 易道博识康铁钢:大小模型深度融合是现阶段OCR的最佳解决方案
  • 【Trans2025】计算机视觉|UMFormer:即插即用!让遥感图像分割更精准!
  • Notepad++插件开发实战指南
  • Radar Forward-Looking Imaging Based on Chirp Beam Scanning论文阅读
  • 《WINDOWS 环境下32位汇编语言程序设计》第1章 背景知识
  • 【Linux】探索Linux虚拟地址空间及其管理机制
  • C# HangFire的使用
  • 概率论基础教程第2章概率论公理(习题和解答)
  • 在 Linux 服务器搭建Coturn即ICE/TURN/STUN实现P2P(点对点)直连
  • HarmonyOS 实战:用 @Observed + @ObjectLink 玩转多组件实时数据更新
  • pyecharts可视化图表-pie:从入门到精通(进阶篇)
  • Python 数据可视化:柱状图/热力图绘制实例解析
  • 概率论基础教程第2章概率论公理
  • 享元模式C++
  • 基于深度学习的零件缺陷识别方法研究(LW+源码+讲解+部署)
  • 力扣hot100 | 普通数组 | 53. 最大子数组和、56. 合并区间、189. 轮转数组、238. 除自身以外数组的乘积、41. 缺失的第一个正数
  • 什么才是真正的白盒测试?
  • 专题三_二分_x 的平方根
  • JavaScript 解析 Modbus 响应数据的实现方法
  • 记录处理:Caused by: java.lang.UnsatisfiedLinkError
  • MARCONet++ 攻克中文文本图像超分难题
  • 疯狂星期四文案网第40天运营日记
  • Web 开发 15