Skip to content

聚类与降维

本页速览 没有标签,机器学习还能做什么?本文深入拆解 K-Means(EM 视角、K 的选择)、DBSCAN 与层次聚类、内部/外部评估困境、PCA 与 t-SNE 的原理与陷阱,并给出 sklearn 实战案例与真实业务落地方式。

聚类与降维 ​

一句话定位:聚类与降维是"没有标准答案"的两类无监督方法——聚类回答"数据分成几组、每组是什么",降维回答"高维数据如何压缩到低维而不丢主要结构"。它们是数据探索阶段最常用的两把扳手:聚类把样本分组,降维把特征压缩,两者常配合使用(先降维再聚类,或降维后可视化聚类结果)。

比起监督学习,聚类的直觉人人都有——"把相似的放一起"谁都会说;但真正做到专业,你会撞上一堵墙:聚类没有客观的"正确答案"。分类有准确率,回归有 MSE,而聚类只能靠"看起来合不合理"和一堆各有缺陷的指标。本文的目标是让你把 K-Means 背后的数学、K 的选择、DBSCAN 的密度直觉、PCA 的特征分解、t-SNE 的随机游走全部吃透,并且知道在真实业务里它们各自能干什么、不能干什么。概念总览见无监督学习,本文是它的深度实战篇。

一、聚类:任务定义与形式化 ​

聚类(clustering)要解决的任务是:

给定无标签样本集 X = {x₁, x₂, …, xₙ}(每个 xᵢ ∈ ℝᵈ 是一个 d 维特征向量),将样本划分为 k 个互不相交的簇 C₁, C₂, …, Cₖ,使得簇内相似度最大、簇间相似度最小。

形式化地,聚类求解的是样本集到簇标签的映射:

聚类问题的输入与输出
┌─────────────────────────────────────────────────────┐
│ 输入: X = {x₁, ..., xₙ}, 每个 xᵢ ∈ ℝᵈ (无标签)        │
│ 输出: 簇划分 {C₁,...,Cₖ} 与样本标签 yᵢ ∈ {1..k}        │
│ 目标: 簇内紧凑(within-cluster) + 簇间分离(between)     │
└─────────────────────────────────────────────────────┘

这里有三个"自由度"决定了聚类的困境:

  1. "相似"如何度量? 欧氏距离、余弦相似度、马氏距离、相关系数……不同的度量导出完全不同的簇。这是聚类里第一个看似琐碎、实则决定成败的选择。
  2. "紧凑与分离"如何量化? 不同目标函数(见下文 K-Means 的 inertia、层次聚类的链接准则)会选出不同的划分,哪怕它们都"合理"。
  3. k 是多少? 很多算法(K-Means、GMM)要求你事先给定簇数,而这个数字在真实业务里几乎从不来自数据本身。

所以专业视角下的聚类是:在目标函数与领域知识之间做权衡的工程,而不是"自动发现真相"的魔法。这一点请务必带进后面的所有阅读——评估一节里我们还会回头讨论"没有真相时如何评估"。

二、K-Means:最流行,也最值得讲透 ​

K-Means 是应用最广的聚类算法:几十行代码、O(n·k·d) 每轮迭代、几乎不需要调参(除了 K)。正因为简单,它常常被当作"无脑一行 KMeans(n_clusters=3).fit(X)",但它的原理里藏着整个无监督学习的两个核心思想:坐标下降与EM 迭代。

1. 目标函数与算法流程 ​

K-Means 最小化的是簇内平方和(within-cluster sum of squares,WCSS,sklearn 里叫 inertia):

$$ J = \sum_{i=1}^{n} \min_{j} | x_i - \mu_j |^2 $$

其中 μ₁, …, μₖ 是 k 个质心(centroid)。等价写法是把每个样本分给最近的质心:

$$ J = \sum_{j=1}^{k} \sum_{x_i \in C_j} | x_i - \mu_j |^2 $$

算法是经典的两阶段交替迭代(坐标下降):

text
① 初始化:随机(或启发式)选 k 个质心 μ₁, ..., μₖ
② 循环直到质心不再变化(或达到最大迭代):
   E 步(分配): 把每个样本 xᵢ 分给距离最近的质心 → 更新簇归属
   M 步(更新): μⱼ = Cⱼ 内所有样本的均值 → 更新质心
③ 输出: 最终簇归属与质心
python
# 手写 50 行 K-Means,剥掉所有封装
import numpy as np

def kmeans(X, k, max_iter=100, tol=1e-4, seed=0):
    rng = np.random.default_rng(seed)
    # ① 从样本中随机挑 k 个点做初始质心
    centers = X[rng.choice(len(X), k, replace=False)]
    for _ in range(max_iter):
        # ② E 步: 距离矩阵 argmin 分配簇
        dists = np.linalg.norm(X[:, None, :] - centers[None, :, :], axis=2)
        labels = dists.argmin(axis=1)
        # ③ M 步: 新质心 = 簇均值
        new_centers = np.array([X[labels == j].mean(axis=0) for j in range(k)])
        if np.abs(new_centers - centers).max() < tol:   # 收敛判定
            break
        centers = new_centers
    return centers, labels

注意 M 步里"质心取均值"这个名字的由来——这就是 K-Means。欧氏距离与均值是天然一对:在欧氏距离下,使簇内平方和最小的簇代表点就是均值。这是微积分里"对 μ 求导置零"的直接结论,也是为什么"平方距离 + 均值"不可拆散。

2. EM 视角:K-Means 是"硬"高斯混合模型 ​

把 K-Means 讲成"迭代分配+更新"只是表层的。更深一层的理解来自 EM(期望最大化)算法:K-Means 是高斯混合模型(GMM)在"每个样本只属于一个簇、簇方差趋于一致且无限小"极限下的特例。

  • GMM 假设数据由 k 个高斯分布混合生成,用 EM 迭代估计参数(均值、协方差、混合权重);
  • K-Means 的 E 步"把样本分配给最近质心",等价于 GMM 的 E 步"计算后验概率"在方差 → 0 时的极限——后验变成 0/1 硬分配;
  • K-Means 的 M 步"取均值",等价于 GMM 的 M 步"更新高斯均值"。

这个视角有两个实战价值。第一,它解释了一个常见困惑:为什么 K-Means 只"擅长"球形簇——因为它在 GMM 族里强制了"各向同性高斯"这一最简形状;数据如果是长条形、环形、嵌套形,K-Means 的欧氏距离就会把它们切开而不是围起来。第二,它给你一条升级路径:当需要"软聚类"(样本属于多个簇的概率)、簇形状是椭圆时,把 K-Means 换成 GMM 即可,数学完全同构。

3. 初始化:为什么 n_init 默认是 10 ​

K-Means 的目标函数是非凸的,迭代只保证收敛到局部最优。初始质心选得差,可能收敛到一个很差的局部解(比如两个质心落在同一个真实簇里)。常用的对策:

初始化方法思想备注
随机初始化从样本里随机挑 k 个点最朴素;配合多次运行取最优
K-Means++依次选质心,每个新质心更可能离已有质心远sklearn 默认;对复杂数据显著改善解的质量
多次运行n_init 次不同初始化,保留 inertia 最小的结果sklearn 中 n_init=10 即为此

K-Means++(Arthur & Vassilvitskii, 2007)保证得到的解以高概率接近最优值 2(ln k+1) 倍以内,是"用随机换质量"的典型工程智慧。所以实战中请保留默认的 n_init=10 或调更高,不要为省几十毫秒设成 1——聚类结果不稳定,是新手最常踩的坑之一(更多坑见常见陷阱)。

4. K 的选择:肘部法与轮廓系数 ​

K 不来自算法,必须由人给定。两条经典辅助手段,各有局限:

肘部法(Elbow Method)——画 inertia 随 K 的曲线,找"下降变缓的拐点":

inertia
  │
  │               ← 拐点即"肘部"
  │            ·
  │         ·
  │      ·
  │   ·
  │·
  └─────────────────────── K
        1   2   3   4   5   6

逻辑是:K 每加 1,簇更细,inertia 必然下降,但"多分一簇带来的收益"会迅速变小,拐点附近是性价比最高的 K。致命弱点:真实数据往往没有明显拐点,曲线平缓下滑——"肘部"取决于观察者的手势。这是启发式,不是方法。

轮廓系数(Silhouette Score)——对每个样本定义:

$$ s_i = \frac{b_i - a_i}{\max(a_i, b_i)} $$

其中 aᵢ 是样本 i 与同簇其他样本的平均距离(簇内凝聚度),bᵢ 是样本 i 与最近的其他簇样本的平均距离(簇间分离度)。sᵢ ∈ [−1, 1]:接近 1 表示"又近又远"(与同簇亲近、与异簇远离),接近 −1 表示样本被分错了簇。整体轮廓系数 = 所有样本 sᵢ 的均值,取曲线峰值对应的 K:

python
from sklearn.metrics import silhouette_score

Ks = range(2, 9)
scores = {k: silhouette_score(X, KMeans(n_clusters=k, random_state=0).fit_predict(X))
          for k in Ks}
best_k = max(scores, key=scores.get)   # 轮廓系数最高的 K

轮廓系数的计算是 O(n²),样本量大(>10 万)时很慢,可先抽样。它比肘部法"客观"一些,但仍只是几何意义上的好,不保证业务意义。

真正的 K 来自业务

把 K=3 与 K=5 的客户分群结果各打印一份,拿给业务方看:"这三组各是什么画像?有没有一组是'高价值沉睡客户'?"业务能解释的 K 才是对的 K。肘部法和轮廓系数只是帮你在 3~8 之间缩小范围,最终决定权在领域知识。这也呼应了可解释性一文的核心:模型结果必须能讲成人话。

三、DBSCAN 与层次聚类:K-Means 之外的两种世界观 ​

K-Means 有一个隐含假设:簇是"球形的、密度均匀的、大小差不多的"。现实数据常违反这些假设。DBSCAN 与层次聚类从两个不同角度绕开了它们。

1. DBSCAN:基于密度的聚类 ​

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)用密度而非距离定义簇。它只有两个超参数:

  • eps(ε):邻域半径;
  • min_samples(minPts):成为"核心点"所需的最少邻居数。

三类点构成它的世界观:

● = 核心点 (邻域内 ≥ minPts 个点)   ○ = 边界点   × = 噪声点
        eps
    ○ ┌────┐
   ●──│ ●  │──○       算法: 从任一核心点出发,沿"密度相连"
   ●  │    │          的关系扩展簇;核心点彼此相连归为一簇;
    ○ └────┘          边界点挂靠到附近核心点的簇;
    ×  ×   × ×        孤立点标为噪声 (-1)。

不需要指定 K、能发现任意形状的簇、自动识别噪声点——这三条让它成为异常检测与任意形状聚类的首选。sklearn 中调用:

python
from sklearn.cluster import DBSCAN

db = DBSCAN(eps=0.5, min_samples=5)
labels = db.fit_predict(X)   # 噪声点的标签为 -1

缺点:eps 对密度变化敏感(密度不均的数据上,一个 eps 无法同时照顾稠密区与稀疏区);在簇密度差异大时不适用;高维下"近邻"概念失效(见下文降维动机);参数不直观。算法与参数细节可查阅术语表中 DBSCAN 词条。

2. 层次聚类:树状图与链接准则 ​

层次聚类(agglomerative)自底向上合并:每个样本先各自成簇,然后不断合并"最近的簇",直到只剩一簇。合并过程的记录就是树状图(dendrogram)——一条纵切的线给出任意粒度的聚类。

"簇与簇的距离"可以有多种定义(即链接准则):

链接准则簇间距离倾向经典出处
单链接(single)两簇最近点距离易连出长条"链式"簇易产生不合理的链
全链接(complete)两簇最远点距离偏球形、对噪声敏感
平均链接(average)两簇所有点对平均距离较稳健、实践中常用
Ward合并后簇内平方和的增量与 K-Means 目标一致、簇偏球形Ward (1963)

层次聚类的独特价值在探索:树状图让你看到"1 个簇到 n 个簇"的完整谱系,K 的选择变成"在树的哪一层切一刀"。代价是计算 O(n²)~O(n³),10 万样本就难以招架。

3. 三者对比 ​

维度K-MeansDBSCAN层次聚类(聚合式)
簇形状仅近似球形任意形状取决于链接准则(Ward 偏球形)
是否需指定 K必须否否(但需决定"切哪层")
噪声处理无概念,噪声被强分进某簇天然识别噪声无概念
簇密度不均不适用不适用(单一 eps)可以
时间复杂度O(n·k·d·iter)O(n²)(可索引加速)O(n²)~O(n³)
超参数Keps, min_samples链接准则, 距离度量
典型用途大规模客户分群异常检测、地理/空间聚类探索性分析、基因聚类

一句话选型:确定是球形簇且要快 → K-Means;形状未知且要揪出离群点 → DBSCAN;要看到聚类"从小到大"的完整谱系 → 层次聚类。

四、聚类的评估困境:内部指标 vs 外部指标 ​

监督学习评估有"标准答案"(y),聚类没有。于是评估天然分裂成两派,各有硬伤。

1. 外部指标:拿"真相"当尺子 ​

当数据恰好有真实标签(比如你知道某些客户确实属于三个已知细分市场)时,可以用外部指标衡量聚类结果与真实分组的吻合度:

指标思想取值范围
ARI(调整兰德指数)统计"样本对"在两次划分中一致的比例,并扣除随机一致的期望0≈随机,1=完全一致
NMI(归一化互信息)两个划分共享的信息量0≈独立,1=完全一致

但这里有个致命的逻辑陷阱,概念页无监督学习也点破过:如果你手里有真实标签,为什么还要做无监督? 直接用监督学习去学那些标签,效果几乎必然更好。外部指标只在两处有价值:①用带标签的基准数据对比算法实现(不是解决业务问题);②检验"无监督发现的簇"与"已知的领域分组"是否吻合(验证而非替代)。

2. 内部指标:不靠标签,但各有偏执 ​

内部指标只看几何结构:

  • inertia / WCSS:越小越好,但随 K 单调下降,且偏爱球形簇;
  • 轮廓系数:衡量"凝聚+分离",对凸簇友好,对不规则的簇会给出偏低分——不是因为分得差,而是指标自带偏见;
  • DB 指数(Davies–Bouldin):簇内散度与簇间距离的比值,越小越好,计算快。

3. 最终的裁判:业务可解释性 ​

把三条思路摆在一起,真实的工程结论是:

外部指标  → 有标签时才能用;有标签时你多半不需要聚类
内部指标  → 无标签时能用;但只反映几何假设,不含业务意义
业务解释  → 永远可用;是客户分群、市场细分真正的验收标准

实践标准动作:内部指标选候选 K → 人工检视各簇画像(均值、分布、典型样本)→ 业务方确认"这组人确实是一个细分市场"。指标负责缩小搜索范围,人负责拍板。别把"轮廓系数最高"当交付理由——那和"准确率最高"是两回事,评估指标背后的完整理论见模型评估与验证。

五、PCA:主成分分析 ​

现在切换话题到降维。降维解决的是高维带来的两个问题:①维度灾难(数据在高维空间变稀疏,距离度量失效);②可视化的极限(人只能看 2D/3D)。PCA(Principal Component Analysis,主成分分析)是最经典、最可解释、应用最广的线性降维方法。

1. 从协方差矩阵到特征分解 ​

PCA 的数学内核只有三步:中心化 → 协方差矩阵 → 特征分解。

设 X 为 n×d 的样本矩阵(已按列中心化,即每列均值 0),协方差矩阵为

$$ C = \frac{1}{n} X^\top X \quad \text{(d×d 对称半正定)} $$

对 C 做特征分解:

$$ C v_j = \lambda_j v_j, \quad \lambda_1 \ge \lambda_2 \ge \dots \ge \lambda_d \ge 0 $$

主成分(principal components)就是协方差矩阵的特征向量 vⱼ,对应特征值 λⱼ 就是该方向上的方差。数据在第 j 个主成分方向上的投影 zⱼ = X vⱼ 方差为 λⱼ。把数据投影到前 k 个特征向量张成的子空间,就是 PCA 降维:

$$ Z = X V_k, \qquad Z \in \mathbb{R}^{n \times k}, \quad V_k = [v_1, \dots, v_k] $$

可以证明(瑞利商 / 变分定理):在 d 维空间中,第 j 个主成分就是"与前面 j−1 个都正交、且投影后方差最大"的方向。换句话:PCA 找的是一组彼此正交、依次捕获最大方差的坐标轴。数学推导所需的线性代数背景见数学基础速查。

数值计算上,scikit-learn 的 PCA 默认用截断 SVD(奇异值分解)而不是直接对协方差矩阵做特征分解——因为 SVD 数值更稳定、不需要先算协方差矩阵(协方差矩阵的平方放大数值误差),且能直接得到截断版本。二者数学上等价:对中心化数据,X 的右奇异向量 = C 的特征向量,奇异值平方 / n = 特征值。

2. 解释方差比:降几维才够? ​

每个主成分的重要性 = 它的方差占比:

$$ \text{解释方差比}j = \frac{\lambda_j}{\sum^{d} \lambda_i} $$

sklearn 中直接读 explained_variance_ratio_。经典经验法则是累计解释方差比 ≥ 80%(或 95%):保留最少的主成分数,使累计方差占比达到阈值。比如一个 20 维数据,前 3 个主成分解释了 85% 的方差,你就可以放心把数据压到 3 维——那 15% 被丢弃的方差大多是噪声。

python
from sklearn.decomposition import PCA

pca = PCA(n_components=0.95)      # 直接传阈值,自动选主成分数
Z = pca.fit_transform(X_scaled)
print(pca.n_components_, pca.explained_variance_ratio_.cumsum()[-1])

3. 标准化:不标准化的 PCA 是量纲的奴隶 ​

这是 PCA 最常被忽略的坑。PCA 最大化的是方差,而方差的大小与特征的量纲直接挂钩:

特征量纲        方差量纲       后果
年消费额(元)    10⁸ 级别       主导第一主成分
消费频次(次)    10¹ 级别       被 PCA 忽略

如果不先标准化,PCA 的"最重要方向"几乎必然指向量纲最大的那个特征——这不是发现了数据结构,而是复读了计量单位。所以做 PCA 前几乎总是先 StandardScaler(把每列变均值为 0、标准差为 1)。这与特征工程里"量纲无关化"的原则一致,详见特征工程与数据工程。注意:当所有特征本身同量纲同尺度(如同一传感器采集的像素、同一量表的题目)时,可以不做标准化,这是少数例外。

4. PCA 的性质与局限 ​

  • 优点:线性、可逆(可以重建近似原数据)、全局最优(在"最大方差"目标下)、主成分相互正交无冗余、有明确的可解释量(解释方差比);
  • 局限:只能捕捉线性结构——数据的非线性流形(如螺旋、环形)PCA 无法摊平;主成分是原始特征的线性组合,语义往往难以解释("0.31×消费额 + 0.52×频次 − 0.44×时间间隔"是什么?);对异常值敏感(方差会被极值主导)。

六、t-SNE 与 UMAP:非线性可视化降维 ​

PCA 搞不定的非线性结构,由流形学习方法接手。最著名的两个是 t-SNE(t-distributed Stochastic Neighbor Embedding)与 UMAP(Uniform Manifold Approximation and Projection)。

1. t-SNE 的原理 ​

t-SNE(van der Maaten & Hinton, 2008)的核心思想非常漂亮:让"高维空间里的邻居关系"在低维空间里尽量保持不变。

  1. 高维侧:把样本间的欧氏距离转化为条件概率 pⱼ|ᵢ——"xⱼ 是 xᵢ 的邻居"的概率,由以 xᵢ 为中心的高斯分布决定。**困惑度(perplexity)**控制这个高斯分布的宽度(有效邻居数,sklearn 默认 30);
  2. 低维侧:低维点 yᵢ, yⱼ 之间的相似度用学生 t 分布(自由度为 1,即柯西分布)建模,记 qᵢⱼ;
  3. 对齐两侧:用 KL 散度衡量两个概率分布的差异,梯度下降最小化它。

关键设计是低维用了重尾的 t 分布而非高斯:高维中"相距很远"的点,在低维中会被推到更远——这缓解了"拥挤问题"(crowding problem),让低维图里的簇能真正分开。这也是名字里 t 的来历。

python
from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, perplexity=30, random_state=0)
Z_tsne = tsne.fit_transform(X_scaled)   # 得到 2D 坐标,可直接散点图

2. 与 PCA 的本质区别 ​

维度PCAt-SNE / UMAP
变换类型线性(投影)非线性(流形学习)
是否全局最优是(最大方差)否(随机初始化 + 梯度下降)
距离含义保留全局距离(近似)只保邻居关系,全局距离无意义
可重建原数据可以(逆变换)不行
可解释性有(解释方差比)几乎无
确定性确定每次运行结果不同
典型用途特征压缩、预处理可视化探索、确认簇结构

3. t-SNE 的三大陷阱 ​

可视化 ≠ 聚类结论

t-SNE 图里"靠得近"只代表局部邻居关系,簇与簇之间的距离、簇的大小、簇的形状都没有真实含义。看到图上两团很近,不能推断"高维里它们也接近"。更危险的是,t-SNE 能"制造"虚假的簇结构——均匀分布的随机噪声在 t-SNE 图上也可能呈现出分团假象。结论必须以聚类算法 + 业务验证为准,t-SNE 只是展示手段。

三个必须知道的坑:

  1. 困惑度敏感:perplexity 过小(<5)图变成碎片化小团,过大(>50)图退化成均匀云团;实践中常在 5~50 之间多试几次看稳定性;
  2. 随机性:不同 random_state 给出不同图;判断"结构是否真实",应固定种子多次运行,看簇结构是否反复出现;
  3. 复杂度 O(n²):几万样本就慢得难受。标准做法是先用 PCA 降到 30~50 维,再跑 t-SNE(去噪 + 提速),或对大数据抽样。这与本节开头"降维缓解维度灾难"互为呼应。

4. UMAP:t-SNE 的当代继任者 ​

UMAP(McInnes et al., 2018)走的是"先用流形假设建模糊拓扑图,再优化低维布局"的路子,数学根基是黎曼几何(声称有理论保证),但工程上它比 t-SNE 强在三点:①快得多(支持近似最近邻,几十万点可跑);②能保留更多全局结构(它的目标不是 KL 散度而是交叉熵,兼顾局部与全局);③可对未知新数据做变换(有 transform 方法,t-SNE 只能处理"已经见过的样本")。当可视化对象达到十万级时,UMAP 基本取代 t-SNE。不过二者共用同一批陷阱:图上的距离与簇大小不可解释、随机性、参数敏感。

七、实战:sklearn 里的 KMeans + PCA 完整流程 ​

下面把前面所有知识串成一个可运行的完整案例:某零售平台的客户分群。数据是合成 RFM 风格特征(也可以换成你手上的任何表),完整走一遍"标准化 → PCA 可视化 → 选 K → KMeans → 画像解读"的标准管线。

python
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt

# ── ① 模拟数据:1000 个客户 × 4 个 RFM 风格特征 ─────
rng = np.random.default_rng(42)
n = 1000
# 三类客户:高价值(0) / 中坚(1) / 沉睡(2)
seg = rng.integers(0, 3, n)
# 每列特征的三类均值 [高价值, 中坚, 沉睡]
means = np.array([
    [8000, 40,  20,  2.0],   # 高价值: 消费多、频次高、间隔短、客单价高
    [3000, 15,  60,  1.5],   # 中坚
    [ 800,  3, 180,  1.0],   # 沉睡: 消费少、频次低、久未光顾
])
scales = np.array([500, 5, 12, 0.3])            # 每列噪声标准差
X = means[seg] + rng.normal(0, scales, size=(n, 4))

# ── ② 标准化:PCA 之前必做(量纲不同!)───────────
X_scaled = StandardScaler().fit_transform(X)

# ── ③ PCA:先看解释方差比,确认降到几维 ────────────
pca = PCA().fit(X_scaled)
cum = np.cumsum(pca.explained_variance_ratio_)
print("累计解释方差比:", np.round(cum, 3))          # 前2维通常已 >0.8
X_2d = pca.transform(X_scaled)[:, :2]

# ── ④ 选 K:肘部法 + 轮廓系数双确认 ────────────────
inertias, sils = [], []
Ks = range(2, 8)
for k in Ks:
    km = KMeans(n_clusters=k, n_init=10, random_state=0).fit(X_scaled)
    inertias.append(km.inertia_)
    sils.append(silhouette_score(X_scaled, km.labels_))

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].plot(Ks, inertias, "o-"); axes[0].set_title("肘部法 (inertia)")
axes[1].plot(Ks, sils, "s-");  axes[1].set_title("轮廓系数")
plt.tight_layout(); plt.show()

# ── ⑤ 取轮廓系数最优 K(真实业务还应人工复核)─────
best_k = Ks[np.argmax(sils)]
print("轮廓系数最优 K =", best_k)

# ── ⑥ 最终建模 + PCA 平面可视化 ───────────────────
km = KMeans(n_clusters=best_k, n_init=10, random_state=0).fit(X_scaled)
plt.scatter(X_2d[:, 0], X_2d[:, 1], c=km.labels_, cmap="tab10", s=8, alpha=0.7)
plt.scatter(pca.transform(km.cluster_centers_)[:, 0],
            pca.transform(km.cluster_centers_)[:, 1],
            marker="X", s=200, c="black")
plt.title(f"PCA 投影 + KMeans 分群 (K={best_k})"); plt.show()

# ── ⑦ 画像解读:把簇"翻译"回原始业务量纲 ──────────
df = pd.DataFrame(X, columns=["年消费额", "年频次", "距上次消费天数", "客单价"])
df["分群"] = km.labels_
print(df.groupby("分群").mean().round(1))

这份代码演示了全部关键动作:标准化在前、PCA 辅助可视化、K 双指标确认、质心投影到 PCA 平面展示簇心、用原始量纲打印簇画像。最后一步(⑦)是整个流程的落点——groupby 出来的三行均值,就是你能拿给业务方讲"高价值活跃 / 中坚型 / 沉睡待唤醒"三群人的依据。

注意图里质心是 pca.transform(km.cluster_centers_) 投影出来的,而不是 km.cluster_centers_ 直接画——因为模型在标准化空间里做,画图在 PCA 平面里做,坐标必须一致。这类"训练空间与展示空间混淆"是常见 bug,更多工程陷阱见常见陷阱。

八、真实业务应用 ​

1. 客户分群(市场细分) ​

最经典的应用。RFM(最近一次消费 Recency、频率 Frequency、金额 Monetary)聚类 → 识别"高价值忠诚 / 沉睡流失 / 价格敏感 / 新客"等群 → 差异化运营:给沉睡群发召回券,给高价值群做专属服务。它与推荐系统里的协同过滤是互补关系:聚类分群解决"给谁什么样的整体策略",协同过滤解决"给这个人推什么单品"。关键纪律:分群不是一次性的——客户行为会漂移,需要定期重跑聚类并监控群迁移率(比如"上季度 12% 的高价值客户滑落到了沉睡群")。

2. 异常检测 ​

DBSCAN 的噪声点天然是"异类";K-Means 里"离任何质心都远"的点也是离群点候选。在金融反欺诈、工业质检、网络入侵检测里,异常往往比正常更有价值——欺诈订单、故障设备、攻击流量在数据中占比极小,恰恰是聚类容易漏掉或误分的对象。两种落地姿势:

  • 无监督异常检测:DBSCAN(labels == -1)或"距最近质心距离 > 阈值";
  • 监督异常检测:有了历史标注后,切换为隔离森林(Isolation Forest)或一类 SVM(one-class SVM)。

两者的边界要拎清:聚类型异常检测假设"正常数据稠密、异常稀疏且孤立",数据生成机制不符合这个假设时,换树模型思路(隔离森林本质上是树模型家族)往往更稳。异常检测也常与时间序列结合,见数据工程中的时序清洗。

3. 特征压缩与降噪 ​

  • 压缩:1000 维的稀疏文本特征(TF-IDF)用 PCA / SVD 压到 100 维,作为下游模型的输入,速度和稳定性都更好;
  • 去相关:PCA 输出的主成分互相正交,喂给对共线性敏感的模型(线性回归、逻辑回归)比原始高相关特征更友好;
  • 可视化:t-SNE/UMAP 让人"看见"数据——模型训练前先可视化一遍,往往能提前发现数据泄漏、重复样本、脏分区等问题,这是数据探索的起点(流程见什么是机器学习中的项目骨架)。

用 PCA 做特征工程的两条红线

① 先只对训练集 fit,再用同一个 pca 去 transform 测试集——对全部数据一起 fit 属于典型的数据泄漏,会让测试评估虚高;② 在线性模型上,PCA 压缩一般无损或有益;但树模型(XGBoost、LightGBM)对特征做的是分位切分,PCA 换掉原始特征有时反而降低可解释性与效果,且主成分无法做特征重要性归因——此时更推荐特征选择而非降维。

4. 其他高价值场景速览 ​

场景方法业务价值
图像/向量检索先 PCA/UMAP 降维再建索引检索速度数量级提升
文档主题探索主题模型/LSA(本质是 SVD)语料库自动归纳主题
基因表达谱层次聚类发现共表达基因模块
异常反欺诈DBSCAN 噪声点识别未知欺诈模式

九、权衡与取舍 ​

把聚类与降维放到项目的全局里,有几组必须正视的取舍:

  • "自动发现结构" vs "结构是你的先验":聚类没有客观正确答案,K 的形状、距离度量、K 的取值,全都带着你的主观选择。承认这一点,比假装"数据自己说话了"专业得多。簇画像必须能讲成人话(可解释性),否则不要上线。
  • 可解释 vs 强大的距离:K-Means 简单但只吃球形簇;GMM 能建模椭圆;t-SNE 图漂亮但不可逆、不可解释。选哪个取决于下游用途:分群要讲给业务听 → 选可解释的(K-Means 配画像表);纯可视化探索 → 才轮到 t-SNE/UMAP。
  • 降维省下的计算 vs 丢掉的语义:PCA 压缩到 5 维可能解释 90% 方差,但每个主成分都是 50 个特征的线性组合,无法向业务解释"这维是什么"。需要可解释时,用特征选择替代降维。
  • 局部保真 vs 全局保真:t-SNE 保局部结构、牺牲全局距离;PCA 保全局方差、无法处理非线性。没有免费午餐,看你的分析目的是"找邻居"还是"量全局"。
  • 快 vs 慢:K-Means 秒级,DBSCAN O(n²),层次聚类 O(n³),t-SNE O(n²) 且不可增量。百万级数据上,算法选择基本由复杂度先行筛掉一半。
  • 无监督 vs 直接上监督:当标签可获得、业务目标明确(如预测流失),不要为了"用上聚类"而聚类——监督模型直接学目标往往更好。聚类最合适的舞台是:标签没有、目标模糊、你要先搞清楚数据长什么样。

一个成熟的数据科学团队会把本文的工具当作探索期标配:拿到新数据先标准化 + PCA 可视化 + 聚类画像,形成对数据的直觉,再决定走监督还是继续无监督。这条工作流与模型评估的严谨评估、数据工程的管线建设一起,构成完整的方法论闭环。

延伸阅读 ​

  • 无监督学习 —— 本文的概念总览,含 GMM、关联规则与四大任务框架
  • 什么是机器学习 —— 三大建模范式与"数据生成规则"的整体视角
  • 特征工程 —— 标准化、特征选择与 PCA 的工程衔接
  • 模型评估与验证 —— 监督评估的严谨框架,与聚类"无真相评估"互为镜像
  • 数据工程 —— 清洗、缺失值与时序处理,聚类的前置环节
  • 可解释性与公平性 —— 簇画像如何"讲成人话",以及公平性风险(聚类放大的群体偏差)
  • 推荐系统 —— 协同过滤与分群在个性化场景中的分工
  • 树模型与集成学习 —— 树模型与聚类/降维的互补场景
  • 术语表 —— K-Means、inertia、轮廓系数、困惑度等词条
  • 数学基础速查 —— 特征分解、SVD、KL 散度等本文用到的数学
  • 常见陷阱 —— 数据泄漏、随机种子、量纲陷阱等工程坑

参考资料 ​