无监督学习:聚类与降维

有监督学习有"标准答案"(标签),无监督学习没有------它要从数据自身结构中"发现"规律。

本文覆盖两大无监督任务:聚类 (把相似的样本分到一堆)和降维(把高维数据压到低维便于可视化与分析),全程配 sklearn / umap-learn 代码。


一、无监督学习概述

1.1 三种学习范式对比

范式 数据 目标 典型任务 典型算法
有监督学习(Supervised) 有标签 (X, y) 学习 X→y 映射 分类、回归 SVM、决策树、神经网络
无监督学习(Unsupervised) 无标签 (X) 发现数据内在结构 聚类、降维、密度估计 KMeans、PCA、t-SNE
半监督学习(Semi-supervised) 少量有标签 + 大量无标签 用无标签数据辅助有标签训练 标签传播 Label Spreading

1.2 无监督学习的两大核心任务

聚类(Clustering):把样本按相似度分成若干组,同组内相似、跨组差异大。用途:客户分群、异常检测、图像分割。

降维(Dimensionality Reduction):把高维数据映射到低维空间,尽量保留原始信息。用途:数据可视化、特征压缩、去噪、加速下游模型训练。

1.3 为什么要学无监督

  • 现实中无标签数据远多于有标签数据,标注成本高
  • 探索性数据分析(EDA)的第一步:先看数据"长什么样"
  • 很多场景根本没有标签可言(异常检测、用户画像分群)
  • 降维是高维数据可视化的唯一手段------人眼最多看 3 维

二、聚类------把相似的样本分到一堆

2.1 KMeans:最经典的"分 K 堆"

原理

给定 K 个簇,KMeans 通过迭代两步收敛:

复制代码
1. 初始化:随机选 K 个样本点作为簇中心(质心)
2. 分配:每个样本分到离它最近的质心所在的簇
3. 更新:每个簇的质心更新为该簇所有样本的均值
4. 重复 2-3,直到质心不再移动(或变化 < 阈值)

目标函数(最小化簇内平方误差和 SSE):

复制代码
SSE = Σ_{k=1}^{K} Σ_{x∈C_k} ||x - μ_k||²

其中 μ_k 是第 k 个簇的质心,C_k 是属于该簇的样本集合。

K 值怎么选

KMeans 需要预先指定 K,这是它最大的"人为干预"环节。两种常用方法:

肘部法则(Elbow Method):对不同的 K 计算 SSE,画 K-SSE 曲线,选"拐弯"处(SSE 下降速率骤减的点)。

轮廓系数(Silhouette Score):对每个样本算一个 -1, 1 的分数,越接近 1 说明分得越好。取不同 K 看平均轮廓系数,选最大的。

sklearn 代码
复制代码
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

# 1. 生成测试数据:3 个簇,共 300 个样本
X, y_true = make_blobs(n_samples=300, centers=3, cluster_std=0.6, random_state=42)

# 2. KMeans 聚类
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
centers = kmeans.cluster_centers_

# 3. 可视化
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=20)
plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='x', s=200, linewidths=3)
plt.title('KMeans 聚类结果')
plt.show()

# 4. 查看簇内平方误差
print(f'SSE (inertia): {kmeans.inertia_:.2f}')
肘部法则选 K
复制代码
sse_list = []
K_range = range(1, 11)
for k in K_range:
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    km.fit(X)
    sse_list.append(km.inertia_)

plt.plot(K_range, sse_list, 'bo-')
plt.xlabel('K (簇数)')
plt.ylabel('SSE (簇内平方误差)')
plt.title('肘部法则选 K')
plt.axvline(x=3, color='red', linestyle='--', label='肘部 K=3')
plt.legend()
plt.show()
优缺点
优点 缺点
简单高效,O(nKt) 复杂度 需预先指定 K
适合球形/凸形簇 对非球形簇效果差(如环形)
结果可解释性强 对异常值敏感(质心被拉偏)
K 个初始质心随机选,可能陷入局部最优(n_init 多跑几次取最优)

2.2 层次聚类:自底向上"合并树"

原理

层次聚类不需要预先指定 K,它生成一棵"聚类树"(树状图 Dendrogram),你可以在任意高度"剪一刀"得到 K 个簇。

两种策略:

策略 做法 sklearn 参数
凝聚法(Agglomerative,自底向上) 每个样本初始为一个簇,反复合并最相似的两簇 AgglomerativeClustering(常用)
分裂法(Divisive,自顶向下) 所有样本初始为一个簇,反复分裂最不均匀的簇 sklearn 未直接实现
簇间距离怎么算

合并两个簇时,"两个簇之间的距离"有多种定义:

链接方式 定义 特点
单链接(Single / Ward min) 两簇中最近的两点距离 容易"链式效应",把长条形数据拉成一串
全链接(Complete / Ward max) 两簇中最远的两点距离 偏好紧凑球形簇
平均链接(Average) 两簇所有点对的平均距离 折中
Ward 链接 合并后使SSE 增量最小 最常用,效果接近 KMeans
sklearn 代码
复制代码
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt

# 1. 凝聚层次聚类(指定最终 3 个簇,Ward 链接)
agg = AgglomerativeClustering(n_clusters=3, linkage='ward')
labels = agg.fit_predict(X)

# 2. 画树状图(看不同高度"剪"出几个簇)
Z = linkage(X, method='ward')   # linkage 矩阵
plt.figure(figsize=(12, 5))
dendrogram(Z, truncate_mode='level', p=5)  # p=显示最后 5 层
plt.title('层次聚类树状图(Dendrogram)')
plt.xlabel('样本索引 / 簇')
plt.ylabel('距离')
plt.show()

怎么用树状图选 K:看纵轴------最长的那条"竖线"(未被横线截断的最大间距)对应的位置横切一刀,切到的横线数就是推荐 K。

优缺点
优点 缺点
不需预先指定 K 计算复杂度高 O(n³),大数据集慢
树状图直观,可交互式选 K 一旦合并/分裂不可撤销
可发现数据的层次结构 对噪声敏感
任意形状簇都能处理

2.3 DBSCAN:基于密度的"自动找簇"

原理

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)的核心思想:簇是高密度区域,由低密度区域分隔。

两个参数:

参数 含义
eps(ε) 邻域半径:以某点为圆心、eps 为半径的圆内的点都算"邻居"
min_samples 核心点阈值:一个点的 eps 邻域内至少有 min_samples 个点,它才是"核心点"

三种点:

类型 定义 簇归属
核心点(Core) eps 邻域内点数 ≥ min_samples 簇的"骨架"
边界点(Border) 不是核心点,但在某核心点的 eps 邻域内 归属该核心点所在簇
噪声点(Noise) 既不是核心点,也不在任何核心点邻域内 标记为 -1,不归属任何簇

算法流程:

复制代码
1. 对每个未访问的点 p:
   a. 找 p 的 eps 邻域
   b. 若邻域点数 < min_samples → 暂标记为噪声(后续可能被重新归为边界点)
   c. 若邻域点数 ≥ min_samples → p 是核心点,创建新簇
      → 把 p 的所有邻居加入簇
      → 对每个邻居递归重复(若邻居也是核心点,扩展其邻居)
2. 直到所有点都被访问
sklearn 代码
复制代码
from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons

# 1. 生成月牙形数据(KMeans 处理不了的非凸簇)
X_moons, _ = make_moons(n_samples=300, noise=0.1, random_state=42)

# 2. DBSCAN 聚类
db = DBSCAN(eps=0.2, min_samples=5)
labels = db.fit_predict(X_moons)

# 3. 可视化(-1 是噪声点,画成黑色)
plt.scatter(X_moons[:, 0], X_moons[:, 1], c=labels, cmap='viridis', s=20)
plt.title(f'DBSCAN: {len(set(labels)) - (1 if -1 in labels else 0)} 个簇, '
          f'{list(labels).count(-1)} 个噪声点')
plt.show()
eps 怎么选

经验方法:对每个点算到最近邻居的距离,排序后画 k-距离图(k=min_samples),曲线"拐弯"处对应 eps。

复制代码
from sklearn.neighbors import NearestNeighbors

nn = NearestNeighbors(n_neighbors=5)
nn.fit(X_moons)
distances, _ = nn.kneighbors(X_moons)
distances = np.sort(distances[:, -1])   # 第 5 近邻的距离,排序

plt.plot(distances)
plt.xlabel('样本(按距离排序)')
plt.ylabel('第 5 近邻距离')
plt.title('k-距离图选 eps')
plt.show()
优缺点
优点 缺点
不需指定簇数 K eps 和 min_samples 对结果影响大
能发现任意形状的簇 密度差异大的数据集(有的簇密、有的疏)效果差
自动识别噪声点(异常检测) 高维数据"维度灾难"------距离失效
对异常值鲁棒

2.4 三种聚类方法对比

方法 簇形状 需指定 K 噪声处理 复杂度 适用场景
KMeans 球形 ✅ ❌ O(nKt) 簇形状规则、数据量大
层次聚类 任意 ❌(树状图选) ❌ O(n³) 小数据集、需层次结构
DBSCAN 任意 ❌ ✅ O(n log n) 非凸簇、异常检测

2.5 聚类评估指标

无标签时怎么评估聚类好坏?三个内部指标:

指标 公式思想 取值范围 sklearn API 越大/越小越好
轮廓系数(Silhouette) (b-a)/max(a,b),a=簇内平均距离,b=最近簇间平均距离 -1, 1 silhouette_score 越大越好(>0.5 好,<0 差)
CH 指数(Calinski-Harabasz) 簇间离散度 / 簇内离散度 × 样本数修正 [0, ∞) calinski_harabasz_score 越大越好
Davies-Bouldin 各簇间最大相似度平均 [0, ∞) davies_bouldin_score 越小越好
复制代码
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score

# 假设 X 是数据,labels 是聚类结果
print(f'轮廓系数: {silhouette_score(X, labels):.4f}')   # 越大越好
print(f'CH 指数:  {calinski_harabasz_score(X, labels):.4f}')  # 越大越好
print(f'DB 指数:  {davies_bouldin_score(X, labels):.4f}')     # 越小越好

有标签时(用聚类结果跟真实标签对比,注意聚类标签编号可能跟真实标签不一致):

指标 sklearn API 说明
ARI(调整兰德指数) adjusted_rand_score -1, 1,0=随机,1=完全一致
NMI(归一化互信息) normalized_mutual_info_score 0, 1,1=完全一致
同质性 / 完整性 / V-measure homogeneity_completeness_v_measure 0, 1

三、降维------把高维压到低维

3.1 为什么降维

  • 可视化:人最多看 3 维,高维数据必须降到 2-3 维才能画图
  • 去噪:低维子空间往往保留了信号、滤掉了噪声
  • 加速:特征少了,下游模型训练更快、内存更省
  • 缓解维度灾难:高维空间中样本稀疏,距离/密度都失效

3.2 PCA:线性降维的"老大哥"

原理

PCA(Principal Component Analysis,主成分分析)通过正交变换把原始特征投影到新的坐标系,新坐标轴按数据方差大小排序------前几个轴(主成分)保留了最多信息。

数学步骤:

复制代码
1. 数据中心化:X = X - mean(X)   (每个特征减去均值,平移到原点)
2. 计算协方差矩阵:C = XᵀX / (n-1)
3. 对 C 做特征值分解:C = V Λ Vᵀ
4. 取前 k 个最大特征值对应的特征向量,组成投影矩阵 W (d×k)
5. 降维:X_reduced = X · W   (n×d · d×k = n×k)

直觉理解:找数据"方差最大"的方向作为第一主成分,再在与它正交的方向里找方差最大的作为第二主成分,依此类推。方差 = 信息量。

sklearn 代码
复制代码
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits

# 1. 加载手写数字数据集:1797 个样本,每个 8×8=64 维
digits = load_digits()
X, y = digits.data, digits.target   # X.shape = (1797, 64)

# 2. PCA 降到 2 维(可视化用)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)   # X_pca.shape = (1797, 2)

# 3. 可视化:不同数字用不同颜色
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
plt.colorbar(label='数字类别')
plt.title(f'PCA 降维 (64→2), 解释方差比: {pca.explained_variance_ratio_.sum():.2%}')
plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.2%})')
plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.2%})')
plt.show()

# 4. 查看各主成分解释了多少方差
print(f'前 2 个主成分解释方差比: {pca.explained_variance_ratio_}')
print(f'累计解释方差: {pca.explained_variance_ratio_.sum():.2%}')
保留多少维
复制代码
# 画累计解释方差曲线,找"够用"的维度数
pca_full = PCA().fit(X)
cumvar = np.cumsum(pca_full.explained_variance_ratio_)

plt.plot(range(1, len(cumvar) + 1), cumvar, 'b-')
plt.axhline(y=0.95, color='r', linestyle='--', label='95% 方差')
plt.xlabel('主成分数')
plt.ylabel('累计解释方差比')
plt.title('PCA 选维度数')
plt.legend()
plt.show()

# 或者直接指定保留 95% 方差
pca_95 = PCA(n_components=0.95)   # 传 0~1 的浮点数 = 保留的方差比例
X_95 = pca_95.fit_transform(X)
print(f'保留 95% 方差需要 {pca_95.n_components_} 维(原始 64 维)')
优缺点
优点 缺点
线性变换,计算高效 只能捕捉线性结构,非线性流形无能为力
降维后各主成分不相关(正交) 主成分可解释性差(是原始特征的线性组合)
可逆(可重建原始数据近似) 对异常值敏感(协方差矩阵被拉偏)
解释方差比量化信息保留量

3.3 t-SNE:非线性降维的可视化利器

原理

t-SNE(t-Distributed Stochastic Neighbor Embedding)专门为可视化 设计(降到 2-3 维),核心思想:高维空间中"近邻"的点,在低维空间中仍然近邻。

两步走:

复制代码
1. 高维空间:用高斯分布算点对之间的"相似度"(条件概率)
   p_{j|i} = exp(-||x_i - x_j||² / 2σ_i²) / Σ_k exp(-||x_i - x_k||² / 2σ_i²)

2. 低维空间:用 t 分布(自由度=1,即柯西分布)算相似度
   q_{ij} = (1 + ||y_i - y_j||²)⁻¹ / Σ_k (1 + ||y_k - y_l||²)⁻¹

3. 最小化 KL 散度:KL(P || Q) = Σ_i Σ_j p_{ij} log(p_{ij} / q_{ij})
   → 用梯度下降优化低维坐标 y_i

为什么用 t 分布而不是高斯分布:高维空间中"中等距离"的点很多(维度灾难),如果低维也用高斯分布,这些点会被挤成一团。t 分布"尾巴更重",能把中等距离的点推到更远的地方,缓解"拥挤问题"。

sklearn 代码
复制代码
from sklearn.manifold import TSNE

# 1. t-SNE 降到 2 维
tsne = TSNE(n_components=2, perplexity=30, learning_rate='auto',
            init='pca', random_state=42, n_iter=1000)
X_tsne = tsne.fit_transform(X)

# 2. 可视化
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
plt.colorbar(label='数字类别')
plt.title('t-SNE 降维 (64→2)')
plt.show()
关键参数
参数 含义 调参建议
perplexity 每个点关注的"有效近邻数",平衡局部/全局结构 5~50,默认 30;样本多就调大,perplexity < n_samples
learning_rate 梯度下降学习率 10~1000;太大簇压缩成点,太远簇缩成密集球
n_iter 最大迭代次数 ≥1000,默认 1000
init 低维坐标初始化 'pca'(用 PCA 结果初始化,比随机更稳)

perplexity 对比实验:

复制代码
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for i, perp in enumerate([5, 30, 100]):
    tsne = TSNE(n_components=2, perplexity=perp, random_state=42, init='pca')
    X_tsne = tsne.fit_transform(X)
    axes[i].scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10', s=10)
    axes[i].set_title(f'perplexity={perp}')
plt.suptitle('t-SNE: perplexity 对比')
plt.show()
优缺点
优点 缺点
非线性,能展开复杂流形 只用于可视化,不适合作为下游模型输入
簇分离效果好,视觉漂亮 计算慢 O(n²),大数据集需先 PCA 降到 50 维再 t-SNE
簇间距离无意义------t-SNE 会"放大"簇间距离让图好看,但不反映真实距离
簇大小无意义------t-SNE 会"均匀化"簇大小
非确定性(每次运行结果不同,需固定 random_state)
不能新增点降维(没有 transform 方法,只能 fit_transform)

3.4 UMAP:又快又好的"新秀"

原理

UMAP(Uniform Manifold Approximation and Projection)基于代数拓扑,核心思想与 t-SNE 类似(保持局部近邻关系),但数学基础更扎实、速度更快。

两步走:

复制代码
1. 构建高维拓扑表示:用 k-近邻图(每个点连 k 个最近邻,边权=距离)
2. 优化低维表示:在低维构建类似的图,最小化两个图之间的交叉熵

与 t-SNE 的关键区别:UMAP 用指数衰减 的边权(不是 t 分布),且优化目标是交叉熵(不是 KL 散度),这让它:

  • 速度更快(O(n·k),k 是近邻数,通常 15)
  • 保留了更多全局结构(t-SNE 只保局部)
  • 可以 transform 新数据 (有 transform 方法,能增量降维)
  • 可以降到任意维度(不限于 2-3 维,也能做特征压缩)
代码(需安装:pip install umap-learn)
复制代码
import umap

# 1. UMAP 降到 2 维
reducer = umap.UMAP(n_components=2, n_neighbors=15, min_dist=0.1, random_state=42)
X_umap = reducer.fit_transform(X)

# 2. 可视化
plt.scatter(X_umap[:, 0], X_umap[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
plt.colorbar(label='数字类别')
plt.title('UMAP 降维 (64→2)')
plt.show()

# 3. UMAP 可以 transform 新数据(t-SNE 做不到)
X_new = X[:10]   # 假设这是新数据
X_new_umap = reducer.transform(X_new)
关键参数
参数 含义 调参建议
n_neighbors 近邻数,平衡局部/全局 2~200,默认 15;小值重局部细节,大值重全局结构
min_dist 低维中点之间的最小距离 0.0~0.99,默认 0.1;小值簇更紧密,大值簇更松散
n_components 降维目标维度 2~100,可视化用 2-3,特征压缩可更高
metric 距离度量 'euclidean'(默认)、'cosine'、'manhattan' 等
优缺点
优点 缺点
速度快(比 t-SNE 快 5-10 倍) 数学理论较深,参数直觉不如 t-SNE 直观
保留局部+全局结构 不同 random_state 结果差异比 t-SNE 小但仍存在
可以 transform 新数据
可降到任意维度(不限于可视化)
支持非欧距离(cosine 等,适合文本/图像)

3.5 三种降维方法对比

方法 线性/非线性 速度 保留结构 可 transform 适用维度 典型用途
PCA 线性 ⚡⚡⚡ 快 全局(方差) ✅ 任意 特征压缩、去噪、预处理
t-SNE 非线性 🐢 慢 (O(n²)) 局部 ❌ 2-3 纯可视化
UMAP 非线性 ⚡⚡ 较快 局部+全局 ✅ 任意 可视化 + 特征压缩

经验流程:

复制代码
高维数据
  │
  ├─ 只为可视化 → UMAP(首选)或 t-SNE
  │
  ├─ 为下游模型做特征压缩 → PCA(先试)或 UMAP(PCA 不够好再试)
  │
  └─ 数据量很大(>10万)→ 先 PCA 降到 50 维 → 再 UMAP/t-SNE 降到 2 维

四、实战案例:手写数字聚类与可视化

把聚类和降维串起来,用 sklearn 的 digits 数据集(1797 个 8×8 手写数字图片,64 维,0-9 共 10 类)做完整流程。

复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.cluster import KMeans, DBSCAN
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.metrics import (silhouette_score, adjusted_rand_score,
                              normalized_mutual_info_score)
import umap

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ========== 1. 数据准备 ==========
digits = load_digits()
X, y = digits.data, digits.target
print(f'数据形状: {X.shape}')      # (1797, 64)
print(f'类别: {np.unique(y)}')      # [0 1 2 3 4 5 6 7 8 9]

# ========== 2. 降维可视化对比 ==========
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# 2.1 PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
axes[0].scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
axes[0].set_title(f'PCA (解释方差: {pca.explained_variance_ratio_.sum():.1%})')

# 2.2 t-SNE
tsne = TSNE(n_components=2, perplexity=30, init='pca', random_state=42)
X_tsne = tsne.fit_transform(X)
axes[1].scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
axes[1].set_title('t-SNE')

# 2.3 UMAP
reducer = umap.UMAP(n_components=2, random_state=42)
X_umap = reducer.fit_transform(X)
axes[2].scatter(X_umap[:, 0], X_umap[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
axes[2].set_title('UMAP')

plt.suptitle('手写数字 64→2 降维对比(颜色=真实标签)', fontsize=14)
plt.tight_layout()
plt.show()
# 预期:PCA 簇重叠多(线性方法不够),t-SNE/UMAP 簇分离清晰

# ========== 3. KMeans 聚类 + 评估 ==========
kmeans = KMeans(n_clusters=10, random_state=42, n_init=10)
labels_km = kmeans.fit_predict(X)

print('\n===== KMeans 聚类评估 =====')
print(f'轮廓系数:  {silhouette_score(X, labels_km):.4f}')
print(f'ARI:       {adjusted_rand_score(y, labels_km):.4f}')
print(f'NMI:       {normalized_mutual_info_score(y, labels_km):.4f}')

# ========== 4. 先降维再聚类(加速 + 去噪) ==========
pca_50 = PCA(n_components=50)
X_50 = pca_50.fit_transform(X)
labels_km_pca = KMeans(n_clusters=10, random_state=42, n_init=10).fit_predict(X_50)

print('\n===== PCA(64→50) + KMeans 评估 =====')
print(f'轮廓系数:  {silhouette_score(X_50, labels_km_pca):.4f}')
print(f'ARI:       {adjusted_rand_score(y, labels_km_pca):.4f}')
print(f'NMI:       {normalized_mutual_info_score(y, labels_km_pca):.4f}')

# ========== 5. 聚类结果可视化(用 UMAP 降维画图) ==========
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.scatter(X_umap[:, 0], X_umap[:, 1], c=y, cmap='tab10', s=10, alpha=0.7)
plt.title('真实标签')

plt.subplot(1, 2, 2)
plt.scatter(X_umap[:, 0], X_umap[:, 1], c=labels_km, cmap='tab10', s=10, alpha=0.7)
plt.title('KMeans 聚类结果')

plt.suptitle('UMAP 可视化:真实标签 vs KMeans 聚类')
plt.tight_layout()
plt.show()

预期输出:

复制代码
数据形状: (1797, 64)
类别: [0 1 2 3 4 5 6 7 8 9]

===== KMeans 聚类评估 =====
轮廓系数:  0.1821
ARI:       0.7366
NMI:       0.7439

===== PCA(64→50) + KMeans 评估 =====
轮廓系数:  0.1832
ARI:       0.7366
NMI:       0.7439

解读:

  • PCA 降到 50 维后聚类效果几乎不变------说明后 14 维主要是噪声,PCA 起到了去噪作用
  • ARI=0.74 说明 KMeans 聚类结果与真实标签有 74% 的一致性------无监督方法能"猜"对大部分
  • t-SNE/UMAP 的可视化中簇分离远比 PCA 清晰------非线性降维在可视化上完胜

五、知识速查表

知识点 核心要点
无监督学习 无标签,从数据结构中发现规律;两大任务:聚类 + 降维
KMeans 指定 K,迭代"分配+更新质心",最小化 SSE;适合球形簇;肘部法则选 K
层次聚类 不需指定 K,生成树状图,Ward 链接最常用;O(n³) 慢
DBSCAN 基于 eps 邻域和 min_samples 密度,自动找簇+识别噪声;任意形状簇
轮廓系数 -1,1,越大越好,>0.5 为好聚类;无需标签
ARI/NMI 有标签时对比聚类结果与真实标签,1=完全一致
PCA 线性降维,最大化方差=信息量,正交主成分;可逆、可 transform、快
t-SNE 非线性,保局部近邻,t 分布缓解拥挤;只用于 2-3 维可视化,O(n²) 慢
UMAP 非线性,保局部+全局,可 transform,可降到任意维度,比 t-SNE 快
降维流程 纯可视化→UMAP/t-SNE;特征压缩→PCA 先试;大数据→先 PCA 再 UMAP
聚类+降维 先降维去噪再聚类,效果常优于直接高维聚类

六、延伸阅读

主题 资源
t-SNE 原论文 https://lvdmaaten.github.io/publications/papers/JMLR_2008.pdf
UMAP 原论文 1802.03426 UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction
t-SNE 交互可视化 How to Use t-SNE Effectively
sklearn 聚类文档 2.3. Clustering --- scikit-learn 1.9.1 documentation
sklearn 降维文档 2.5. Decomposing signals in components (matrix factorization problems) --- scikit-learn 1.9.1 documentation
UMAP 文档 UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction --- umap 0.5.8 documentation
聚类算法对比图 Comparing different clustering algorithms on toy datasets --- scikit-learn 1.9.1 documentation
相关推荐
Data-Miner2 小时前
AI 做表数据分析知识百科:定义、原理、概念与应用
人工智能·数据挖掘·数据分析
听风若依2 小时前
函数栈帧的创建与销毁-重置版
c语言·学习
Wang's Blog2 小时前
Java 项目部署之 Docker工具快速入门: 学习路径与命令速查导引
java·学习·docker
程序猿阿森2 小时前
Warmup 学习率预热详解:为什么训练初期要“先热身再加速”?
深度学习·学习·语言模型
lisw053 小时前
人类认知和语言学习中的控制论!
人工智能·学习·数学建模
鱼很腾apoc3 小时前
【MySQL】 第1期 从核心概念到表约束全解析
linux·c语言·数据库·学习·mysql
residual_fan3 小时前
基于控制感知域对抗网络的变工况故障诊断:从控制系统与运行状态协同的角度建立模型
人工智能·算法·数据挖掘·数据分析
数脉3 小时前
数据血缘的八个典型企业应用场景
数据挖掘·数据分析·数据可视化
妖精的羽翼3 小时前
Web3D(Three.js)应用分级 + 学习路线规划(由浅入深,贴合接单 + 求职)
学习·three.js