Bisecting KMeans (二分K均值)算法講解及實現


算法原理


由於傳統的KMeans算法的聚類結果易受到初始聚類中心點選擇的影響,因此在傳統的KMeans算法的基礎上進行算法改進,對初始中心點選取比較嚴格,各中心點的距離較遠,這就避免了初始聚類中心會選到一個類上,一定程度上克服了算法陷入局部最優狀態。
二分KMeans(Bisecting KMeans)算法的主要思想是:首先將所有點作為一個簇,然后將該簇一分為二。之后選擇能最大限度降低聚類代價函數(也就是誤差平方和)的簇划分為兩個簇。以此進行下去,直到簇的數目等於用戶給定的數目k為止。以上隱含的一個原則就是:因為聚類的誤差平方和能夠衡量聚類性能,該值越小表示數據點越接近於他們的質心,聚類效果就越好。所以我們就需要對誤差平方和最大的簇進行再一次划分,因為誤差平方和越大,表示該簇聚類效果越不好,越有可能是多個簇被當成了一個簇,所以我們首先需要對這個簇進行划分。

代碼實現


本文在實現過程中采用數據集4k2_far.txt,聚類算法實現過程中默認的類別數量為4。其中輔助函數存於myUtil.py文件和K均值核心函數存於kmeans.py文件,具體參考《KMeans (K均值)算法講解及實現》
二分K均值主函數邏輯思想如下代碼所示:

# -*- encoding:utf-8 -*-

from kmeans import *
import matplotlib.pyplot as plt

dataMat = file2matrix("testData/4k2_far.txt", "\t")   # 從文件構建的數據集
dataSet = dataMat[:, 1:]   # 提取數據集中的特征列

k = 4   # 外部指定1,2,3...通過觀察數據集有4個聚類中心
m = shape(dataSet)[0]  # 返回矩陣的行數

# 初始化第一個聚類中心: 每一列的均值
centroid0 = mean(dataSet, axis=0).tolist()[0]
centList =[centroid0] # 把均值聚類中心加入中心表中
# 初始化聚類距離表,距離方差
# 列1:數據集對應的聚類中心,列2:數據集行向量到聚類中心距離的平方
ClustDist = mat(zeros((m, 2)))
for j in range(m):
    ClustDist[j,1] = distEclud(centroid0,dataSet[j,:])**2
'''
color_cluster(ClustDist[:, 0:1], dataSet, plt)
drawScatter(plt, mat(centList), size=60, color='red', mrkr='D')
plt.show()
'''

# 依次生成k個聚類中心
while (len(centList) < k):
    lowestSSE = inf # 初始化最小誤差平方和。核心參數,這個值越小就說明聚類的效果越好。
    # 遍歷cenList的每個向量
    #----1. 使用ClustDist計算lowestSSE,以此確定:bestCentToSplit、bestNewCents、bestClustAss----#
    for i in xrange(len(centList)):
        # 從dataSet中提取類別號為i的數據構成一個新數據集
        ptsInCurrCluster = dataSet[nonzero(ClustDist[:, 0].A == i)[0], :]
        # 應用標准kMeans算法(k=2),將ptsInCurrCluster划分出兩個聚類中心,以及對應的聚類距離表
        centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2)
        # 計算splitClustAss的距離平方和
        sseSplit = sum(multiply(splitClustAss[:, 1], splitClustAss[:, 1]))   # 此處求歐式距離的平方和
        # 計算ClustDist[ClustDist第1列!=i的距離平方和
        sseNotSplit = sum(ClustDist[nonzero(ClustDist[:, 0].A != i)[0], 1])
        if (sseSplit + sseNotSplit) < lowestSSE: # 算法公式: lowestSSE = sseSplit + sseNotSplit
            bestCentToSplit = i                 # 確定聚類中心的最優分隔點
            bestNewCents = centroidMat          # 用新的聚類中心更新最優聚類中心
            bestClustAss = splitClustAss.copy() # 深拷貝聚類距離表為最優聚類距離表
            lowestSSE = sseSplit + sseNotSplit  # 更新lowestSSE
    # 回到外循環
    # ----2. 計算新的ClustDist----#
    # 計算bestClustAss 分了兩部分:
    # 第一部分為bestClustAss[bIndx0,0]賦值為聚類中心的索引
    bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
    # 第二部分 用最優分隔點的指定聚類中心索引
    bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
    # 以上為計算bestClustAss

    # ----3. 用最優分隔點來重構聚類中心----#
    # 覆蓋: bestNewCents[0,:].tolist()[0]附加到原有聚類中心的bestCentToSplit位置
    # 增加: 聚類中心增加一個新的bestNewCents[1,:].tolist()[0]向量
    centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0]
    centList.append(bestNewCents[1, :].tolist()[0])
    # 以上為計算centList
    # 將bestCentToSplit所對應的類重新更新類別
    ClustDist[nonzero(ClustDist[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
    '''
    color_cluster(ClustDist[:, 0:1], dataSet, plt)
    drawScatter(plt, mat(centList), size=60, color='red', mrkr='D')
    plt.show()
    '''

# 輸出生成的ClustDist:對應的聚類中心(列1),到聚類中心的距離(列2),行與dataSet一一對應
color_cluster(ClustDist[:, 0:1], dataSet, plt)
print "cenList:",mat(centList)
# 繪制聚類中心圖形
drawScatter(plt, mat(centList), size=60, color='red', mrkr='D')
plt.show()

評估分類結果


上述代碼的”’注釋部分給出了每次迭代時,聚類中心的變化情況,如下所示:

                                                                          二分K均值聚類中心變化情況

相關


1、KMeans (K均值)算法講解及實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM