层次聚类实例以及Java实现

来源:互联网 发布:战网更新网络错误 编辑:程序博客网 时间:2024/06/05 14:25

本文主要讲述一个层次聚类实例,使用层次聚类算法将相似图书的目录进行聚类。由于不知道要聚为多少类,所以Kmeans聚类算法不适用,而层次聚类由于不需要设置聚类中心数,只要传递一个阈值即可,所以正好适合于这个问题。本文将结合层次聚算法的原理以及其Java代码实现来展现文本聚类的一个实现方式。具体代码见我的GitHub(点击打开链接)


层次聚类

层次聚类方法对给定的数据集进行层次的分解,直到某种条件满足为止。具体又可分为凝聚和分裂两种方案。
1.凝聚的层次聚类是一种自底向上的策略,首先将每个对象作为一个簇,然后合并这些原子簇为越来越大的簇,直到所有的对象都在一个簇中,或者某个终结条件被满足,绝大多数层次聚类方法属于这一类,它们只是在簇间相似度的定义上有所不同。
2.分裂的层次聚类与凝聚的层次聚类相反,采用自顶向下的策略,它首先将所有对象置于同一个簇中,然后逐渐细分为越来越小的簇,直到每个对象自成一簇,或者达到了某个终止条件。

这里重点介绍一下第一种自下而上的层次聚类:
假设有N个待聚类的样本,对于层次聚类来说(自下而上),基本步骤就是:
1、(初始化)把每个样本归为一类,计算每两个类之间的距离,也就是样本与样本之间的相似度;
2、寻找各个类之间最近的两个类,把他们归为一类(这样类的总数就少了一个);
3、重新计算新生成的这个类与各个旧类之间的相似度;
4、重复2和3直到所有样本点都归为一类, 或者某个终结条件被满足(比如类之间的相似度小于设定的阈值),则结束。

整个聚类过程其实是建立了一棵树,在建立的过程中,可以通过在第二步上设置一个阈值,当最近的两个类的距离大于这个阈值,则认为迭代可以终止。另外关键的一步就是第三步,如何判断两个类之间的相似度有不少种方法。这里介绍一下三种:
SingleLinkage:又叫做 nearest-neighbor ,就是取两个类中距离最近的两个样本的距离作为这两个集合的距离,也就是说,最近两个样本之间的距离越小,这两个类之间的相似度就越大。容易造成一种叫做 Chaining 的效果,两个 cluster 明明从“大局”上离得比较远,但是由于其中个别的点距离比较近就被合并了,并且这样合并之后 Chaining 效应会进一步扩大,最后会得到比较松散的 cluster 。
CompleteLinkage:这个则完全是 Single Linkage 的反面极端,取两个集合中距离最远的两个点的距离作为两个集合的距离。其效果也是刚好相反的,限制非常大,两个 cluster 即使已经很接近了,但是只要有不配合的点存在,就顽固到底,老死不相合并,也是不太好的办法。这两种相似度的定义方法的共同问题就是指考虑了某个有特点的数据,而没有考虑类内数据的整体特点。
Average-linkage:这种方法就是把两个集合中的点两两的距离全部放在一起求一个平均值,相对也能得到合适一点的结果。
average-linkage的一个变种就是取两两距离的中值,与取均值相比更加能够解除个别偏离样本对结果的干扰。

缺点:层次聚类描述起来比较简单,但是计算复杂度比较高,为了寻找距离最近/远和均值,都需要对所有的距离计算个遍,需要用到双重循环。另外从算法中可以看出,每次迭代都只能合并两个子类,这是非常慢的。

文本聚类

这里我们对关于“计算机网络”的多本图书的所有一级目录进行聚类,目录如下图所示(总共有1118条目录):


使用层次聚类算法对文本进行聚类首先需要计算文本之间的相似度,这里文本之间的相似度采用的是余弦相似度计算公式:
文本的向量表示采用两种方式,第一种是文本分词之后每一个词的word2vec向量相加求均值代表文本的向量;第二种使用文本的doc2vec向量代表文本的向量。
这里,我使用来自13万多本图书的所有一级目录训练Word2vec模型和doc2vec模型,具体的方法可以借鉴我之前的一篇文章(深度学习笔记——Word2vec和Doc2vec应用举例:词和句子的相似度计算)。
在得到目录标题两两之间的相似度矩阵之后,将相似度矩阵传入层次聚类算法,进行聚类,聚类结束的阈值设为0.8,代码如下:
public static void main(String[] args) throws IOException{double disThreshold = 0.8;//加载数据ArrayList<String> sentencelist = new ArrayList<>();ArrayList<Integer> senindex = new ArrayList<>();int count = 0;try (InputStreamReader output1 = new InputStreamReader(new FileInputStream(sentWordsavepath), "utf-8")) {BufferedReader reader = new BufferedReader(output1);String line = "";while((line = reader.readLine()) != null) {//if (count == 10) break;sentencelist.add(line.split("\t")[0].split(":")[1]);senindex.add(count);count++;}}ArrayList<double[]> sen2vec_word = TextSimilarity.getVector(sentWordsavepath);ArrayList<double[]> sen2vec_doc = TextSimilarity.getVector(sentDocsavepath);//计算相似度矩阵double[][] simmatrix1 = TextSimilarity.CalSimMatrix(sen2vec_word);double[][] simmatrix2 = TextSimilarity.CalSimMatrix(sen2vec_doc);//层次聚类HierarchicalClustering hc = new HierarchicalClustering();List<Cluster> cluste_res1 = hc.starAnalysis(senindex, simmatrix1, disThreshold);List<Cluster> cluste_res2 = hc.starAnalysis(senindex, simmatrix2, disThreshold);HierarchicalClustering.writeClusterToFile(clusterFilePath1, cluste_res1, sentencelist);HierarchicalClustering.writeClusterToFile(clusterFilePath2, cluste_res2, sentencelist);}

HierarchicalClustering.starAnalysis()方法实现层次聚类(这里两个类之间相似度计算采用的是最简单的一种方式,即取两个类中距离最近的两个样本的距离作为这两个集合的距离,另外两种方式也很容易就可以实现):
/** * 对输入的句子进行聚类 * @param CatalogList * @param sim * @param threshold * @return * @throws IOException */public List<Cluster> starAnalysis(ArrayList<Integer> CatalogList, double[][] sim,double threshold) throws IOException{List<Cluster> finalClusters = new ArrayList<Cluster>();List<Cluster> originalClusters = initialCluster(CatalogList);finalClusters = originalClusters;while(finalClusters.size() >  1){double max = Double.MIN_VALUE; int mergeIndexA = 0;              int mergeIndexB = 0;            for (int i = 0; i < finalClusters.size(); i++) {                  for (int j = i + 1; j < finalClusters.size(); j++) {                       Cluster clusterA = finalClusters.get(i);//获取聚类1                      Cluster clusterB = finalClusters.get(j);//获取聚类2                      List<Integer> dataPointsA = clusterA.getDataPoints();                      List<Integer> dataPointsB = clusterB.getDataPoints();                                                //以下两个for循环来确定应聚那两个类                     double minTempWeight = Double.MAX_VALUE;                    boolean flag = false;                    for (int m = 0; m < dataPointsA.size(); m++) {                      for (int n = 0; n < dataPointsB.size(); n++) {//划分类的标准: 两类中相似度最低的两个focus作为类的距离。                        //计算两个聚类focus之间的相似度,选择相似度最低的focus之间的相似度作为类的相似度                            double tempWeight = sim[dataPointsA.get(m)][dataPointsB.get(n)];                            if (tempWeight > 0.7)                                 System.out.println(tempWeight);                                                            if(tempWeight < minTempWeight){                                minTempWeight = tempWeight;                                flag = true;                            }                                                                                       }//end_for                      }//end_for                                                                                          if (minTempWeight > max && flag == true) {                          max = minTempWeight;                          mergeIndexA = i;// --保存相似度最高的focus所在的簇                        mergeIndexB = j;// --保存相似度最高的focus所在的簇                    }//end_if                  } // end for j              }// end for i              //将cluster[mergeIndexA]与cluster[mergeIndexB]合并            if(max < threshold) {                  System.out.println("相似度小于指定的阈值,聚类结束!");                  break;              }              finalClusters = mergeCluster(finalClusters, mergeIndexA, mergeIndexB);          }// end while          return finalClusters;             }

接下来让我们来看看这两种向量方式表达的图书目录聚类结果:
Word2vec取均值代表目录标题的向量:
Cluster 0953664:数据传输与通信接口952103:数据传输Cluster 1953665:数据链路控制912973:数据链路控制Cluster 2953666:网络层与传送层1132928:传送层与会话层959433:会晤层、表示层和应用层716323:物量层Cluster 3953667:高层协议798185:高层协议1127825:高层协议1130729:高层协议898055:传输协议及高层协议Cluster 4953668:局域网956411:局域网705438:局域网810551:局域网813180:局域网796715:局域网838449:局域网870661:局域网845949:局域网1127826:局域网955068:局域网716326:局域网755213:局域网785957:局域网872479:局域网852828:局域网846647:局域网888551:局域网904831:局域网905050:局域网908543:局域网908593:局域网897097:局域网705375:局域网与广域网934294:局域网与广域网799471:局域网与广域网953848:Novell局域网1130727:局域网的体系结构841870:局域网互联915305:虚拟局域网VLANCluster 5953669:高速局域网845950:高速以太网Cluster 6953670:网络管理798189:网络管理817528:网络管理845952:网络管理898060:网络管理952315:网络管理758307:网络管理796710:网络管理876976:网络管理852832:网络管理934303:网络管理798959:网络软件和网络管理764079:网络管理技术895232:网络管理技术Cluster 7953671:网络的规划与设计705342:网络系统集成、规划与设计954290:网络设计705404:网络设计846649:网络设计743259:WindowsNT网络设计与架设959439:企业网络的设计Cluster 8953846:计算机网络与社会956410:计算机网络的体系结构1127819:计算机网络的体系结构818600:计算机网络的体系结构888550:计算机网络的体系结构904093:计算机网络的通信子网915303:计算机网络寻址852690:计算机网络的若干热点问题766470:计算机网络的层次化结构1132924:计算机网络的结构954284:计算机网络的结构Cluster 9953847:计算机信息系统与网络基本概念749728:计算机网络基本概念874002:计算机网络的基本概念1035678:计算机网络基本概念724660:计算机网络基本概念762865:计算机网络的基本概念835686:计算机网络的基本概念Cluster 11953849:Internet网813703:工作组网与资源共享952388:CHINAPAC网、INTERNET网、CHINANET网介绍877339:网浏览952001:Novell网与NetWare网络操作系统Cluster 12953850:+以太网Cluster 13953851:计算机网络安全852698:计算机网络安全705407:计算机网络安全798175:计算机网络安全951887:计算机局域网络30890:计算机局域网络1108079:计算机局域网络824918:计算机局域网络841578:计算机局域网络753420:计算机联网799149:计算机局域网和城域网705338:计算机局域网705400:计算机局域网796704:计算机局域网813706:计算机局域网951131:计算机局域网951539:计算机局域网952393:计算机局域网926089:计算机局域网1075062:计算机局域网951540:计算机广域网络Cluster 14956408:计算机网络基础知识705437:计算机网络基础知识736376:计算机网络基础知识751993:计算机网络基础知识785954:计算机网络基础知识813700:计算机网络基础知识860486:计算机网络基础知识844435:计算机网络基础知识951999:计算机网络的基础知识1052997:计算机网络的基础知识798168:计算机网络与数据通信基础知识705397:计算机网络基础743258:计算机网络基础808552:计算机网络基础828866:计算机网络基础863160:计算机网络基础846636:计算机网络基础904828:计算机网络基础907770:计算机网络基础951537:计算机网络基础796702:计算机网络技术基础837683:计算机网络技术基础915298:计算机网络技术基础1035313:计算机网络技术基础841576:计算机网络与网络体系结构概述845946:网络概述905049:网络的概述860715:网络技术基础879169:网络基础852827:网络基础897422:网络基础877334:网络基础知识877898:网络基础知识Cluster 15956409:网络中的数据通信技术876968:网络数据通信技术1154291:网络中的数据通信技术749729:数据通信技术基础1035679:数据通信技术基础810596:数据通信技术基础796460:数据通信技术概论810550:数据通信技术887789:数据通信技术824914:数据通信技术904829:数据通信技术897100:计算机网络通信技术及发展Cluster 18956412:网络互连技术751999:网络互连技术796707:网络互连技术798958:网络互连技术888553:网络互连技术951543:网络互连技术及应用30891:网络互联及建网技术824920:网络互联及建网技术846648:网络互联技术841580:网络互联与路由技术852830:网络互联与接入技术709927:ATM与宽带网络技术876980:网络存储与磁盘阵列技术705340:高速网络技术1035683:网络技术发展818599:网络技术的发展724665:网络技术发展及应用954292:网络技术1154296:Unix网络技术915308:VPN网络技术Cluster 19956413:国际互联网Internet798187:国际互联网Internet951542:国际互联网——INTERNETCluster 20956414:NetWare网络操作系统的使用

Doc2vec代表目录标题的向量:
Cluster 0953664:数据传输与通信接口Cluster 1953665:数据链路控制912973:数据链路控制Cluster 2953666:网络层与传送层Cluster 3953667:高层协议798185:高层协议897098:网络高层协议898055:传输协议及高层协议Cluster 4953668:局域网Cluster 5953669:高速局域网Cluster 6953670:网络管理Cluster 7953671:网络的规划与设计705342:网络系统集成、规划与设计926093:计算机网络应用及网络规划Cluster 8953846:计算机网络与社会953847:计算机信息系统与网络基本概念749728:计算机网络基本概念724660:计算机网络基本概念874002:计算机网络的基本概念762865:计算机网络的基本概念835686:计算机网络的基本概念1035678:计算机网络基本概念Cluster 10953848:Novell局域网959438:Novell局域网的基本原理Cluster 11953849:Internet网Cluster 12953850:+以太网Cluster 13953851:计算机网络安全852698:计算机网络安全705407:计算机网络安全798175:计算机网络安全819903:计算机网络安全技术概论828867:计算机网络安全基础874007:计算机网络安全与管理828875:计算机网络安全管理技术Cluster 14956408:计算机网络基础知识705437:计算机网络基础知识736376:计算机网络基础知识751993:计算机网络基础知识813700:计算机网络基础知识844435:计算机网络基础知识860486:计算机网络基础知识951999:计算机网络的基础知识785954:计算机网络基础知识1052997:计算机网络的基础知识798168:计算机网络与数据通信基础知识753419:计算机基础知识Cluster 15956409:网络中的数据通信技术876968:网络数据通信技术810550:数据通信技术824914:数据通信技术887789:数据通信技术904829:数据通信技术796460:数据通信技术概论30888:数据通信的基础知识769585:数据通信的基础知识749729:数据通信技术基础1035679:数据通信技术基础810596:数据通信技术基础Cluster 16956410:计算机网络的体系结构818600:计算机网络的体系结构888550:计算机网络的体系结构1127819:计算机网络的体系结构Cluster 17956411:局域网Cluster 18956412:网络互连技术751999:网络互连技术796707:网络互连技术888553:网络互连技术951543:网络互连技术及应用Cluster 19956413:国际互联网Internet798187:国际互联网Internet951542:国际互联网——INTERNETCluster 20956414:NetWare网络操作系统的使用844437:NetWare网络操作系统952395:网络操作系统NetWare

从上面的结果可以看出,虽然两个都取得了不错的效果,但是Word2vec取均值代表的目录标题向量聚类结果还是要比Doc2vec向量表示的聚类结果好一些,这可能跟Doc2vec训练不充分有关系。不过也从一个侧面显示了,用句子中词的词向量相加取均值代表句子的向量表示往往也能取得很不错的结果。

具体的工程代码可以在我的GitHub上查看,欢迎fork和star。地址:点击打开链接





原创粉丝点击