统计学习方法01-14.2 层次聚类

本博客主要用于本人重新复习知识点,所有参考列在文尾。如有错误,希望一起交流。

基础知识

层次聚类假设类别之间存在层次结构,将样本聚到层次化的类中。属于硬聚类

层次聚类

  • 聚合聚类(自下而上聚类)(agglomerative)
  • 分裂聚类(自上而下聚类)(divisive)(本博客不涉及)

聚合聚类

  • 具体过程:对于给定的样本集合,开始将每个样本分到一个类 ==> 按照一定的规则,例如类间距离最小,将最满足规则条件的两个类进行合并 ==> 如此反复,每次减少一个类,直到满足停止条件,如所有样本聚为一类。
  • 需要预先确定的要素:(根据这些要素的不同组合,可以构成不同的聚类方法)
    • 距离或者相似度:样本与样本之间的距离如何度量。(闵可夫斯基距离、马氏距离、相关系数、夹角余弦等等)
    • 合并规则:合并满足什么条件的两类。(类间距离最小,类间距离可以是最长距离、最短距离、中心距离、平均距离)
    • 停止条件:(所有样本聚为一类等等)

例子

  • 样本间距离度量:欧氏距离;合并规则:类间距离(最短距离)最小;停止条件:类的个数为1。
  • 算法14.1
  • 聚合层次聚类算法复杂度: O ( n 3 m ) O(n^3m) O(n3m) m m m 为样本维数, n n n 为样本个数。

    在层次 t t t 上,有 N − t N-t Nt 个聚类,为了确定 t + 1 t+1 t+1 层上要合并的聚类对,必须考虑 C N − t 2 = ( N − t ) ( N − t − 1 ) / 2 C_{N-t}^2=(N-t)(N-t-1)/2 CNt2=(Nt)(Nt1)/2 个聚类对。这样,聚类过程总共要考虑的聚类对数量就是 C N 2 + C N − 1 2 + . . . + C 2 2 = C N + 1 3 = ( N − 1 ) N ( N + 1 ) / 6 C_{N}^2+C_{N-1}^2+...+C_{2}^2=C_{N+1}^3=(N-1)N(N+1)/6 CN2+CN12+...+C22=CN+13=(N1)N(N+1)/6. m m m 应该是计算距离出现的。

  • 例14.1:
    右下角图可能不是很准确(可忽略)

优点

  • 无需选择K值,无需初始化聚类中心
  • 距离和规则的相似度容易定义,限制少
  • 可以发现类的层次关系

缺点

  • 计算复杂度太高
  • 奇异值也能产生很大影响
  • 每一步都着力于将两个最相似的簇合并,达到了局部最优。但无法根据之后的结果对之前的步骤进行更新,所以最后不一定可以达到全局最优。即当在算法开始阶段,若出现聚类错误,那么这种错误将一直会被延续,无法修改。
  • 算法很可能聚类成链状

Python 实现

Scipy

主要是两个函数:

  • linkage(y, method=‘single’, metric=‘euclidean’, optimal_ordering=False):执行层次聚类
  • fcluster(Z, t, criterion=‘inconsistent’, depth=2, R=None, monocrit=None):从给定链接矩阵定义的层次聚类中形成平面聚类
# -*- coding: utf-8 -*-
import pandas as pd
import matplotlib.pyplot as plt
from scipy.spatial.distance import pdist
from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
from sklearn.cluster import AgglomerativeClustering
from itertools import cycle
from sklearn.datasets import make_blobs
  • scipy.spatial.distance 距离计算模块
  • scipy.cluster 是 scipy 下的一个做聚类的 package,包含两类聚类算法:
    • 矢量量化(scipy.cluster.vq):支持 vector quantization 和 k-means 聚类方法
    • 层次聚类(scipy.cluster.hierarchy):支持 hierarchical clustering 和 agglomerative clustering(凝聚聚类)
  • itertools 是 python 的迭代器模块,提供的工具相当高效且节省内存。
    能够创建自己定制的迭代器用于高效率的循环。
# ========== 生成数据 ===========
# 产生数据的个数、数据中心点
n_samples = 300
centers = [[2, 2], [-1, -1], [1, -2]]

X, labels = make_blobs(n_samples=n_samples, centers=centers, cluster_std=1, random_state=0)
variables = ['X', 'Y']
  • make_blobs 参数: cluster_std:样本中簇的标准差;random_state:指定随机数种子,每个种子生成的序列相同
# =========== 层次聚类 ============
df = pd.DataFrame(X, columns=variables, index=labels)
row_clusters = linkage(pdist(df, metric='euclidean'), method='single')
print(pd.DataFrame(row_clusters, columns=['row label1', 'row label2', 'distance', 'no. of items in clust.'],
                   index=['cluster %d' % (i + 1) for i in range(row_clusters.shape[0])]))

# 平面聚类
f = fcluster(row_clusters, 0.6, 'distance')  # 第二个参数为距离阈值
print("平面聚类结果:", f)

# 绘图
row_dendr = dendrogram(row_clusters, labels=labels)  # 使用树形图查看每个步骤中簇的形成方式
plt.tight_layout()  # tight_layout会自动调整子图参数,使之填充整个图像区域。
plt.title('canberra-complete')
plt.show()

输出:

              row label1  row label2  distance  no. of items in clust.
cluster 1         2656.0      2668.0  0.000975                     2.0
cluster 2          242.0      1964.0  0.001864                     2.0
cluster 3         1043.0      1133.0  0.002340                     2.0
cluster 4         1258.0      1272.0  0.002940                     2.0
cluster 5          278.0      2328.0  0.003447                     2.0
...                  ...         ...       ...                     ...
cluster 2995      5981.0      5993.0  0.668463                  2996.0
cluster 2996       875.0      5994.0  0.700367                  2997.0
cluster 2997      2292.0      5995.0  0.704809                  2998.0
cluster 2998       719.0      5996.0  0.739750                  2999.0
cluster 2999       916.0      5997.0  0.818214                  3000.0

[2999 rows x 4 columns]
  • (n-1)*4 矩阵:第一字段与第二字段分别为聚类簇的编号,第三个字段表示前面两个聚类簇之间的距离,第四个字段表示新生成聚类簇所包含的元素的个数。

样本数为30
样本数为300

Sklearn

sklearn 库下的层次聚类是在 sklearn.cluster 的 AgglomerativeClustering 中

def __init__(self, n_clusters=2, affinity="euclidean",
             memory=None,
             connectivity=None, compute_full_tree='auto',
             linkage='ward', distance_threshold=None):
  • affinity:是一个簇间距离的计算方法
  • linkage:选择计算簇与簇之间距离的策略,包含:ward(方差最小化),complete(最大距离),average(平均距离),single(最小距离)。
n_clusters_ = 3 

ac = AgglomerativeClustering(n_clusters=n_clusters_, affinity='euclidean', linkage='ward')  # 聚合层次聚类
clustering = ac.fit_predict(X)
print('簇的标签:%s' % clustering)

# 绘图
plt.figure(1)
plt.clf()
colors = cycle('bgrcmykbgrcmykbgrcmykbgrcmyk')
for k, col in zip(range(0, 3), colors):
    # 根据lables中的值是否等于k,重新组成一个True、False的数组
    my_members = clustering == k
    # X[my_members, 0] 取出my_members对应位置为True的值的横坐标
    plt.plot(X[my_members, 0], X[my_members, 1], col + '.')
plt.title('euclidean-ward')
plt.show()

这个里面是ward作为类间距离较好。原标签的分类下左图,ward聚类中图,最小距离为右图。明显ward作为距离度量更好:

参考

  1. 李航,统计机器学习方法(第二版),2019:261 - 263.
  2. 复杂度部分:https://www.cnblogs.com/emanlee/archive/2012/02/28/2371273.html
  3. 代码:https://blog.csdn.net/zcmlimi/article/details/87929070
  4. linkage包进行层级聚类:https://blog.csdn.net/yibo492387/article/details/88065036
  5. scipy,sklearn:https://blog.csdn.net/pentiumCM/article/details/105695414

猜你喜欢

转载自blog.csdn.net/sanctuary03/article/details/114687515
今日推荐