AI 工程基础体系 · 第 49/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。

聚类算法:K-Means、层次、DBSCAN、GMM 与聚类评估

聚类(clustering)是在没有预先提供类别标签的情况下,根据样本之间的相似性,把样本划分为若干组。设数据集为

X={x1,x2,,xn},xiRdX=\{x_1,x_2,\ldots,x_n\},\quad x_i\in\mathbb{R}^d

聚类算法试图发现一个结构,使同一组中的样本更相似,不同组中的样本差异更大。

“相似”不是数据固有的属性,而是由特征表示和距离或概率模型共同定义的。例如:

  • 对金额、年龄等连续变量,欧氏距离可能有意义;
  • 对文本向量,余弦相似度通常比原始欧氏距离更符合语义;
  • 对类别变量,直接把字符串编码为整数后计算欧氏距离通常是错误的;
  • 对尺度差异很大的特征,未标准化的距离会被大数值特征主导。

因此,聚类问题通常不是“运行哪个算法”这么简单,而是:

原始数据特征表示相似性定义聚类模型结果评估与解释\text{原始数据} \rightarrow \text{特征表示} \rightarrow \text{相似性定义} \rightarrow \text{聚类模型} \rightarrow \text{结果评估与解释}

聚类没有统一适用的正确答案。不同算法对应不同的几何假设、密度假设或概率假设。


一、聚类之前:表示、距离与问题定义

1. 特征尺度会改变聚类结果

考虑两个用户:

用户 A:年龄  20,月消费 1000
用户 B:年龄  40,月消费 1000
用户 C:年龄  20,月消费 3000

如果使用欧氏距离:

d(A,B)=(2040)2+(10001000)2=20d(A,B)=\sqrt{(20-40)^2+(1000-1000)^2}=20

d(A,C)=(2020)2+(10003000)2=2000d(A,C)=\sqrt{(20-20)^2+(1000-3000)^2}=2000

算法会认为消费差异远大于年龄差异。若业务上年龄和消费同等重要,就应先进行标准化:

zij=xijμjσjz_{ij}=\frac{x_{ij}-\mu_j}{\sigma_j}

其中,μj\mu_jσj\sigma_j 分别是第 jj 个特征在训练数据上的均值和标准差。

标准化之后,每个特征大致处于相近尺度,但这不表示标准化永远正确:

  • 金融金额的极端值可能需要对数变换;
  • 稀疏文本向量常使用 TF-IDF 后的归一化;
  • 具有明确业务权重的变量不应机械地等权处理;
  • 对树模型产生的距离或混合类型数据,标准化方法需要重新设计。

2. 距离度量决定“相似”的含义

常见距离包括:

欧氏距离:

d(x,y)=j=1d(xjyj)2d(x,y)=\sqrt{\sum_{j=1}^{d}(x_j-y_j)^2}

曼哈顿距离:

d(x,y)=j=1dxjyjd(x,y)=\sum_{j=1}^{d}|x_j-y_j|

余弦相似度:

cos(x,y)=xyx2y2\operatorname{cos}(x,y)= \frac{x^\top y}{\|x\|_2\|y\|_2}

余弦距离通常写作:

dcos(x,y)=1cos(x,y)d_{\text{cos}}(x,y)=1-\operatorname{cos}(x,y)

如果两个文本嵌入的方向相近,但向量长度不同,余弦相似度可能更符合语义相似性。对已经做了单位归一化的向量:

x2=y2=1\|x\|_2=\|y\|_2=1

有:

xy22=22xy\|x-y\|_2^2=2-2x^\top y

所以在单位球面上,最大化余弦相似度与最小化欧氏距离是等价的。否则,直接用 K-Means 处理原始嵌入,优化的仍然是欧氏平方距离,而不是余弦距离。

3. 聚类结果需要一个可验证的问题

“把用户分成五组”不是完整需求。至少还需要明确:

  1. 每个样本只能属于一个簇,还是允许属于多个簇?
  2. 是否需要识别噪声和离群点?
  3. 簇是否应该是近似球形?
  4. 是否必须知道簇的数量?
  5. 聚类用于探索、推荐、压缩、异常检测还是下游决策?
  6. 下游系统是否会把簇标签作为高风险决策依据?

例如,客户分群用于探索分析时,可以接受较粗的聚类;若用于自动授信,则不能把无监督聚类结果直接当成风险标签,因为聚类目标并未保证与违约风险相关,也未保证公平性或稳定性。


二、K-Means:最小化簇内平方误差

1. 目标函数

K-Means 假设数据可以被划分为 KK 个簇,每个簇由一个中心点表示。

令:

  • ci{1,,K}c_i\in\{1,\ldots,K\} 表示样本 xix_i 所属的簇;
  • μk\mu_k 表示第 kk 个簇的中心;
  • Ck={i:ci=k}C_k=\{i:c_i=k\} 表示分配到第 kk 个簇的样本索引集合。

K-Means 的目标是最小化簇内平方误差:

J(c,μ)=i=1nxiμci22J(c,\mu)=\sum_{i=1}^{n}\|x_i-\mu_{c_i}\|_2^2

这个目标隐含了几个重要假设:

  • 使用欧氏距离;
  • 一个样本最终只属于一个簇;
  • 簇大致可以用中心点代表;
  • 簇内方差较小;
  • 不同簇的尺度和形状不能过于极端。

2. Lloyd 算法的两个交替步骤

K-Means 通常使用 Lloyd 算法。它不是一次性求出全局最优解,而是交替执行两个步骤。

第一步:分配样本

固定中心 μ1,,μK\mu_1,\ldots,\mu_K,为每个样本选择最近的中心:

ci=argminkxiμk22c_i=\arg\min_{k}\|x_i-\mu_k\|_2^2

第二步:更新中心

固定样本分配,重新计算每个簇的均值:

μk=1CkiCkxi\mu_k=\frac{1}{|C_k|}\sum_{i\in C_k}x_i

为什么均值是最优中心?对某个簇 CkC_k,需要最小化:

f(μ)=iCkxiμ22f(\mu)=\sum_{i\in C_k}\|x_i-\mu\|_2^2

μ\mu 求导:

f(μ)=2iCk(μxi)\nabla f(\mu)=2\sum_{i\in C_k}(\mu-x_i)

令导数为零:

CkμiCkxi=0|C_k|\mu-\sum_{i\in C_k}x_i=0

因此:

μ=1CkiCkxi\mu=\frac{1}{|C_k|}\sum_{i\in C_k}x_i

分配步骤不会增加目标函数,更新步骤也不会增加目标函数,所以目标函数在迭代过程中单调不增。由于可能的划分和目标值有限或有下界,算法通常会收敛到一个局部最优或固定点,但不保证是全局最优。

3. 一维完整算例

数据为:

X={1,2,8,9},K=2X=\{1,2,8,9\},\quad K=2

假设初始中心为:

μ1=1,μ2=9\mu_1=1,\quad \mu_2=9

初始分配:

样本 μ1=1\mu_1=1 的距离 μ2=9\mu_2=9 的距离 分配
1 0 8 1
2 1 7 1
8 7 1 2
9 8 0 2

更新中心:

μ1=1+22=1.5\mu_1=\frac{1+2}{2}=1.5

μ2=8+92=8.5\mu_2=\frac{8+9}{2}=8.5

再次分配时结果不变,因此收敛。最终目标函数为:

J=(11.5)2+(21.5)2+(88.5)2+(98.5)2=1J=(1-1.5)^2+(2-1.5)^2+(8-8.5)^2+(9-8.5)^2=1

如果初始中心都位于左侧,例如:

μ1=1,μ2=2\mu_1=1,\quad \mu_2=2

第一次分配可能得到:

  • 11 分到簇 1;
  • 2,8,92,8,9 分到簇 2。

更新后中心为:

μ1=1,μ2=2+8+93=193\mu_1=1,\quad \mu_2=\frac{2+8+9}{3}=\frac{19}{3}

之后可能得到另一个局部结果。这个例子说明:K-Means 的结果依赖初始化。

实际实现通常使用 K-Means++ 初始化。它不是随机均匀选择所有中心,而是让后续中心更倾向于远离已选中心,以降低得到糟糕初始划分的概率。它改善的是初始化效果,不是把 Lloyd 算法变成全局最优算法。

4. K-Means 的失败形状

K-Means 的决策边界由距离中心的比较产生。在使用欧氏距离时,两个中心之间的边界是一个超平面,因此它更适合近似凸形、球状或椭球状的簇。

两个典型反例:

月牙形数据

两个交错的月牙簇具有非凸形状。K-Means 会把空间按直线边界切开,而不是沿月牙形状识别簇。

相同方向但不同密度的簇

如果一个簇很紧密,另一个簇很分散,K-Means 只关注平方误差,可能把一个真实簇拆开,或者把不同簇合并。

K-Means 还对离群点敏感,因为平方距离会放大远处样本的影响。一个极端值可能显著拉动中心。


三、层次聚类:从局部合并构造树

层次聚类(hierarchical clustering)通过逐步合并或拆分样本,构造出一个层次结构,而不是只输出一个固定的 KK 个簇。

常见的是自底向上的凝聚层次聚类(agglomerative clustering):

  1. 初始时每个样本都是一个簇;
  2. 计算簇与簇之间的距离;
  3. 合并距离最近的两个簇;
  4. 重复直到只剩一个簇;
  5. 在某个层级切断树,得到最终分组。

这个过程可以表示为树状图(dendrogram)。树上的高度表示合并时的距离或代价。切断高度不同,会得到不同数量的簇。

1. 链接准则

层次聚类的关键不是只有“合并”,还包括如何定义两个簇之间的距离。

AABB 是两个簇。

Single linkage

Dsingle(A,B)=minxA,yBd(x,y)D_{\text{single}}(A,B)=\min_{x\in A,y\in B}d(x,y)

只要两个簇各有两个样本相互接近,就可能合并。它可以发现细长结构,但容易产生“链式效应”:一串中间点可能把本来相距较远的簇连接起来。

Complete linkage

Dcomplete(A,B)=maxxA,yBd(x,y)D_{\text{complete}}(A,B)=\max_{x\in A,y\in B}d(x,y)

它更关注最远的样本,通常得到较紧凑的簇,但对离群点较敏感。

Average linkage

Daverage(A,B)=1ABxA,yBd(x,y)D_{\text{average}}(A,B)= \frac{1}{|A||B|} \sum_{x\in A,y\in B}d(x,y)

它在 single 和 complete 之间折中。

Ward linkage

Ward 方法不是简单地比较样本间距离,而是选择使簇内平方误差增加最少的合并:

Δ(A,B)=SSE(AB)SSE(A)SSE(B)\Delta(A,B)= \operatorname{SSE}(A\cup B) -\operatorname{SSE}(A) -\operatorname{SSE}(B)

其中:

SSE(A)=xAxxˉA22\operatorname{SSE}(A)=\sum_{x\in A}\|x-\bar{x}_A\|_2^2

xˉA\bar{x}_A 是簇 AA 的均值。Ward linkage 与 K-Means 的目标有相似之处,但层次过程一旦合并通常不能撤销,而 K-Means 可以在迭代中重新分配样本。

在 scikit-learn 中,linkage="ward" 要求使用欧氏距离;其他 linkage 对距离度量的支持不同,应以所使用版本的 API 约束为准。

2. 层次聚类的优缺点

层次聚类的一个优点是可以先构造完整层次,再从不同高度观察结果。对于样本量较小、需要解释层次关系的探索任务,这比直接指定一个 KK 更有信息。

缺点包括:

  • 传统实现需要保存或计算大量样本间关系;
  • 早期错误合并会影响后续所有层级;
  • 凝聚过程通常不可撤销;
  • 结果对距离和 linkage 非常敏感;
  • 大数据集上的时间和内存成本可能显著。

它并非天然比 K-Means “更准确”。如果真实结构是球状簇,Ward 层次聚类和 K-Means 可能都有效;如果真实结构是噪声中的高密度区域,DBSCAN 更合适。


四、DBSCAN:用密度识别簇与噪声

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)不要求预先指定簇的数量,而是按照局部密度寻找簇,并显式标记噪声。

它需要两个主要参数:

  • ε\varepsiloneps):邻域半径;
  • min_samples:一个点的 ε\varepsilon-邻域至少包含多少个样本,才被认为足够稠密。

定义点 ppε\varepsilon-邻域:

Nε(p)={qd(p,q)ε}N_\varepsilon(p)= \{q\mid d(p,q)\le \varepsilon\}

若:

Nε(p)min_samples|N_\varepsilon(p)|\ge \text{min\_samples}

pp 是核心点(core point)。

1. 核心点、边界点与噪声点

  • 核心点:自身邻域足够密集;
  • 边界点:自身邻域不够密集,但位于某个核心点的邻域中;
  • 噪声点:既不是核心点,也不在任何核心点邻域中。

一个重要细节是:min_samples 通常包含点自身。具体实现对样本权重和边界条件的处理需要查看 API,但不能把它简单理解为“必须有这么多个其他点”。

DBSCAN 通过“密度可达”连接核心点:

  1. 核心点 pp 与核心点 qq 的邻域相连;
  2. 这种连接可以沿多个核心点传播;
  3. 与核心点相邻但自身不是核心点的样本成为边界点;
  4. 远离所有核心区域的样本被标记为噪声,通常在 scikit-learn 中表示为 -1

2. 一个二维小例子

设:

A=(0,0)
B=(0.1,0)
C=(0,0.1)
D=(3,3)
E=(3.1,3)
F=(10,10)

取:

eps = 0.15
min_samples = 3

若计入点自身:

  • A、B、C 互相都在半径 0.15 内,因此各自邻域至少有 3 个点,它们是核心点;
  • D、E 只有两个点,可能不是核心点;
  • F 远离其他点,是噪声;
  • D、E 即使不是核心点,也可能因为靠近某个核心点而成为边界点;在此例中它们距离 A、B、C 太远,因此仍可能是噪声。

DBSCAN 的簇不是由中心定义,而是由相互连接的高密度区域定义,因此能够识别弯曲、环形或不规则形状。

3. DBSCAN 的边界与参数问题

DBSCAN 的主要困难是 eps。它同时控制:

  • 什么距离算邻近;
  • 密度传播可以跨多大间隙;
  • 两个区域是否会被连接。

eps 太小会导致:

  • 大量样本成为噪声;
  • 一个真实簇被拆成多个小簇。

eps 太大会导致:

  • 不同簇通过稀疏桥接点合并;
  • 噪声被吸收到某些簇中;
  • 结果退化为少数大簇。

min_samples 越大,对密度的要求越高,通常会产生更多噪声;但它也受数据维度和采样密度影响。

DBSCAN 还隐含“不同簇具有相近密度”的假设。若一个簇非常稠密,另一个簇非常稀疏,单一的 eps 很难同时适配两者。这时可以考虑 OPTICS、HDBSCAN 等密度层次方法,但它们的 API、版本支持和参数语义需要单独确认,不能直接把 DBSCAN 参数照搬过去。

4. 顺序与标签的细节

DBSCAN 的簇标签数值没有语义。例如标签 0 不表示“最重要的簇”,也不表示中心最小的簇。

在边界点同时接触多个簇时,具体归属可能受遍历顺序或实现细节影响。因此,应重点关注密度结构和稳定性,而不是把每个边界点的标签当作绝对事实。


五、GMM:用概率分布表示软聚类

高斯混合模型(Gaussian Mixture Model,GMM)假设数据由多个高斯分布混合生成。

其概率密度为:

p(x)=k=1KπkN(xμk,Σk)p(x)=\sum_{k=1}^{K}\pi_k\mathcal{N}(x\mid\mu_k,\Sigma_k)

其中:

  • KK:高斯成分数量;
  • πk\pi_k:第 kk 个成分的混合权重,满足 πk0\pi_k\ge 0kπk=1\sum_k\pi_k=1
  • μk\mu_k:均值向量;
  • Σk\Sigma_k:协方差矩阵;
  • N(xμk,Σk)\mathcal{N}(x\mid\mu_k,\Sigma_k):均值为 μk\mu_k、协方差为 Σk\Sigma_k 的高斯密度。

与 K-Means 的硬分配不同,GMM 为每个样本计算属于各个成分的后验概率:

γik=P(zi=kxi)=πkN(xiμk,Σk)j=1KπjN(xiμj,Σj)\gamma_{ik} = P(z_i=k\mid x_i) = \frac{ \pi_k\mathcal{N}(x_i\mid\mu_k,\Sigma_k) }{ \sum_{j=1}^{K} \pi_j\mathcal{N}(x_i\mid\mu_j,\Sigma_j) }

γik\gamma_{ik} 称为责任度(responsibility)。它可以表达不确定性,例如:

样本 x:
簇 1:0.55
簇 2:0.43
簇 3:0.02

相比直接输出“属于簇 1”,这个结果还说明样本处在簇 1 和簇 2 的交界处。

1. EM 算法

GMM 通常使用 EM(Expectation-Maximization)算法估计参数。

E 步:计算责任度

使用当前的 πk,μk,Σk\pi_k,\mu_k,\Sigma_k,计算每个样本属于各成分的后验概率:

γik=πkN(xiμk,Σk)j=1KπjN(xiμj,Σj)\gamma_{ik} = \frac{ \pi_k\mathcal{N}(x_i\mid\mu_k,\Sigma_k) }{ \sum_{j=1}^{K} \pi_j\mathcal{N}(x_i\mid\mu_j,\Sigma_j) }

M 步:根据软分配重新估计参数

定义:

Nk=i=1nγikN_k=\sum_{i=1}^{n}\gamma_{ik}

更新混合权重:

πk=Nkn\pi_k=\frac{N_k}{n}

更新均值:

μk=1Nki=1nγikxi\mu_k= \frac{1}{N_k}\sum_{i=1}^{n}\gamma_{ik}x_i

更新协方差:

Σk=1Nki=1nγik(xiμk)(xiμk)\Sigma_k= \frac{1}{N_k} \sum_{i=1}^{n} \gamma_{ik} (x_i-\mu_k)(x_i-\mu_k)^\top

E 步用当前参数推断隐变量,M 步用推断结果更新参数。两步交替执行,直到对数似然变化足够小或达到最大迭代次数。

GMM 最大化的是对数似然:

logL=i=1nlog(k=1KπkN(xiμk,Σk))\log L= \sum_{i=1}^{n} \log\left( \sum_{k=1}^{K} \pi_k\mathcal{N}(x_i\mid\mu_k,\Sigma_k) \right)

它和 K-Means 的目标函数不同,因此二者可能产生不同分组。

2. GMM 与 K-Means 的关系

当满足以下近似条件时,GMM 的硬分类结果与 K-Means 可能相似:

  • 每个高斯成分的协方差相同;
  • 协方差近似为 σ2I\sigma^2I,即各方向方差相同;
  • 混合权重相近;
  • 每个样本只选择后验概率最大的成分。

此时,比较高斯密度近似等价于比较样本到均值的欧氏距离。

但如果协方差不同,GMM 可以表示不同方向、不同尺度的椭圆簇。例如,两个簇中心距离不远,但一个沿横轴拉长、另一个沿纵轴拉长,K-Means 的球形距离假设可能不合适,而 GMM 可以用协方差表示这种形状。

3. GMM 的失败表现

GMM 的概率模型也有明确边界:

  • 它假设数据可以由若干高斯成分解释;
  • 对非高斯、环形或复杂拓扑结构可能不适合;
  • 协方差矩阵可能接近奇异,导致数值不稳定;
  • EM 只能保证在迭代中不降低当前目标的局部结果,不保证全局最优;
  • 成分数 KK 仍然需要选择。

常见实现会使用 reg_covar 对协方差矩阵添加小的对角正则项,避免矩阵不可逆。这个参数不是消除建模错误的办法;如果数据表示、维度或成分数根本不合理,增大正则项只会改变模型形状。


六、四类算法的假设对比

算法 主要假设 是否需要簇数 是否能识别噪声 分配方式 适合的结构
K-Means 簇近似球状、欧氏距离合理 硬分配 大规模、紧凑簇
层次聚类 合并关系可由 linkage 表示 最终切分时通常需要 通常否 硬分配 小中规模、层次探索
DBSCAN 簇是密度连通区域 硬分配 任意形状、含噪数据
GMM 数据来自多个高斯成分 否,除非另行建模 软分配或硬化 椭圆簇、需要概率归属

“是否需要簇数”还要谨慎理解。层次聚类可以先生成树,但如果要得到一个固定标签向量,仍然要选择切分高度或簇数。GMM 可以用 BIC 或 AIC 辅助选择成分数,但这不是业务意义上的自动发现。


七、聚类评估:内部指标、外部指标与稳定性

聚类评估不能只看一个分数。因为无监督任务通常没有唯一真值,指标反映的是不同性质。

1. 轮廓系数

对样本 ii

  • a(i)a(i):它与同簇其他样本的平均距离;
  • b(i)b(i):它与其他簇中样本的平均距离的最小值。

轮廓系数为:

s(i)=b(i)a(i)max(a(i),b(i))s(i)= \frac{b(i)-a(i)} {\max(a(i),b(i))}

其取值范围为 [1,1][-1,1]

  • 接近 1:样本与本簇接近,与其他簇较远;
  • 接近 0:样本位于簇边界;
  • 小于 0:样本可能更接近其他簇。

整体轮廓系数通常是所有样本 s(i)s(i) 的平均值。

它适合评估基于距离的分离性,但并不等于“业务上正确”。对于月牙形数据,即使聚类符合真实结构,轮廓系数也可能不高,因为该指标更偏好紧凑、分离的簇。

2. Calinski-Harabasz 指数

Calinski-Harabasz(CH)指数比较簇间离散度和簇内离散度:

CH=Tr(Bk)/(k1)Tr(Wk)/(nk)\operatorname{CH} = \frac{\operatorname{Tr}(B_k)/(k-1)} {\operatorname{Tr}(W_k)/(n-k)}

其中:

  • BkB_k:簇间散布矩阵;
  • WkW_k:簇内散布矩阵;
  • nn:样本数量;
  • kk:簇数量;
  • Tr\operatorname{Tr}:矩阵迹。

通常 CH 越大,表示簇间更分离、簇内更紧凑。但它可能偏好某些簇数,不能脱离候选范围和数据结构解释。

3. Davies-Bouldin 指数

Davies-Bouldin(DB)指数衡量每个簇与最相似其他簇之间的相似程度。典型形式为:

DB=1Ki=1KmaxjiSi+SjMij\operatorname{DB} = \frac{1}{K} \sum_{i=1}^{K} \max_{j\ne i} \frac{S_i+S_j}{M_{ij}}

其中:

  • SiS_i:簇 ii 的簇内散度;
  • MijM_{ij}:簇 ii 与簇 jj 中心之间的距离。

DB 越小通常越好。它同样偏好紧凑且分离的几何结构,对非凸簇的解释能力有限。

4. 外部指标:有参考标签时使用

如果有真实标签 yiy_i,可以使用外部指标,但要明确标签的来源和含义。

Adjusted Rand Index

ARI 比较预测分组和真实分组在样本对上的一致性,并校正随机一致性:

  • 1 表示完全一致;
  • 0 大致相当于随机分组;
  • 也可能出现负值。

ARI 不要求簇标签编号一致。例如预测标签 [1,1,0,0] 和真实标签 [0,0,1,1] 的分组完全相同,ARI 仍然为 1。

NMI

归一化互信息(NMI)衡量预测簇标签与真实标签之间共享的信息量。它适合比较分组信息,但不同归一化方式和实现细节会影响数值,跨工具比较时需要确认定义。

外部指标不能替代公平性、业务效果或因果验证。一个聚类可能高度复现历史标签,但历史标签本身可能带有采样偏差或人为规则。

5. 评估噪声点与边界点

DBSCAN 会输出 -1 噪声标签。计算内部指标时如果直接把噪声视为普通簇,结果可能失真;如果删除噪声,又可能人为提高分数。

更透明的报告应同时给出:

  • 噪声比例;
  • 非噪声样本上的聚类指标;
  • 包含噪声的业务覆盖率;
  • 对边界点或低置信度点的单独分析。

GMM 的软分配还可以报告熵:

Hi=k=1KγiklogγikH_i=-\sum_{k=1}^{K}\gamma_{ik}\log\gamma_{ik}

熵越高,表示样本在多个成分之间越不确定。不能只看 argmax 后的硬标签而丢失这部分信息。

6. 稳定性比单次分数更重要

如果换一个随机种子、抽取一部分样本或改变少量参数,聚类结果就完全改变,那么它不适合作为稳定的生产分组。

可以采用以下方法检查稳定性:

  1. 对数据进行多次重采样;
  2. 对 K-Means 或 GMM 使用多个随机初始化;
  3. 比较不同结果之间的 ARI 或 NMI;
  4. 观察每个样本被分到同一簇的频率;
  5. 检查簇大小、中心和业务统计量是否稳定。

稳定性不等于正确性,但不稳定通常意味着数据证据不足、参数处于临界区,或者算法假设不适合数据。


八、一个可运行的 scikit-learn 对比示例

下面的示例构造三个近似高斯簇,并比较 K-Means、层次聚类、DBSCAN 和 GMM。它使用真实生成标签计算 ARI,同时计算内部指标。

import numpy as np

from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import (
    KMeans,
    AgglomerativeClustering,
    DBSCAN,
)
from sklearn.mixture import GaussianMixture
from sklearn.metrics import (
    silhouette_score,
    calinski_harabasz_score,
    davies_bouldin_score,
    adjusted_rand_score,
)

# 1. 生成示例数据
X, y_true = make_blobs(
    n_samples=600,
    centers=[(-4, -2), (0, 4), (4, -1)],
    cluster_std=[0.8, 1.0, 0.7],
    random_state=42,
)

# 2. 标准化:这里两个特征本来尺度接近,但生产数据通常不能假设如此
X_scaled = StandardScaler().fit_transform(X)

models = {
    "kmeans": KMeans(
        n_clusters=3,
        init="k-means++",
        n_init=20,
        random_state=42,
    ),
    "hierarchical": AgglomerativeClustering(
        n_clusters=3,
        linkage="ward",
    ),
    "dbscan": DBSCAN(
        eps=0.25,
        min_samples=8,
    ),
    "gmm": GaussianMixture(
        n_components=3,
        covariance_type="full",
        n_init=10,
        reg_covar=1e-6,
        random_state=42,
    ),
}

for name, model in models.items():
    if name == "gmm":
        model.fit(X_scaled)
        labels = model.predict(X_scaled)
        probabilities = model.predict_proba(X_scaled)
        max_probability = probabilities.max(axis=1).mean()
        extra = f", 平均最大后验概率={max_probability:.3f}"
    else:
        labels = model.fit_predict(X_scaled)
        extra = ""

    unique_labels = set(labels)
    noise_mask = labels == -1
    non_noise_mask = ~noise_mask
    n_clusters = len(unique_labels - {-1})
    noise_ratio = noise_mask.mean()

    print(f"\n{name}")
    print(f"簇数={n_clusters}, 噪声比例={noise_ratio:.3f}{extra}")
    print(f"ARI={adjusted_rand_score(y_true, labels):.3f}")

    # 轮廓系数等内部指标要求至少有两个簇
    # 对 DBSCAN,先明确只在非噪声样本上计算,并同时报告噪声比例
    if n_clusters >= 2 and non_noise_mask.sum() > n_clusters:
        X_eval = X_scaled[non_noise_mask]
        labels_eval = labels[non_noise_mask]

        print(f"Silhouette={silhouette_score(X_eval, labels_eval):.3f}")
        print(f"Calinski-Harabasz={calinski_harabasz_score(X_eval, labels_eval):.3f}")
        print(f"Davies-Bouldin={davies_bouldin_score(X_eval, labels_eval):.3f}")
    else:
        print("非噪声样本不足以计算这些内部指标")

代码中的关键点

StandardScaler 必须只在训练数据上拟合:

scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

如果把训练集和未来数据一起计算均值、标准差,就会把未来分布信息泄露到预处理步骤中。聚类虽然没有监督标签,但同样存在数据泄露问题。

n_init 表示使用多少组初始化重复运行 K-Means 或 GMM。不同 scikit-learn 版本对默认值和某些参数行为可能存在变化,因此生产代码应显式写出关键参数,并记录库版本。

DBSCAN 的 eps=0.25 不是通用值。它只对当前数据尺度有效。若省略标准化或换一批数据,该值的含义会改变。实际选择时可以观察第 kk 近邻距离曲线,并结合领域距离阈值验证,而不是只从指标上搜索。

这段代码中的 ARI 使用了人为生成的 y_true。真实无监督生产任务通常没有这样的真值,因此 ARI 只能用于这个演示,不能因为没有真实标签就伪造一个“标准答案”。


九、模型选择不能只看“最高分”

1. K-Means 的肘部法

K-Means 的簇内平方误差:

inertia(K)=i=1nxiμci22\operatorname{inertia}(K) = \sum_{i=1}^{n} \|x_i-\mu_{c_i}\|_2^2

随着 KK 增大,inertia 必然不会增加,因为更多中心可以更好地拟合数据。选择 KK 时观察下降曲线的“肘部”只能提供启发式依据,不能证明该 KK 是真实簇数。

当每个样本都单独成为一个簇时,inertia 可以降到 0,所以不能单独追求最小 inertia。

2. GMM 的 BIC 与 AIC

GMM 可以使用:

AIC=2p2logL\operatorname{AIC}=2p-2\log L

BIC=plogn2logL\operatorname{BIC}=p\log n-2\log L

其中:

  • pp:模型参数数量;
  • LL:最大似然;
  • nn:样本数。

两者都在拟合优度和模型复杂度之间权衡,通常越小越好。BIC 对复杂模型惩罚更强,常用于辅助选择成分数。

但 BIC 选择的是统计意义上的模型解释,不一定等于业务上希望的分群数。比如业务需要把客户分为三个可运营群体,而 BIC 可能偏好五个统计成分。

3. 先确定几何假设,再比较指标

正确的流程不是:

尝试所有算法 → 找到分数最高者 → 宣布最优

更合理的流程是:

理解数据和用途
→ 选择候选距离或概率假设
→ 设定合理参数范围
→ 比较内部指标、稳定性和业务解释
→ 在下游任务中验证价值与风险

如果文本嵌入的聚类目标是检索组织,应重点验证主题纯度、跨批次稳定性和人工可解释性;如果目标是异常发现,则噪声覆盖率、误报率和人工审核成本比轮廓系数更重要。


十、深度学习与生成式 AI 中的聚类

在深度学习和生成式 AI 系统中,聚类通常作用于嵌入(embedding),而不是直接作用于原始图像、文本或长对话。

典型数据流是:

原始文档/图像/对话
        ↓
模型生成向量嵌入
        ↓
归一化、降维或去噪
        ↓
聚类
        ↓
主题发现、数据去重、路由、评测分层或缓存

1. 嵌入空间不是天然可聚类空间

嵌入模型的训练目标决定了空间中的几何关系。一个为语义检索训练的模型,可能适合按主题聚类;一个为分类训练的模型,可能更强调类别边界;一个受强烈领域偏移影响的模型,可能把格式、语言或来源聚在一起,而不是把业务主题聚在一起。

因此需要检查:

  • 向量是否已归一化;
  • 使用余弦还是欧氏距离;
  • 向量维度是否过高导致距离集中;
  • 是否存在来源、语言、模板等非目标因素;
  • 聚类是否只是复现数据管道中的元信息。

降维可以帮助可视化,但不能把二维可视化结果直接当作高维真实结构。t-SNE 等方法尤其可能改变全局距离关系;它适合探索展示,不应无条件作为聚类输入。

2. 聚类用于检索和生成系统时的风险

聚类可用于:

  • 发现文档主题;
  • 为检索系统建立分片或路由;
  • 对重复内容进行归并;
  • 按主题抽样评估生成质量;
  • 发现数据集中缺失或异常的领域。

但聚类标签不能自动成为访问控制标签。若文档嵌入来自不同权限域,必须先在数据层执行权限过滤,再做可见范围内的聚类和检索。否则,簇中心、代表样本、摘要或评估报告都可能间接泄露受限内容。

对生成式 AI 的评估,也不能只报告“每个簇的平均分”。一个大簇可能掩盖少数关键场景的严重失败。应按簇报告样本量、置信区间或误差范围,并保证测试集、提示词模板和人工标注过程没有把答案泄露给模型。

3. 成本与增量更新

不同算法的成本结构不同:

  • K-Means 适合批量迭代,可使用小批量变体处理更大数据;
  • 层次聚类需要保留合并关系,大规模数据上的内存和时间压力较大;
  • DBSCAN 依赖近邻查询,数据维度高或距离计算昂贵时成本会上升;
  • GMM 需要计算协方差和概率密度,高维全协方差模型的参数量可能很大。

如果每天新增少量嵌入,不应默认每天对全部历史数据重新做昂贵聚类。可以根据用途选择批量重训、增量近似、固定中心分配或只对新数据局部处理。但增量结果与全量重训可能不同,必须通过稳定性和业务指标验证。


十一、生产化时的状态、失败路径与结果治理

一个可上线的聚类任务至少包含以下状态:

数据快照已确认
    ↓
特征与预处理已拟合
    ↓
模型训练完成
    ↓
内部指标与稳定性检查通过
    ↓
人工/业务解释完成
    ↓
模型与标签映射发布
    ↓
线上分配与监控

每个状态都应保存可复现信息:

  • 数据快照或分区版本;
  • 特征字段和缺失值处理规则;
  • 标准化器或其他预处理对象;
  • 距离度量;
  • 算法、参数和随机种子;
  • 依赖库版本;
  • 模型文件和评估报告;
  • 权限范围与数据保留策略。

1. 聚类标签不具有跨版本稳定语义

本次训练的:

簇 0:高频、小额用户
簇 1:低频、大额用户

下一次训练可能变成:

簇 0:低频、大额用户
簇 1:高频、小额用户

标签编号只是实现细节。上线系统若直接把整数标签写入业务规则,就可能在重训后发生静默错误。

应根据簇中心、分布统计或与旧簇的重叠程度建立新旧簇映射,并在映射不稳定时阻止自动发布。对 GMM,还应记录成分参数和后验分布,而不是只保存最大概率标签。

2. 常见故障及诊断

所有样本被分到一个簇

可能原因:

  • eps 太大;
  • K-Means 的特征未缩放;
  • GMM 成分数或协方差设置不合理;
  • 数据本身缺乏可分结构。

诊断方法:

  • 查看特征分布和距离分位数;
  • 检查每个簇的样本量;
  • 画出近邻距离曲线;
  • 比较不同标准化和参数范围;
  • 查看降维图,但不把图作为唯一证据。

DBSCAN 几乎全部是噪声

可能原因:

  • eps 太小;
  • min_samples 太大;
  • 高维空间距离集中;
  • 真实数据密度不均匀。

应先确认距离矩阵和特征尺度,再调整参数。盲目增大 eps 可能把多个簇连接起来。

GMM 出现数值错误或协方差异常

可能原因:

  • 某些特征近似常数;
  • 维度远高于有效样本量;
  • 成分数过大;
  • 数据中存在重复点或极端离群点。

可检查特征秩、删除无信息特征、降低维度、调整协方差类型或使用协方差正则。但这些措施应记录并重新评估,不能只为了让训练不报错。

指标很好但业务无效

内部指标偏好几何上紧凑的分组,而业务可能关心收入、留存、投诉或风险等结果。解决方法不是继续堆指标,而是检查:

  • 簇之间是否存在可解释的业务差异;
  • 聚类是否提升了下游任务;
  • 结果是否只复现了数据来源或时间批次;
  • 是否对少数群体产生系统性误判;
  • 线上分布变化后簇是否仍然稳定。

聚类最终应被视为一个需要验证的模型组件,而不是天然可信的客观分类器。


系列导航与关联阅读

官方资料

本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。