k-means 聚类:没有标签怎么分组
约 14 分钟
前面七节都是监督学习——数据带标签,模型学「输入→答案」的映射。这一节回到无监督学习:只有数据,没有答案,让算法自己找结构。
k-means 的四步循环
目标:把数据分成 个簇。
① 随机选 k 个点作为初始中心
② 把每个样本分给离它最近的中心
③ 重新计算每个簇的中心(取簇内所有点的均值)
④ 回到 ②,直到中心不再移动
就这么简单,而且一定会收敛(每一轮的目标函数都不会增大)。
它在最小化什么:簇内平方和(惯性)
即:让每个点尽量靠近自己簇的中心。
三个必须知道的坑
① 结果依赖初始点
初始中心选得不好,可能收敛到很差的局部最优。
解决:k-means++——初始点不完全随机,而是倾向于选彼此距离远的点。sklearn 默认就用它(init='k-means++'),并且默认跑 10 次取最好的(n_init=10)。
② k 要自己定
算法不会告诉你该分几类。两个常用方法:
肘部法则:把 从 1 到 10 各跑一遍,画「 vs 惯性」曲线。曲线下降由陡变缓的「肘部」就是候选 。
轮廓系数:衡量「簇内紧密度」和「簇间分离度」,取值 ,越接近 1 越好。比肘部法则更客观。
from sklearn.metrics import silhouette_score
for k in range(2, 11):
labels = KMeans(n_clusters=k, n_init=10).fit_predict(X)
print(k, silhouette_score(X, labels))
③ 只能找「球形」的簇
因为它用欧氏距离、以均值为中心,k-means 假设簇是各向同性的球状且大小相近。
遇到这些情况会失败:
- 月牙形、环形的簇
- 大小悬殊的簇
- 密度差异大的簇
替代方案:
| 算法 | 擅长 |
|---|---|
| DBSCAN | 任意形状的簇;自动确定簇数;能识别噪声点 |
| 层次聚类 | 需要树状结构(谱系图);不用预设 k |
| 高斯混合模型 | 椭圆形簇;给出软分配(属于各簇的概率) |
同样必须做特征缩放
和 KNN 一样,k-means 基于欧氏距离,量纲大的特征会主导聚类结果。
聚类结果怎么评价
没有标签,就没有「准确率」。 分两种情况:
| 情况 | 指标 |
|---|---|
| 完全没有标签 | 轮廓系数、Calinski-Harabasz 指数 |
| 有真实标签(用来检验算法) | 调整兰德指数 ARI、归一化互信息 NMI |
注意不能直接用准确率——聚类给出的簇编号是任意的(把簇 0 和簇 1 换个名字,结果没变但准确率会全错)。ARI 和 NMI 就是为了解决这个问题。
实际用在哪
- 用户分群:把用户按行为聚成几类,分别做运营策略
- 图像压缩:把几百万种颜色聚成 16 种,只存簇中心
- 异常检测:离所有中心都很远的点,可能是异常
- 特征工程:把「属于哪个簇」当成一个新特征喂给监督模型
最后一条很实用——聚类不一定是终点,它常常是特征工程的一环。
练习:一份数据里,「月消费额」范围是 0–50000,「购买次数」范围是 0–50。不做缩放直接 k-means,聚类结果实际上是按什么分的?
k-means 的 k 怎么确定?它假设簇是什么形状,遇到月牙形数据该换什么算法?
登录 后可看答案