1. 程式人生 > >機器學習實戰(Machine Learning in Action)學習筆記————06.k-均值聚類演算法(kMeans)學習筆記

機器學習實戰(Machine Learning in Action)學習筆記————06.k-均值聚類演算法(kMeans)學習筆記

機器學習實戰(Machine Learning in Action)學習筆記————06.k-均值聚類演算法(kMeans)學習筆記

關鍵字:k-均值、kMeans、聚類、非監督學習
作者:米倉山下
時間:2018-11-3
機器學習實戰(Machine Learning in Action,@author: Peter Harrington)
原始碼下載地址:https://www.manning.com/books/machine-learning-in-action
[email protected]:pbharrin/machinelearninginaction.git

*************************************************************
一、k-均值聚類演算法(kMeans)原理

聚類是一種無監督學習,它將相似的物件歸到同一個簇中。K-均值聚類演算法以k個隨機質心開始。演算法會計算每個點到質心的距離。每個點會被分到距其最近的簇質心,然後緊接著基於新分配到簇的點更新質心。以上過程重複數次,直到質心不再改變。這個簡單地演算法非常有效但是容易受到初始質心的影響。
為了獲得更好的的聚類結果,可以使用另一種稱為二分K-均值的聚類方法。二分K-均值首先將所有點作為一個簇,然後使用K-均值聚類(k=2)對其劃分。下一次迭時,選擇有最大誤差的簇進行劃分。該過程直到k個簇建立成功為止。二分K-均值的聚類效果要好於K-均值聚類。


*************************************************************
二、k-均值聚類(kMeans)
演算法:

輸入:dataSet——待聚類的資料集;k——要聚類的簇的個數;distMeas——計算距離的函式,預設為歐式距離函式distEclud;createCent——建立質心的函式,預設為randCent;
輸出:centroids——各個簇的質心;clusterAssment——儲存最小距離平方和對應質心索引

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): #計算該點到每個質心的距離 distJI = distMeas(centroids[j,:],dataSet[i,:]) if distJI < minDist: #最小距離和對應質心索引 minDist
= distJI; minIndex = j if clusterAssment[i,0] != 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) #ptsInClust的列平均值,axis=0 列方向 return centroids, clusterAssment

其他函式:

loadDataSet(fileName)——構造資料,讀取檔案中的資料矩陣
distEclud(vecA, vecB)——計算兩個向量的歐式距離
randCent(dataSet, k)——根據給定的資料,在其取值邊界內隨機構造k個質心
-------------------------------------------------
測試:

>>> import kMeans
>>> data=kMeans.loadDataSet('testSet.txt')
>>> from numpy import *
>>> centpoint,cluster=kMeans.kMeans(mat(data),4)
[[ 2.70503374 -1.42834359]
 [-2.09874174  0.13175831]
 [ 4.36781866  1.23667688]
 [-1.57667561 -3.89341615]]
[[ 3.03713839 -2.62802833]
 [-2.605345    2.35623864]
 [ 2.6265299   3.10868015]
 [-2.9085278  -3.11811235]]
[[ 2.80293085 -2.7315146 ]
 [-2.46154315  2.78737555]
 [ 2.6265299   3.10868015]
 [-3.38237045 -2.9473363 ]]
>>>

*************************************************************
三、二分K-均值聚類(biKmeans)

一種用來度量聚類效果的指標SSE(誤差平方和),SSE值越小表示資料點越接近他們的質心,聚類效果也越好。因為對誤差取了平方,因此更加註重遠離中心的點。通過增加簇的個數來降低SSE值。

克服K-均值演算法收斂於區域性最小值的問題,出現了二分K-均值聚類演算法。首先將所有點作為一個簇,然後將該簇一分為二。之後選擇其中一個簇繼續劃分,選擇哪一個簇進行劃分取決於對其劃分知否可以最大程度降低SSE的值。對上述過程不斷重複,直到達到指定的簇數目為止。

演算法:

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         #寄存較小的SSE值
        for i in range(len(centList)):
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]    #取出i簇的資料
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)    #將該簇分為兩簇0和1
            sseSplit = sum(splitClustAss[:,1])       #比較前後的SSE值
            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   #寄存較小的SSE值
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)   #更新簇分配結果,centList增加一簇
        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]  #更新簇質心
        centList.append(bestNewCents[1,:].tolist()[0])             #更新簇質心
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss    #更新clusterAssment
    return mat(centList), clusterAssment

測試:

>>> reload(kMeans)
<module 'kMeans' from 'kMeans.py'>
>>> data=kMeans.loadDataSet('testSet2.txt')
>>> centList,mycluster=kMeans.biKmeans(mat(data),3)
sseSplit, and notSplit:  453.0334895807502 0.0
the bestCentToSplit is:  0
the len of bestClustAss is:  60
sseSplit, and notSplit:  77.59224931775066 29.15724944412535
sseSplit, and notSplit:  12.753263136887313 423.8762401366249
the bestCentToSplit is:  0
the len of bestClustAss is:  40
>>> centList
matrix([[-2.94737575,  3.3263781 ],
        [-0.45965615, -2.7782156 ],
        [ 2.93386365,  3.12782785]])
>>>

*************************************************************
四、示例:在地圖上的點聚類

places.txt檔案中有70行資料,每行資料包括了地址、座標等資訊,其中第四、第五列是座標資訊
將這些點按照座標遠近分為5簇
其中距離的計算用到了地球表面球面距離
最後將其繪製按照不同顏色繪製出來

>>> kMeans.clusterClubs()
sseSplit, and notSplit:  3431.621150997616 0.0
the bestCentToSplit is:  0
the len of bestClustAss is:  69
sseSplit, and notSplit:  1230.242092830394 1062.0271973840918
sseSplit, and notSplit:  515.6100923704457 2369.5939536135247
the bestCentToSplit is:  0
the len of bestClustAss is:  53
sseSplit, and notSplit:  318.6390236086656 1892.3442135982214
sseSplit, and notSplit:  515.6100923704457 1230.242092830394
sseSplit, and notSplit:  471.8115196045904 1461.9522740003558
the bestCentToSplit is:  1
the len of bestClustAss is:  16
sseSplit, and notSplit:  197.38636407063862 1345.9271085845755
sseSplit, and notSplit:  53.299046126034725 1437.9528665515088
sseSplit, and notSplit:  549.8565865332125 915.5351689867098
sseSplit, and notSplit:  109.50254173619503 1538.1414114797253
the bestCentToSplit is:  2
the len of bestClustAss is:  35