《机器学习实战》笔记之十——利用K均值聚类算法对未标注数据分组

来源:互联网 发布:epub手机打开知乎 编辑:程序博客网 时间:2024/05/01 02:16

第十章 利用K均值聚类算法对未标注数据分组


10.1 K-均值聚类算法

K-均值是发现给定数据集的k个簇的算法,每个簇通过其质心来描述。其优点为容易实现,但可能收敛到局部最小值,在大规模数据集上收敛较慢。
随机确定k个初始点为质心,为每个点找距其最近的质心,并将其分配给该质心所对应的簇,每个簇的质心更新为该簇所有点的平均值。质心可用任意距离度量方式,但结果相应的受到距离度量方式影响。

伪代码:

coding:

#!/usr/bin/env python# coding=utf-8from 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 dataMatdef 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 centroidsdatMat = 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, clusterAssmentdatMat = mat(loadDataSet("testSet.txt"))myCentroids, clustAssing = kMeans(datMat, 4)

效果:

    
Figure 10-1: kmeans聚类效果
分析:
从上图可以看出,选定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), clusterAssmentdataMat3 = 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。

0 0
原创粉丝点击