聚类是一种无监督的学习,它将相似的对象归到同一簇中,它有点像全自动分类。聚类方法几乎可以应用于所有的对象,簇内的对象越相似,聚类的效果越好。
K-均值聚类算法就是一种典型的聚类算法,之所以称之为K-均值是因为它可以发现k个不同的簇,且每个簇的中心采用簇中所含值的均值计算而成。
簇识别给出聚类结果的含义,假定有一些数据,现在将相似数据归到一起,簇识别会告诉我们这些簇到底都是些什么。聚类与分类的最大不同在于,分类的目标事先已知,而聚类则不同。因为其产生的结果与分类相同,而只是类别没有预先定义,聚类有时也被称为无监督分类。
聚类分析试图将相似对象归入同一簇,将不相似对象归到不同簇。相似这一概念取决于所选择的相似度计算方法。
K-均值聚类的优缺点:
优点:容易实现
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢
适用数据类型:数值型数据。
K-均值是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每一个簇通过其质心,即簇中所有点的中心描述。
K-均值算法的工作流程是这样的:首先,随机确定k个初始点作为质心;然后将数据集中的每个点分配到一个簇中,具体来讲,为每个点找距其最近的质心,并将其分配给该质心所对应的簇;这一步完成之后,每个簇的质心更新为该簇所有点的平均值。
上述过程的伪代码表示如下:
创建k个点作为起始质心(经常是随机选择)
当任意一个点的簇分配结果发生改变时:
对数据集中的每个数据点:
对每个质心:
计算质心与数据点之间的距离
将数据点分配到距其最近的簇
对每一个簇,计算簇中所有点的均值并将均值作为质心
K-均值聚类的一般流程:
1、收集数据:使用任意方法
2、准备数据:需要数值型数据来计算距离,也可以将标称型数据映射为二值型数据再用于距离计算
3、分析数据:使用任意方法
4、训练算法:不适用于无监督学习,即无监督学习没有训练过程
5、测试算法:应用聚类算法,观察结果。可以使用量化的误差指标,如误差平方和来评价算法的结果
6、使用算法:可以用于所希望的任何应用。通常情况下,簇质心可以代表整个簇的数据来做出决策。
“最近”质心意味着需要进行某种距离计算。数据集上K-均值算法的性能会受到所选距离计算方法的影响。
K-均值聚类支持函数的实现:
from numpy import *
def loadDataSet(fileName):
dataMat=[]
fr=open(fileName)
for line in fr.readlines():
curLine=line.strip().split('\t')
fltLine=list(map(float,curLine))
dataMat.append(fltLine)
return dataMat
def distEclud(vecA,vecB):
return sqrt(sum(power(vecA-vecB,2)))
def randCent(dataSet,k):
n=shape(dataSet)[1]
centroids=mat(zeros((k,n)))
for j in range(n):
minJ=min(dataSet[:,j])
rangeJ=float(max(dataSet[:,j])-minJ)
centroids[:,j]=minJ+rangeJ*random.rand(k,1)
return centroids
上述代码中包含了几个K-均值算法中要用到的辅助函数。
loadDataSet()将文本文件导入到一个列表中,文本文件每一行为tab分隔的浮点数。每一个列表会被添加到dataMat中,最后返回dataMat,该返回值是一个包含许多其他列表的列表。这种格式可以很容易将很多只封装到矩阵中。
distEclud()计算两个向量的欧式距离。
randCent()为给定数据集构建一个包含k个随机质心的集合。随机质心必须要在整个数据集的边界之内,这可以通过找到数据集每一维的最小和最大值来完成。然后生成0到1.0之间的随机数并通过取值范围和最小值,以便确保随机点在数据的边界之内。
运行效果:
dataMat=mat(loadDataSet('test/testSet.txt'))
print(min(dataMat[:,0]))
print(min(dataMat[:,1]))
print(max(dataMat[:,0]))
print(max(dataMat[:,1]))
print(randCent(dataMat,2))
print(distEclud(dataMat[0],dataMat[1]))
下面实现完整的K-均值算法,该算法会创建k个质心,然后将每个点分配到最近的质心,再重新计算质心。这个过程重复多次,直到数据点的簇分配结果不再改变为止:
def kMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
m=shape(dataSet)[0]
clusterAssment=mat(zeros((m,2)))
centroids=createCent(dataSet,k)
clusterChanges=True
while clusterChanges:
clusterChanges=False
for i in range(m):
minDist=inf
minIndex=-1
for j in range(k):
distJI=distMeas(centroids[j,:],dataSet[i,:])
if distJI<minDist:
minDist=distJI
minIndex=j
if clusterAssment[i,0]!=minIndex:
clusterChanges=True
clusterAssment[i,:]=minIndex,minDist**2
print(centroids)
for cent in range(k):
ptsInClust=dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]
centroids[cent,:]=mean(ptsInClust,axis=0)
return centroids,clusterAssment
kMeans()函数接受4个输入参数。只有数据集及簇的数目是必选参数,而用来计算距离和创建初始质心的函数都是可选的。kMeans()函数一开始确定数据集中数据点的总数,然后创建一个矩阵来存储每个点的分配结果。簇分配结果矩阵clusterAssment包含两列:一列记录簇索引值,第二列存储误差。这里的误差指的是当前点到簇质心的距离。
按照计算质心-分配-重新计算的方式反复迭代,直到所有数据点的簇分配结果不再改变为止。程序中创建了一个标志变量clusterChanges,如果该值为True,则继续迭代。上述迭代使用while循环来实现。接下来遍历所有数据找到距离每个点最近的质心,这可以通过对每个点遍历所有质心并计算点到每个质心的距离来完成。计算距离是使用distMeas参数给出的距离函数,默认距离函数是distEclud()。如果任一点的簇分配结果发生改变,则更新clusterChanges标志。
最后,遍历所有质心并更新它们的取值。具体实现步骤:首先通过数组过滤来获得给定簇的所有点;然后计算所有点的均值,选项axis=0表示沿矩阵的列方向计算均值计算;最后,程序返回所有的类质心与点的分配结果。
运行效果:
dataMat=mat(loadDataSet('test/testSet.txt'))
myCentroids,clustAssing=kMeans(dataMat,4)
print('质心:',myCentroids)
print('分配结果:',clustAssing)