第十章 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

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