基于scipy的层次聚类探究

最近又要开始做聚类了,之前做过的层次聚类的方法是用sklearn下的聚类,但是存在一定的缺陷:库并没有给我们提供足够的函数去评价聚类效果,如果想要对层次聚类树自定义损失函数,那么我们就需要剖析整棵层次聚类树了。在这样的背景下,我仔细研究了scipy下的层次聚类的API函数,并运用到了实际项目中。

我们分两种情况来进行讨论:1、给定了距离矩阵,2、没给定距离矩阵

1、给定了距离矩阵:

我们可以基于距离矩阵来计算。

当然,在凝聚层次聚类的过程中,我们需要对簇间的距离进行刻画。在scipy中,linkage的method有很多种:single、average、complete、weighted、centroid、median、ward等,这个method表示的是linkage算法,即聚类的连接算法。比如single表示最近距离,在计算两个簇的距离的时候,选取两个簇中最近的两个观测点作为簇间距离。average表示的是平均距离,complete表示的是最远距离。

此外还有一个参数metric,即计算两个观测点的距离,由于我们给定了距离矩阵,可以直接获得两个观测点的距离,但是,linkage函数并没有给我们提供类似于sklearn的precomputed选项,好在metric可以自定义,我们自定义metric。这里举个例子:

 # 计算距离矩阵中两行的距离

def func(u, v):
    v_list = list(v)
    pos_v = v_list.index(0.0)
    return u[pos_v]

这里的两个参数u和v,代表距离矩阵中的任意两行,那么要得到二者的距离,则需要找到相应的位置,如函数中所写。

然后,我们再用linkage,使用average连接算法,func度量方式

z = sch.linkage(data_square, method='average', metric=func)

如此一来我们可以计算任意两个观测点的距离。同时,由于凝聚层次聚类会合并距离矩阵的凝聚点的行,因此,该函数还可以计算两个簇的距离,这里的簇被抽象成了观测点。

然后我们再绘制聚类图谱,可以看聚类凝聚顺序。

sch.dendrogram(z)
plt.show()

另外,由于我们要对整棵树进行剖析,我们需要对树的节点进行分析:

tree_node = sch.to_tree(z, True)

这个函数可以根据z矩阵得到树中的各个节点,返回一个列表。从叶子结点开始,然后再依次是凝聚点。次序和凝聚的次序是一致的,即先凝聚的点先放进节点列表中。假设有N个观测点,那么前N个点为观测节点,后面的N-1个点为凝聚点。

node_list = tree_node[1][N:]

可以以这种方式得到凝聚点。

树中的节点的数据结构的组织形式为[id, left, right,dist,count]:节点id,左子树“指针”,右子树“指针”,凝聚时的高度,以该节点为根节点的子树的节点总数。对于叶子结点而言,left和right都为None,dist为0,count为0。

之后,我们就可以自定义损失函数来对聚类质量进行评估了,当然我们评估聚类质量的目的是找到一个好的聚类参数譬如阈值或者聚类数目,无需人工预设。下面介绍几种方法:

1、轮廓系数,思路是以轮廓系数为最优化目标,绘制阈值-轮廓系数曲线,取最大点对应的阈值。这个方法的结果取决于数据分布,数据分布太重要了!在一些情况下会产生过拟合,如果簇内有两个簇距离特别远,而这两个簇内部理应继续划分的,但是使用轮廓系数却只会得到两个簇,这严重影响了聚类质量。

2、考虑簇内误差度量每个凝聚点的误差,思路是通过设定阈值,计算每次聚类的每个簇内的点到簇中心点的距离,然后求和,作为损失函数,采用“肘方法”或者失真函数的方法得到一个最优阈值。在这里,对于层次聚类而言,由于中心点不能直接计算,我们可以选择一个簇内的点代表中心点,满足它到簇内其他点的距离和最小。

3、根据层次聚类的层次聚类树谱图来发现最优聚类个数,可以在凝聚点的距离差上面做文章。

通过阈值的方式进行聚类的函数如下,d为设定的阈值,可以取各凝聚点对应的阈值,即dist

labels_pred = sch.fcluster(z, t=d, criterion='distance')

2、没有给定距离矩阵

如果我们的数据是向量形式的,那么我们可以将数据直接以“样本数-特征数”矩阵的方式进行输入,也可以先转换为距离矩阵再进行给定距离矩阵情形下的操作。

data_pdist = dist.pdist(data, metric="cosine") # 对于样本-数目矩阵,可以计算cosine相似度

data_square = dist.squareform(data_pdist)  # 生成距离矩阵

总的来说,层次聚类的方法取决于数据的形式:如果对于“样本数-特征数”矩阵形式的数据,那么无需距离矩阵的构造了。如果数据无法表示成“样本数-特征数”,譬如短文本的表示,我们可能没法或者很难使用一个向量来表达一个句子——因为单词的太多容易造成矩阵稀疏。那么我们可以构造一个距离矩阵,只需得到两两观测点的距离进而进行聚类分析。缺点是一旦数目很大,聚类算法的复杂度会很高,同时层次聚类要一次性将数据载入内存,数据量太大会报Memory Error,对于千万级的数据无能为力。

猜你喜欢

转载自blog.csdn.net/u012260341/article/details/79849347