分类与聚类
数据洗干净了,这一课开始挖规律。学完它,你会用两个"分类"算法和一个"聚类"算法各跑通一个完整例子,并且说清它们各自适合什么场景。
先记住一句话:分类是照着答案学,聚类是自己找分堆。
一、先分清两类问题
- 分类(监督学习):手里有一批带答案的数据——每位会员标好了"流失 / 没流失"。学的是"特征 → 答案"的映射,目的是给新会员贴标签
- 聚类(无监督学习):手里只有特征,没有答案。目的是把相似的东西归到一堆,自己发现"原来数据里有几类人"
| 分类 | 聚类 | |
|---|---|---|
| 有没有标签 | 有 | 没有 |
| 典型算法 | KNN、决策树、朴素贝叶斯 | K-Means、层次聚类 |
| 怎么评估 | 划一部分当考题算准确率 | 轮廓系数、人工解读分得合不合理 |
| 店长问法 | "这位会员会流失吗?" | "我的会员分几群?" |
二、准备工作:一份自带答案的数据集
scikit-learn 自带鸢尾花数据集(150 朵花,4 个特征,3 个品种),不用下载,本课全部实验都用它:
python
from sklearn.datasets import load_iris
iris = load_iris(as_frame=True)
X, y = iris.data, iris.target # X 是 4 列特征,y 是标准答案(0/1/2 三种花)
print(iris.data.head())
print(iris.target_names) # 三个品种的名字三、分类第一式:KNN,看邻居投票
K 近邻(KNN)的思路朴素到不像算法:来了一个新样本,在历史数据里找和它最像的 k 个,这 k 个邻居里哪类最多,它就是哪类——物以类聚,近朱者赤。
动手之前必须先做一件事:把数据分成训练集和测试集(第一课说的"上课题"和"考试题"),否则准确率是自欺欺人:
python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
iris = load_iris(as_frame=True)
X, y = iris.data, iris.target
# 80% 上课(训练),20% 考试(测试);random_state 固定随机种子,保证每次结果一样
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
model = KNeighborsClassifier(n_neighbors=5) # k = 5:找 5 个邻居投票
model.fit(X_train, y_train) # 学习:KNN 其实只是把题背下来
pred = model.predict(X_test) # 考试
print("准确率:", accuracy_score(y_test, pred)) # 大概率在 0.93 ~ 1.0 之间stratify=y 让两次划分里三个品种比例一致——考试题和上课题的"题型分布"要一样,这个参数几乎总该带上。
关于 k 的取值:k 太小,容易被个别噪声邻居带偏;k 太大,边界又会被抹平。它没有标准答案,习惯是拿奇数、再动手试——下面实验 1 就是让你试。
四、分类第二式:决策树,一连串 if-else
决策树学到的不是"背题",而是一串按重要性排好的提问:"花 petal 长度小于 2.45 吗?是 → 品种 A;不是 → 再问下一个问题……"。它最大的优点是人能看懂,用一行代码把树打印出来:
python
from sklearn.tree import DecisionTreeClassifier, export_text
tree = DecisionTreeClassifier(max_depth=3, random_state=42)
tree.fit(X_train, y_train)
print(export_text(tree, feature_names=list(X.columns)))
print("准确率:", accuracy_score(y_test, tree.predict(X_test)))
# 还能看模型认为哪个特征最有分量
for name, imp in sorted(zip(X.columns, tree.feature_importances_), key=lambda t: -t[1]):
print(name, imp)对照打印出的树你会发现:判定品种几乎只靠 petal length / petal width 两列,其他特征的重要性接近 0——这就是决策树的附带福利:顺手完成了特征重要性分析。max_depth=3 限制树的深度,防它把每道训练题都背下来(那叫过拟合,考试必翻车)。
🧠 记忆锚点:KNN 靠背,决策树靠问。 KNN 把训练集原样存着用的时候翻;决策树把规律总结成一连串是非题。一个快而懒,一个慢工出常识。
五、聚类:K-Means,自己找分堆
没有答案时用 K-Means。四步循环:
text
① 随手定 k 个中心 → ② 每个样本就近入伙
③ 中心移到本堆的平均位置 → ④ 重复②③直到中心不再挪动用 K-Means 把鸢尾花在没有答案的情况下分成 3 堆,再和真答案对一对:
python
from sklearn.cluster import KMeans
import pandas as pd
km = KMeans(n_clusters=3, n_init=10, random_state=42)
clusters = km.fit_predict(X) # 只用特征 X,完全不碰答案 y
# 把"自己分的堆"和"真实品种"放一张表里对照
print(pd.crosstab(clusters, y, rownames=["分出的堆"], colnames=["真实品种"]))
print("各堆中心:\n", km.cluster_centers_)crosstab 的结果会让你会心一笑:有一堆和品种 0 完全对上,另外两堆大体对上、略有串门——没看答案,也基本把三堆挖出来了。这就是聚类的价值:它能在没人标注的数据里先探出"天然有几群"。
k 怎么选? 常用肘部法:把 k 从 1 试到 8,画"簇内平方和"(km.inertia_)随 k 变化的曲线,曲线从陡变缓的拐点(胳膊肘)就是合适的 k:
python
for k in range(1, 9):
km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X)
print(k, round(km.inertia_, 1))六、避坑:准确率的两个陷阱
- 考题 = 上课题:不给
train_test_split直接算准确率,能轻松接近 100%——但那是在背书,不是在考试 - 答案泄进了特征:如果你不小心把"是否流失"这一列也喂给了模型当特征,准确率会高得吓人。准确率好得不真实时,第一反应是查有没有数据泄露,第二反应才是高兴
⚠️ 聚类没有"准确率"可言。分出来的堆必须人工解读——"这一堆消费高、来得勤,是宝贝;那一堆快三个月没来,快流失了"。模型只负责分堆,讲故事得靠你。
七、动手实验
💡 动手实验 1:把 KNN 的
n_neighbors从 1 依次改到 21(只取奇数),每次都打印准确率。你会看到准确率随 k 起伏——亲手感受"k 太小学噪声、k 太大和稀泥",然后写下你认为最好的 k。
💡 动手实验 2:把决策树的
max_depth依次改成 1、2、3、5、10,对比准确率。深度太大时它可能反而考得更差——这就是过拟合的现场:训练题全对,考试题拉胯。
💡 动手实验 3:自己造 30 行"两列特征"的数据(比如会员的
月均消费和月均到店次数,随手编三档人群各 10 行),跑 K-Means 聚成 3 堆并打印每堆中心。你编数据时"心里想的那三类",模型凭两列数字就找回来了——这就是"分群"最直观的体感。
小结
- 分类照着答案学(监督),聚类自己找分堆(无监督)——分界线就一条:有没有标签
- KNN 看邻居投票,k 太小学噪声、k 太大和稀泥;决策树是一串 if-else,还附送特征重要性
- 训练集 / 测试集必须分开(
train_test_split),考试题绝不能和上课题重复 - K-Means 四步:定中心 → 就近入伙 → 移中心 → 循环;k 用肘部法挑
- 准确率高得不真实,先查数据泄露;聚类结果必须人工解读才有意义
下一课我们钻进超市小票堆里:关联规则——啤酒和尿布到底是怎么回事,支持度、置信度、提升度又是什么。