一、聚类基本概念
在之前学习当中,我们遇到的大多数分类问题,对于数据集中的样本我们已经有了对应的分类标签,我们通过训练数据集可以绘制出一条最合适的决策边界,明确了具体的样本属于哪一类,当在测试集上测试时,我们的分类误差尽可能的小,对于未知的数据,我们也能对其进行准确率尽可能大的分类。 而本次学习的聚类,对于很多样本而言,他们没有分类标签,我们可以通过某个特定标准,使同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性尽可能地大,我们不关心数据具体属于哪一类,而是把相似性大的数据归为一类。 白话来说,给我们一篮子苹果和香蕉,分类就是我们通过学习知道什么香蕉、苹果,然后把它们分成香蕉类和苹果类;聚类就是我们不知道他们属于哪一类,不需要进行学习过程,但是我们可以通过他们的外观、颜色进行归类。
二、聚类种类
(一)K均值 K均值算法是一种广泛使用的聚类算法或者成为其他聚类算法的基础,预将数据分为K组,则随机选取K个对象作为初始的聚类中心(这里的K为常数,需事先设定,通俗地说该算法是将没有标注的 M 个样本通过迭代的方式聚集成K个簇),然后计算每个对象与各个种子聚类中心之间的距离(在对样本进行聚集的过程往往是以样本之间的距离作为指标来划分),把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。每分配一个样本,聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小。
(二)密度聚类 基于密度的聚类算法假设聚类结构能够通过样本分布的紧密程度确定,以数据集在空间分布上的稠密程度为依据进行聚类,即只要一个区域中的样本密度大于某个阈值,就把它划入与之相近的簇中。密度聚类从样本密度的角度进行考察样本之间的可连接性,并由可连接样本不断扩展直到获得最终的聚类结果。它能克服基于距离的算法只能进行“类圆形”(凸)聚类的缺点,聚类可为任意形状,且对噪声不敏感。 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是密度聚类的典型算法,是基于一组邻域参数(ε,MinPts)来描述样本分布的紧密程度,相比于基于划分的聚类方法和层次聚类方法,DBSCAN算法将簇定义为密度相连的样本的最大集合,能够将密度足够高的区域划分为簇,不需要给定簇数量,并可在有噪声的空间数据集中发现任意形状的簇。其原理简单说画圈儿,其中要定义两个参数,一个是圈儿的最大半径,一个是一个圈儿里最少应容纳几个点。只要邻近区域的密度(对象或数据点的数目)超过某个阈值,就继续聚类,最后在一个圈里的,就是一个类。
(三)层次聚类 层次聚类方法的基本思想是:通过某种相似性测度计算节点之间的相似性,并按相似度由高到低排序,逐步重新连接个节点。可分为: AGNES算法: 采用自底向上的聚合策略.先将每个样本看作一个初始聚类簇,然后找出距离最近的两个簇进行合并,直到达到预设的聚类簇个数. DIANA算法: 采用自顶向下的分拆策略.先将所有样本看作一个簇,然后逐渐细分为越来越小的簇,直到达到预设的聚类簇个数。 这里以AGNES为例
- 将每个对象看作一类,计算两两之间的最小距离;
- 将距离最小的两个类合并成一个新类;
- 重新计算新类与所有类之间的距离;
- 重复2、3,直到所有类最后合并成一类
三、实验内容
(一)数据集 这一部分网上的代码也非常多,我这里采用的sklearn的datasets中选取的三种不同的数据集,首先对他们加噪,并设置相应的参数,然后对他们调用sklearn的方法对他们进行样本预测归类。 (1)make_blobs make_blobs()是sklearn.datasets中的一个函数,主要是产生聚类数据集,需要熟悉每个参数,继而更好的利用。我们可以设置样本数量,样本维度(默认是2,两个特征),标准差、样本维度等,这里我们设置200个样本,数据中心端为2,标准差0.6,样本维度默认2。
(2)make_moons make_blobs()是sklearn.datasets中的一个函数,也可以用来生成非线性的数据集,形象的看,他就像两个月牙形的月亮,将数据分成两类。我们也可以设置样本数量、加噪、设置随机种子等,我们这里的样本数量设置为200,加噪系数0.1,随机种子设置0。
(3)make_circles make_blobs()是sklearn.datasets中的一个函数生成一个二维的大圆,包含一个小圆,我们也可以设置样本数量、加噪、随机种子、内外圆之间的比例因子等,我们这里的样本数量设置为200(如果是奇数,内圆比外圆多一点),加噪系数0.1,随机种子设置0,内外圆之间的比例因子设置为0.4。
(二)训练及结果 (1)K-means
'''
k均值算法:K-means
'''
import matplotlib.pyplot as plt
from sklearn import cluster
from sklearn import datasets
x, y_true = datasets.make_blobs(n_samples=200, centers=4,cluster_std=0.60, random_state=0)
x2, y2_true = datasets.make_moons(n_samples=200, noise=0.1, random_state=0)
x3, y3_true = datasets.make_circles(n_samples=200, noise=0.1, random_state=0, factor=0.4)
gmm = cluster.KMeans(2)
label = gmm.fit_predict(x)
label2 = gmm.fit_predict(x2)
label3 = gmm.fit_predict(x3)
print(y_true, 'make_blobs-label真实标签')
plt.scatter(x[y_true == 0, 0], x[y_true == 0, 1], color = "orange")
plt.scatter(x[y_true == 1, 0], x[y_true == 1, 1], color = "pink")
plt.scatter(x[y_true == 2, 0], x[y_true == 2, 1], color = "red")
plt.scatter(x[y_true == 3, 0], x[y_true == 3, 1], color = "blue")
plt.show()
plt.scatter(x2[y2_true == 0, 0], x2[y2_true == 0, 1], color = "orange")
plt.scatter(x2[y2_true == 1, 0], x2[y2_true == 1, 1], color = "pink")
plt.show()
plt.scatter(x3[y3_true == 0, 0], x3[y3_true == 0, 1], color = "orange")
plt.scatter(x3[y3_true == 1, 0], x3[y3_true == 1, 1], color = "pink")
plt.show()
plt.scatter(x[:, 0], x[:, 1], c=label)
plt.title("blobs")
plt.show()
plt.scatter(x2[:, 0], x2[:, 1], c=label2)
plt.title("moons")
plt.show()
plt.scatter(x3[:, 0], x3[:, 1], c=label3)
plt.title("circles")
plt.show()
针对以上三种不同的数据集,我们在使用sklearn的K-means方法进行聚类的时候,给定的k值都是2,将他们分为2个簇,直观从视图上对比归类结果与真实结果(这里就没有打印出真实分类结果与预测的分类标签结果,因为视图上其实已经可以也很明显的看到效果了)。
针对make_blobs,他主要是产生聚类数据集,我们本身给定的中心点个数为2,通过调用sklearn的聚类方法后,发现对其聚类的效果和真实的归类结果是一致的。 而另外两种数据集,对其归类的效果显然不理想(我们对两种数据集都进行了加噪处理,在K-means方法中也会产生影响),针对make_blobs生成的数据集而言,上半月右侧黄色的点聚类下半月的距离更近;同理下半月的左侧部分距离上半月更近,导致了这样的归类结果。 而针对make_blobs生成的同心圆数据集,这种类圆形的聚类通过K-means聚类方法没有办法归类成原始的分类的结果。 针对K-means方法的缺陷:k值必须人为给定初始值,对于不合适的k值,我想进一步看一下分类错误的结果,于是通过make_blobs给定4个中心点生成4个簇,而在进行聚类的时候传入k=2,观察结果。
这里选取了不合适的k值显然导致了不理想的归类结果。 (2)DBSCAN
'''
密度聚类:DBSCAN
'''
import matplotlib.pyplot as plt
from sklearn import cluster
from sklearn import datasets
x, y_true = datasets.make_blobs(n_samples=200, centers=2,cluster_std=0.60, random_state=0)
x2, y2_true = datasets.make_moons(n_samples=200, noise=0.07, random_state=0)
x3, y3_true = datasets.make_circles(n_samples=200, noise=0.07, random_state=0, factor=0.4)
gmm = cluster.DBSCAN(eps=0.2, min_samples=4)
label = gmm.fit_predict(x)
label2 = gmm.fit_predict(x2)
label3 = gmm.fit_predict(x3)
plt.title("blobs-initial")
plt.scatter(x[y_true == 0, 0], x[y_true == 0, 1], color = "orange")
plt.scatter(x[y_true == 1, 0], x[y_true == 1, 1], color = "pink")
plt.show()
plt.title("moons-initial")
plt.scatter(x2[y2_true == 0, 0], x2[y2_true == 0, 1], color = "orange")
plt.scatter(x2[y2_true == 1, 0], x2[y2_true == 1, 1], color = "pink")
plt.show()
plt.title("circles-initial")
plt.scatter(x3[y3_true == 0, 0], x3[y3_true == 0, 1], color = "orange")
plt.scatter(x3[y3_true == 1, 0], x3[y3_true == 1, 1], color = "pink")
plt.show()
plt.scatter(x[:, 0], x[:, 1], c=label)
plt.title("blobs-result")
plt.show()
plt.scatter(x2[:, 0], x2[:, 1], c=label2)
plt.title("moons-result")
plt.show()
plt.scatter(x3[:, 0], x3[:, 1], c=label3)
plt.title("circles-result")
plt.show()
仍然针对上述三个数据集,我们采用sklearn的DBSCAN方法进行密度聚类,首先我们不设置聚类时的任何设置。
上述对数据集进行DBSCAN聚类的时候效果很不理想,是因为我们没有对参数进行设置,我们需要对邻域的大小、邻域内最小样本数量进行调参,才能得到比较好的聚类结果。 我们设置邻域的大小值为0.2,邻域内最小样本数量为4个,同时改小了数据集的加噪系数,才能达到比较好的聚类结果。
在经过参数调试之后,我们发现使用DBSCAN方法对其进行聚类,针对半月形和同心圆形的类圆形、凸聚类的聚类有很好的效果。 (3)AGNES
'''
层次聚类:AGENS
'''
import matplotlib.pyplot as plt
from sklearn import cluster
from sklearn import datasets
x, y_true = datasets.make_blobs(n_samples=200, centers=2,cluster_std=0.60, random_state=0)
x2, y2_true = datasets.make_moons(n_samples=200, noise=0.1, random_state=0)
x3, y3_true = datasets.make_circles(n_samples=200, noise=0.1, random_state=0, factor=0.4)
gmm = cluster.AgglomerativeClustering(n_clusters=2)
label = gmm.fit_predict(x)
label2 = gmm.fit_predict(x2)
label3 = gmm.fit_predict(x3)
plt.title("blobs-initial")
plt.scatter(x[y_true == 0, 0], x[y_true == 0, 1], color = "orange")
plt.scatter(x[y_true == 1, 0], x[y_true == 1, 1], color = "pink")
plt.show()
plt.title("moons-initial")
plt.scatter(x2[y2_true == 0, 0], x2[y2_true == 0, 1], color = "orange")
plt.scatter(x2[y2_true == 1, 0], x2[y2_true == 1, 1], color = "pink")
plt.show()
plt.title("circles-initial")
plt.scatter(x3[y3_true == 0, 0], x3[y3_true == 0, 1], color = "orange")
plt.scatter(x3[y3_true == 1, 0], x3[y3_true == 1, 1], color = "pink")
plt.show()
plt.scatter(x[:, 0], x[:, 1], c=label)
plt.title("blobs-result")
plt.show()
plt.scatter(x2[:, 0], x2[:, 1], c=label2)
plt.title("moons-result")
plt.show()
plt.scatter(x3[:, 0], x3[:, 1], c=label3)
plt.title("circles-result")
plt.show()
仍然针对上述三个数据集,我们采用sklearn的AgglomerativeClustering方法进行AGENS层次聚类。我们设置n_clusters为2,初始簇数为2。
针对以上的聚类结果,如果对比真实的分类结果,有一部分聚类的结果是不够理想的,当出现一个聚类错误的节点以后,后面的聚类结果都会按照错误的结果进行下去。 四、实验总结 虽然聚类是针对样本本身没有标签的情况,但是以上的实验采取了有分类标签的数据集,当我们采用不同的聚类方法进行聚类的时候将其聚类结果与原样本的分类结果进行了对比。 (一)K-means k均值算法是作为聚类学习的入门算法,它是一种经典的聚类算法,尤其当簇近似为高斯分布时聚类效果好。但它也有一些缺陷。 K-means缺陷: 1、K-means聚类中心的个数K 需要事先给定,但在实际中这个 K 值的选定是非常难以估计的,很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适, K-means需要人为地确定初始聚类中心,不同的初始聚类中心可能导致完全不同的聚类结果。如同上述实验部分,我们生成数据集的时候通过make_blobs给定k=4的簇个数,进行聚类的时候给定k=2,不合适的k值导致了不理想的归类结果。 2、异常点的存在:K-means算法在迭代的过程中使用所有点的均值作为新的质点(中心点),如果簇中存在异常点,将导致均值偏差比较严重。 比如一个簇中有2、4、6、8、100五个数据,那么新的质点为24,显然这个质点离绝大多数点都比较远(此种缺陷在此后的改进方法中可以得到改善)。 3、初值敏感:K-means算法是初值敏感的,选择不同的初始值可能导致不同的簇划分规则。 (二)DBSCAN 优点: 1、可以对任意形状分布的进行聚类。 2、不需要事先给定簇的数目k。 3、可以在聚类时发现噪音点、对数据集中的异常点不敏感(抗噪声) 缺点: 1、必须指定一个密度阈值,从而去除低于此密度阈值的噪音点。 2、调参复杂 给定eps选择过大的MinPts会导致核心对象数量减少,使得一些包含对象较少的自然簇被丢弃,选择过小的MinPts会导致大量对象被标记为核心对象,从而将噪声归入簇; 给定MinPts选择过小的eps会导致大量的对象被误标为噪声,一个自然簇被误拆为多个簇;选择过大的eps则可能有很多噪声被归入簇,而本应分离的若干自然簇也被合并为一个簇。 3、如果样本的密度不均, 效果会不理想 (三)AGNES 优点: 1、处理速度很快,通常这是与目标数据库中记录的个数无关的,只与把数据空间分为多少个单元有关。 2、可解释性好(如当需要创建一种分类法时);还有些研究表明这些算法能产生高质量的聚类,也会应用在上面说的先取K比较大的K-means后的合并阶段;还有对于K-means不能解决的非球形族就可以解决了。 缺点: 1、时间复杂度高。 2、贪心算法的原因导致一步错,步步错,他是不可逆的,由于对象在合并或分裂之后,下一次聚类会在前一次聚类基础之上继续进行合并或分裂,也就是说,一旦聚类结果形成,想要再重新合并来优化聚类的性能是不可能的了
以上内容参考文章: 聚类实现 聚类介绍 在他们的基础上进行修改改进与综合
|