二分K-means聚類(bisecting K-means)
算法優缺點:
由於這個是K-means的改進算法,所以優缺點與之相同。
算法思想:
1.要了解這個首先應該了解K-means算法,可以看這里這個算法的思想是:首先將所有點作為一個簇,然后將該簇一分為二。之后選擇能最大程度降低聚類代價函數(也就是誤差平方和)的簇划分為兩個簇(或者選擇最大的簇等,選擇方法多種)。以此進行下去,直到簇的數目等於用戶給定的數目k為止。
2.以上隱含着一個原則是:因為聚類的誤差平方和能夠衡量聚類性能,該值越小表示數據點月接近於它們的質心,聚類效果就越好。所以我們就需要對誤差平方和最大的簇進行再一次的划分,因為誤差平方和越大,表示該簇聚類越不好,越有可能是多個簇被當成一個簇了,所以我們首先需要對這個簇進行划分。
3.關於二分K-means的優點《Machine Learning in Action》說的是能夠克服K-means收斂於局部最小,但是想了一下感覺這個並不能保證收斂到全局最優值(而且后面運行代碼結果也會出現不太好的情況,不知道這算不算是個證據)
4.通過查閱一些資料和總結,二分K-means聚類的優點有:
- 二分K均值算法可以加速K-means算法的執行速度,因為它的相似度計算少了
- 不受初始化問題的影響,因為這里不存在隨機點的選取,且每一步都保證了誤差最小
所以說這個算法也並不能夠保證完全不受K的影響一定歸到全局最小,只是相對較優,並且還有了一定的速度提升。理解有偏差歡迎指正。
函數:
biKmeans(dataSet, k, distMeas=distEclud)
這個函數實現了二分算法,過程大體如下(代碼中注釋已經很詳細了):
1.初始化全部點的質心,並建立所需要的數據存儲結構
2.對每一個簇嘗試二分(最開始就是一個簇),選出最好的
3.更新各個簇的元素個數
-
1 def biKmeans(dataSet, k, distMeas=distEclud): 2 m = shape(dataSet)[0] 3 clusterAssment = mat(zeros((m,2)))#記錄簇分配的結果及誤差 4 centroid0 = mean(dataSet, axis=0).tolist()[0]#計算整個數據集的質心 5 centList =[centroid0] #create a list with one centroid 6 for j in range(m):#計算初始聚類點與其他點的距離 7 clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2 8 while (len(centList) < k): 9 lowestSSE = inf 10 for i in range(len(centList)):#嘗試划分每一簇 11 ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]#get the data points currently in cluster i 12 centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)#對這個簇運行一個KMeans算法,k=2 13 sseSplit = sum(splitClustAss[:,1])#compare the SSE to the currrent minimum 14 sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) 15 print "sseSplit, and notSplit: ",sseSplit,sseNotSplit 16 if (sseSplit + sseNotSplit) < lowestSSE:##划分后更好的話 17 bestCentToSplit = i 18 bestNewCents = centroidMat 19 bestClustAss = splitClustAss.copy() 20 lowestSSE = sseSplit + sseNotSplit 21 bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #更新簇的分配結果change 1 to 3,4, or whatever 22 bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit 23 print 'the bestCentToSplit is: ',bestCentToSplit 24 print 'the len of bestClustAss is: ', len(bestClustAss) 25 centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids 26 centList.append(bestNewCents[1,:].tolist()[0]) 27 clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSE 28 return mat(centList), clusterAssment
聚類的效果應該還是不錯的,比K-means要好
