无监督学习实战:聚类与降维(K-Means、层次聚类、PCA 与 t-SNE)

用真实客户细分数据实战无监督学习:K-Means 原理与肘部法则选 k、层次聚类与谱系图、DBSCAN 处理不规则簇、PCA 主成分分析与降维可视化、t-SNE/UMAP 高维数据呈现,以及无监督结果的业务落地验证。

引言

监督学习有「标准答案」(标签),无监督学习没有——它的目标是发现数据内在的结构。两大任务:聚类(把样本分成几群)与降维(压缩特征但不丢失关键信息)。应用场景遍地都是:客户细分、异常检测、基因分型、推荐系统冷启动、高维可视化。

本文用零售客户数据走一遍完整实战:先讲 K-Means 的原理与「选几个簇」的核心难题(肘部法则、轮廓系数),再对比层次聚类与 DBSCAN 各自擅长的数据形态;降维部分讲清 PCA 的原理直觉、用它做可视化与去噪,最后用 t-SNE/UMAP 呈现高维数据。全程强调一个关键问题:无监督结果如何验证它真的有意义。

前置:[[ml]] 专题的环境与 pandas 基础(https://plumephp.com/ml-python-environment-setup/)。深度原理见 [[ai-ml]] 专题的无监督学习文章。


目录


1. 无监督学习:没有标签怎么学

1.1 聚类 vs 降维

任务目标输出典型算法
聚类把样本分组每个样本一个簇标签K-Means、层次、DBSCAN
降维压缩特征更少的新特征PCA、t-SNE、UMAP

1.2 共同难题:没有「标准答案」

监督学习可以用测试集验证;无监督的「对不对」依赖领域判断——这就是为什么要结合业务语义去解释簇的含义。


2. 数据准备:零售客户细分数据集

2.1 构造客户特征

每位客户三个维度:年度消费金额、消费频次、平均客单价。

import numpy as np
import pandas as pd

np.random.seed(42)
n = 500

# 三种真实群体:高端少次高客单 / 大众高频低客单 / 低价沉默
group1 = np.random.normal([5000, 8, 650], [500, 2, 80], (170, 3))
group2 = np.random.normal([1500, 40, 40],  [200, 6, 10], (180, 3))
group3 = np.random.normal([300,  3, 100], [80,  1, 25], (150, 3))

data = np.vstack([group1, group2, group3])
df = pd.DataFrame(data, columns=['年消费额', '消费频次', '客单价'])
print(df.describe())

2.2 聚类前必须标准化

聚类基于距离,量级大的特征会主导结果。

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X = scaler.fit_transform(df)

3. K-Means 原理与实现

3.1 算法四步

1. 随机选 k 个初始中心
2. 每个样本归到最近的中心
3. 重新计算每簇中心(均值)
4. 重复 2-3 直到中心几乎不动

3.2 用轮廓看分群效果

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

kmeans = KMeans(n_clusters=3, n_init=10, random_state=42)
labels = kmeans.fit_predict(X)

df['簇'] = labels
plt.scatter(df['年消费额'], df['消费频次'], c=df['簇'], cmap='viridis', alpha=0.6)
plt.xlabel('年消费额')
plt.ylabel('消费频次')
plt.title('K-Means (k=3)')
plt.show()

3.3 解释簇的含义

profile = df.groupby('簇').mean().round(1)
print(profile)

三簇分别对应「高端低频」「大众高频」「低价沉默」,可落地为差异化运营策略。


4. 选几个簇:肘部法则与轮廓系数

4.1 肘部法则(Elbow)

看「簇内误差平方和 SSE」随 k 的下降曲线,找拐点:

sse = []
for k in range(1, 9):
    km = KMeans(n_clusters=k, n_init=10, random_state=42)
    km.fit(X)
    sse.append(km.inertia_)        # SSE

plt.plot(range(1, 9), sse, 'o-')
plt.xlabel('k')
plt.ylabel('SSE')
plt.title('肘部法则')
plt.show()

4.2 轮廓系数(Silhouette)

衡量「样本与自己簇的紧密度 vs 与其他簇的分隔度」,取值 [-1,1],越高越好:

from sklearn.metrics import silhouette_score

for k in range(2, 7):
    km = KMeans(n_clusters=k, n_init=10, random_state=42)
    labels = km.fit_predict(X)
    score = silhouette_score(X, labels)
    print(f"k={k} 轮廓系数={score:.3f}")

4.3 综合判断

方法怎么用
肘部法则SSE 下降「拐点」对应的 k
轮廓系数多个 k 里取最高
业务常识簇数要可解释、可运营

5. 层次聚类与谱系图

5.1 原理:自底向上合并

从每个样本自成一簇开始,逐步合并距离最近的两簇,直到剩下指定簇数。

from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt

Z = linkage(X, method='ward')
plt.figure(figsize=(10, 5))
dendrogram(Z, truncate_mode='level', p=5)
plt.title('谱系图')
plt.show()

5.2 谱系图怎么看

谱系图横线高度 = 合并时的距离。在高度差异大的地方切一刀,得到合理的簇划分。

agg = AgglomerativeClustering(n_clusters=3, linkage='ward')
agg_labels = agg.fit_predict(X)

5.3 与 K-Means 对比

维度K-Means层次聚类
簇形状球形假设更灵活
数据规模可大规模计算 O(n²),适合中小
是否需要预选 k需要可从谱系图判断
可解释性一般谱系图直观

6. DBSCAN:不规则簇与噪声

6.1 为什么需要 DBSCAN

K-Means 假设「簇是球形」,遇到环形、狭长、带噪声的簇会失败。DBSCAN 基于密度:密度相连的区域成一簇,稀疏点是噪声。

6.2 构造环形数据并对比

from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN

Xm, _ = make_moons(n_samples=300, noise=0.05, random_state=42)

# K-Means 会把两个月牙错切成上下两半
km = KMeans(n_clusters=2, n_init=10, random_state=42)
km_labels = km.fit_predict(Xm)

# DBSCAN 正确识别两个月牙
db = DBSCAN(eps=0.3, min_samples=5)
db_labels = db.fit_predict(Xm)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].scatter(Xm[:,0], Xm[:,1], c=km_labels); axes[0].set_title('K-Means')
axes[1].scatter(Xm[:,0], Xm[:,1], c=db_labels); axes[1].set_title('DBSCAN')
plt.show()

6.3 参数含义

参数含义影响
eps邻域半径太小→碎片多;太大→合并
min_samples成为核心点的最少邻居越大→越少簇

labels == -1 的点是噪声——这本身就是一种异常检测能力。


7. PCA 降维:原理直觉与应用

7.1 PCA 做了什么

PCA 找到数据方差最大的几个方向(主成分),把数据投影到这些方向,用少数几个新特征承载原数据的大部分信息。

原数据(28维) → PCA → 前2个主成分(2维)  ← 保留主要结构

7.2 可视化高维数据

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

plt.scatter(X_pca[:,0], X_pca[:,1], c=labels, cmap='viridis', alpha=0.6)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.title('PCA 降维后的客户分布')
plt.show()

7.3 看每个主成分解释多少方差

print(pca.explained_variance_ratio_)
# [0.62, 0.25] → 前两个主成分解释了 87% 的方差

7.4 PCA 的三种用途

用途做法
可视化降到 2-3 维画图
去噪/压缩只保留前 k 个主成分
消除共线性替代原始相关特征

PCA 是把「有损压缩」做得最好的线性方法;但它假设结构是线性的,非线性结构用 t-SNE/UMAP。


8. 高维可视化:t-SNE 与 UMAP

8.1 t-SNE:保留局部结构的非线性降维

from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X)

plt.scatter(X_tsne[:,0], X_tsne[:,1], c=labels, cmap='viridis', alpha=0.6)
plt.title('t-SNE')
plt.show()

8.2 UMAP:更快、保留更多全局结构

pip install umap-learn
import umap

reducer = umap.UMAP(random_state=42)
X_umap = reducer.fit_transform(X)

8.3 三种降维对比

方法类型速度擅长
PCA线性快压缩/去噪
t-SNE非线性慢可视化聚类结构
UMAP非线性中可视化 + 大规模

t-SNE/UMAP 的坐标不代表真实距离,只能看「分没分开」,不要解读坐标轴。


9. 总结:无监督结果的验证套路

9.1 完整工作流

标准化 → 降维(可选PCA) → 聚类(K-Means/DBSCAN)
→ 选簇数(肘部/轮廓) → 画图看分离度
→ 分簇画像(各簇均值) → 业务语义验证

9.2 无监督结果如何「验证」

没有标签,但有这些替代验证:

手段说明
簇内紧凑、簇间分离轮廓系数、可视化
分簇画像可解释每簇均值符合业务直觉
稳定性换种子/子样本,簇是否稳定
下游验证分簇后做运营/推荐,看效果

9.3 一句话心法

无监督的价值在「把数据讲成人话」:聚类给出可解释的群组,降维让人看得见结构。指标是辅助,业务语义才是最终裁判。


延伸阅读

  • https://plumephp.com/ml-supervised-classification/ — 有标签的评估体系(与无监督互补)
  • https://plumephp.com/ml-feature-engineering/ — 特征工程:聚类/降维常与特征加工协作
  • [[ai-ml]] 专题的无监督学习与特征工程深度文章
  • scikit-learn 聚类文档

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 迁移学习实战:预训练模型、特征提取与微调全流程
  3. 计算机视觉入门实战:图像处理与 CNN 图像分类