聚类是一种无监督的学习,它将相似的对象归到同一个簇中。它有点像全自动分类。聚类方法几乎可以应用于所有对象,簇内的对象越相似,聚类的效果越好。
簇识别给出聚类结果的含义。假定有一些数据,现在将相似数据归到一起,簇识别会告诉我们这些簇到底都是些什么。聚类与分类的最大不同在于,分类的目标事先已知,而聚类则不一样。因为其产生的结果与分类相同,而只是类别没有预先定义,聚类有时也被称为无监督分类。
聚类分析试图将相似对象归入同一簇,将不相似对象归到不同簇。相似这一概念取决于所选择的相似度计算方法。
下面会构建K-均值方法并观察其实际效果。接下来还会讨论简单K-均值算法中的一些缺陷。为了解决其中的一些缺陷,可以通过后处理来产生更好的簇。接着会给出一个更有效的称为二分K-均值的聚类算法。

10.1 K-均值聚类算法

K-均值聚类
优点:容易实现。
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。
适用数据类型:数值型数据。

K-均值是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每一个簇通过其质心,即簇中所有点的中心来描述。
K-均值算法的工作流程:首先,确定随机k个初始点作为质心。然后将数据集中的每个点分配到一个簇中,具体来讲,为每个点找距其最近的质心,并将其分配给该质心所对应的簇。这一步完成之后,每个簇的质心更新为该簇所有点的平均值。伪代码如下:

“最近”质心意味着需要进行某种距离计算。数据集上K-均值算法的性能会受到所选距离计算方法的影响。下面给出K-均值算法的代码实现。首先创建一个名为kMeans.py的文件,然后将下面的代码加入其中:

from numpy import *def loadDataSet(fileName):      #general function to parse tab -delimited floatsdataMat = []                #assume last column is target valuefr = open(fileName)for line in fr.readlines():curLine = line.strip().split('\t')fltLine = list(map(float,curLine)) #map all elements to float()dataMat.append(fltLine)return dataMatdef distEclud(vecA, vecB):return sqrt(sum(power(vecA - vecB, 2))) #la.norm(vecA-vecB)def randCent(dataSet, k):n = shape(dataSet)[1]centroids = mat(zeros((k,n)))#create centroid matfor j in range(n):#create random cluster centers, within bounds of each dimensionminJ = min(list(dataSet[:,j]))rangeJ = float(max(dataSet[:,j]) - minJ)centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))return centroids

代码中包含几个K-均值算法中要用到的辅助函数。第一个函数loadDataSet()和上一章完全相同,它将文本文件导入到一个列表中。文本文件每一行为tab分隔的浮点数。每一个列表会被添加到dataMat中,最后返回dataMat。该返回值是一个包含许多其他列表的列表。这种格式可以很容易将很多值封装到矩阵中。
下一个函数 distEclud() 计算两个向量的欧氏距离。这是本章最先使用的距离函数,也可以使用其他距离函数。
最后一个函数是 randCent() ,该函数为给定数据集构建一个包含k个随机质心的集合。随机质心必须要在整个数据集的边界之内,这可以通过找到数据集每一维的最小和最大值来完成。然后生成0到1.0之间的随机数并通过取值范围和最小值,以便确保随机点在数据的边界之内。

import kMeans
from numpy import *
datMat = mat(kMeans.loadDataSet('testSet.txt'))
print(min(datMat[:,0]))
print(min(datMat[:,1]))
print(max(datMat[:,1]))
print(max(datMat[:,0]))

使用 randCent() 函数生成min到max之间的值:

import kMeans
from numpy import *
datMat = mat(kMeans.loadDataSet('testSet.txt'))
print(min(datMat[:,0]))
print(min(datMat[:,1]))
print(max(datMat[:,1]))
print(max(datMat[:,0]))
print(kMeans.randCent(datMat,2))

可以看到函数 randCent() 生成min到max之间的值。下面测试一下距离计算方法:

print(kMeans.distEclud(datMat[0],datMat[1]))

函数均正常运行,可以准备实现完整K-均值算法。该算法会创建k个质心,然后将每个点分配到最近的质心,再重新计算质心。这个过程重复数次,直到数据点的簇分配结果不再改变为止。为kMeans.py文件加入下面的代码:

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):m = shape(dataSet)[0]clusterAssment = mat(zeros((m,2)))#create mat to assign data points#to a centroid, also holds SE of each pointcentroids = createCent(dataSet, k)clusterChanged = Truewhile clusterChanged:clusterChanged = Falsefor i in range(m):#for each data point assign it to the closest centroidminDist = inf; minIndex = -1for j in range(k):distJI = distMeas(centroids[j,:],dataSet[i,:])if distJI < minDist:minDist = distJI; minIndex = jif clusterAssment[i,0] != minIndex: clusterChanged = TrueclusterAssment[i,:] = minIndex,minDist**2print(centroids)for cent in range(k):#recalculate centroidsptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this clustercentroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to meanreturn centroids, clusterAssment

代码给出了K-均值算法,kMeans() 函数接受4个输入参数。只有数据集及簇的数目是必选参数,而用来计算距离和创建初始质心的函数都是可选的。kMeans() 函数一开始确定数据集中数据点的总数,然后创建了一个矩阵来存储每个点的簇分配结果。簇分配结果矩阵clusterAssment包含两列:一列记录簇索引值,第二列存储误差。误差是指当前点到簇质心的距离,后面会使用该误差来评价聚类效果。
按照计算质心——分配——重新计算反复迭代,直到所有数据点的簇分配结果不再改变为止。程序中可以创建一个标志变量clusterChanged,如果该值为True,则继续迭代。上述迭代使用while循环来实现。接下来遍历所有数据找到距离每个点最近的质心,这可以通过对每个点遍历所有质心并计算点到每个质心的距离来完成。计算距离是使用distMeans参数给出的距离函数,默认距离函数是 distEclud() ,该函数的实现已经给出。如果任一点的簇分配结果发生改变,则更新clusterChanged标志。
最后,遍历所有质心并更新它们的取值。步骤如下:首先通过数组过滤来获得给定簇的所有点;然后计算所有点的均值,选项 axis=0 表示沿矩阵的列方向进行均值计算;最后,程序返回所有的类质心与点分配结果。下图给出了一个聚类结果的示意图。

import kMeans
from numpy import *
datMat = mat(kMeans.loadDataSet('testSet.txt'))
myCentroids,clustAssing = kMeans.kMeans(datMat,4)

运行结果给出了4个簇(质心),经过3次迭代后K-均值算法收敛。

10.2 使用后处理来提高聚类性能

K-均值聚类中簇的数目k是一个用户预先定义的参数,那么用户如何才能知道k的选择是否正确?如何才能知道生成的簇比较好?在包含簇分配结果的矩阵中保存着每个点的误差,即该点到簇质心的距离平方值。
考虑下图中的聚类结果,这是在一个包含三个簇的数据集上运行K-均值算法之后的结果,但是点的簇分配结果值没有那么准确。K-均值算法收敛但聚类效果较差的原因是,K-均值算法收敛到了局部最小值,而非全局最小值(局部最小值指结果还可以但非最好结果,全局最小值是可能的最好结果)。
一种用于度量聚类效果的指标是SSE(误差平方和),对应程序中clusterAssment矩阵的第一列之和。SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。因为对误差取了平方,因此更加重视那些远离中心的点。一种肯定可以降低SSE值的方法是增加簇的个数,但这违背了聚类的目标。聚类的目标是在保持簇数目不变的情况下提高簇的质量。
那么如何对下图结果进行改进?可以对生成的簇进行后处理,一种方法是将具有最大SSE值的簇划分成两个簇。具体实现时可以将最大簇包含的点过滤出来并在这些点上运行K-均值算法,其中的k设为2。

为了保持簇总数不变,可以将某两个簇进行合并。图中可以明显看出,应该将下部两个出错的簇质心进行合并。可以很容易对二位数据上的聚类进行可视化,但如果遇到更高维数据该如何去做?
有两种可以量化的方法:合并最近的质心,或者合并两个使得SSE增幅最小的质心。第一种思路通过计算所有质心之间的距离,然后合并距离最近的两个点来实现。第二种方法需要合并两个簇然后计算总SSE值。必须在所有可能的两个簇上重复上述处理过程,知道直到合并最佳的两个簇为止。

10.3 二分K-均值算法

为克服K-均值算法收敛于局部最小值的问题,有人提出另一个称为二分K-均值的算法。该算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪一个簇机芯划分取决于对其划分是否可以最大程度降低SSE的值。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。
二分K-均值算法伪代码形式如下:

将所有点看成一个簇
当簇数目小于k时
对于每一个簇计算总误差在给定的簇上面进行K-均值聚类(k=2)计算将该簇一分为二之后的总误差
选择使得误差最小的那个簇进行划分操作

另一种做法是选择SSE最大的簇进行划分,直到簇数目达到用户指定的数目为止。打开kMeans.py加入下面的代码:

def biKmeans(dataSet, k, distMeas=distEclud):m = shape(dataSet)[0]clusterAssment = mat(zeros((m,2)))centroid0 = mean(dataSet, axis=0).tolist()[0]centList =[centroid0] #create a list with one centroidfor j in range(m):#calc initial ErrorclusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2while (len(centList) < k):lowestSSE = inffor i in range(len(centList)):ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]#get the data points currently in cluster icentroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)sseSplit = sum(splitClustAss[:,1])#compare the SSE to the currrent minimumsseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])print("sseSplit, and notSplit: ",sseSplit,sseNotSplit)if (sseSplit + sseNotSplit) < lowestSSE:bestCentToSplit = ibestNewCents = centroidMatbestClustAss = splitClustAss.copy()lowestSSE = sseSplit + sseNotSplitbestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whateverbestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplitprint('the bestCentToSplit is: ',bestCentToSplit)print('the len of bestClustAss is: ', len(bestClustAss))centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids centList.append(bestNewCents[1,:].tolist()[0])clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSEreturn mat(centList), clusterAssment

上述程序中的函数与 kMeans() 的参数相同。在给定数据集、所期望的簇数目和计算方法的条件下,函数返回聚类结果。同 kMeans() 一样,用户可以改变所使用的距离计算方法。
该函数首先创建一个矩阵来存储集中在每个点的簇分配结果及平方误差,然后计算整个数据集的质心,并使用一个列表来保留所有的质心。得到上述质心之后,可以遍历数据集中所有点来计算每个点到质心的误差值。
接下来程序进入while循环,该循环会不停对簇进行划分,直到得到想要的簇数目为止。可以通过考察簇列表中的值来获得当前簇的数目。然后遍历所有的簇来决定最佳的簇进行划分。为此需要比较划分前后的SSE。一开始将最小SSE设置成无穷大,然后遍历簇列表cenList中的每一个簇。对每个簇,将该簇中的所有点砍成一个小的数据集ptsInCurrCluster。将ptsInCurrCluster输入到函数 kMeans() 中进行处理(k=2)。K-均值算法会生成两个质心(簇),同时给出每个簇的误差值。这些误差与剩余数据集的误差之和作为本次划分的误差。如果该划分的SSE值最小,则本次划分被保存。一旦决定了要划分的簇,接下来就要实际执行划分操作。划分操作很容易,只需要将要划分的簇中的所有点的簇分配结果进行修改即可。当使用 kMeans() 函数并且指定簇树为2时,会得到两个编号分别为0和1的结果簇。需要将这些簇编号修改为划分簇及新加簇的编号,该过程可以通过两个数组过滤器来完成。最后,新的簇分配结果被更新,新的质心会被添加到cenList中。
当while循环结束时,同 kMeans() 函数一样,函数返回质心列表与簇分配结果。

import kMeans
from numpy import *
datMat3 = mat(kMeans.loadDataSet('testSet2.txt'))
cenList,myNewAssments = kMeans.biKmeans(datMat3,3)

查看质心结果:

上述函数可以运行多次,聚类会收敛到全局最小值,而原始的 kMeans() 函数偶尔会陷入局部最小值。下图给出了数据集及运行 biKmeans() 后的质心的示意图。

《机器学习实战》——第10章 利用K-均值聚类算法对未标注数据分组相关推荐

  1. 机器学习实战(十)——利用K-均值聚类算法对未标注数据分组

    机器学习实战(十)--利用K-均值聚类算法对未标注数据分组 聚类是一种无监督的学习,即对于训练数据来说并没有已知标签,我们是根据样本之间的相似程度将其划分为多个类. 一.K-均值聚类算法 K-均值算法 ...

  2. 机器学习——利用K-均值聚类算法对未标注数据分组

    聚类是一种无监督的学习,它将相似的对象归到同一簇中.它有点像全自动分类.聚类方法几乎可以应用到所有对象,簇内的对象越相似,聚类的效果越好. K-均值(K-means)聚类算法,之所以称之为K-均值是因 ...

  3. 利用K-均值聚类算法对未标注数据分组

    无监督学习简介 无监督学习是一种机器学习的训练方式,它本质上是一个统计方法,在没有标签的数据里可以发现潜在的一些结构的一种训练方式. 无监督学习主要具备3个特点: 无监督学习没有明确的目的 无监督学习 ...

  4. 『ML』利用K-Means聚类算法对未标注数据分组——《机器学习实战》学习笔记(Ch10)

    本节用Python实现K-Means算法,对未标注的数据进行聚类. 在做完聚类后,如何对于聚类结果进行评估?请看 用Python实现聚类效果的评估(轮廓系数.互信息) 导航 K-Means简介 代码实 ...

  5. 机器学习实战(十)利用K-means算法对未标注数据分组

    第十章 利用K-means算法对未标注数据分组 10.1 K-均值聚类算法 10.2 使用后处理来提高聚类性能 10.3 二分K-均值算法 10.4 总结 第十章 利用K-means算法对未标注数据分 ...

  6. 机器学习实战-61:K均值聚类算法(K-Means)

    K均值聚类算法(K-Means) 深度学习原理与实践(开源图书)-总目录,建议收藏,告别碎片阅读! 机器学习分为监督学习.无监督学习和半监督学习(强化学习).无监督学习最常应用的场景是聚类(clust ...

  7. 机器学习之无监督学习-K均值聚类算法

    机器学习之无监督学习-K均值聚类算法 对于无监督学习,有两类重要的应用,一个是聚类,一个是降维.我们今天主要学习聚类中的K均值聚类. 我们先看看下图,图a为原始的数据点,我们想要对图a的数据点进行分类 ...

  8. k均值聚类算法(K Means)及其实战案例

    算法说明 K均值聚类算法其实就是根据距离来看属性,近朱者赤近墨者黑.其中K表示要聚类的数量,就是说样本要被划分成几个类别.而均值则是因为需要求得每个类别的中心点,比如一维样本的中心点一般就是求这些样本 ...

  9. Python金融数据挖掘 第11章 复习思考题1 (聚类)给出一个数据集data_multivar.txt,里面有200个点坐标,在平面坐标系下可以画出它的散点图,用K均值聚类算法来训练模型,分4类。

    1.题目 给出一个数据集data_multivar.txt,里面有200个点坐标,在平面坐标系下可以画出它的散点图,如图11-12所示. data_multivar.txt 图11-12 数据集 da ...

最新文章

  1. C# Socket编程(5)使用TCP Socket
  2. python 中主线程结束 子线程还在运行么_python 线程之一:线程的创建、启动及运行方式
  3. NAC网络访问控制,你需要知道的!
  4. c语言中指针的类型,学习C语言中的指针类型
  5. SQL Server 2012 Managed Service Account
  6. 7.1Python异常处理
  7. 调试 Dockerfile - 每天5分钟玩转 Docker 容器技术(15)
  8. 解决django前端使用iframe标签报错127.0.0.1 refused to connect.
  9. 百度人脸识别离线SDK_Android版_在线激活失败:not enough param_解决方案---百度人脸识别技术应用005
  10. ctfshow-WEB-web14( 利用数据库读写功能读取网站敏感文件)
  11. ftp4j的android应用
  12. python自定义标识符_《Python 3程序开发指南(第2版•修订版)》——第2章 数据类型 2.1 标识符与关键字...
  13. 慢就是快的人生哲理_快和慢人生感悟
  14. 十分钟免费拥有永久网站
  15. 如何获得指定进程的主窗口
  16. 蓝牙5.1之Direction Finding
  17. sharesdk分享qq空间-错误码1001
  18. 题目34 众数和中位数
  19. LabVIEW之MSComm控件注册
  20. Stata 实证 问题记录

热门文章

  1. OSChina 周三乱弹 ——你知道开源项目是怎么维护的么
  2. 实战案例:使用Python制作疾风剑豪-亚索词云图
  3. 你的工作是挑水还是挖井
  4. centos7.4 mini从系统安装到声卡配置
  5. 诛仙服务器状态查询,服务器数据互通查询 - 《诛仙3》官方网站 - 完美世界 - 玄幻巅峰·全新纪元...
  6. Android手机接入usb屏幕失灵,手机USB连接电脑没反应的解决方法已测可用
  7. 如何使用学校邮箱申请JetBrains全家桶
  8. 达梦数据库迁移,无法使用dts时怎么办。
  9. 简单爱c语言音乐程序代码,Ubuntu下实现歌词解析,
  10. openlayers 给数据点(散点)添加点击事件 和hover事件