第十章 利用k-均值聚類算法對未標注的數據進行分組
一.導語
聚類算法可以看做是一種無監督的分類方法,之所以這么說的原因是它和分類方法的結果相同,區別它的類別沒有預先的定義。簇識別是聚類算法中經常使用的一個概念,使用這個概念是為了對聚類的結果進行定義。
聚類算法幾乎可以用於所有的對象,並且簇內的對象越相似,效果越好。
二.K-均值聚類算法的基本概念
K-均值聚類算法它的目的是將數據分成k個簇。它的一般過程是如下:
隨機的選擇k個數據點作為初始的質心
當任意一個簇的分配結果發生變化的情況下
對於每一個數據點
對於每一個質心
計算數據點到質心的距離
將當前的數據點分配到距離最近的那個質心所在的簇
對於每一個簇計算其質心
在上面的過程中質心的計算方法一般采用平均;距離的計算方法可以自由選擇,比如歐氏距離等等,但是不同的距離度量方式可能會有不同的結果。
K-均值聚類算法它的特點:
1.優點:計算簡便,算法簡單,容易實現
2.缺點:容易陷入局部最小,對於大數據樣本收斂較慢
3.適用的數據類型:數值型數據(如果是標稱型數據可以考慮轉化成數值型數據)
三.K-means算法的具過程
1.首先從文件中讀取數據並保存到數組中。
2.定義距離函數
3.初始化質心。在初始化質心時采用的方法是對於每一個特征在它給定的范圍之內進行隨機
4.kmeans算法的實現
五.使用后處理來提高聚類性能
因為我們使用聚類算法的時候很容易獲得局部最小值,而不是全局最小值,所以需要采取一些措施來提高聚類的性能。
當然最簡單的就是增加簇的個數,但是這樣違背了我們優化的初衷,因此我們采用了后處理的方式進行優化。所謂的后處理指的就是找到簇內誤差平方和最大的那個簇,然后將這個簇拆分成兩個簇,因為要維持簇的個數不變,我們有需要找到兩個出錯的質心進行合並。這里衡量質心是否為出錯質心有兩種量化方法:一是將距離最近的兩個質心定義為出錯質心;二是將合並后誤差平方和增幅最小的兩個質心作為出錯質心。
六.二分k-均值算法
二分k-均值算法是為了解決聚類算法局部最小的問題而提出的。它的基本思想是首先將所有的點看做是一個簇,然后2-means算法將簇一分為二,然后選擇其中一個簇繼續划分,選擇哪一個簇這決定於對其划分是否能夠最大程度的降低誤差平方和。一種常見的方法如下:
將所有的點看做是一個簇
當簇的個數小於k時
對於每一個簇
計算當前簇的總誤差1
計算將當前簇一分為二后的總誤差2
選擇總誤差1和總誤差2差值最大的簇作為下一個划分的簇
(也可以選擇划分后簇的總誤差最小的簇作為所選擇的的划分的簇)
當然還有一種更簡單的方式就是直接選擇總誤差最大的簇作為下一個要划分的簇。
根據上述的算法,我們可以得到如下的代碼
def bikmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m, 2)))
centroid0 = mean(dataSet, axis=0).tolist()[0]
centList = [centroid0] # create a list with one centroid
for j in range(m): # calc initial Error
clusterAssment[j, 1] = distMeas(mat(centroid0), dataSet[j, :]) ** 2
while (len(centList) < k):
lowestSSE = inf
for i in range(len(centList)):
ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0],
:] # get the data points currently in cluster i
centroidMat, splitClustAss = kmeans(ptsInCurrCluster, 2, distMeas)
sseSplit = sum(splitClustAss[:, 1]) # compare the SSE to the currrent minimum
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
print "sseSplit, and notSplit: ", sseSplit, sseNotSplit
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList) # change 1 to 3,4, or whatever
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] # replace a centroid with two best centroids
centList.append(bestNewCents[1, :].tolist()[0])
clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0],
:] = bestClustAss # reassign new clusters, and SSE
return mat(centList), clusterAssment
七.對地圖上的點進行聚類
對地圖上的點進行聚類的時候,首先是獲取數據和分析數據,這兩部省略。假設我們已經擁有了數據,該數據保存在places.txt文件中,並且文件中的第四列和第五列是我們需要的數據。現在我們將根據bikmeans算法找到當前數據中的五個簇,並將結果顯示出來
程序的結果如下:
八.總結
聚類算法是一種無監督的算法,常見的聚類算法有k-means算法和二分k-means算法。后者是前者的進階版,效果也比前者更好。因為k-means算法很容易受到初始點選擇的影響,並且很容易陷入局部最小。當然這並不是僅有的聚類算法,聚類算法有很多,還有層次聚類等。
總的來說,聚類算法的目的就是從一堆數據中中無目的的尋找一些簇。這些簇是沒有經過事先定義的,但是其確實能夠展示出數據中的一些特征。