楼层: 首页/ 软件技术/ Python 机器学习与深度学习/ 聚类:K-Means / DBSCAN / 层次聚类
9

聚类:K-Means / DBSCAN / 层次聚类

Clustering: KMeans, DBSCAN, Hierarchical

无监督学习的代表:没有标签,让算法自己把"相似的"凑成一堆。客户分群、异常检测、图片压缩都用得到。

K-Means

论四步迭代

① 随机选 K 个中心点;② 每个样本归到最近中心;③ 重新算每簇中心(均值);④ 重复 ②③ 直到中心不动。问题:随机初始化可能卡局部最优,所以用 K-Means++(初始中心尽量散开)+ 多次运行。K 怎么选?肘部法则(Elbow Method):画 SSE 随 K 变化的曲线,找"拐点"。

from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score # 假设 X 是客户特征(消费、频次、客单价) scaler = StandardScaler() X_s = scaler.fit_transform(X) # K-Means++ + 跑 10 次取最好 kmeans = KMeans(n_clusters=4, init="k-means++", n_init=10, random_state=42) labels = kmeans.fit_predict(X_s) # 轮廓系数:-1~1,越大越好(>0.5 不错) score = silhouette_score(X_s, labels) print(f"轮廓系数: {score:.3f}") # 轮廓系数: 0.512 # 每簇大小 import numpy as np for k in range(4): print(f"簇 {k}: {(labels==k).sum()} 人")

DBSCAN:基于密度

论能发现任意形状的簇

K-Means 假设簇是"球形",遇到月牙形、环形就抓瞎。DBSCAN 看密度:核心点(半径 ε 内至少有 minPts 个邻居)、边界点(在核心点邻居里但自己邻居不够)、噪声点(谁也不是)。不用指定 K,自动识别噪声。缺点:ε 和 minPts 难调,密度不均匀时效果差。

from sklearn.cluster import DBSCAN db = DBSCAN(eps=0.5, min_samples=5, metric="euclidean") labels = db.fit_predict(X_s) # label=-1 是噪声点 print(f"噪声点:{(labels==-1).sum()}")

层次聚类

Agglomerative(自底向上):开始每个点自己一簇,每次合并最近的两个,直到满意。画成树状图(Dendrogram)直观。适合小数据。

from sklearn.cluster import AgglomerativeClustering agg = AgglomerativeClustering(n_clusters=4, linkage="ward") labels = agg.fit_predict(X_s)

肘部法则选 K

# 画 SSE 随 K 变化曲线,找拐点 import matplotlib.pyplot as plt sse = [] for k in range(1, 11): km = KMeans(n_clusters=k, n_init=10, random_state=42) km.fit(X_s) sse.append(km.inertia_) # 簇内平方和 plt.plot(range(1, 11), sse, "o-") plt.xlabel("K") plt.ylabel("SSE") plt.title("肘部法则:找拐点") plt.show() # 拐点通常在 K=3~5 附近

聚类对比表

维度K-MeansDBSCAN层次聚类
要指定 K?是否事后定
簇形状球形任意形状任意
噪声点不识别自动识别不识别
大数据快慢很慢 O(n²)
必须标准化是是是

DBSCAN 参数怎么调

参数说明
eps(ε)邻域半径。太大所有点成一类,太大多类变噪声。常用 k-distance 图选拐点。
min_samples核心点需要的邻居数。越大越严格,噪声越多。
metric距离度量,默认欧氏。高维数据用余弦。

GMM 高斯混合模型:带"软归属"的聚类

K-Means 有个毛病:每个点必须"非黑即白"地判给某一类。但现实里一个用户可能 60% 像购物狂、40% 像游客。GMM(Gaussian Mixture Model)不硬判,而是告诉你"这个点属于每一类的概率是多少"——软聚类。

论大白话:K-Means 是"站队",GMM 是"打比例"

GMM 假设数据是由 K 个高斯分布(正态分布)混在一起生成的,每个分布就是一个"团"。它用 EM 算法同时学出:① 每个高斯的中心和形状;② 每个点属于每个高斯的概率。K-Means 其实是 GMM 在"高斯形状圆且等大、概率非 0 即 1"时的特例。

sklearn 跑 GMM

from sklearn.mixture import GaussianMixture from sklearn.datasets import make_blobs X, _ = make_blobs(n_samples=300, centers=3, random_state=0) gmm = GaussianMixture(n_components=3, random_state=0) gmm.fit(X) # 硬归属:每个点最可能属于哪一类(和 K-Means 一样) labels = gmm.predict(X) # 软归属:关键!每个点属于各类的概率 proba = gmm.predict_proba(X) print(proba[0]) # [9.9e-01 1.0e-08 1.2e-06] 第0个点 99% 属于第0类,但不是100% # 选 K:用 BIC / AIC,越小越好(自动找合理的簇数) for k in range(1, 6): m = GaussianMixture(n_components=k, random_state=0).fit(X) print(k, f"BIC={m.bic(X):.0f}")

应用场景:① 需要"打分"而非"贴标签"时(客户分层、异常检测——一个点对所有簇概率都低就是异常);② 簇的形状是椭圆而非圆形时,GMM 比 K-Means 拟合得好。

防坑:GMM 假设簇是高斯形

GMM 假设每个簇近似高斯分布。如果真实簇是弯的、环形的(像两个月牙),GMM 和 K-Means 都会切错,这种情况交给 DBSCAN 才对。另外它对初始值也敏感,记得多跑几次或用 n_init。

练习:GMM vs K-Means(点开对答案)

问:什么时候宁可慢也要用 GMM 而不是 K-Means?
答:当你需要"点属于每类的概率"(软聚类),或簇的形状是椭圆、大小不一的时候。只需要一个硬标签、簇近似球形且要快,K-Means 更简单直接。

本章面试题

面试 · 聚类

Q1. K-Means 为什么对初始中心敏感?

查看答案

目标函数非凸,不同初始中心可能收敛到不同局部最优。K-Means++ 让初始中心尽量分散,n_init 跑多次取最优缓解。

Q2. 怎么选 K?

查看答案

肘部法则看 SSE 拐点;轮廓系数看"簇内紧、簇间松";业务上也要看分出来能不能解释。

Q3. DBSCAN 相比 K-Means 优势?

查看答案

不用指定 K;能发现任意形状;能识别噪声点。

Q4. 聚类为什么必须标准化?

查看答案

聚类靠距离,量纲大的特征会主导距离。