观文听傑

返回

上一篇的线性判别分析(Linear Discriminant Analysis,LDA)知道每个训练样本属于哪一类,因此能估计各类均值、先验和共享协方差。但用户画像、设备状态或新药分子常常只有特征,没有现成标签:我们甚至不知道应当寻找哪些类别。

聚类(Clustering)试图从样本之间的相似性发现结构。K 均值(K-Means)是最基本的原型聚类方法:预先指定 KK 个中心,让每个样本靠近某个中心,再让中心移动到所负责样本的均值。本文只讲透四件紧密相关的事:目标函数、分配—更新循环、初始化与尺度,以及它为什么会被错误几何欺骗。

01 没有标签时,“分得好”是什么意思?#

设输入矩阵为:

X=[x1;;xN]RN×DX=[x_1^\top;\ldots;x_N^\top]\in\mathbb{R}^{N\times D}

NN 是样本数,DD 是特征数。我们希望得到:

  • 簇编号 zi{0,,K1}z_i\in\{0,\ldots,K-1\},全部编号 zNNz\in\mathbb{N}^{N}
  • 簇中心 μkRD\mu_k\in\mathbb{R}^{D},全部中心 MRK×DM\in\mathbb{R}^{K\times D}

K 均值把“好分组”定义为最小化簇内平方和(Within-Cluster Sum of Squares,WCSS):

J(z,M)=i=1Nxiμzi22J(z,M)=\sum_{i=1}^{N}\left\lVert x_i-\mu_{z_i}\right\rVert_2^2

scikit-learn 把这个值称为 inertia_。它只关心样本到最近中心的平方欧氏距离,不知道“客户类型”“疾病亚型”等语义,也没有分类准确率可供优化。

02 为什么一次不能同时求出编号和中心?#

若中心 MM 已知,每个样本的最佳编号很直接:

ziargminkxiμk22z_i\leftarrow\arg\min_k\lVert x_i-\mu_k\rVert_2^2

若编号 zz 已知,第 kk 个中心的最佳位置是该簇样本均值:

μk1Cki:zi=kxi\mu_k\leftarrow\frac{1}{|C_k|}\sum_{i:z_i=k}x_i

其中 Ck={i:zi=k}C_k=\{i:z_i=k\}。困难在于两者互相依赖:不知道中心就无法分配,不知道分配又无法算中心。Lloyd 算法采用交替优化(Alternating Optimization):固定一边优化另一边。

X [N,D] + 初始中心 M⁽⁰⁾ [K,D]


      两两平方距离 [N,K]
              │ 每行 argmin

        簇编号 z [N]
              │ 按编号分组求均值

        新中心 M⁽¹⁾ [K,D]

       中心移动是否足够小?
          ├── 否:继续循环
          └── 是:输出 z、M、J
text

每个分配步不会增大 JJ,每个更新步也不会增大 JJ,所以目标会下降并最终停止。但这只保证到达一个局部最优解,不保证全局最好。

03 为什么更新一定是“均值”?#

先看一个簇,固定它包含的样本集合 CC,中心为 μ\mu

JC(μ)=iCxiμ22J_C(\mu)=\sum_{i\in C}\lVert x_i-\mu\rVert_2^2

对向量 μ\mu 求梯度:

μJC=2iC(μxi)\nabla_\mu J_C=2\sum_{i\in C}(\mu-x_i)

令梯度为零:

Cμ=iCxiμ=1CiCxi|C|\mu=\sum_{i\in C}x_i \quad\Longrightarrow\quad \mu=\frac{1}{|C|}\sum_{i\in C}x_i

所以“均值”并非经验规则,而是平方欧氏距离下的最优代表点。若把损失换成绝对距离,最优代表会转向中位数;算法名称与几何也随之改变。

04 用四个二维点手算一轮#

四个样本为:

x1=(0,0),  x2=(0,2),  x3=(6,0),  x4=(6,2)x_1=(0,0),\;x_2=(0,2),\;x_3=(6,0),\;x_4=(6,2)

K=2K=2,初始中心取 μ0(0)=(0,0)\mu_0^{(0)}=(0,0)μ1(0)=(6,2)\mu_1^{(0)}=(6,2)

分配步的平方距离矩阵为:

D(0)=[040436364400]R4×2D^{(0)}= \begin{bmatrix} 0 & 40\\ 4 & 36\\ 36 & 4\\ 40 & 0 \end{bmatrix}\in\mathbb{R}^{4\times2}

每行取最小值位置,得到 z=[0,0,1,1]z=[0,0,1,1]。更新中心:

μ0(1)=(0,0)+(0,2)2=(0,1)\mu_0^{(1)}=\frac{(0,0)+(0,2)}{2}=(0,1) μ1(1)=(6,0)+(6,2)2=(6,1)\mu_1^{(1)}=\frac{(6,0)+(6,2)}{2}=(6,1)

再次分配时编号不变,算法收敛。最终目标为:

J=1+1+1+1=4J=1+1+1+1=4
x₂
2   ● x₂          ● x₄
1   × μ₀          × μ₁
0   ● x₁          ● x₃
    0              6          x₁

    左右两个圆团适合 K 均值;× 是均值中心。
text

05 用广播写出可检查的 NumPy 核心#

下面的实现故意不调用 fit,以暴露每个中间张量:

真实实现还必须处理空簇、样本权重、稀疏矩阵、停止容差和高效距离计算。上面的列表推导若某个 labels == k 没有样本,会产生非数(Not a Number,NaN);这是手写实现最先应添加的保护。

06 初始化为什么能改变最终答案?#

K 均值目标非凸。若初始中心挤在同一个真实簇附近,算法可能把另一个大簇与少数离群点错误合并,最后停在较差的局部最优。

K-Means++ 初始化的核心思路是让后续中心更倾向于从“离已有中心很远”的样本中产生:

  1. 随机选择第一个中心;
  2. 计算每个样本到最近已有中心的平方距离 di2d_i^2
  3. 按与 di2d_i^2 成比例的概率选择下一个中心;
  4. 重复到获得 KK 个中心。

这不是最终聚类,只是为 Lloyd 循环提供更分散的起点。scikit-learn 1.9 默认 init='k-means++',其实现会对候选做多次试探;n_init='auto' 在该初始化下只运行一次。对高维、稀疏或重要任务,显式设 n_init=10 并比较不同种子通常更稳妥。

07 特征尺度如何偷偷改写“相似”?#

假设年龄范围约 20–60,而年收入以元计,范围 30 000–1 000 000。平方欧氏距离中收入差会压倒年龄差。此时模型不是“发现收入更重要”,而是被单位选择支配。

若所有连续特征都应等权,可在训练数据上标准化:

xij=xijμ^jσ^jx'_{ij}=\frac{x_{ij}-\hat\mu_j}{\hat\sigma_j}

但标准化也不是无条件正确:经纬度、周期角度、计数、类别变量和有明确业务权重的特征需要合适的距离或编码。异常值还会同时拉动标准差与簇中心;必要时比较稳健缩放、截尾或专门的异常检测。

08 用 scikit-learn 1.9 正确落地#

按照当前官方 KMeans 应用程序接口(Application Programming Interface,API),把会学习数据统计量的预处理和聚类放进同一条流水线(Pipeline):

几个 API 细节值得明确:

  • fit_predict(X) 等价于拟合后返回训练样本编号;
  • predict(X_new) 只把新样本分配给最近的既有中心,不会更新中心;
  • transform(X_new) 输出到每个中心的欧氏距离,而不是平方距离;
  • score(X) 返回 K 均值目标的相反数,因此越大越好但通常为负;
  • algorithm='elkan' 可用三角不等式减少部分距离计算,但额外需要 [N,K] 内存;是否更快取决于簇是否分离良好和实际数据表示。

若样本量很大,可评估 MiniBatchKMeans。它用小批量近似更新中心,速度更快,但最终 inertia_ 和稳定性可能略差;不能只因名称相近就假设与全量 K 均值结果完全一致。

09 K 应该怎样选?#

训练目标会随 KK 增大而单调下降:当 K=NK=N 时每个点自成一簇,J=0J=0,却通常毫无概括价值。因此不能用最小训练 inertia_ 直接选 K。

可联合使用三类证据:

  1. 肘部图(Elbow Plot):画 KK 与 WCSS,寻找继续增加中心后收益明显变缓的位置;
  2. 轮廓系数(Silhouette Coefficient):比较样本与本簇的紧密度和最近其他簇的分离度,范围约为 [1,1][-1,1]
  3. 稳定性与可用性:在重采样、时间切片和不同种子下重复聚类,检查簇大小、中心与业务解释能否稳定复现。

轮廓系数也偏好分离良好的凸簇;它不是领域真相。若业务必须得到 5 个可执行人群,而统计曲线在 3–6 都接近,应把约束、稳定性和后续效用一起写进决策。

10 怎样调试一个“能运行但分错了”的聚类?#

按数据流逐层检查:

  1. 输入:确认无非数(Not a Number,NaN)或无穷值(Infinity,Inf),重复行、单位和类别编码符合预期;
  2. 尺度:打印缩放后每列均值与标准差,定位支配距离的特征;
  3. 优化:记录多种 random_stateinertia_n_iter_ 与簇大小;
  4. 几何:在原特征和二维投影中画样本、中心和边界,但不要把二维图等同于全部高维结构;
  5. 稳定性:对重采样数据重新拟合,用调整兰德指数等置换不变指标比较分区;
  6. 外部效用:只在分群确定后,用未参与聚类的结果变量检查群体是否产生可复现差异。

最小检查代码:

counts = np.bincount(labels, minlength=model.n_clusters)
assert counts.sum() == X_train.shape[0]
assert np.all(counts > 0)
assert model.n_iter_ <= model.max_iter
print('cluster sizes:', counts)
print('inertia per sample:', model.inertia_ / X_train.shape[0])
python

11 K 均值会在哪些几何中失败?#

K 均值隐含偏好大小相近、密度相近、近似球形且可由维诺(Voronoi)边界分开的簇。

适合:两个紧凑圆团        失败:两个月牙          失败:密度悬殊

  ●●      ○○             ●●●○○○               ●●●●●      ○  ○
 ●●●    ○○○           ●●       ○○              ●●●       ○
  ●●      ○○          ●           ○              ●●

最近中心边界合理         直线切碎弯曲流形         大簇被拆、小簇被吞
text

典型失败场景包括:

  • 同心圆、月牙和细长流形;
  • 不同簇方差或样本量相差悬殊;
  • 离群点把均值中心拉远;
  • 高维空间距离集中,最近与最远差别变小;
  • 簇重叠而任务需要概率或不确定性;
  • 纯类别数据,均值本身没有意义;
  • 数据持续漂移,但部署端仍使用旧中心。

12 与相近方法的边界#

基于密度的含噪空间聚类(Density-Based Spatial Clustering of Applications with Noise,DBSCAN)通过密度连通形成簇。

方法分配方式主要几何/假设更适合什么
K 均值到最近均值的硬分配近似球形、平方欧氏距离快速基线、向量量化
高斯混合模型概率软分配椭圆高斯、可估计协方差重叠簇与不确定性
DBSCAN核心点密度连通任意形状、密度阈值噪声点与非凸簇
层次聚类逐步合并或拆分由距离与 linkage 决定需要树状层级、小中数据
K 中心点最近真实样本可配更一般距离、较抗异常中心必须可解释为样本

表中这些方法回答的不是同一道题;换算法前先说明你希望保持的结构:中心、密度、连通性、概率,还是层次。

13 今天真正需要记住什么?#

K 均值通过两步循环降低簇内平方和:给定中心时分配到最近中心,给定分配时把中心更新为均值。它快速、可扩展、容易解释,但结果依赖 KK、初始化、特征尺度和近似球形几何。一个低 inertia_ 只说明模型优化了自己的目标,不代表发现了真实类别。

14 思考题与小练习#

  1. 对点 0,2,3,100,2,3,10 做一维 K=2K=2 聚类,初始中心为 0022。手算每轮编号、中心和 WCSS,观察是否得到直觉中的分组。
  2. 若把四点例子中的第二维整体乘以 100,分配会不会改变?构造一个确实改变的六点数据集,并解释单位为何等价于特征权重。
  3. 在同一数据上用 20 个种子运行 KMeans(n_init=1),记录最优与最差 inertia_、簇大小和轮廓系数;再与 n_init=20 比较。

相关工作#

15 下一篇预告#

K 均值直接在原始特征空间计算距离;当几十个传感器高度相关或图像像素维度巨大时,冗余方向会增加计算并掩盖结构。下一篇将进入主成分分析:怎样在尽量保留方差的前提下,把高维样本投影到少数正交方向,并从重构误差看清“保留信息”到底是什么意思。

没有标签怎样自动分组?K 均值的分配—更新循环与失败几何
https://zwjcode.cn/blog/kmeans-assignment-update-failure-geometry
作者
发布于 2026年8月26日
版权协议 CC BY-NC-SA 4.0
评论加载似乎遇到了问题,请尝试刷新页面。