上篇k-means算法却是一种方便好用的聚类算法,但是始终有K值选择和初始聚类中心点选择的问题,而这些问题也会影响聚类的效果。为了避免这些问题,我们可以选择另外一种比较实用的聚类算法-层次聚类算法。顾名思义,层次聚类就是一层一层的进行聚类,可以由上向下把大的类别(cluster)分割,叫作分裂法;也可以由下向上对小的类别进行聚合,叫作凝聚法;但是一般用的比较多的是由下向上的凝聚方法。本文会对分裂法略作简介和举个例子说明,重点介绍凝聚法,并且附上代码。

分裂法:

    分裂法指的是初始时将所有的样本归为一个类簇,然后依据某种准则进行逐渐的分裂,直到达到某种条件或者达到设定的分类数目。用算法描述:

输入:样本集合D,聚类数目或者某个条件(一般是样本距离的阈值,这样就可不设置聚类数目)

输出:聚类结果

1.将样本集中的所有的样本归为一个类簇;

repeat:

2.在同一个类簇(计为c)中计算两两样本之间的距离,找出距离最远的两个样本a,b;

3.将样本a,b分配到不同的类簇c1和c2中;

4.计算原类簇(c)中剩余的其他样本点和a,b的距离,若是dis(a)<dis(b),则将样本点归到c1中,否则归到c2中;

util: 达到聚类的数目或者达到设定的条件

举个例子来说明问题:

在平面上有6个点:p0(1,1), p1(1,2), p2(2,2), p3(4,4), p4(4,5), p5(5,6),我现在需要对这6个点进行聚类,对应着上边的步骤我可以这样做:

1.将所有的点归为一个类簇c(p0,p1,p2,p3,p4,p5)

repeat:

2.在类簇c中计算他们的距离(简单的欧式距离)我们可以得到:

dis p0 p1 p2 p3 p4 p5
po 0 1 sqrt(2) sqrt(18) 5 sqrt(41)
p1 1 0 1 sqrt(13) sqrt(18) sqrt(32)
p2 sqrt(2) 1 0 sqrt(8) sqrt(13) 5
p3 sqrt(18) sqrt(13) sqrt(8) 0 1 sqrt(5)
p4 5 sqrt(18) sqrt(13) 1 0 sqrt(2)
p5 sqrt(41) sqrt(32) 5 sqrt(5) sqrt(2) 0

由上边的表格可以看出距离最远的两个点为p0和p5

3.将p0分配到类簇c1,将p5分配到类簇c2;

4.查表可以看出,剩余的点中p1和p2与p0的距离小,所以将它们两个归到类簇c1中;p3和p4与p5的距离小,所以将它们两个归到类簇c2中。这样我们得到了一次新的聚类 结果c1=(p1,p2,p3),c2=(p3,p4,p5);

util: 若是我要求就聚类成两个,则这个聚类到此结束,最终我们的聚类 结果是(p1,p2,p3)和(p3,p4,p5)。若是我要求同一个类中,最大样本距离不大于sqrt(2),那么上述的分类结果没有到达要求,则需要返回到repeat处继续聚类,因为c1中的样本的距离都不大于sqrt(2),所以不需要再分了;而类簇c2中的dis(p3,p5)=sqrt(5)>sqrt(2),还需要继续分,c2最后分聚类成两个类(p3,p4)和(p5),这样我们最终得到了三个类簇(p1,p2,p3)、(p3,p4)和(P5)。

-----------------------------------------------------------------------------------------------------------------------------------------------------

      简单的介绍完分裂法以后,我们开始我们的重头戏,凝聚层次聚类的介绍

      凝聚法:

       凝聚法指的是初始时将每个样本点当做一个类簇,所以原始类簇的大小等于样本点的个数,然后依据某种准则合并这些初始的类簇,直到达到某种条件或者达到设定的分类数目。用算法描述:

输入:样本集合D,聚类数目或者某个条件(一般是样本距离的阈值,这样就可不设置聚类数目)

输出:聚类结果

1.将样本集中的所有的样本点都当做一个独立的类簇;

repeat:

2.计算两两类簇之间的距离(后边会做介绍),找到距离最小的两个类簇c1和c2;

3.合并类簇c1和c2为一个类簇;

util: 达到聚类的数目或者达到设定的条件

还是拿分裂法的例子来说具体的讲解凝聚法。

1.首先经所有的样本看作是一个类簇,这样我可以得到初始的类簇有6个,分别为c1(p0),c2(p1),c3(p2),c4(p3),c5(p4),c6(p5)

repeat:

2.由上边的表可以得到两两类簇间的最小距离(并不是唯一,其他两个类簇间距离也可能等于最小值,但是先选取一个)是1,存在类簇c1和c2之间

注意:这个类簇间距离的计算方法有许多种。

            (1).就是取两个类中距离最近的两个样本的距离作为这两个集合的距离,也就是说,最近两个样本之间的距离越小,这两个类之间的相似度就越大

         (2).取两个集合中距离最远的两个点的距离作为两个集合的距离

         (3).把两个集合中的点两两的距离全部放在一起求一个平均值,相对也能得到合适一点的结果。

         (4).取两两距离的中值,与取均值相比更加能够解除个别偏离样本对结果的干扰。

(5).把两个集合中的点两两的距离全部放在一起求和然后除以两个集合中的元素个数

(6).求每个集合的中心点(就是将集合中的所有元素的对应维度相加然后再除以元素个数得到的一个向量),然后用中心点代替集合再去就集合间的距离

前四种,在点击打开链接中有介绍,后边的两种在工业界也经常使用,当然,还会有其他的一些方法。

3.合并类簇c1和c2,得到新的聚类结果c1(p0,p1),c3(p2),c4(p3),c5(p4),c6(p5)。

util:若是我们要求聚成5个类别的话,我们这里就可以结束了。但是如果我们设定了一个阈值f,要求若存在距离小于阈值f的两个类簇时则将两个类簇合并并且继续迭代,我们又会回到repeat继续迭代从而得到新的聚类结果。

好了,多了不说了,开始上代码,代码中我都写好了注释

      Cluster.java(用于存放类名和该类下的样本)

import java.util.ArrayList;
import java.util.List;public class Cluster {private List<DataPoint> dataPoints = new ArrayList<DataPoint>(); // 类簇中的样本点private String clusterName;public List<DataPoint> getDataPoints() {return dataPoints;}public void setDataPoints(List<DataPoint> dataPoints) {this.dataPoints = dataPoints;}public String getClusterName() {return clusterName;}public void setClusterName(String clusterName) {this.clusterName = clusterName;}
}

    DataPoint.java(存放样本点,包括样本点的名字、所属的类簇、向量空间的表示)

public class DataPoint {String dataPointName; // 样本点名Cluster cluster; // 样本点所属类簇private double dimensioin[]; // 样本点的维度public DataPoint(){}public DataPoint(double[] dimensioin,String dataPointName){this.dataPointName=dataPointName;this.dimensioin=dimensioin;}public double[] getDimensioin() {return dimensioin;}public void setDimensioin(double[] dimensioin) {this.dimensioin = dimensioin;}public Cluster getCluster() {return cluster;}public void setCluster(Cluster cluster) {this.cluster = cluster;}public String getDataPointName() {return dataPointName;}public void setDataPointName(String dataPointName) {this.dataPointName = dataPointName;}
}

   HCluster.java(主函数,包括数据的读取整理,聚类的执行等等。。。)

/*** 我写这个程序的目的是对我的次有进行聚类*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;import Cluster;
import DataPoint;public class HCluster {public static void main(String[] args) {HCluster hc = new HCluster();// 使用链表存放样本点ArrayList<DataPoint> dp = new ArrayList<DataPoint>();// 读入样本文件dp = hc.readData("E:/test_cc/vec.txt");/** freq代表了聚类的终止条件,判断还有没有距离小于freq的两个类簇,若有则合并后继续迭代,否则终止迭代*/double freq = 0.5;List<Cluster> clusters = hc.startCluster(dp, freq);// 输出聚类的结果,两个类簇中间使用----隔开System.out.println();System.out.println("结果输出---:");for (Cluster cl : clusters) {List<DataPoint> tempDps = cl.getDataPoints();for (DataPoint tempdp : tempDps) {System.out.println(tempdp.getDataPointName());}System.out.println("----");}}// 聚类的主方法private List<Cluster> startCluster(ArrayList<DataPoint> dp, double freq) {// 声明cluster类,存放类名和类簇中含有的样本List<Cluster> finalClusters = new ArrayList<Cluster>();// 初始化类簇,开始时认为每一个样本都是一个类簇并将初始化类簇赋值给最终类簇List<Cluster> originalClusters = initialCluster(dp);finalClusters = originalClusters;// flag为判断标志boolean flag = true;int it = 1;while (flag) {System.out.println("第" + it + "次迭代");// 临时表量,存放类簇间余弦相似度的最大值double max = -1;// mergeIndexA和mergeIndexB表示每一次迭代聚类最小的两个类簇,也就是每一次迭代要合并的两个类簇int mergeIndexA = 0;int mergeIndexB = 0;/** 迭代开始,分别去计算每个类簇之间的距离,将距离小的类簇合并*/for (int i = 0; i < finalClusters.size() - 1; i++) {for (int j = i + 1; j < finalClusters.size(); j++) {// 得到任意的两个类簇Cluster clusterA = finalClusters.get(i);Cluster clusterB = finalClusters.get(j);// 得到这两个类簇中的样本List<DataPoint> dataPointsA = clusterA.getDataPoints();List<DataPoint> dataPointsB = clusterB.getDataPoints();/** 定义临时变量tempDis存储两个类簇的大小,这里采用的计算两个类簇的距离的方法是* 得到两个类簇中所有的样本的距离的和除以两个类簇中的样本数量的积,其中两个样本 之间的距离用的是余弦相似度。* 注意:这个地方的类簇之间的距离可以 换成其他的计算方法*/double tempDis = 0;/** 此处计算距离可以优化,事先一次性将两两样本点之间的余弦距离计算好存放一个MAP中,* 这个地方使用的时候直接取出来,就不用每次再去计算了,可节省很多时间。* 注意:若是类簇间的距离计算换成了别的方法,也就没有这种优化的说法了*/for (int m = 0; m < dataPointsA.size(); m++) {for (int n = 0; n < dataPointsB.size(); n++) {tempDis = tempDis + getDistance(dataPointsA.get(m), dataPointsB.get(n));}}tempDis = tempDis / (dataPointsA.size() * dataPointsB.size());if (tempDis >= max) {max = tempDis;mergeIndexA = i;mergeIndexB = j;}}}/** 若是余弦相似度的最大值都小于给定的阈值, 那说明当前的类簇没有再进一步合并的必要了,* 当前的聚类可以作为结果了,否则的话合并余弦相似度值最大的两个类簇,继续进行迭代 注意:这个地方你可以设定别的聚类迭代的结束条件*/if (max < freq) {flag = false;} else {finalClusters = mergeCluster(finalClusters, mergeIndexA, mergeIndexB);}it++;}return finalClusters;}private List<Cluster> mergeCluster(List<Cluster> finalClusters, int mergeIndexA, int mergeIndexB) {if (mergeIndexA != mergeIndexB) {// 将cluster[mergeIndexB]中的DataPoint加入到 cluster[mergeIndexA]Cluster clusterA = finalClusters.get(mergeIndexA);Cluster clusterB = finalClusters.get(mergeIndexB);List<DataPoint> dpA = clusterA.getDataPoints();List<DataPoint> dpB = clusterB.getDataPoints();for (DataPoint dp : dpB) {DataPoint tempDp = new DataPoint();tempDp.setDataPointName(dp.getDataPointName());tempDp.setDimensioin(dp.getDimensioin());tempDp.setCluster(clusterA);dpA.add(tempDp);}clusterA.setDataPoints(dpA);finalClusters.remove(mergeIndexB);}return finalClusters;}private double getDistance(DataPoint dataPoint, DataPoint dataPoint2) {double distance = 0;double[] dimA = dataPoint.getDimensioin();double[] dimB = dataPoint2.getDimensioin();if (dimA.length == dimB.length) {double mdimA = 0;// dimA的莫double mdimB = 0;// dimB的莫double proAB = 0;// dimA和dimB的向量积for (int i = 0; i < dimA.length; i++) {proAB = proAB + dimA[i] * dimB[i];mdimA = mdimA + dimA[i] * dimA[i];mdimB = mdimB + dimB[i] * dimB[i];}distance = proAB / (Math.sqrt(mdimA) * Math.sqrt(mdimB));}return distance;}// 初始化类簇private List<Cluster> initialCluster(ArrayList<DataPoint> dpoints) {// 声明存放初始化类簇的链表List<Cluster> originalClusters = new ArrayList<Cluster>();for (int i = 0; i < dpoints.size(); i++) {// 得到每一个样本点DataPoint tempDataPoint = dpoints.get(i);// 声明一个临时的用于存放样本点的链表List<DataPoint> tempDataPoints = new ArrayList<DataPoint>();// 链表中加入刚才得到的样本点tempDataPoints.add(tempDataPoint);// 声明一个类簇,并且将给类簇设定名字、增加样本点Cluster tempCluster = new Cluster();tempCluster.setClusterName("Cluster " + String.valueOf(i));tempCluster.setDataPoints(tempDataPoints);// 将样本点的类簇设置为tempClustertempDataPoint.setCluster(tempCluster);// 将新的类簇加入到初始化类簇链表中originalClusters.add(tempCluster);}return originalClusters;}private ArrayList<DataPoint> readData(String path) {ArrayList<DataPoint> dp = new ArrayList<DataPoint>();File file = new File(path);if (!file.exists()) {System.out.println("输入文件不存在");System.exit(1);}FileInputStream fis = null;InputStreamReader isr = null;BufferedReader br = null;try {fis = new FileInputStream(file);isr = new InputStreamReader(fis, "utf-8");br = new BufferedReader(isr);String line = br.readLine();String s[] = null;while (line != null) {/** 说明一下我的数据格式为 word v1 v2 ....v200,word代表要聚类的词语,* v1-v200是word的词向量表示的每一个维度值,我实验的样本的维度为200,* 所有下边我声明了一个200的double数组*/s = line.split(" ");double[] b = new double[200];for (int i = 1; i < s.length; i++) {b[i - 1] = Double.parseDouble(s[i]);}dp.add(new DataPoint(b, s[0]));line = br.readLine();}} catch (Exception ex) {ex.printStackTrace();} finally {try {br.close();isr.close();fis.close();} catch (IOException e) {e.printStackTrace();}}System.out.println("加载数据完毕,数据大小为:" + dp.size());return dp;}
}

来一张我自己做项目时的聚类的结果的截图:

好了,到此,层次聚类我就算讲完了,希望大家批评指正。。。。。

聚类系列-层次聚类(Hierarchical Clustering)相关推荐

  1. 机器学习--聚类系列--层次聚类

    层次聚类 层次聚类(Hierarchical Clustering)是聚类算法的一种,通过计算不同类别数据点间的相似度来创建一棵有层次的嵌套聚类树.在聚类树中,不同类别的原始数据点是树的最低层,树的顶 ...

  2. 聚类(2)——层次聚类 Hierarchical Clustering

    聚类系列: 聚类(序)----监督学习与无监督学习 聚类(1)----混合高斯模型 Gaussian Mixture Model 聚类(2)----层次聚类 Hierarchical Clusteri ...

  3. Python计算机视觉编程第六章——图像聚类(K-means聚类,DBSCAN聚类,层次聚类,谱聚类,PCA主成分分析)

    Python计算机视觉编程 图像聚类 (一)K-means 聚类 1.1 SciPy 聚类包 1.2 图像聚类 1.1 在主成分上可视化图像 1.1 像素聚类 (二)层次聚类 (三)谱聚类 图像聚类 ...

  4. 聚类之层次聚类、基于划分的聚类(…

    5.聚类之层次聚类.基于划分的聚类(k-means).基于密度的聚类.基于模型的聚类 目录(?)[-] 1.      一层次聚类 1.      层次聚类的原理及分类 2.      层次聚类的流程 ...

  5. 【R】【密度聚类、层次聚类、期望最大化聚类】

    文章目录 1.对数据集进行加载.预处理集可视化 1.1 加载数据集 1.2 数据预处理 1.3 将样本点进行可视化 2.密度聚类(DBSCAN 算法) 2.1 加载程序包 2.2 设置聚类参数阈值并可 ...

  6. 【机器学习】聚类【Ⅴ】密度聚类与层次聚类

    主要来自周志华<机器学习>一书,数学推导主要来自简书博主"形式运算"的原创博客,包含自己的理解. 有任何的书写错误.排版错误.概念错误等,希望大家包含指正. 由于字数限 ...

  7. 了解聚类是什么。聚类方法:k-means、核聚类、层次聚类、谱聚类

    聚类 1.什么是聚类 2.聚类方法 2.1 划分式聚类方法 k-means k-means++ bi-kmeans 基于密度的方法 DBSCAN OPTICS算法 层次化聚类算法 核聚类 支持向量聚类 ...

  8. 聚类算法——层次聚类算法

    每篇一句: You must strive to find your own voice. Because the longer you wait to begin, the less likely ...

  9. 说话人聚类--谱聚类和层次聚类

    谱聚类和层次聚类 在讯飞实习了一个月了,做了点说话人聚类的工作,现在总结一下主要用到的谱聚类和层次聚类. 层次聚类 在层次聚类这块,我主要学习了 凝聚型层次聚类和 BIRCH方法,主要参考的博客有 [ ...

最新文章

  1. 人工智能及其应用(第5版).蔡自兴-5章课后习题。【部分答案】
  2. [转]获取Setup文件的运行路径
  3. 深入理解Java虚拟机-常用vm参数分析
  4. Ztree-JQuery树插件的学习(未完)
  5. pfsense下的流量管理(转)
  6. caffe网络结构图绘制
  7. oracle11gR版本GI中新增,Oracle11g新特性MemberKillEscalation简介
  8. 忽略证书_中专考生注意!忽略这一点,四科全过也拿不到执业药师证书!
  9. 从零开始学习java一般需要多长时间?
  10. win10系统蓝牙服务器,如何打开win10系统的蓝牙并进行设备添加
  11. 【android开发】手机应用管理器的实现之获取应用列表(一)
  12. 指挥调度中心大屏幕用液晶拼接屏还是DLP大屏?
  13. Linux 常用基本命令 ps top kill
  14. 微信赚钱越来越难?该如何转型?
  15. 论文写作注意事项(格式)
  16. 【Oracle】 sql 中的字符(串)替换与转换
  17. MOS管在2020年即将爆发的十大电子应用领域-KIA MOS管
  18. 2013 癸巳年开年记事
  19. C++实现——动态规划法(切割钢条问题)
  20. 鱼福php管理员密码修改_改无线网密码管理员密码是什么?

热门文章

  1. 【生信】常见测序数据格式
  2. aws开发工具codePipeline使用简介
  3. dell服务器双系统怎么切换,戴尔17rt4728双系统肿么切换?
  4. 320亿美元收购ARM出价过高?软银股价跌幅超10%
  5. Arduino板上通过操作端口寄存器来进行控制
  6. 【源码】物理信息神经网络设计与仿真
  7. 算法设计与分析基础-8.4、背包问题和记忆功能
  8. npm升级到最新版本、指定版本
  9. 软硬件配置大幅提升!解读戴尔新一代PowerEgde14服务器
  10. 乐鑫 ESP-Touch Sensor 智能触控方案