純Python實現k-means聚類算法

2021-12-27 南極Python
開篇

在本系列的前面幾期中,我們介紹了包含決策樹及其相關算法在內的一系列有監督學習算法。本期不妨換換口味,學習一種比較簡單的無監督學習算法:k-means.

k-means 算法理論講解

你可能已經聽說過這個算法,沒有也不要緊,它的原理並不複雜,讓我們來看看~

給你一批樣本的數據,就像這樣:

其中不同的id代表不同樣本,共10個樣本。x和y是兩個特徵,每一個樣本都有且只有這兩個特徵。

現在希望你能夠對這些樣本進行分類。

我們知道,在屬於有監督學習的分類問題中,訓練數據往往會包含類別標籤。而上面這份數據本身是不含標籤的,所以這並不是一個分類問題。

既然不知道類別標籤,你又讓我對這些樣本進行分類,那如何聚類?又要聚成幾類呢?

其實,在k-means中,最終聚成幾類是由我們自己決定的;這裡的類別標籤通常用數字表示,如0,1,2等;當我們指定好類別總數後,每一個樣本被歸到哪一類,就需要聚類算法自己去學習了。

k-means算法學習將相似的樣本聚在一起,稱為一個類別(也稱為一個"簇"),而將不相似的樣本劃分到不同的類別("簇")中。

這裡的相似程度,通常是用樣本之間的距離來度量的,比如最常用的歐氏距離。

囉嗦了這麼多,k-means算法的執行步驟可以總結如下:

(1) 對於給定的一批樣本,初始化聚類中心(這裡需要手動指定聚成幾類),初始化每個樣本所屬簇;

(2) 對於每一個樣本,分別計算該樣本與所有聚類中心之間的距離,選擇距離該樣本最近的聚類中心作為該樣本所屬簇;

(3) 經過(2)之後,每個樣本所屬簇就被更新了,現在來更新聚類中心:計算每一個簇內所有樣本在所有特徵維度上的平均值,將其作為該簇新的聚類中心。

(4) 重複(2)、(3)若干次,便完成了聚類。

接下來,我們將根據以上四步,用Python實現k-means算法。

k-means 算法的Python實現

我們需要手動指定要聚成幾類,這裡用參數k來表示,比如k=3就代表我們希望聚成3類。

data是訓練數據,毫無疑問也是需要手動傳入的。

另外,我們還設置了一個是否隨機打亂樣本順序的開關:do_shuffle,默認保持樣本原序。

class MyKmeans:
    def __init__(self,k,data,do_shuffle=False):
        self.k=k#自己指定聚成幾類
        self.data=data#訓練數據
        self.do_shuffle=do_shuffle#是否隨機打亂樣本順序

現在實現(1),即:初始化聚類中心,初始化每個樣本所屬簇。

這裡,我直接以前k個樣本的坐標分別作為k個初始聚類中心,並將每個樣本的類別標籤(所屬簇)初始化為0。

    def initialize(self):
        if self.do_shuffle:
            np.random.shuffle(self.data)
        #初始化聚類中心
        self.centers=self.data[:self.k]
        #初始化每個樣本所屬簇
        self.targets=np.zeros(self.data.shape[0])

再看(2),其中涉及到了求距離,所以先來實現兩個樣本點之間的距離計算方法,這裡採用歐氏距離:

    def cal_dist(self,p1,p2):
        return np.sqrt(np.sum((p1-p2)**2))

然後來實現(2),即:更新樣本所屬簇

        for i,item in enumerate(self.data):#對於每一個樣本點
            c=[]#分別計算該樣本點到k個聚類中心的距離,並存入c
            for j in range(len(self.centers)):
                c.append(self.cal_dist(item,self.centers[j]))
            #print(c)
            self.targets[i]=np.argmin(c)#更新targets

現在來實現(3),即:更新聚類中心

        #更新聚類中心
        new_centers=[]
        for i in range(self.k):#對於每一個類中的全部樣本點
            #此時target的取值集合是0到k-1
            
            #挑選出屬於簇i的全部樣本
            i_data=[]
            for index,item in enumerate(self.data):
                if self.targets[index]==i:
                    i_data.append(item)
            if len(i_data)!=0:
                cent=[]#存儲新的聚類中心的每一個維度
                for p in range(len(self.data[0])):#對於點的每一個維度
                    sums=0

                    #計算第i個簇中,每個維度的新值
                    for x in i_data:
                        sums+=x[p]
                    res=sums/len(i_data)
                    cent.append(res)
                new_centers.append(cent)
            else:
                new_centers.append(self.centers[i])#若某一個簇中所含樣本數為0,則不更新該簇的中心點
        self.centers=new_centers

注意這裡需要判斷某個簇中所含樣本個數是否為0,因為當樣本數為0時,上述代碼中求解均值所做的除法會報錯。

為了避免這個錯誤,當出現這種情況時,我們選擇本次不更新該簇的聚類中心。

以上便完成了(2)和(3),注意到(4)中需要重複(2)和(3)這兩個步驟,因此,我們可以將(2)和(3)封裝到一個方法裡面,就像這樣:

    def one_step(self):
        for i,item in enumerate(self.data):#對於每一個樣本點
            c=[]#分別計算該樣本點到k個聚類中心的距離,並存入c
            for j in range(len(self.centers)):
                c.append(self.cal_dist(item,self.centers[j]))
            #print(c)
            self.targets[i]=np.argmin(c)#更新targets

        #更新聚類中心
        new_centers=[]
        for i in range(self.k):#對於每一個類中的全部樣本點
            #此時target的取值集合是0到k-1
            
            #挑選出屬於簇i的全部樣本
            i_data=[]
            for index,item in enumerate(self.data):
                if self.targets[index]==i:
                    i_data.append(item)
            if len(i_data)!=0:
                cent=[]#存儲新的聚類中心的每一個維度
                for p in range(len(self.data[0])):#對於點的每一個維度
                    sums=0

                    #計算第i個簇中,每個維度的新值
                    for x in i_data:
                        sums+=x[p]
                    res=sums/len(i_data)
                    cent.append(res)
                new_centers.append(cent)
            else:
                new_centers.append(self.centers[i])#若某一個簇中所含樣本數為0,則不更新該簇的中心點
        self.centers=new_centers

有了上面實現的這些方法,第(4)步的實現就很簡單啦,直接來個for循環即可

    def run(self,iterations):
        self.initialize()#初始化聚類中心和各樣本所屬類別
        #經過iterations次迭代,基本就完成了聚類
        for it in range(iterations):
            #print('iterations',it)
            self.one_step()
        return self.centers,self.targets

到這裡,我們就完成了整個k-means算法,是時候測試一下效果了。

我們的訓練數據是隨機生成的,共100個樣本,每個樣本含2個特徵。

import matplotlib.pyplot as plt
import numpy as np
if __name__ == '__main__':
    datasets=np.random.randint(0,100,size=(100,2))
    k=4#聚類數
    iterations=100#總迭代次數
    mykmeans=MyKmeans(k,datasets,True)#實例化
    centers,targets=mykmeans.run(iterations)#迭代求解
    
    #可視化聚類結果
    plt.scatter(datasets[:,0],datasets[:,1],c=targets)
    for center in centers:
        plt.scatter(center[0],center[1],marker='*',s=300)
        plt.text(center[0], center[1], (round(center[0],2),round(center[1],2)), fontsize=10, color = "r")
    plt.show()

聚類結果如下圖所示

現將完整的k-means實現代碼匯總如下:

import numpy as np

class MyKmeans:
    def __init__(self,k,data,do_shuffle=False):
        self.k=k
        self.data=data
        self.do_shuffle=do_shuffle
    
    #計算歐氏距離
    def cal_dist(self,p1,p2):
        return np.sqrt(np.sum((p1-p2)**2))
    
    #初始化聚類中心和每個樣本所屬類別
    def initialize(self):
        if self.do_shuffle:
            np.random.shuffle(self.data)
        #初始化聚類中心
        self.centers=self.data[:self.k]
        #初始化每個樣本所屬簇
        self.targets=np.zeros(self.data.shape[0])
        
    def one_step(self):
        for i,item in enumerate(self.data):#對於每一個樣本點
            c=[]#分別計算該樣本點到k個聚類中心的距離,並存入c
            for j in range(len(self.centers)):
                c.append(self.cal_dist(item,self.centers[j]))
            #print(c)
            self.targets[i]=np.argmin(c)#更新targets

        #更新聚類中心
        new_centers=[]
        for i in range(self.k):#對於每一個類中的全部樣本點
            #此時target的取值集合是0到k-1
            
            #挑選出屬於簇i的全部樣本
            i_data=[]
            for index,item in enumerate(self.data):
                if self.targets[index]==i:
                    i_data.append(item)
            if len(i_data)!=0:
                cent=[]#存儲新的聚類中心的每一個維度
                for p in range(len(self.data[0])):#對於點的每一個維度
                    sums=0

                    #計算第i個簇中,每個維度的新值
                    for x in i_data:
                        sums+=x[p]
                    res=sums/len(i_data)
                    cent.append(res)
                new_centers.append(cent)
            else:
                new_centers.append(self.centers[i])#若某一個簇中所含樣本數為0,則不更新該簇的中心點
        self.centers=new_centers
        
                   
    def run(self,iterations):
        self.initialize()#初始化聚類中心和各樣本所屬類別
        #經過iterations次迭代,基本就完成了聚類
        for it in range(iterations):
            #print('iterations',it)
            self.one_step()
        return self.centers,self.targets


長按即可關注我們

                    原創不易,感謝你的每一次點讚與分享!

相關焦點

  • Python聚類算法——K-means軌跡聚類
    實際上NCL就可以實現聚類算法,官方提供了k-means的算法函數(kmeans_as136),這個函數筆者也用過,相比與Python的聚類可以說還是比較麻煩的。       另外我也見過大佬們用MetoInfo的軟體做軌跡和聚類研究,十分方便。
  • Python機器學習之k-means聚類算法
    ,聚類算法屬於無監督學習算法的一種.引言k-均值聚類算法屬於最基礎的聚類算法,該算法是一種迭代的算法,將規模為n的數據集基於數據間的相似性以及距離簇內中心點的距離劃分成k簇.這裡的k通常是由用戶自己指定的簇的個數,也就是我們聚類的類別個數.
  • python之kmeans數據聚類算法
    一 Kmeans原理kmeans是屬於無監督學習的數據聚類算法,根據點與點之間的距離推測每個點屬於哪個中心,常用計算距離的方式有:餘弦距離、歐式距離、曼哈頓距離等,本文以歐式距離為例。圖3kmeans實現邏輯:需要輸入待聚類的數據和欲聚類簇數k1.隨機生成k個初始點作為質心
  • python機器學習之k-means聚類算法(1)
    k-means算法是一種無監督的機器學習算法,雖然是機器學習,但它簡單易於實現。本篇採用python語言,自主編程實現k-menas算法,當然python用專門的庫函數來實現該算法,但本次主要使用該算法闡述編程思想,所以不採用內置函數。採用自主編寫的程序的方式。
  • Python氣象數據處理與繪圖:聚類算法(K-means軌跡聚類)
    實際上NCL就可以實現聚類算法,官方提供了k-means的算法函數(kmeans_as136),這個函數筆者也用過,相比與Python的聚類可以說還是比較麻煩的。       另外我也見過大佬們用MetoInfo的軟體做軌跡和聚類研究,十分方便。
  • K-Means聚類算法
    是一種聚類算法,與之前提到的樸素貝葉斯等算法不同,它屬於無監督學習。簡單來說,之前的算法中我們是利用特徵 x 和類別 y 來進行訓練、分類的,而無監督學習是指不需要我們提供具體的類別 y ,而讓數據自己聚在一起,形成 k 個簇,以實現分類的目的。具體方法是通過對給定的樣本進行劃分,分為 k 個簇,使得簇內的點儘量緊密的連在一起,而簇間的距離儘量大,評判的標準就是通過歐氏距離。
  • K-means 算法實現二維數據聚類
    聚類分析是一種無監督的觀察式學習方法,在聚類前可以不知道類別甚至不用給定類別數量。目前聚類廣泛應用於統計學、生物學、資料庫技術和市場營銷等領域。聚類算法有很多種,如K-means(K均值聚類)、K中心聚類、密度聚類、譜系聚類、最大期望聚類等。這裡我們重點介紹K-means聚類算法,該算法的基本思想是以空間中K個點為中心進行聚類,對最靠近它們的對象歸類。
  • 機器學習之SKlearn(scikit-learn)的K-means聚類算法
    在工程應用中,用python手寫代碼來從頭實現一個算法的可能性非常低,這樣不僅耗時耗力,還不一定能夠寫出構架清晰,穩定性強的模型。更多情況下,是分析採集到的數據,根據數據特徵選擇適合的算法,在工具包中調用算法,調整算法的參數,獲取需要的信息,從而實現算法效率和效果之間的平衡。而sklearn,正是這樣一個可以幫助我們高效實現算法應用的工具包。
  • 手把手聚類算法之K-means算法原理及python實現
    而這一類算法,應用最為廣泛的就是「聚類」。聚類算法可以對數據進行數據歸納,即在儘可能保證數據完整的前提下,減少數據的量級,以便後續處理。也可以對聚類數據結果直接應用或分析。而K-means 算法可以說是聚類算法裡面較為基礎的一種算法。K-means算法採用距離作為相似性的評價指標,認為兩個對象的距離越近,其相似度就越大。該
  • k-means聚類算法原理總結
    目錄1. k-means聚類算法原理2. k-means聚類算法步驟3. k-means++聚類優化算法4.小批量處理的k-means聚類算法5. k值的選取6. k-means聚類算法不適用的幾個場景7. k-means與knn區別8. 小結聚類算法性能度量的文章提到若簇類相似度好簇間的相似度差,則聚類算法的性能較好。我們基於此定義k-means聚類算法的目標函數: 其中
  • K-means 聚類算法及其代碼實現
    K-means算法是非監督學習(unsupervised learning)中最簡單也是最常用的一種聚類算法,具有的特點是:本文章介紹
  • 不足 20 行 Python 代碼,高效實現 k-means 均值聚類算法!
    k-means均值算法雖然是聚類算法中比較簡單的一種,卻包含了豐富的思想內容,非常適合作為初學者的入門習題。關於 k-means 均值聚類算法的原理介紹、實現代碼,網上有很多,但運行效率似乎都有點問題。今天稍微有點空閒,寫了一個不足20行的 k-means 均值聚類算法,1萬個樣本平均耗時20毫秒(10次均值)。同樣的數據樣本,網上流行的算法平均耗時3000毫秒(10次均值)。
  • K-Means聚類算法詳解
    還是老規矩,我們在實戰之前,先看一下如何調用sklearn實現KMeans。4.1 如何使用sklearn中的KMeans算法sklearn 是 Python 的機器學習工具庫,如果從功能上來劃分,sklearn 可以實現分類、聚類、回歸、降維、模型選擇和預處理等功能。
  • K-means 在 Python 中的實現
    算法簡介K-means是機器學習中一個比較常用的算法,屬於無監督學習算法,其常被用於數據的聚類,只需為它指定簇的數量即可自動將數據聚合到多類中,相同簇中的數據相似度較高,不同簇中數據相似度較低。K-menas的優缺點:優點:缺點:K-means的聚類過程其聚類過程類似於梯度下降算法,建立代價函數並通過迭代使得代價函數值越來越小該算法的最大優勢在於簡潔和快速。算法的關鍵在於初始中心的選擇和距離公式。
  • k-means聚類算法從入門到精通
    目錄1. k-means聚類算法原理2. k-means聚類算法步驟3. k-means++聚類優化算法4.小批量處理的k-means聚類算法5. k值的選取6. k-means聚類算法不適用的幾個場景7. k-means與knn區別8. 小結聚類算法性能度量的文章提到若簇類相似度好簇間的相似度差,則聚類算法的性能較好。我們基於此定義k-means聚類算法的目標函數: 其中
  • Python聚類算法學習之K-Means
    1.簡介       K-means算法是最為經典的基於劃分的聚類方法,是十大經典數據挖掘算法之一
  • 詳解 kmeans 聚類算法
    也是聚類算法中最簡單的一種了,但是裡面包含的思想卻是不一般。最早我使用並實現這個算法是在學習韓爺爺那本數據挖掘的書中,那本書比較注重應用。看了Andrew Ng的這個講義後才有些明白K-means後面包含的EM思想。聚類屬於無監督學習,以往的回歸、樸素貝葉斯、SVM等都是有類別標籤y的,也就是說樣例中已經給出了樣例的分類。而聚類的樣本中卻沒有給定y,只有特徵x,比如假設宇宙中的星星可以表示成三維空間中的點集。
  • 聚類算法之Kmeans
    聚類算法之Kmeans聚類算法其實有很多,包括層次聚類,密度聚類,譜聚類等等。但是為何這篇只提Kmeans,是因為Kmeans是算法理解簡單,而又使用最多,並且從原理上來說,其它的聚類算法只是其算法或求解思路有些差別,但是其核心思路還是一致,具體做的事也是有著同一個目標,故而這篇我們重點詳細單獨闡述Kmeans算法。對於其它聚類算法,如果必要,我們在下一篇再進行展開。
  • 深入理解K-Means聚類算法
    還是老規矩,我們在實戰之前,先看一下如何調用sklearn實現KMeans。4.1 如何使用sklearn中的KMeans算法sklearn 是 Python 的機器學習工具庫,如果從功能上來劃分,sklearn 可以實現分類、聚類、回歸、降維、模型選擇和預處理等功能。
  • 機器學習十大經典算法之K-Means聚類算法
    聚類也能用於對Web上的文檔進行分類,以發現信息。諸如此類,聚類有著廣泛的實際應用。K-Means聚類算法簡介K-Means是發現給定數據集的 K 個簇的聚類算法, 之所以稱之為 K-均值是因為它可以發現 K 個不同的簇, 且每個簇的中心採用簇中所含值的均值計算而成。簇個數 K 是用戶指定的, 每一個簇通過其質心(centroid), 即簇中所有點的中心來描述。