第十章 利用K均值聚类算法对未标注数据分组
10.1 K-均值聚类算法
K-均值是发现给定数据集的k个簇的算法,每个簇通过其质心来描述。其优点为容易实现,但可能收敛到局部最小值,在大规模数据集上收敛较慢。
随机确定k个初始点为质心,为每个点找距其最近的质心,并将其分配给该质心所对应的簇,每个簇的质心更新为该簇所有点的平均值。质心可用任意距离度量方式,但结果相应的受到距离度量方式影响。伪代码:
coding:
#!/usr/bin/env python # coding=utf-8 from numpy import * def loadDataSet(fileName): #导入数据集 dataMat = [] fr = open(fileName) for line in fr.readlines(): curLine = line.strip().split("\t") fltLine = 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] #取dataSet的列数 centroids = mat(zeros((k,n))) #每维都创建k个随机数,数在每维最大最小值之间 for j in range(n): minJ = min(dataSet[:,j]) #每维最小值 rangeJ = float(max(dataSet[:,j]) - minJ) centroids[:,j] = minJ + rangeJ*random.rand(k,1) #random.rand(k,1),k个0到1.0之间的随机数 return centroids datMat = mat(loadDataSet("testSet.txt")) #print distEclud(datMat[0],datMat[1]) def kMeans(dataSet, k, distMeas = distEclud, createCent = randCent): m = shape(dataSet)[0] #取数据的行数 clusterAssment = mat(zeros((m,2))) #创建m*2的矩阵,第一列存簇索引值,第二列存误差 centroids = createCent(dataSet, k) clusterChanged = True while clusterChanged: clusterChanged = False for i in range(m): #对每个数据点来说, minDist = inf minIndex = -1 for j in range(k): #对每维的k个质心,哪个数据点距其最近 distJI = distMeas(centroids[j, :], dataSet[i, :])#距离度量计算质心与数据点之间的距离 if distJI < minDist: #寻找最近质心 minDist = distJI minIndex = j #每个数据点距哪个质心j近,则将其归到j这个质心 if clusterAssment[i,0] != minIndex: #若数据点i的质心是minIndex这个质心,或者说其质心不变的时候,退出循环 clusterChanged = True clusterAssment[i,:] = minIndex, minDist**2 print centroids #打印出每次质心的变化过程 for cent in range(k): ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#将所有归为cent这个质心的数据点都提出来,计算均值,更新质心的位置。 centroids[cent,:] = mean(ptsInClust, axis = 0) return centroids, clusterAssment datMat = mat(loadDataSet("testSet.txt")) myCentroids, clustAssing = kMeans(datMat, 4)
效果:
分析:
从上图可以看出,选定4个簇进行迭代。迭代次数不是确定的,左边10次,右边4次,因为初始质心由随机数生成,迭代次数跟初始质点的选定还是有很大区别的。
10.2 使用后处理来提高聚类性能
K-均值的缺点是需要预先确定簇的数目k,如何确定k的选择是否正确是比较重要的问题。K-均值算法收敛但聚类效果较差的原因是K-均值算法收敛到了局部最小值而非全局最小值。
一种度量聚类效果的指标是SSE(sum of squared error,误差平方和)。SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。可通过增加簇的个数降低SSE,但不符合聚类的目标:保持簇数目不变的情况下提高簇的质量。可以对生成的簇进行后处理,将具有最大SSE值的簇划分成两个簇。将最大簇包含的点过滤出来并在这些点上运行k-均值算法。也可以将两个簇进行合并。
10.3 二分K-均值算法
二分K-均值算法能克服K-均值算法收敛于局部最小值的问题。首先将所有点作为一个簇,然后将该簇一分为二,选择其中一个簇继续划分,选择哪个取决于对其划分是否可以最大程度降低SSE的值,不断划分,直到用户指定的簇数目为止。
伪代码:
将所有点看成一个簇
当簇数目小于k时,对于每一个簇
选择使得误差最小的那个簇进行划分操作计算总误差
在给定的簇上面进行K-均值聚类(k=2)
计算将该簇一分为二之后的总误差
coding
#============二分K-均值聚类算法====================== def biKmeans(dataSet, k, distMeas=distEclud): m = shape(dataSet)[0] clusterAssment = mat(zeros((m,2))) centroid0 = mean(dataSet, axis=0).tolist()[0] #计算每维均值,得到质心 centList = [centroid0] #用来保留所有质心 for j in range(m): clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2 while (len(centList) < k): lowestSSE = inf for i in range(len(centList)): ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:] #get the data points currently in cluster i centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas) sseSplit = sum(splitClustAss[:,1]) #compare the SSE to the currrent minimum sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) print "sseSplit, and notSplit: ",sseSplit,sseNotSplit if (sseSplit + sseNotSplit) < lowestSSE: bestCentToSplit = i bestNewCents = centroidMat bestClustAss = splitClustAss.copy() lowestSSE = sseSplit + sseNotSplit bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whatever bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit print '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 SSE return mat(centList), clusterAssment dataMat3 = mat(loadDataSet("testSet2.txt")) centList, myNewAssments = biKmeans(dataMat3,3)
效果
Figure 10-2: 二分k-均值预测结果
10.4 小结
聚类是一种无监督的学习方法。聚类将数据点归到多个簇中,其中相似数据点处于同一簇中,不相似点处于不同簇中。聚类中可以使用多种不同的方法来计算相似度。
在python scipy包中也实现了一些聚类算法,from scipy.cluster.vp import *可以找到kmeans2函数,通过内置函数直接计算聚类的质心,可参考博客http://blog.csdn.net/u010454729/article/details/41158167。