无监督学习
聚类、PCA、Autoencoder。
35 分钟
无监督学习
这一课你将能做到
- 用 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。