共计 3557 个字符,预计需要花费 9 分钟才能阅读完成。
背景介绍:为什么需要 AGNES?
刚接触机器学习时,大家最先遇到的聚类算法往往是 K -means。但实际业务中,数据之间可能存在层级关系(比如电商商品分类体系),这时候就需要层次聚类方法。AGNES(Agglomerative Nesting)作为最经典的层次聚类算法,它像搭积木一样自底向上合并最相似的数据点,最终形成树状的聚类结构。

与 K -means 这种 ” 硬划分 ” 不同,AGNES 的特点是:
- 不需要预先指定聚类数量
- 通过树状图可以直观看到不同粒度下的聚类结果
- 对非球形分布的数据集效果更好
算法原理拆解
层次聚类 vs 划分式聚类
想象要把教室里的同学分成若干小组:
- K-means 相当于直接喊 ” 分成 3 组 ”,然后不断调整分组
- AGNES 则是让每个同学先自成一组,然后让最亲密的两人组队,接着组与组之间继续合并,直到所有人成为一个大组
三种经典链接方式
合并两个簇时,如何定义 ” 距离 ”?以下是数学表达:
-
单链接(最小距离):
$$d(C_1,C_2) = \min_{x\in C_1, y\in C_2} d(x,y)$$
适合发现长条形簇,但对噪声敏感 -
全链接(最大距离):
$$d(C_1,C_2) = \max_{x\in C_1, y\in C_2} d(x,y)$$
得到更紧凑的簇,但可能拆分大簇 -
平均链接(UPGMA):
$$d(C_1,C_2) = \frac{1}{|C_1||C_2|}\sum_{x\in C_1}\sum_{y\in C_2} d(x,y)$$
平衡型选择,最常用
手把手实验对比
数据准备与可视化
我们先创建一个有明显层级结构的模拟数据:
import numpy as np
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 生成 500 个样本点,3 个中心点
X, y = make_blobs(n_samples=500, centers=3, cluster_std=0.6, random_state=0)
# 添加层级结构:在每类内部再生成子簇
for i in range(3):
cluster_points = X[y == i]
sub_centers = cluster_points[:3] # 取三个点作为子中心
sub_labels = np.argmin(np.linalg.norm(cluster_points[:, np.newaxis] - sub_centers, axis=2),
axis=1)
X[y == i] = cluster_points + 0.1 * np.random.randn(*cluster_points.shape) * sub_labels[:, np.newaxis]
plt.scatter(X[:,0], X[:,1], s=10)
plt.title("原始数据分布")
plt.show()
AGNES 完整实现
虽然 sklearn 没有直接提供 AGNES,但我们可以用 AgglomerativeClustering 结合 linkage_matrix 来实现:
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
# 方法一:使用 sklearn(适合快速获取聚类结果)agg = AgglomerativeClustering(n_clusters=3, linkage='average')
labels = agg.fit_predict(X)
plt.scatter(X[:,0], X[:,1], c=labels, s=10)
plt.title("sklearn 聚类结果")
plt.show()
# 方法二:使用 scipy 绘制树状图(更直观)Z = linkage(X, method='average')
plt.figure(figsize=(10, 5))
plt.title("层次聚类树状图")
dendrogram(Z, truncate_mode='lastp', p=12, show_leaf_counts=True)
plt.xlabel("样本索引或簇大小")
plt.ylabel("距离")
plt.show()
三大算法对比
用同一组数据对比 AGNES、K-means 和 DBSCAN:
from sklearn.cluster import KMeans, DBSCAN
# K-means
kmeans = KMeans(n_clusters=3, random_state=0).fit(X)
plt.scatter(X[:,0], X[:,1], c=kmeans.labels_, s=10)
plt.title("K-means 聚类结果")
plt.show()
# DBSCAN
dbscan = DBSCAN(eps=0.3, min_samples=5).fit(X)
plt.scatter(X[:,0], X[:,1], c=dbscan.labels_, s=10)
plt.title("DBSCAN 聚类结果")
plt.show()
通过对比可以发现:
- K-means 强制划分为球形簇,破坏了原有层级结构
- DBSCAN 能发现噪声点,但对密度变化敏感
- AGNES 保留了数据的层次特性
性能优化技巧
加速计算的方法
当样本量较大时,原始 AGNES 的 O(n³)复杂度会成为瓶颈。两种优化方案:
-
使用
scipy.spatial.distance.pdist预计算距离矩阵:from scipy.spatial.distance import pdist # 只计算上三角距离矩阵,节省内存 dist_matrix = pdist(X, metric='euclidean') Z = linkage(dist_matrix, method='average') -
对于超大数据集(>1 万样本),改用近似算法:
# 使用 FastCluster 库(需安装)import fastcluster Z = fastcluster.linkage_vector(X, method='average')
树状图解读要领
- 纵轴距离:反映合并时的相似度,突然跃升的位置往往对应合理的聚类数目
- 颜色划分 :用
color_threshold参数突出关键切割线dendrogram(Z, color_threshold=0.7 * max(Z[:,2])) - 水平线法则:寻找不与任何垂直线相交的水平线作为切割点
新手避坑指南
内存优化
当特征维度很高时,直接计算距离矩阵可能内存溢出。解决方案:
- 先做 PCA 降维
- 使用稀疏矩阵格式
- 分块计算距离
from sklearn.decomposition import PCA
# 降至 5 维再聚类
pca = PCA(n_components=5)
X_pca = pca.fit_transform(X)
Z = linkage(X_pca, method='average')
大样本处理
样本量超过 1 万时,考虑这些替代方案:
- BIRCH 算法:适合数值型特征,先构建 CF 树再聚类
- Mini-Batch AGNES:随机分批次合并
- 基于 Spark 的实现:
# 使用 Spark MLlib(需 PySpark 环境)from pyspark.ml.clustering import BisectingKMeans bkm = BisectingKMeans(k=3).fit(spark_df)
进阶思考方向
质量评估方法
除了肉眼观察,还可以用轮廓系数定量评估:
from sklearn.metrics import silhouette_score
# 遍历不同聚类数
for n_clusters in range(2, 6):
agg = AgglomerativeClustering(n_clusters=n_clusters, linkage='average')
labels = agg.fit_predict(X)
score = silhouette_score(X, labels)
print(f"聚类数 {n_clusters} 的轮廓系数:{score:.3f}")
分布式实现思路
要处理超大规模数据,可以参考以下架构:
- 数据分片:各 worker 计算局部聚类
- 合并策略:
- 定期同步全局聚类中心
- 采用两阶段合并(局部→全局)
- 容错机制:检查点保存中间状态
总结回顾
通过这次实验,我们完整走过了 AGNES 聚类的全流程:
- 理解了层次聚类相比划分式聚类的优势
- 掌握了三种链接方式的适用场景
- 学会了用树状图选择最佳聚类数
- 实践了大数据场景下的优化技巧
建议下一步:
- 在 UCI 数据集上测试不同距离度量(欧式、余弦等)的效果
- 尝试将 AGNES 用于文本聚类(需先做 TF-IDF 向量化)
- 结合业务场景设计自定义距离函数
AGNES 就像显微镜,能让我们看到数据中隐藏的层次结构。虽然计算成本较高,但在需要精细分析场景下仍是不可替代的工具。
