第十章 10.2 提高聚类性能 10.3 二分K-均值算法

上一节提到,在K-均值聚类中的簇的数目K是一个用户预先定义的参数,那么用户如何才能知道 K 的选择是不是正确?如何才能知道生成的簇比较好呢?

在包含簇分配结果的矩阵中保存着每个点的误差,即该点到簇质心的距离平方值。下面要做的就是利用该误差来评价聚类质量的方法。

一种用于度量聚类效果的指标是SSE,对应程序里面 clusterAssment 矩阵第一列治和。SSE 值越小表示数据点越接近于它们的质心,聚类效果也越好。因为对误差去了平方,因此更加重视远离中心的点。


书上有例子我就不多说了。



================================================================================


10.3  二分k-均值算法

为克服 k-均值 算法收敛于局部最小的问题,有人提出了称为 二分k-均值 的算法。该算法首先将所有的点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续划分。选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE的值。

伪代码如下:

将所有点看成一个簇
当簇数目小于 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] # s.tolist(),它可以将自己转换为一个列表对象返回。 
    centList = [centroid0]
    for j in range(m):
        clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:]) ** 2
    while (len(centList) < k):
        lowestSSE = inf # 一开始将最小的SSE设置为无穷大
        for i in range(len(centList)): # 遍历所有的簇来决定最佳的簇进行划分
            # 尝试划分每一簇
            # 将该簇中的所有点看做一个小的数据集
            ptsInCurrCluster =\
                    dataSet[nonzero(clusterAssment[:,0].A == i)[0],:]
            centroidMat, splitClustAss = \
                    kMeans(ptsInCurrCluster, 2, distMeas) # 放入kMeans 函数中处理
            sseSplit = sum(splitClustAss[:,1])
            sseNotSplit = \
                    sum(clusterAssment[nonzero(clusterAssment[:,0].A != i)[0],1])
            print "sseSplit, and notSplit: ", sseSplit, sseNotSplit
            if (sseSplit + sseNotSplit) < lowestSSE: # 如果该划分的SSE值最小,则本次划分被保存。
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
                # 更新簇的分配结果,通过两个数组过滤器来完成
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = \
                            len(centList)
        bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = \
                            bestCentToSplit
        print 'the bestCentToSplit is: ', bestCentToSplit 
        print 'the len of bestClustAss is:', len(bestClustAss)
        # 新的簇分配结果被更新,新的质心会被添加到 centList 中。
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:,0].A ==\
                                bestCentToSplit)[0],:] = bestClustAss
    return mat(centList), clusterAssment # 返回质心列表和簇分配结果


注意书上的代码最后几行少了 tolist()[0]) 


运行函数重写为:

# -*- coding:utf-8 -*-
# run_kMeans.py
# ex10.2
import kMeans
from numpy import *

datMat3 = mat(kMeans.loadDataSet('testSet2.txt'))
centList, myNewAssments = kMeans.biKmeans(datMat3,3)
print centList
print myNewAssments
print '\n'
print centList

结果太长就不给出了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章