第5章聚类 聚 类(clustering)是一种无监督的学习方法,它是按照某一个特定的标准(比如距离),把一个数据集分隔成不同的类或簇,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇内的数据对象的差异性也尽可能地大。与分类算法类似,它也是要确定一个事物的类别,即把相似的对象归为一类; 但它与分类的区别是,聚类不知道真实的样本类别,分类是利用已知的样本类别训练学习器来预测未知样本的类别。聚类分析常被用于图像分割、离群点检测,广泛应用于图像处理、推荐系统、数据挖掘等领域。 5.1聚类算法简介 聚类分析是一种无监督学习的算法,它是将数据对象按照相似性划分为多个子集的过程,每个子集被称为一个“簇”(cluster)。同一个簇中的数据相似性高,不同簇中的数据相似性低。在利用无监督学习方法划分数据时,数据是没有类别标记的,它需要利用样本间的相似性去划分类别,而相似性是根据样本间的距离进行度量的。 聚类任务的形式化描述如下。 假设样本集合为D={x1,x2,…,xN},通过聚类把样本划分到不同的簇,使得具有相似特征的样本在同一个簇中,不具有相似特征的样本在不同的簇中,最终形成k个不同簇C={C1,C2,…,Ck},若各个簇互不相交,即对任意两个簇Ci∩Cj=,则称为硬聚类,否则称为软聚类。 5.1.1聚类算法的分类 聚类本质上是集合划分问题。它的基本原则是使簇内的样本尽可能相似,通常的做法是根据簇内样本之间的距离,或是样本点在数据空间中的密度来确定。对簇的不同定义可以得到各种不同的聚类算法。聚类分析的算法可以分为基于划分的方法(partitioning method)、基于层次的方法(hierarchicalbased method)、基于密度的方法(densitybased method)、基于网格的方法(gridbased method)、基于模型的方法(modelbased method)。 1. 基于划分的方法 基于划分的方法是基于距离作为判断依据,将数据对象划分为不重叠的簇,使每个数据对象属于且只属于一个簇。首先要确定这些样本点最后聚成几类,然后挑选几个样本点作为初始中心点,通过不断迭代,直到达到“类(簇)内的样本点都足够近,类(簇)间的样本点都足够远”的目标。基于划分的距离算法有Kmeans、Kmedoids、kernel Kmeans等算法。 2. 基于层次的方法 基于层次的聚类可分为两种: 凝聚法和分裂法。凝聚法采用的是一种自底向上的方法,从最底层开始,每一次通过合并最相似的聚类来形成上一层次中的聚类,当全部数据都合并到一个簇或者达到某个终止条件时,算法结束。分裂法采用的是一种自顶向下的方法,从一个包含全部样本数据的簇开始,逐层分裂为若干簇,每个簇继续不断地往下分裂,直到每个簇中仅包含一个样本数据。 3. 基于密度的方法 在基于密度的聚类方法中,簇被看作是由低密度区域分隔开来的高密度对象区域。基于密度的聚类方法定义了领域的范围,当邻近区域的密度超过某个阈值,就继续聚类,即某区域内的对象个数超过一个给定范围,则将其添加到簇中。基于密度的聚类方法可以对不规则形状的数据样本点进行聚类,同时过滤噪声数据的效果比较好。DBSCAN(DensityBased Spatial Clustering of Applications with Noise)就是典型的代表。 4. 基于网格的方法 基于网格的聚类方法将数据空间划分为由若干有限的网格单元(Cell)组成的网格结构,将数据对象集映射到网格单元中,所有聚类操作都在该结构上进行。该方法的处理与数据对象的个数无关,只依赖于每个量化空间中每一维上的单元数,处理速度快,但算法效率的提高是以聚类结果的准确率为代价的,经常与基于密度的聚类算法结合使用。 5. 基于模型的方法 基于模型的方法包括基于概率模型的方法和基于神经网络模型的方法。概率模型主要指概率生成模型,同一“类”的数据属于同一种概率分布,即假设数据是根据潜在的概率分布生成的。高斯混合模型(Gaussian Mixture Models,GMM)就是最典型、常用的基于概率模型的聚类方法。自组织映射(Self Organized Maps,SOM)则是一种常见的基于神经网络模型的方法。 5.1.2距离度量的方法 无论是基于划分的聚类,还是基于层次的聚类,核心问题都是度量两个样本之间的距离,常用的距离度量方法有闵可夫斯基距离、马氏距离、汉明距离、夹角余弦等。 1. 闵可夫斯基距离 闵可夫斯基距离(Minkowski distance)将样本看作高维空间中的点进行距离度量。对于n维空间中任意两个样本点P= (x1,x2,…,xn)T和Q= (y1,y2,…,yn)T,P和Q的闵可夫斯基距离的定义为 dPQ=∑ni=1xi-yip1p(51) 其中,p≥1,xi-yip 为 xi-yi的p范数。当p=1时,有 dPQ=∑ni=1xi-yi (52) 此时,dPQ的取值就是两个点的绝对值之和,称为曼哈顿距离(Manhattan distance)。几何意义就是沿水平方向从P到Q的距离。 当p=2时,P和Q的距离为 dPQ=∑ni=1xi-yi212 (53) 此时,dPQ就表示二维空间中两个点P和Q之间的直线距离,称为欧几里得距离或欧氏距离(Euclidean distance)。 2. 马氏距离 与欧氏距离、曼哈顿距离一样,马氏距离(Mahalanobis distance)常被用于评定数据之间的相似度指标,它可以看作是欧氏距离的修正,修正了欧氏距离中各维度尺度不一致且相关的问题。单个数据点的马氏距离的定义为 dx=(x-μ)TΣ-1(x-μ) (54) 数据点P和Q之间的马氏距离定义为 dPQ=(x-y)TΣ-1(x-y) (55) 其中,Σ是多维随机变量的协方差矩阵,μ为样本均值。当样本的各个特征向量相互独立时,协方差是单位向量,马氏距离就成了欧氏距离。 3. 汉明距离 汉明距离(Hamming distance)需要将处理的样本数据转换为0和1表示的二进制串,样本中各分量的取值只能是0或1,例如字符串“1110”与“1001”之间的汉明距离为3。对于任意样本特征x和y, 有x,y∈{0,1},其汉明距离为 dij=∑ni=11xi≠yi (56) 汉明距离常应用在信息论、编码理论、密码学等领域。 4. 夹角余弦 夹角余弦(cosine)度量将样本看成是高维空间中的向量进行度量,度量方法就是计算两个向量间的余弦夹角。对于任意两个n维样本P=(xi1,xi2,…,xin)和Q=(yi1,yi2,…,yin),其夹角余弦为 cosPQ=∑nk=1xikyik∑nk=1x2ik∑nk=1y2ik (57) 当n=2时,夹角余弦计算的就是二维空间中两条直线的夹角余弦值。夹角余弦的取值范围为[-1,1]。夹角余弦值越大,表示两个向量的夹角越小; 夹角余弦越小,表示两个向量的夹角越大。当两个向量的方向重合时,夹角余弦取最大值1; 当两个向量的方向完全相反时,夹角余弦取最小值-1。 视频讲解 5.2Kmeans聚类 Kmeans聚类,也称为k均值聚类,是聚类分析中使用最广泛的聚类算法之一。Kmeans算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为k个簇,使簇内的样本点的连接尽可能紧密,不同簇内的样本点的距离尽量大。 5.2.1Kmeans聚类算法的思想 假设簇划分为(C1,C2,…,Ck),则目标就是最小化平方误差 E=∑ki=1∑x∈Cix-μi22 (58) 其中, μi 为聚类簇 Ci 的均值向量, 也称为质心(centroid), 其计算公式为 μi=1Ci∑x∈Cix (59) 直接求最小误差是一个NP难问题,因此只能采用启发式的迭代方法求解。 k均值聚类算法的描述如下: 输入: 训练数据集D={x1,x2,…,xN},聚类个数k。 过程: (1) 从D中随机选择k个样本作为初始的均值向量: μ1,μ2,…,μk。 (2) 重复执行以下过程,直至当前均值向量不再更新: ① 令Ci=,其中1≤i≤k。 ② 对于i=1,2,…,N,选择每个样本xi与各均值向量μj(1≤j≤k)的距离: distij=xi-μj22,根据离均值距离最小的xi确定其聚类标记: λi=argminj∈{1,2,…,k}distij,将样本xi划入相应的聚类 Cλi=Cλi∪xi。 ③ 对于i=1,2,…,k,计算新的均值向量: μ′i=1Ci∑x∈Cix,如果新的均值向量与之前的均值向量不相等,则更新,即μ′i=μi,否则不更新。 输出: 聚类划分C={C1,C2,…,Ck}。 1. k均值聚类算法的计算过程 下面以西瓜数据集为例,说明k均值聚类算法的计算过程,并对该过程进行算法验证。这里的西瓜数据集共30条记录,包含密度和含糖率两个特征,如表51所示。 表51西瓜数据集 序号 密度 含糖率 序号 密度 含糖率 序号 密度 含糖率 1 0.697 0.460 11 0.245 0.057 21 0.748 0.232 2 0.774 0.376 12 0.343 0.099 22 0.714 0.346 3 0.634 0.264 13 0.639 0.161 23 0.483 0.312 4 0.608 0.318 14 0.657 0.198 24 0.478 0.437 5 0.556 0.215 15 0.360 0.370 25 0.525 0.369 6 0.403 0.237 16 0.593 0.042 26 0.751 0.489 7 0.481 0.149 17 0.719 0.103 27 0.532 0.472 8 0.437 0.211 18 0.359 0.188 28 0.473 0.376 9 0.666 0.091 19 0.339 0.241 29 0.725 0.445 10 0.243 0.267 20 0.282 0.257 30 0.446 0.459 下面使用聚类算法演示聚类过程: (1) 假设聚类个数为k=3,首先随机选取3个样本x3=(0.634,0.264)、x8=(0.437,0.211)、x9=(0.666,0.091)作为初始的均值向量,即 μ1= x3、μ2= x8、μ3= x9,分别对应于 3 个聚类 C1、C2、C3 中的均值向量, 初始时每个聚类中的元素为空。 (2) 考察样本x1=(0.697,0.460),它与当前的均值向量μ1、μ2、μ3 距离(欧氏距离)分别为 0.206、0.360、0.370,因此 x1 被划入聚类 C1 中, 类似地, 对 x2,x3,…,x30 所有样本都执行类似的过程, 将每个样本进行了划分, 故有。 C1={x1,x2,x3,x4,x5,x14,x21,x22,x25,x26,x27,x29} C2={x6,x7,x8,x10,x11,x12,x15,x18,x19,x20,x23,x24,x28,x30} C3={x9,x13,x16,x17} (3) 根据得到的C1、C2、C3更新均值向量: μ1=(0.660,0.349) μ2=(0.384,0.261) μ3=(0.654,0.0993) 2. k均值聚类的算法实现 根据前面的k均值算法思想实现k均值算法,并在西瓜数据集上验证其正确性。这里同样使用表51所示的西瓜数据集。 k均值聚类的算法实现如下。 import pandas as pd import numpy as np from numpy import * import matplotlib.pyplot as plt def dist_eclud(v1, v2): return sqrt(sum(power(v1 - v2, 2))) def update_clusters(k,mu,X,y_label): for i in range(X.shape[0]): min_dist = float('inf') for index in range(k): dist = dist_eclud(mu[index], X[i]) if dist < min_dist: min_dist = dist y_label[i] = index return y_label def update_centroids(k,mu,X,y_label): for i in range(k): sum=np.array([0.0,0.0]) num = np.sum(y_label == i) cluster_index,label=np.where(y_label==i) print("cluster_index:",list(cluster_index)) for j in cluster_index: sum=sum+X[j] print("sum:",sum) centroid=sum/num centroid = np.mean(X[cluster_index],axis=0) mu[i]=centroid return mu def show(dataset, k, centroids, clusters): num_samples, dim = dataset.shape marker = ['or', 'ob', 'og', 'ok'] marker2 = ['*r', '*b', '*g', '*k'] for i in range(num_samples): mark_index = int(clusters[i]) plt.plot(dataset[i, 0], dataset[i, 1], marker[mark_index]) for i in range(k): plt.plot(centroids[i, 0], centroids[i, 1], marker2[i], markersize=10) plt.xlim(0.1, 0.9) #把x轴的刻度范围设置为0.1~0.9 plt.ylim(0, 0.8) #把y轴的刻度范围设置为0~0.8 plt.xlabel('密度') plt.ylabel('含糖率') plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文 plt.rcParams['axes.unicode_minus'] = False if __name__ == '__main__': k=3 dataTrain = pd.read_csv("xiguadata.csv") dataTrain = [ [0.697, 0.460], [0.774, 0.376],[0.634, 0.264],[0.608, 0.318], [0.556, 0.215],[0.403, 0.237],[0.481,0.149],[0.437, 0.211], [0.666, 0.091],[0.243, 0.267],[0.245, 0.057],[0.343, 0.099], [0.639, 0.161],[0.657, 0.198],[0.360, 0.370],[0.593, 0.042], [0.719, 0.103],[0.359,0.188],[0.339,0.241],[0.282,0.257], [0.748, 0.232],[0.714,0.346],[0.483,0.312],[0.478,0.437], [0.525, 0.369],[0.751,0.489],[0.532,0.472],[0.473,0.376], [0.725, 0.445],[0.446,0.459]] dataTrain=np.array(dataTrain) y_label=np.zeros((dataTrain.shape[0],1)) mu1=np.array([0.403, 0.237]) mu2=np.array([0.343, 0.099]) mu3=np.array([0.478,0.437]) #mu=np.zeros((k,dataTrain.shape[1])) mu=np.array([[0.403, 0.237],[0.343, 0.099],[0.478,0.437]]) iters=4 for iter in range(iters): y_label=update_clusters(k,mu,dataTrain,y_label) mu=update_centroids(k,mu,dataTrain,y_label) print("mu=",mu) plt.subplot(2, 2, iter+1, frameon=True) #两行、两列的子图 t='第'+str(iter+1)+'次迭代后' plt.legend(title=t) show(dataTrain, k, mu, y_label) plt.show() 利用以上Kmeans算法对西瓜集数据进行聚类,经过4次迭代后,每一次聚类结果其散点图及聚类结果如图51所示。 图51利用Kmeans聚类算法的迭代结果(见彩插) 图51中,不同颜色的五角星表示各样本的聚类中心,圆点表示各类样本数据。 针对三文鱼和鲈鱼数据的聚类,仍然使用Kmeans聚类的算法实现如下: from numpy import * import matplotlib.pyplot as plt from matplotlib import pyplot as plt import numpy as np import xlrd def load_dataset(file_name): xlbook = xlrd.open_workbook(file_name) sheet = xlbook.sheet_by_index(3) #索引的方式,从0开始 sheet = xlbook.sheet_by_name('joint_normal') #名字的方式 nrows = sheet.nrows #行 ncols = sheet.ncols #列 fish = [] y=[] for i in range(1, nrows): v1 = sheet.cell(i, 0).value #获取i行3列的表格值 v2 = sheet.cell(i, 1).value #获取i行3列的表格值 print(v1, v2) fish.append([v1, v2]) y.append(int(sheet.cell(i,2).value)) train0 = fish[0:500] train1 = fish[1000:1500] train = train0 + train1 y0=y[0:500] y1=y[1000:1500] y=y0+y1 return train,y #利用欧几里得距离公式计算两个向量的距离 def dist_eclud(v1, v2): return sqrt(sum(power(v1 - v2, 2))) #随机生成初始的质心 def make_rand_centroids(dataset, k): n = shape(dataset)[1] centroids = mat(zeros((k, n))) for i in range(n): min_data = min(dataset[:, i]) data_range= float(max(array(dataset)[:, i]) – min_data) centroids[:, i] = min_data + data_range * random.rand(k, 1) return centroids #初始时随机选择k个质心 def select_rand_centroids(X, k): n,cols = shape(X) centroids = np.zeros((k, cols)) for i in range(k): centroid = X[np.random.choice(range(n))] centroids[i]=centroid return centroids def divide_closest_centroid(feature,centroids): min_index=0 min_dist=float('inf') for index,centroid in enumerate(centroids): dist=dist_eclud(feature,centroid) if dist12MB,则不提前计算样本距离。  true: 总是预先计算样本距离。  false: 永远不预先计算样本距离。 (7) n_jobs: 整型,指定计算所用的进程数。若值为-1,则用所有的CPU进行运算; 若值为1,则不进行并行运算; 若值为-2,则用到的CPU数为总CPU数减1。 (8) random_state: 整型,NumPy.RandomState类型或None。用于初始化质心的生成器(Generator)。如果值为整数,则确定一个seed。默认值为NumPy的随机数生成器。 (9) copy_x: 布尔型,默认值为True。当设置precomputing_distances为True时,该参数才有效。如果此参数值设为True,则原始数据不会被改变; 如果参数设置为False,则原始数据会改变。 (10) verbose: 整型,默认值为0。verbose表示是否输出详细信息,0表示不输出日志信息; 1表示每隔一段时间打印一次日志信息; 大于1表示打印次数频繁。 (11) algorithm: 可选参数为auto、full、elkan,默认值为auto。auto表示采用的是Kmeans 算法,full表示采用的是经典的EMstyle算法,elkan是elkan KMeans算法,利用了两边之和大于第三边及两边之差小于第三边的三角形性质,来减少距离的计算。 常用属性说明如下。  cluster_centers_: 类别的均值向量。  labels_: 每个样本所属的类别标记。  inertia_: 每个样本与其各个簇的质心的距离之和。 常用方法说明如下。  fit(X[,y]): Kmeans聚类训练模型。  fit_predictt(X[,y]): 计算簇质心并预测每个样本的类别。  transform(X): 在fit的基础上,进行标准化,降维,归一化等操作。  fit_transform(X[,y]): fit_transform是fit和transform的组合,既包括了训练又包含了转换。  predict(X): 预测每个样本所属的簇类别标签。  score(X[,y]): 计算聚类误差。 2. 调用Kmeans聚类函数对鸢尾花进行聚类处理 import matplotlib.pyplot as plt import numpy as np from sklearn.datasets import load_iris from sklearn.cluster import KMeans iris=load_iris() X_data=iris.data km=KMeans(n_clusters=3) km.fit(X_data) kc=km.cluster_centers_ y_kmeans=km.predict(X_data) print(y_kmeans,kc) print(kc.shape,y_kmeans.shape,X_data.shape) markers=['o','p','*','s','D'] colors=['r','g','b','y'] for i,j in enumerate(km.labels_): plt.scatter(X_data[i, 0], X_data[i, 1],marker=markers[j],color=colors[j]) plt.scatter(kc[:,0],kc[:,1],color='r',s=80) plt.xlabel('花萼长度') plt.ylabel('花萼宽度') plt.title('K-means在鸢尾花数据集上的聚类效果k=3') plt.show() 程序运行结果如图53所示。 图53Kmeans聚类函数在鸢尾花数据集上的运行结果(k=3) 提示 Kmeans聚类函数运用了Lloyd算法,平均计算复杂度是O(knt),其中n是样本数量,t是迭代次数。在最坏的情况下,计算复杂度为O(n^(k+2/p)),其中p是特征个数。一般情况下,Kmeans算法运算速度非常快,但是其局限性在于聚类结果是由特定初始值所产生的局部解。为了使聚类结果更准确,需要尝试使用不同的初始值反复实验。 3. 轮廓系数——Kmeans评价方法 当文本类别未知时,可以选择轮廓系数作为聚类性能的评估指标。轮廓系数的取值范围为[-1,1],取值越接近1,则说明聚类性能越好; 反之,取值越接近-1,则说明聚类性能越差。轮廓系数越大,表明簇内样本之间越紧凑,簇间距离越大。 下面通过对鸢尾花数据设置不同的k值,观察其轮廓系数的取值变化,确定对数据进行聚类时k的取值何时为最优。 from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt silhouette_score_set=[] n=15 for i in range(2,n): kmeans=KMeans(n_clusters=i,random_state=100).fit(X_data) score=silhouette_score(X_data,kmeans.labels_) silhouette_score_set.append(score) plt.plot(range(2,n), silhouette_score_set,linewidth=2,linestyle='-') plt.show() k的取值从2到14,对应的轮廓系数得分如下: [0.681046169211746, 0.5528190123564091, 0.4980505049972867, 0.4887488870931048, 0.3648340039670018, 0.34750423280461507, 0.35745369258527043, 0.3203121081683388, 0.31784160872963685, 0.3151992769724403, 0.3026804038545623, 0.28457124261699057, 0.2889218914644372] 执行结果如图54所示。 图54不同簇个数取值时的轮廓系数 轮廓系数就是看这条曲线的畸变程度,也就是斜率变化,变化快的部分就是分类的最佳选择。从图54中可以看出,在2~3、5~6两段之间的变化比较快,结合实际情况判断,还是分3类比较好。 视频讲解 5.3基于密度的聚类——DBSCAN聚类 基于密度的聚类假设聚类结构可通过样本分布的紧密程度来确定。同一样本中,样本之间是紧密相连的。根据样本之间的紧密程度,可将不同的样本划分到不同的聚类簇中。DBSCAN就是一种著名的基于密度的聚类算法。 5.3.1DBSCAN算法的原理及相关概念 DBSCAN通过一组邻域参数(,minPts)刻画样本分布的紧密程度,其中,表示某一样本的邻域距离阈值,minPts表示某一样本的距离为的邻域中样本个数的阈值。 给出样本数据集合D=(x1,x2,…,xN),相关概念描述如下: (1) 邻域: 对于任一样本xj∈D,其邻域包含的样本集是D中与xj的距离不大于的样本,即N(xj)={xi∈D|distance(xi,xj)≤},其样本集个数记作|N(xj)|。 (2) 核心对象: 对于任一样本xj∈D,如果其邻域包含至少minPts个样本,即当|N(xj)|≥minPts时,则xj为一个核心对象。 (3) 密度直达: 如果xi位于xj的邻域中,且xj是核心对象,则称xi由xj密度直达。 (4) 密度可达: 对于xi和xj,如果存在样本序列p1,p2,…,pn,满足p1=xi、pn=xj,且1≤i= min_pts): core_object.append(X_data[i]) #生成聚类簇 no_access_data = copy(X_data).tolist() while(len(core_object) != 0): old_no_access_data = copy(no_access_data).tolist() index = random.randint(0, len(core_object)) Q = [] Q.append(core_object[index]) core_object.remove(core_object[index]) while len(Q) != 0: e = copy(Q[0]).tolist() Q.remove(Q[0]) K = get_divide_set(e, X_data, max_dist) if len(K) >= min_pts: delta = get_comm_set(K, no_access_data) for i in range(len(delta)): Q.append(delta[i]) no_access_data = del_aggregate(no_access_data, delta) clu_k = del_aggregate(old_no_access_data, no_access_data) cluster.append(clu_k) core_object = del_aggregate(core_object, clu_k) return cluster def get_divide_set(x0, X_data, max_dist): N = len(X_data) M = [] for i in range(N): d = get_dist(x0, X_data[i]) print(X_data[i]) if(d <= max_dist): M.append(X_data[i]) return M def set_compare(set1, set2): len1 = len(set1) len2 = len(set2) flag = True if(len1 != len2): return False for i in range(len1): if(set1[i] != set2[i]): flag = False return flag def get_comm_set(set1, set2): m = len(set1) n = len(set2) delta = [] for i in range(m): for j in range(n): if(set_compare(set1[i], set2[j]) == True): delta.append(set1[i]) return delta def del_aggregate(X_data, x): m = len(x) N = len(X_data) lst_deleted = [] for i in range(m): for j in range(N): if(set_compare(x[i], X_data[j]) == True): lst_deleted.append(X_data[j]) for i in range(len(lst_deleted)): print("lst_deleted[", i, "] = ", lst_deleted[i]) X_data.remove([lst_deleted[i][0], lst_deleted[i][1]]) return X_data def show_figure(X_data): C1 = mat(array(X_data[0])) C2 = mat(array(X_data[1])) C3 = mat(array(X_data[2])) C4 = mat(array(X_data[3])) fig,ax=plt.subplots(1,1) l1,=ax.plot(C1[:, 0], C1[:, 1], "Dr") l2,=ax.plot(C2[:, 0], C2[:, 1], "sg") l3,=ax.plot(C3[:, 0], C3[:, 1], "*b") l4,=ax.plot(C4[:, 0], C4[:, 1], "oc") plt.legend(handles=[l1,l2,l3,l4],labels=['C1','C2','C3','C4'],loc=0) ax.set_xlabel("密度") ax.set_ylabel("含糖率") plt.title('基于密度的聚类') plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文 plt.rcParams['axes.unicode_minus'] = False plt.show() if __name__ == "__main__": data_set = load_dataset("xiguadata.txt") max_dist=0.1 cluster = DBSCAN_clustering(data_set, max_dist, 5) print(data_set) print(len(cluster)) show_figure(cluster) 当minPts=5时,程序运行结果如图56所示。 图56DBSCAN聚类(minPts=5) 5.4基于层次的聚类——AGNES聚类 基于层次的聚类是一种很直观的算法,顾名思义,就是要逐层地进行聚类。按照层次聚类策略,可分为自底而上的合并聚类和自顶而下的分割聚类。其中AGNES(agglomerative nesting)聚类是一种最为常见的自底而上的合并聚类算法。 5.4.1AGNES聚类算法的思想 AGNES采用自底而上合并聚类簇,每次找到距离最短的两个聚类簇,然后合并成一个大的聚类簇,以此类推,直到全部样本数据合并为一个聚类簇。整个聚类过程就形成了一个树状结构,如图57所示。 图57AGNES算法聚类结构 如何计算两个聚类簇之间的距离呢?初始时,每个样本数据点作为一个类,它们的距离就是这两个点之间的距离。对于一个包含不止一个数据样本的聚类簇,有3种聚类簇度量方法: 最小距离、最大距离和平均距离。 最小距离: dmin(Ci,Cj)=minx∈Ci,y∈Cjdist(x,y)(510) 最大距离: dmax(Ci,Cj)=maxx∈Ci,y∈Cjdist(x,y)(511) 平均距离: daverage(Ci,Cj)=1CiCj∑x∈Ci∑y∈Cjdist(x,y)(512) AGNES聚类算法描述如下。 输入: 样本数据集D={x1, x2,…,xN}、聚类簇个数k、聚类簇度量函数get_dist。 过程: (1) 将每个对象看成一个聚类簇,即对于任意的1≤j≤N,有Cj={xj}。 (2) 根据聚类簇度量函数get_dist确定各个簇之间的距离。 (3) 设置当前簇个数q=N。 (4) 当q>k时,重复执行以下步骤: ① 找出距离最近的两个簇Ci和Cj,合并Ci和Cj: Ci=Ci∪Cj。 ② 对编号为j+1,j+2,…,q的簇重新编号,依次为j,j+1,…,q-1。 ③ 对j=1,2,…,q-1,更新聚类簇之间的距离。 ④ 根据约束条件,确定新参数λ2的上下界。 输出: 簇划分C={C1,C2,…,Ck}。 根据AGNES聚类算法思想,对西瓜数据集进行聚类,当k=4时,聚类结果如下。 簇1: [[0.697, 0.46], [0.725, 0.445], [0.751, 0.489], [0.774, 0.376], [0.714, 0.346]] 簇2: [[0.666, 0.091], [0.719, 0.103], [0.639, 0.161], [0.657, 0.198], [0.748, 0.232], [0.593, 0.042], [0.634, 0.264], [0.608, 0.318], [0.556, 0.215], [0.481, 0.149]] 簇3: [[0.243, 0.267], [0.282, 0.257], [0.403, 0.237], [0.437, 0.211], [0.359, 0.188], [0.339, 0.241], [0.245, 0.057], [0.343, 0.099]] 簇4: [[0.36, 0.37], [0.483, 0.312], [0.525, 0.369], [0.473, 0.376], [0.478, 0.437], [0.446, 0.459], [0.532, 0.472]] 5.4.2AGNES算法的实现 根据AGNES聚类算法的思想,针对西瓜数据集,其算法实现如下。 import math import matplotlib.pyplot as plt #data_train包含30个西瓜样本(密度,含糖量) data_train = [ [0.697, 0.460], [0.774, 0.376],[0.634, 0.264],[0.608, 0.318], [0.556, 0.215],[0.403, 0.237],[0.481,0.149],[0.437, 0.211], [0.666, 0.091],[0.243, 0.267],[0.245, 0.057],[0.343, 0.099], [0.639, 0.161],[0.657, 0.198],[0.360, 0.370],[0.593, 0.042], [0.719, 0.103],[0.359,0.188],[0.339,0.241],[0.282,0.257], [0.748, 0.232],[0.714,0.346],[0.483,0.312],[0.478,0.437], [0.525, 0.369],[0.751,0.489],[0.532,0.472],[0.473,0.376], [0.725, 0.445],[0.446,0.459]] #计算样本点之间的欧几里得距离 def dist_eclud(x1, x2): return math.sqrt(math.pow(x1[0]-x2[0], 2)+math.pow(x1[1]-x2[1], 2)) #计算簇之间的最小距离 def get_min_dist(C_x, C_y): return min(dist_eclud(x, y) for x in C_x for y in C_y) #计算簇之间的平均距离 def get_average_dist(C_x, C_y): return sum(dist_eclud(x, y) for x in C_x for y in C_y)/(len(C_x)*len(C_y)) #找到距离最小的下标 def find_min_index(D): min = float('inf') min_i = 0 min_j = 0 for i in range(len(D)): for j in range(len(D[i])): if i != j and D[i][j] < min: min = D[i][j] min_i = i min_j = j return min_i, min_j, min def get_dist(data): #获得样本点距离 C = [] D = [] for x in data: C_x = [] C_x.append(x) C.append(C_x) for x in C: D_x_start = [] for y in C: d = dist_eclud(x[0], y[0]) D_x_start.append(d) D.append(D_x_start) return C,D #AGNES层次聚类算法 def AGNES_clustering(data, dist, k): n = len(data) C,D=get_dist(data) #合并各簇 while n > k: min_i, min_j, min = find_min_index(D) print(min_i,min_j,min) C[min_i].extend(C[min_j]) C.remove(C[min_j]) D = [] for x in C: D_x_start = [] for y in C: D_x_start.append(dist(x, y)) D.append(D_x_start) n -= 1 return C #显示聚类结果 def show_figure(C): color_value = ['r', 'g', 'b', 'y', 'm', 'k', 'c'] marker_value=['o', 's', '*', 'd', 'x', 'v', 'p'] for i in range(len(C)): crd_x = [] #x坐标 crd_y = [] #y坐标 for j in range(len(C[i])): crd_x.append(C[i][j][0]) crd_y.append(C[i][j][1]) class_name='类'+str(i) plt.scatter(crd_x, crd_y, marker=marker_value[i%len(marker_value)], color=color_value[i%len(color_value)], label=class_name) plt.xlim(0.1,0.9) plt.ylim(0,0.8) plt.xlabel('密度') plt.ylabel('含糖率') plt.legend(loc='upper right') plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文 plt.rcParams['axes.unicode_minus'] = False plt.show() if __name__ == "__main__": k=4 C = AGNES_clustering(data_train, get_average_dist, k) show_figure(C) AGNES聚类算法在西瓜数据集上的运行结果如图58所示。 图58基于层次的聚类在西瓜数据集上的运行结果(k=4) 视频讲解 5.5高斯混合聚类 高斯混合聚类是一种采用高斯混合模型(Gaussian Mixture Model,GMM)的聚类算法,主要采用概率统计的方法进行聚类。高斯混合聚类假设样本服从不同的独立的高斯分布,通过采用EM(ExpectationMaximization)算法实现聚类。 5.5.1概率密度函数 对于样本X服从均值为μ、方差为σ2的正态分布p(x)~N(μ,σ2),即 p(X)=12πσe-(X-μ)22σ2 (513) 其概率密度函数如图59所示。 图59高斯分布的概率密度函数(见彩插) 服从正态分布的概率密度函数具有以下特征: (1) 当自变量x=μ时,f(x)取最大值。 (2) 概率密度函数的图像(曲线图像)关于x=μ对称。 (3) 标准差σ越大,则图像峰值(峰值也就是概率最大值,即峰值=f(x=μ))越小。 这是样本服从单个分布的情况, 对于n维样本空间χ中的随机向量Xi, 图510高斯分布的混合概率密度函数 若Xi服从高斯分布,其概率密度函数为 p(X)=12πΣe-12(X-μ)TΣ-1(X-μ) (514) 其中,μ是n维均值向量,Σ是n×n的协方差矩阵。一个混合高斯概率密度函数如图510所示。 提示 高斯混合模型其实就是若干单高斯混合模型的线性叠加,直观上看,高斯混合概率密度函数是由若干单高斯概率密度函数组合而成的。 5.5.2高斯混合聚类算法的推导过程 若这些数据是由服从若干高斯分布的模型生成,根据最大似然估计和贝叶斯公式可得EM算法的E步: γ(i)j=pz(i)=j|x(i),α,μ,Σ (515) 该公式的含义为: 每个样本的隐含类别 z(i) 可通过各混合成分的后验概率得到。基于此求解M步,利用最大似然估计求以下函数的参数: f(Θ,Z)=∑ni=1∑zj=1QiZjln pXi,Zj|ΘQiZj=∑ni=1∑zj=1QiZjln pXi,Zj|γ,μ,ΣQiZj =∑ni=1∑zj=1QiZ(i)jln pXi|Z(i)j,γ,μ,ΣpZ(i)j|γ,μ,ΣQiZ(i)j =∑ni=1∑zj=1γ(i)jln 1(2π)n2Σjexp -12x(i)-μjTΣ-1j-12x(i)-μjαjγ(i)j (516) 固定参数 αj 和 Σj,对 μj 求导可得 f(Θ,Z)μj=12∑ni=1γ(i)jΣ-1jx(i)-Σ-1jμj(517) 令式(517)为零, 即得参数 μj 的更新公式 μ′j=∑ni=1γ(i)jx(i)∑ni=1γ(i)j。同理, 分别固定参数 μj 和 αj、μj 和 Σj,对 Σj、αj 求导,可分别得到参数 Σj 和 αj 的更新公式: Σ′j=∑ni=1γ(i)jx(i)-μ(i)jTx(i)-μ(i)j∑ni=1γ(i)j,α′j=∑ki=1γ(i)jk(518) 5.5.3高斯混合聚类算法思想 类似于单高斯模型,高斯混合分布定义如下。 pM(X)=∑ki=1αipX|μi,Σi(519) 该公式表示该高斯分布由k个服从高斯分布的成分构成。其中, αi 是混合成分的系数, ∑ki=1αi=1且 αi>0; μi 和 Σi 分别是第 i 个高斯混合成分的均值和方差。 对于随机变量X={X1,X2,X3,…,Xm}来说,每个样本Xj属于zj=i的概率可由贝叶斯定理获得,即隐变量 zj 的后验概率为 pMzj=i|Xj=Pzj=ipMXj|zj=ipMXj=αipXj|μi,Σi∑ki=1αipX|μi,Σi (520) 高斯混合聚类的算法描述如下: 输入样本集为D={X1,X2,X3,…,Xm},高斯混合成分个数为k。 步骤如下: 初始化高斯混合分布的各模型参数 k、αi、μi 和 Σi。 while 迭代次数iter