这篇文章讲 K-means 的算法步骤、目标函数、收敛性,以及两个著名的问题------k 值怎么选、初始化怎么不"踩坑"(k-means++)------配可运行的代码。
一次"把用户自动分组"的需求
产品经理说:我们有十万用户,但没打任何标签,你能不能把他们"自动分成几类",看看都有哪些典型的用户画像?
这跟之前的分类任务完全不同------没有标签,没有"正确答案" 。你要做的,是让机器自己从数据里"发现"哪些用户天然聚在一起。这类任务叫聚类(clustering) ,而其中最经典、用得最广的算法,就是 K-means。这篇文章把它讲透。
算法步骤:四步循环,简单到"朴素"
K-means 的思路朴素得近乎"直觉":先随便放几个"中心点",把每个点分给离它最近的中心,再根据分好的点重新算中心,反复迭代,直到不动。
具体四步:
text
1. 随机选 k 个点作为初始"聚类中心"(centroid)
2. 分配:把每个样本,分给离它最近的那个中心(形成 k 个簇)
3. 更新:对每个簇,把所有样本的均值作为新的中心
4. 重复 2、3 步,直到中心不再变化(或变化足够小)
就这么简单。它背后的目标也很清晰------让"簇内距离之和"尽可能小。
目标函数:最小化簇内平方和
K-means 优化的目标,是所有样本到它所属中心距离的平方和(SSE,也叫 inertia):
J = ∑ k = 1 K ∑ x ∈ C k ∥ x − μ k ∥ 2 J = \sum_{k=1}^{K} \sum_{x \in C_k} \|x - \mu_k\|^2 J=k=1∑Kx∈Ck∑∥x−μk∥2
其中 μ k \mu_k μk 是第 k k k 个簇的中心。"分配"这一步把 J J J 变小(每个点挪到更近的中心),"更新"这一步也把 J J J 变小(均值是簇内平方和最小的点) ------所以每一步都在单调下降,算法最终会收敛到一个局部最优(但不保证全局最优)。
两个著名的坑:k 怎么选,初始点怎么放
坑一:k 值怎么定
K-means 需要你事先指定分几类(k) ,可现实里你往往不知道。一个常用办法是肘部法则(elbow method):从小到大试不同的 k,画出每个 k 对应的 SSE,找"下降变缓的拐点":
text
SSE
│
│ ╲
│ ╲
│ ╲___← 拐点(肘部),k 就取这里
│ ╲___
└────────────── k
拐点之前,加一个簇能显著降低 SSE;拐点之后,再加簇收益就很小了。那个"肘部"往往就是比较合理的 k。
坑二:初始中心放得不好,结果很差
K-means 对初始中心非常敏感 。初始点如果都挤在一起,可能收敛到一个很差的局部最优。解法是 k-means++ :选初始中心时,让它们尽量互相离得远------
text
1. 随机选第一个中心
2. 选下一个中心时,离"已有中心"越远的点,被选中的概率越大
3. 重复,直到选出 k 个中心
k-means++ 大幅降低了"初始化翻车"的概率,现在 sklearn 默认就用它。
一段能跑的代码
python
from sklearn.cluster import KMeans
import numpy as np
# 造两团数据
X = np.vstack([
np.random.randn(200, 2) + [2, 2],
np.random.randn(200, 2) + [-2, -2],
])
km = KMeans(n_clusters=2, init="k-means++", n_init=10, random_state=42)
km.fit(X)
print("中心点:\n", km.cluster_centers_)
print("每个样本的簇标签:", km.labels_)
print("SSE (inertia):", km.inertia_)
init="k-means++" 用更好的初始化,n_init 是"用不同初始点多跑几次、取最好"------都是为了躲开"初始化翻车"的坑。
优缺点,一条条说清
优点:简单、快(每轮 O(n·k·d))、可解释(中心点就是"典型代表")、可扩展(大数据也能用 MiniBatchKMeans)。
缺点:
- 要事先指定 k;
- 对初始中心敏感(靠 k-means++ 缓解);
- 只擅长"球形、大小相近"的簇,遇到细长形、环形的簇就抓瞎;
- 对异常点敏感(一个离群点能把中心"拽偏");
- 结果可能只是局部最优。
遇到"非球形"的簇,往往要换基于密度 的方法(如 DBSCAN)或谱聚类。
收个尾
回到"自动分组用户"的需求:K-means 用"就近分配 + 重新取均值"这两个反复交替的动作,把十万个用户自动聚成了 k 堆,每堆的中心就是一张"典型用户画像"。它不完美------要猜 k、怕离群点、只认球形簇------但胜在简单高效,所以几十年过去了,它依然是聚类的"默认第一选择"。
K-means 用到的距离度量、均值与最优化,是机器学习数学的典型应用,B站【408实验室】的《机器学习数学基础》能帮你把这些数学底子打牢。