1)层次法(Hierarchicalmethods):先计算样本之间的距离。每次将距离最近的点合并到同一个类。然后,再计算类与类之间的距离,将距离最近的类合并为一个大类。不停的合并,直到合成了一个类。其中类与类的距离的计算方法有:最短距离法,最长距离法,中间距离法,类平均法等。比如最短距离法,将类与类的距离定义为类与类之间样本的最短距离。
层次聚类算法根据层次分解的顺序分为:自下向上和自上向下,即凝聚的层次聚类算法和分裂的层次聚类算法(agglomerative和divisive),也可以理解为自下而上法(bottom-up)和自上而下法(top-down)。
自下而上法:凝聚型层次聚类,就是一开始每个个体(object)都是一个类,然后根据linkage寻找同类,最后形成一个“类”。
自上而下法:分裂型层次聚类,就是反过来,一开始所有个体都属于一个“类”,然后根据linkage排除异己,最后每个个体都成为一个“类”。
这两种路方法没有孰优孰劣之分,只是在实际应用的时候要根据数据特点以及你想要的“类”的个数,来考虑是自上而下更快还是自下而上更快。根据linkage判断”类”的方法就是:最短距离法、最长距离法、中间距离法、类平均法等,其中类平均法往往被认为是最常用也最好用的方法,一方面因为其良好的单调性,另一方面因为其空间扩张/浓缩的程度适中。
为弥补分解与合并的不足,层次合并经常要与其它聚类方法相结合,如循环定位。
2)Hierarchical methods中比较新的算法有:
BIRCH(Balanced Iterative Reducingand Clustering Using Hierarchies利用层次方法的平衡迭代规约和聚类)主要是在数据量很大的时候使用,而且数据类型是numerical。首先利用树的结构对对象集进行划分,然后再利用其它聚类方法对这些聚类进行优化;
sklearn.cluster.Birch
ROCK(A Hierarchical ClusteringAlgorithm for Categorical Attributes)主要用在categorical的数据类型上;
Chameleon(A Hierarchical Clustering AlgorithmUsing Dynamic Modeling)里用到的linkage是kNN(k-nearest-neighbor)算法,并以此构建一个graph,Chameleon的聚类效果被认为非常强大,比BIRCH好用,但运算复杂度很高,O(n^2)。
凝聚型层次聚类的策略是先将每个对象作为一个簇,然后合并这些原子簇为越来越大的簇,直到所有对象都在一个簇中,或者某个终结条件被满足。绝大多数层次聚类属于凝聚型层次聚类,它们只是在簇间相似度的定义上有所不同。 这里给出采用最小距离的凝聚层次聚类算法流程:
(1) 将每个对象看作一类,计算两两之间的最小距离;
(2) 将距离最小的两个类合并成一个新类;
(3) 重新计算新类与所有类之间的距离;
(4) 重复(2)、(3),直到所有类最后合并成一类。
另外我们可以看出凝聚的层次聚类并没有类似基本K均值的全局目标函数,没有局部极小问题或是很难选择初始点的问题。合并的操作往往是最终的,一旦合并两个簇之后就不会撤销。当然其计算存储的代价是昂贵的。
优点:
1,距离和规则的相似度容易定义,限制少;
2,不需要预先制定聚类数;
3,可以发现类的层次关系;
4,可以聚类成其它形状
缺点:
1,计算复杂度太高;
2,奇异值也能产生很大影响;
3,算法很可能聚类成链状
聚类质量的评价方法,本质上,都是根据簇内和簇间的效果对比进行衡量。
(1)轮廓系数(Silhouette Coefficient)
使用轮廓系数评估聚类质量。 对于n个对象的数据集D,假设D被划分为k个簇。对于D中的每个对象o,计算它和它所属的簇的其他对象的平均距离a(o)。类似的,计算它和它不属于的簇的最小平均距离b(o)。对数据集中的每个对象计算轮廓系数然后取平均值作为聚类的质量度量。轮廓系数的取值范围是[-1,1],越靠近1代表聚类质量越好,越靠近-1代表聚类质量越差。
计算样本i到同簇其他样本的平均距离ai,ai越小,说明样本i越应该被聚类到该簇。将ai 成为样本i的簇内不相似度。
计算样本i到其他某簇Cj的所有样本的平均距离bij,称为样本i与簇cj的不相似度。定义为样本i的簇间不相似度:bi=min{bi1,bi2,......bik}
si接近1,则说明样本i聚类合理。
si接近-1,则说明样本i更应该分类到另外的簇。
若si近似为0,则说明样本i在两个簇的边界上。
代码:sklearn.metric库的silhouette_score函数,silhouette_score的参数有:X、labels、metric,X为距离矩阵,labels为聚类结果,metric为度量方法。由于我们输入的是距离矩阵,因此选择"precomputed"。
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt
silhouettteScore = []
for i in range(2,15):
# 构建并训练模型
kmeans = KMeans(n_clusters = i,random_state=123).fit(iris_data)
score = silhouette_score(iris_data,kmeans.labels_)
silhouettteScore.append(score)
plt.figure(figsize=(10,6))
plt.plot(range(2,15),silhouettteScore,linewidth=1.5, linestyle="-")
plt.show()
(2)Calinski-Harabasz指数
n:表示聚类的数目;
k:表示当前的类
trB(k):表示类间离差矩阵的迹
trW(k):表示类内离差矩阵的迹。
可以得出:CH越大代表着类自身越紧密,类与类之间越分散,即更优的聚类结果。
from sklearn.metrics import calinski_harabaz_score
for i in range(2,7):
# 构建并训练模型
kmeans = KMeans(n_clusters = i,random_state=123).fit(iris_data)
score = calinski_harabaz_score(iris_data,kmeans.labels_)
print('iris数据聚%d类calinski_harabaz指数为:%f'%(i,score))
(3)FMI:需要真实值
from sklearn.metrics import fowlkes_mallows_score
for i in range(2,7):
##构建并训练模型
kmeans = KMeans(n_clusters = i,random_state=123).fit(iris_data)
score = fowlkes_mallows_score(iris_target,kmeans.labels_)
print('iris数据聚%d类FMI评价分值为:%f' %(i,score))
参数详解:
sklearn库下的层次聚类是在sklearn.cluster的 AgglomerativeClustering中:
AgglomerativeClustering类的构造函数的参数有簇的个数n_clusters,连接方法linkage,连接度量选项affinity三个重要参数。下面就这三个参数进行描述。
簇的个数n_clusters:需要用户指定的,按照常理来说,凝聚层次聚类是不需要指定簇的个数的,但是sklearn的这个类需要指定簇的个数。算法会根据簇的个数判断最终的合并依据,这个参数会影响聚类质量。
连接方法linkage:指的是衡量簇与簇之间的远近程度的方法。具体说来包括最小距离,最大距离和平均距离三种方式。对应于簇融合的方法,即簇间观测点之间的最小距离作为簇的距离,簇间观测点之间的最大距离作为簇的距离,以及簇间观测点之间的平均距离作为簇的距离。一般说来,平均距离是一种折中的方法。
连接度量affinity:是一个簇间距离的计算方法,包括各种欧式空间的距离计算方法以及非欧式空间的距离计算方法。此外,该参数还可以设置为‘precomputed’,即用户输入计算好的距离矩阵。距离矩阵的生成方法:假设用户有n个观测点,那么先依次构造这n个点两两间的距离列表,即长度为n*(n-1)/2的距离列表,然后通过scipy.spatial.distance的dist库的squareform函数就可以构造距离矩阵了。这种方式的好处是用户可以使用自己定义的方法计算任意两个观测点的距离,然后再进行聚类。 聚类结束以后,需要对聚类质量进行评估。
实例:
from sklearn.cluster import AgglomerativeClustering
import numpy as np
X = [[1,2],[3,2],[4,4],[1,2],[1,3]] # 生成数据
clustering = AgglomerativeClustering().fit(X) # 训练模型
clustering.labels_
array([1, 0, 0, 1, 1])
clustering.children_
array([[0, 3],
[4, 5],
[1, 2],
[6, 7]])
简单解释下(后面还会详细说明):
X一共有5个样本,那么在进行层次聚类是,这5个样本各自一类,类别名称是0、1、2、3、4
第一行:[0, 3]意思是类别0和类别3距离最近,首先聚成一类,并自动定义类别为5(=len(X)-1+1)
第二行:[4, 5]意思是类别4和上面聚类的新类别5距离为第二近,4、5聚成一类,类别为6(=len(X)-1+2)
第三行:[1, 2]意思是类别1、类别2距离为第三近,聚成一类,类别为7(=len(X)-1+3)
第四行:[6, 7]意思是类别6、7距离为第四近,聚成一类,类别为8(=len(X)-1+4)
因为类别5有两个样本,加上类别4形成类别6,有3个样本;
类别7是类别1、2聚类形成,有两个样本;
类别6、7聚成一类后,类别8有5个样本,这样X全部样本参与聚类,聚类完成。
这样大家可能觉得优点乱,所以建议大家进行层次聚类是可以用下面的方法。
简单介绍:
(1)linkage(y, method=’single’, metric=’euclidean’)
共包含3个参数: y是距离矩阵,可以是1维压缩向量(距离向量),也可以是2维观测向量(坐标矩阵)。若y是1维压缩向量,则y必须是n个初始观测值的组合,n是坐标矩阵中成对的观测值。返回值:(n-1)*4的矩阵Z(后面会仔细的讲解返回值各个字段的含义)
linkage方法用于计算两个聚类簇s和t之间的距离d(s,t),这个方法的使用在层次聚类之前。当s和t形成一个新的聚类簇u时,s和t被从森林(已经形成的聚类簇群)中移除,而用新的聚类簇u来代替。当森林中只有一个聚类簇时算法停止。而这个聚类簇就成了聚类树的根。 距离矩阵在每次迭代中都将被保存,d[i,j]对应于第i个聚类簇与第j个聚类簇之间的距离。每次迭代必须更新新形成的聚类簇之间的距离矩阵。 假定现在有|u|个初始观测值u[0],...,u[|u|-1]在聚类簇u中,有|v|个初始对象v[0],...,v[|v|-1]在聚类簇v中。回忆s和t合并成u。让v成为森林中的聚类簇,而不是u。
method是指计算类间距离的方法,比较常用的有3种: (1)single:最近邻,把类与类间距离最近的作为类间距 (2)complete:最远邻,把类与类间距离最远的作为类间距 (3)average:平均距离,类与类间所有pairs距离的平均 其他的method还有如weighted,centroid等等,具体可以参考:
http://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html#scipy.cluster.hierarchy.linkage
(2)fcluster(Z, t, criterion=’inconsistent’, depth=2, R=None, monocrit=None)
第一个参数Z是linkage得到的矩阵,记录了层次聚类的层次信息; t是一个聚类的阈值-“The threshold to apply when forming flat clusters”,在实际中,感觉这个阈值的选取还是蛮重要的. 其他的参数我用的是默认的,具体可以参考:
http://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.fcluster.html#scipy.cluster.hierarchy.fcluster
为了跟前面sklearn进行对照,我们再来重新做一遍,顺便详细介绍下
from scipy.cluster.hierarchy import dendrogram, linkage,fcluster
from matplotlib import pyplot as plt
X = [[1,2],[3,2],[4,4],[1,2],[1,3]]
Z = linkage(X, 'ward')
f = fcluster(Z,4,'distance')
fig = plt.figure(figsize=(5, 3))
dn = dendrogram(Z)
plt.show()
同时我们看下Z:numpy.ndarry。
层次聚类编码为一个linkage矩阵。Z共有四列组成,第一字段与第二字段分别为聚类簇的编号,在初始距离前每个初始值被从0~n-1进行标识,每生成一个新的聚类簇就在此基础上增加一对新的聚类簇进行标识,第三个字段表示前两个聚类簇之间的距离,第四个字段表示新生成聚类簇所包含的元素的个数。
array([[0. , 3. , 0. , 2. ],
[4. , 5. , 1.15470054, 3. ],
[1. , 2. , 2.23606798, 2. ],
[6. , 7. , 4.00832467, 5. ]])
我们可以根据树状图来进行分类,运用切割法进行:
想分两类时,就从上往下数有两根竖线时进行切割,那么所对应的竖线下面所连接的为一类
例如,左边竖线下面链接的样本有4、0、3,那么4、0、3就聚为一类,
右边竖线相连的样本有1、2,那么1、2就聚为一类。
想分三类时
就从上往下数有三根竖线时进行切割,那么所对应的竖线下面所连接的为一类
例如,左边竖线下面链接的样本有4、0、3,那么4、0、3就聚为一类,
中间竖线相连的样本有1,那么1就聚为一类,
右边竖线相连的样本有2,那么2就聚为一类。