第十章 利用k-均值聚類算法對未標註數據分組

本章內容:

1. K-均值聚類算法。

2. 對聚類得到的簇進行後處理。

3. 二分K-均值聚類算法。

4.  對地理位置進行聚類。

========================================================================

K-聚類算法是一種無監督學習算法。首先明白什麼是無監督學習:輸入數據有標籤,則爲有監督學習,沒標籤則爲無監督學習。可以參考這個知乎的鏈接:什麼是無監督學習?

聚類就是一種無監督的學習,它將相似的對象歸到同一個簇中,有點像全自動分類。聚類方法幾乎可以應用於所有對象。簇內的對象越相似,聚類的效果越好。本章要學習一種稱爲 K-means 聚類的算法。之所以稱爲 K-均值 是因爲它可以發現 k 個不同的簇,且每個簇的中心採用簇中所含值的均值計算而成。

聚類與分類的最大不同在於:分類的目標事先已知,而聚類則不一樣。因爲其產生的結果與分類相同,而只是類別沒有預先定義,聚類有時也被稱爲無監督學習( unsupervised classification )。

===========================================================================================


10.1 K-均值聚類算法

優點:容易實現。

缺點:可能收斂到局部最小值,在大規模數據集上收斂較慢。

適用數據類型:數值型數據。


k-均值僞代碼:

創建 k 個點作爲起始質心(經常是隨機選擇)
當任意一個點的簇分類結果發生改變時
    對數據集中的每一個數據點
        對每個質心
            計算質心與數據點之間的距離
        將數據點分配到距其最近的簇
    對每一個簇,計算簇中所有點的均值並將均值作爲質心


上面提到的“最近”質心的說法,意味着需要進行某種距離計算。數據集上的 K-均值 算法的性能會受到所選距離計算方法的影響。首先創建一個文件,寫入代碼:

# -*- coding:utf-8 -*-
# kMeans.py
from numpy import *

# 將文本文件導入到一個列表中
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t') # 以 tab 分割浮點數
        fltLine = map(float, curLine)
        dataMat.append(fltLine) # append() 方法用於在列表末尾添加新的對象,這裏是 dataMat
    return dataMat

def distEclud(vecA, vecB): # 歐拉距離
    return sqrt(sum(power(vecA - vecB, 2)))

# 給定數據集構建一個包含 K 個隨機質心的集合
def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k, n))) # 調用 mat() 函數可以將數組轉化爲矩陣
    for j in range(n): # 構建簇質心
        minJ = min(dataSet[:,j])
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = minJ + rangeJ * random.rand(k,1)
    return centroids


老樣子,建立一個 run_kMeans.py 文件:

# -*- coding:utf-8 -*-
# run_kMeans.py
import kMeans
from numpy import *

datMat = mat(kMeans.loadDataSet('testSet.txt')) # 從文本文件中構建矩陣

print min(datMat[:,0])
print min(datMat[:,1])
print max(datMat[:,1])
print max(datMat[:,0])

print kMeans.randCent(datMat, 2)# 給定數據集構建一個包含 2 個隨機質心的集合

print kMeans.distEclud(datMat[0], datMat[1])

然後運行結果:


運行了兩次,看得出來隨機數結果是不同的。


現在準備實現完整的 k-均值 算法。該算法創建 k 個質心,然後將每個點分配到最近的質心,再重新計算質心。這個過程重複多次,直到數據點的簇分配結果不再改變爲止。

在 kMeans.py 裏面輸入下面的代碼。

# 接受四個輸入參數,只有數據集和簇的數目是必選參數,用來計算距離和創建初始質心的函數都是可選的
def kMeans(dataSet, k, distMeas = distEclud, createCent = randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((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 # True 表示可以繼續迭代
            clusterAssment[i,:] = minIndex,minDist ** 2
        print centroids
        for cent in range(k):
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]
            centroids[cent,:] = mean(ptsInClust, axis = 0)
    return centroids, clusterAssment # 更新質心的位置


按照上面的程序反覆迭代計算,直到所有數據點的簇分配結果不再改變位置。
遍歷所有質心並且更新的具體步驟是:首先通過數組過濾來獲得給定簇的所有點;然後計算所有點的均值,選項 axis = 0 表示沿舉證的列方向進行均值計算;最後,程序返回所有的類質心與點分配結果。

運行裏面添加:

print'\n***************************************\n'
reload(kMeans)

datMat = mat(kMeans.loadDataSet('testSet.txt'))
print 'myCentroids, clustAssing = kMeans.kMeans(datMat, 4)'
myCentroids, clustAssing = kMeans.kMeans(datMat, 4)
print myCentroids, clustAssing

不知道爲什麼出來的是一長串結果,這裏就不貼了。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章