您的位置:首页 > 其它

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

2015-10-12 17:00 585 查看
第十章 利用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)


效果:







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), 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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: