1. 程式人生 > >K-means聚類的演算法原理及實現

K-means聚類的演算法原理及實現

原地址

1、如何理解K-Means演算法?
2、如何尋找K值及初始質心?
3、如何應用K-Means演算法處理資料?



K-means聚類的演算法原理


K-Means是聚類演算法中的一種,其中K表示類別數,Means表示均值。顧名思義K-Means是一種通過均值對資料點進行聚類的演算法。K-Means演算法通過預先設定的K值及每個類別的初始質心對相似的資料點進行劃分。並通過劃分後的均值迭代優化獲得最優的聚類結果。

K值及初始質心

K值是聚類結果中類別的數量。簡單的說就是我們希望將資料劃分的類別數。K值決定了初始質心的數量。K值為幾,就要有幾個質心。選擇最優K值沒有固定的公式或方法,需要人工來指定,建議根據實際的業務需求,或通過層次聚類(Hierarchical Clustering)的方法獲得資料的類別數量作為選擇K值的參考。這裡需要注意的是選擇較大的K值可以降低資料的誤差,但會增加過擬合的風險。



以下是一組使用者的年齡資料,我們將K值定義為2對使用者進行聚類。並隨機選擇16和22作為兩個類別的初始質心。

 
 

計算距離並劃分資料

我們以圖的形式展示聚類的過程。在這組年齡資料中,我們選擇了16和22作為兩個類別的初始質心,並通過計算所有使用者的年齡值與初始質心的距離對使用者進行第一次分類。
 

計算距離的方法是使用歐式距離。以下是歐式距離的計算公式。距離值越小表示兩個使用者間年齡的相似度越高。

 

通過計算,我們獲得了每個年齡資料點與兩個初始質心的距離。這裡我們以黑色實心圓點標記較大的距離值,空心圓點標記較小的距離值。例如第一個資料點15,到第一個初始質心16的距離為1,到第二個初始質心22的距離為7。相比之下15與16的距離更近,距離值為1,並以空心圓點標記。因此15這個年齡資料點被劃分在第一個組(16)中。如果年齡資料點到兩個初始質心的距離相等,可以劃分到任意組中,例如年齡資料點19,到16和22的距離都為3。在這個示例中我們將資料點19劃分到第二個組(22)中。

 

按相似程度(距離)對資料分完組後,分別計算兩個分組中資料的均值15.33和36.25,並以這兩個均值作為新的質心。在下圖中可以看到,藍色的數字為初始質心,紅色的數字為新的質心。目前的質心和新的質心並不是同一個資料點,我們將以新的質心替代初始質心,迭代計算每個資料點到新質心的距離。直到新的質心和原質心相等,演算法結束。
 

使用均值作為新質心

將兩個分組中資料的均值作為新的質心,並重復之前的方法計算每個年齡資料點到新質心的距離。下面是年齡資料點到兩個新質心的距離。以年齡資料點19為例,到新質心15.33的距離為3.67,到另一個新質心36.25的距離為17.25。相比之下資料點19到15.33的距離更近,為3.67。因此被分到第一組(15.33)中。

 

以年齡資料點到新質心的距離值完成分組後,再次計算兩組的均值18.56和45.90,並以均值作為新質心替代原質心。下圖中藍色數字為原質心,紅色數字為新質心。在新質心下,年齡資料的分組情況發生了變化,但新質心與原質心沒有重合。

 

重複之前的方法和步驟,計算年齡資料點到新質心的距離。並對比資料點到兩個新質心的距離,選擇較小的距離值對年齡資料點進行分組。年齡資料點28到18.56的距離為9.44,到45.90的距離為17.90。因此年齡資料點28被分配到第一個18.56的分組中。
 

再次以年齡資料點到新質心的距離完成分組後,新質心(紅色)與原質心(藍色)仍然沒有重合,但與之前相比分組的調整已經很小。我們繼續計算新分組的均值19.50和47.89,並將均值作為新質心替代原質心。
 

演算法停止條件

開始計算的第一步我們說迭代計算每個資料到新質心的距離,直到新的質心和原質心相等,演算法結束。使用上一步分組的均值19.50和47.89作為新質心。並計算年齡資料點到新質心的距離。以下為計算結果。
 

按照年齡資料點到新質心的距離對資料進行分組,並計算每組的均值作為新質心。這裡兩組的均值與原質心相等。也就是說新質心與原質心相等,都是19.50和47.89.。演算法停止計算。年齡資料點被劃分為兩類,如下圖所示分別為15-28和35-65。

 

k-means演算法虛擬碼:

******************************************************************************

1.選取k個質心(這一步會影響整個聚類的結果)
2.將任意一個點分配到離質心最近的簇。
3.用每個簇的均值作為質心。
4.重複2和3直到所有點的分配結果不再發生變化,或者誤差小於給定誤差

********************************************************

 

K-means聚類演算法實現(python程式碼)

Irst.txt資料集:

5.1 3.5 1.4 0.2
4.9 3.0 1.4 0.2
4.7 3.2 1.3 0.2
4.6 3.1 1.5 0.2
5.0 3.6 1.4 0.2
5.4 3.9 1.7 0.4
4.6 3.4 1.4 0.3
5.0 3.4 1.5 0.2
4.4 2.9 1.4 0.2
4.9 3.1 1.5 0.1
5.4 3.7 1.5 0.2
4.8 3.4 1.6 0.2
4.8 3.0 1.4 0.1
4.3 3.0 1.1 0.1
5.8 4.0 1.2 0.2
5.7 4.4 1.5 0.4
5.4 3.9 1.3 0.4
5.1 3.5 1.4 0.3
5.7 3.8 1.7 0.3
5.1 3.8 1.5 0.3
5.4 3.4 1.7 0.2
5.1 3.7 1.5 0.4
4.6 3.6 1.0 0.2
5.1 3.3 1.7 0.5
4.8 3.4 1.9 0.2
5.0 3.0 1.6 0.2
5.0 3.4 1.6 0.4
5.2 3.5 1.5 0.2
5.2 3.4 1.4 0.2
4.7 3.2 1.6 0.2
4.8 3.1 1.6 0.2
5.4 3.4 1.5 0.4
5.2 4.1 1.5 0.1
5.5 4.2 1.4 0.2
4.9 3.1 1.5 0.2
5.0 3.2 1.2 0.2
5.5 3.5 1.3 0.2
4.9 3.6 1.4 0.1
4.4 3.0 1.3 0.2
5.1 3.4 1.5 0.2
5.0 3.5 1.3 0.3
4.5 2.3 1.3 0.3
4.4 3.2 1.3 0.2
5.0 3.5 1.6 0.6
5.1 3.8 1.9 0.4
4.8 3.0 1.4 0.3
5.1 3.8 1.6 0.2
4.6 3.2 1.4 0.2
5.3 3.7 1.5 0.2
5.0 3.3 1.4 0.2
7.0 3.2 4.7 1.4
6.4 3.2 4.5 1.5
6.9 3.1 4.9 1.5
5.5 2.3 4.0 1.3
6.5 2.8 4.6 1.5
5.7 2.8 4.5 1.3
6.3 3.3 4.7 1.6
4.9 2.4 3.3 1.0
6.6 2.9 4.6 1.3
5.2 2.7 3.9 1.4
5.0 2.0 3.5 1.0
5.9 3.0 4.2 1.5
6.0 2.2 4.0 1.0
6.1 2.9 4.7 1.4
5.6 2.9 3.9 1.3
6.7 3.1 4.4 1.4
5.6 3.0 4.5 1.5
5.8 2.7 4.1 1.0
6.2 2.2 4.5 1.5
5.6 2.5 3.9 1.1
5.9 3.2 4.8 1.8
6.1 2.8 4.0 1.3
6.3 2.5 4.9 1.5
6.1 2.8 4.7 1.2
6.4 2.9 4.3 1.3
6.6 3.0 4.4 1.4
6.8 2.8 4.8 1.4
6.7 3.0 5.0 1.7
6.0 2.9 4.5 1.5
5.7 2.6 3.5 1.0
5.5 2.4 3.8 1.1
5.5 2.4 3.7 1.0
5.8 2.7 3.9 1.2
6.0 2.7 5.1 1.6
5.4 3.0 4.5 1.5
6.0 3.4 4.5 1.6
6.7 3.1 4.7 1.5
6.3 2.3 4.4 1.3
5.6 3.0 4.1 1.3
5.5 2.5 5.0 1.3
5.5 2.6 4.4 1.2
6.1 3.0 4.6 1.4
5.8 2.6 4.0 1.2
5.0 2.3 3.3 1.0
5.6 2.7 4.2 1.3
5.7 3.0 4.2 1.2
5.7 2.9 4.2 1.3
6.2 2.9 4.3 1.3
5.1 2.5 3.0 1.1
5.7 2.8 4.1 1.3
6.3 3.3 6.0 2.5
5.8 2.7 5.1 1.9
7.1 3.0 5.9 2.1
6.3 2.9 5.6 1.8
6.5 3.0 5.8 2.2
7.6 3.0 6.6 2.1
4.9 2.5 4.5 1.7
7.3 2.9 6.3 1.8
6.7 2.5 5.8 1.8
7.2 3.6 6.1 2.5
6.5 3.2 5.1 2.0
6.4 2.7 5.3 1.9
6.8 3.0 5.5 2.1
5.7 2.5 5.0 2.0
5.8 2.8 5.1 2.4
6.4 3.2 5.3 2.3
6.5 3.0 5.5 1.8
7.7 3.8 6.7 2.2
7.7 2.6 6.9 2.3
6.0 2.2 5.0 1.5
6.9 3.2 5.7 2.3
5.6 2.8 4.9 2.0
7.7 2.8 6.7 2.0
6.3 2.7 4.9 1.8
6.7 3.3 5.7 2.1
7.2 3.2 6.0 1.8
6.2 2.8 4.8 1.8
6.1 3.0 4.9 1.8
6.4 2.8 5.6 2.1
7.2 3.0 5.8 1.6
7.4 2.8 6.1 1.9
7.9 3.8 6.4 2.0
6.4 2.8 5.6 2.2
6.3 2.8 5.1 1.5
6.1 2.6 5.6 1.4
7.7 3.0 6.1 2.3
6.3 3.4 5.6 2.4
6.4 3.1 5.5 1.8
6.0 3.0 4.8 1.8
6.9 3.1 5.4 2.1
6.7 3.1 5.6 2.4
6.9 3.1 5.1 2.3
5.8 2.7 5.1 1.9
6.8 3.2 5.9 2.3
6.7 3.3 5.7 2.5
6.7 3.0 5.2 2.3
6.3 2.5 5.0 1.9
6.5 3.0 5.2 2.0
6.2 3.4 5.4 2.3
5.9 3.0 5.1 1.8

以下是以資料集Irst.txt進行演算法實現:

# coding=utf-8
from numpy import *
# 載入資料
def loadDataSet(fileName):  # 解析檔案,按tab分割欄位,得到一個浮點數字型別的矩陣
    dataMat = []              # 檔案的最後一個欄位是類別標籤
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split(' ')
        fltLine =map(float, curLine)# 將每個元素轉成float型別
        dataMat.append(list(fltLine))
    return dataMat

# 計算歐幾里得距離
def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2))) # 求兩個向量之間的距離

# 構建聚簇中心,取k個(此例中為4)隨機質心
def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))   # 每個質心有n個座標值,總共要k個質心
    for j in range(n):
        minJ = min(dataSet[:,j])
        maxJ = max(dataSet[:,j])
        rangeJ = float(maxJ - minJ)
        centroids[:,j] = minJ + rangeJ * random.rand(k, 1)
    return centroids

# k-means 聚類演算法
def kMeans(dataSet, k, distMeans =distEclud, createCent = randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))    # 用於存放該樣本屬於哪類及質心距離
    # clusterAssment第一列存放該資料所屬的中心點,第二列是該資料到中心點的距離
    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 = distMeans(centroids[j,:], dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI; minIndex = j  # 如果第i個數據點到第j箇中心點更近,則將i歸屬為j
            if clusterAssment[i,0] != minIndex: clusterChanged = True;  # 如果分配發生變化,則需要繼續迭代
            clusterAssment[i,:] = minIndex,minDist**2   # 並將第i個數據點的分配情況存入字典
        print(centroids)
        for cent in range(k):   # 重新計算中心點
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]   # 去第一列等於cent的所有列
            centroids[cent,:] = mean(ptsInClust, axis = 0)  # 算出這些資料的中心點
    return centroids, clusterAssment
# --------------------測試----------------------------------------------------
# 用測試資料及測試kmeans演算法
datMat = mat(loadDataSet('E:\\Iris.txt'))
myCentroids,clustAssing = kMeans(datMat,4)
print(myCentroids)
print(clustAssing)

部分執行結果:

      

 

聚類分析之K-means演算法案例及其Python實現(二)

https://blog.csdn.net/lilu916/article/details/72997644

國家數學建模賽C題問題一求解參照

聚類分析之K-means演算法案例及其Python實現(三)

https://blog.csdn.net/zouxy09/article/details/17589329

scikit-learn 原始碼解讀之Kmeans

http://midday.me/article/f8d29baa83ae41ec8c9826401eb7685e

Kmeans聚類過程的動態視覺化

https://blog.csdn.net/happyyear1/article/details/50973675

K的選擇:肘部法則

https://www.cnblogs.com/wuchuanying/p/6264025.html

如有不足改正,請多多指教!