无监督学习
这一课你将能做到
- 用 K-Means 把题目自动聚成「简单/中等/困难」三档,并给每簇赋予业务含义
- 用肘部法则和轮廓系数回答「K 到底取几」
- 用 PCA 把高维题目特征压到 2 维画散点图,看清楚聚类的结构
为什么要学
不是所有数据都有标签。给全站几百道题人工标注难度费时费力,但每道题的「正确率、平均用时、平均尝试次数」现成就有——能不能让算法自己把题目分成几档?这就是无监督学习:没有标准答案,从数据里发现结构。
两个最常用的工具:K-Means 负责分组(题目分难度档、用户分人群画像),PCA 负责压缩(几十维特征压成 2-3 维用于可视化和降噪)。本站「题目自动分级」和「用户画像分群」都能直接落地。
先建立图像
K-Means 迭代过程(二维示意):
第1轮 第2轮 收敛
× ● × ● × ●
× ● × ● → × ● ×=一簇 ●=另一簇
① ② ① ② ① ② ①②=质心
步骤:随机放质心 → 每个点归最近质心 → 质心移到簇中心 → 重复直到不动
跟着做
步骤 1:构造题目特征表
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
n = 200
# 造三类题目:简单(70) / 中等(80) / 困难(50),但算法不知道这个标签
accuracy = np.concatenate([
rng.uniform(0.75, 0.98, 70),
rng.uniform(0.40, 0.75, 80),
rng.uniform(0.05, 0.40, 50),
])
seconds = 230 - 150 * accuracy + rng.normal(0, 15, n) # 越难用时越长
attempts = 1 + (1 - accuracy) * 6 + rng.normal(0, 0.4, n) # 越难尝试越多
步骤 2:标准化 + K-Means 聚类
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
X = StandardScaler().fit_transform(np.column_stack([accuracy, seconds, attempts]))
km = KMeans(n_clusters=3, n_init=10, random_state=42)
labels = km.fit_predict(X)
df = pd.DataFrame({"accuracy": accuracy.round(2), "seconds": seconds.round(0),
"attempts": attempts.round(1), "cluster": labels})
summary = df.groupby("cluster").mean().round(2)
print(summary)
看输出给簇起名:正确率高/用时短/尝试少的那簇是「简单题」,反之是「困难题」。聚出来的簇没有名字,解释权在业务手里——这是无监督和监督最大的区别。
步骤 3:K 取几?肘部法则 + 轮廓系数
from sklearn.metrics import silhouette_score
for k in range(2, 8):
m = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X)
sil = silhouette_score(X, m.labels_)
print(f"k={k} inertia={m.inertia_:8.1f} silhouette={sil:.3f}")
- inertia(簇内平方和):随 k 增大单调下降,找「下降骤缓的拐点」(肘部);
- 轮廓系数:[-1, 1],越高说明「簇内紧、簇间远」,一般 > 0.5 算结构清晰。
两个指标 + 业务需要(产品上就想要三档难度),三者合起来定 K。
步骤 4:PCA 降维可视化
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
pca = PCA(n_components=2)
X2 = pca.fit_transform(X)
print("各主成分方差占比:", pca.explained_variance_ratio_.round(3))
plt.scatter(X2[:, 0], X2[:, 1], c=labels, cmap="viridis", s=15)
plt.xlabel("主成分1"); plt.ylabel("主成分2"); plt.title("题目聚类 PCA 可视化")
plt.colorbar(label="簇")
plt.show()
200 道题、3 个特征压成平面上的三个色块,肉眼可辨。特征有 20 个时 PCA 的价值才真正显现——你不可能肉眼画 20 维。
讲清楚要点
- 无监督没有「对错」,只有「有没有用」:聚类的评估不是准确率,而是轮廓系数这类内部指标 + 业务解释力。同一个数据集聚成 3 类或 5 类都可能合理,取决于用途。
- K-Means 三步循环:初始化质心(k-means++ 让初始点尽量分散)→ 每个样本归最近的质心 → 质心更新为簇均值,重复到收敛。对初值敏感,所以要
n_init=10多跑几轮取最优。 - 标准化对 K-Means 是生死线:K-Means 基于欧氏距离,「用时 200 秒」和「正确率 0.8」量纲差 250 倍,不标准化则聚类完全被用时一个特征主导。
- PCA 找方差最大的方向:第一主成分是数据「最伸展」的方向,第二主成分与它垂直且次之。
explained_variance_ratio_告诉你每个方向保留了多少信息(常用累计 90%-95% 做取舍)。 - PCA 的两个用途:可视化(降到 2-3 维)与降噪提速(高维数据先 PCA 再喂模型,训练更快且能缓解维度灾难)。
你可能会懵的地方
- 聚类结果一定是「对的」吗? 不存在「对」。算法只是按距离把数据分了堆,堆的含义靠业务解释。危险的是把簇标签当成真值标签直接拿去做监督学习的 ground truth——错了没人发现。
- K-Means 一定要球形簇吗? 是的,它隐含假设簇是「大小相近的凸球」(基于欧氏距离+均值质心)。簇是月牙形、长条形时用它就是错的,改用 DBSCAN 或层次聚类。
- PCA 之后的特征还能解释业务吗?
主成分是原特征的线性组合,本身没有业务名字。建模可以用,向产品解释时需要换回原特征(看
pca.components_的系数)。 - 为什么我聚出来的簇大小悬殊?
正常现象,真实分布本来就偏。K-Means 倾向切出均等的球,若业务需要「长尾自然分布」,考虑
KMeans换 GMM(高斯混合)或直接用分位数切档。 - 用户分群和题目分档,特征该怎么选? 原则一致:选「区分目的相关」的特征。分难度用正确率/用时/尝试次数;分人群用活跃度/正确率/知识点覆盖度。塞一堆无关特征只会稀释结构。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 原始量纲直接 K-Means | 大数值特征主导距离 | 先 StandardScaler 再聚类 |
| 把簇标签当真值评估「准确率」 | 无监督没有真值 | 用轮廓系数/业务评审来检验 |
| k 无脑取 3 或 8 | 簇过多失去业务含义 | 肘部法则 + 轮廓系数 + 业务需求共同定 |
| PCA 后不看 explained_variance_ratio | 丢了关键信息不自知 | 累计方差 ≥ 90% 再决定保留几个成分 |
| 用欧氏距离聚类 One-Hot 高维稀疏特征 | 距离区分度极差 | 连续特征聚类,类别维度单独处理 |
卡点急救
- KMeans 警告
memory leak on Windows with MKL:Windows + sklearn 老版本的已知问题,升级 scikit-learn 即可;不影响结果正确性。 - 轮廓系数报错 k=1:silhouette_score 至少要 2 个簇,循环从
range(2, 8)开始。 - 聚类结果每次跑都不一样:没固定
random_state,或没设n_init;两者都固定后结果可复现。
面试常问
- K-Means 的原理和主要缺点? 原理:随机初始化 K 个质心,反复「样本归最近质心、质心移到簇均值」直到收敛,最小化簇内平方和。缺点:要预先指定 K、对初值和离群点敏感、只适合球形凸簇、对量纲敏感(必须先标准化)。
- PCA 是怎么降维的?什么时候用? 对协方差矩阵做特征值分解,取方差最大的若干方向作为新坐标轴,投影实现降维。用途:高维可视化、去相关降噪、加速训练、缓解维度灾难;前提是特征间存在相关性(否则压不出意义)。
- 怎么评价聚类的好坏? 内部指标:轮廓系数(簇内紧簇间远)、inertia 拐点、DB 指数;外部验证:与业务专家标注对照、看簇的下游效果(分档后推题点击率是否提升)。
自测三问
- 为什么 K-Means 之前必须标准化?
- 肘部法则看的是哪个量?轮廓系数什么范围算好?
- PCA 的第一主成分是什么方向的?怎么知道保留几个成分够用?
小结一句话
没有标签也能干活:K-Means 把相似的东西分堆(记得标准化、科学选 K),PCA 把高维压扁好看好算——簇的含义由业务解释,不是算法给的。
延伸
- scikit-learn 聚类指南
- 下一课:ml-eval 模型评估——准确率的陷阱、精确率召回率的取舍、AUC 和交叉验证。