1. 程式人生 > 其它 >基於numpy實現的簡單k-means,k-means淺析

基於numpy實現的簡單k-means,k-means淺析

技術標籤:機器學習聚類演算法python機器學習

基於numpy實現的簡單k-means

該文從簡單的資料角度講解基於k-means和基於numpy實現k-means程式碼。


文章目錄


前言

k-means是什麼,它的做什麼的,它與先前所說的聚類有什麼關係。


一、 k-means

上一篇文章已經詳解了聚類,那麼基礎應該已經沒有什麼問題了,k-means其實就是用中文來說的話就是k均值聚類,它屬於聚類中的"基於原型的聚類"

基於原型的聚類:假設聚類結構能通過一組原型刻畫,通常情況下,演算法先對原型進行初始化,繼而對原型進行不斷的更新迭代。而不同的更新方式和不同的求解將產生不同的演算法。

k均值演算法

官方定義:給定樣本集D={x1,x2,…,xn};所謂k均值演算法,是求得針對聚類所得簇劃分C={C1,C2…,Ck}最小化的平方誤差,如下

在這裡插入圖片描述
其中:
在這裡插入圖片描述

那麼μi就可以看為是x集合也就是Ci的均值向量。直觀的來看,E在一定程度上刻畫了簇內的所有樣本圍繞簇均值向量的緊密程度。E越小則,越緊密,意味著簇內相似度越高。我們只需要求最小E即可。

接下來我們解析官方這些概念和公式:
從概念入手,我們知道最後是要把資料集劃分為K個簇,那麼"均值"這個倆個字,也很容易理解,從公式入手,所謂的均值其實指的就是均值向量了。那麼如果要實現該演算法的話,我們只需要幾步:

  1. 隨機選取k個質心,因為要求有k個簇。
  2. 接下來求解每個樣本到所有質心的距離,並得到最小距離,將該樣本歸入該質心屬於的簇之中
  3. 計算簇均值向量
  4. 判斷簇均值向量是否與上次確定的簇均值向量相等,若相等,則返回簇若不相等,則繼續迭代,同時設定迭代上限,防止沒有結果的崩潰。

從理論上來說,該方法,適用於所有的資料集,無論是有序屬性還是無序屬性,需要變更的僅僅是距離的演算法問題罷了。

  • 顯然不難理解,這是k-means的優點之一,同時它還擁有了收斂速度快,聚類效果優,調參簡單等優點。

二、程式碼原理實現

1.引入庫

程式碼如下(示例):

#基於numpy的K-means
#coding:UTF-8
#倒庫
import numpy as np
import
random as rd import matplotlib.pyplot as plt from math import *

2.面向物件實現

class k_m():
    #計算距離 返回歐氏距離 這裡僅僅考慮到了有序屬性
    def __init__(self):
        self.color=['.b','.g','.r','.c','.m','.y','.k','.w']
        self.cencolor=['*b','*g','*r','*c','*m','*y','*k','*w']
        #0表示當前顏色的索引
        self.now=0
    def dist(x,y):
        return sqrt(pow(x[0]-y[0],2)+pow(x[1]-y[1],2))
    #crowd_count:簇的個數,也是定義K_means點的個數,傳入x座標和y座標,
    def k_means(self,x,y,k):
        #獲取含有k個隨機屬性的屬性序列 
        k_list=[(x[i],y[i]) for i in rd.sample(range(len(x)),k)] 
        #對屬性排序,以便處理
        k_list.sort()
        while 1:
        #crowds為簇列表,每一次遍歷都要更新簇列表
            crowds=[[] for i in range(k)]
            for i in range(len(x)):
                t_now=(x[i],y[i]) 
                #計算到所有點的距離 存列表 接下來我們需要該點到所有質心的距離最近並且將其加入簇列表中
                all_dist=[k_m.dist(k_list[j],t_now) for j in range(k)]
                min_dist=all_dist.index(min(all_dist))
                #加入索引之中
                crowds[min_dist].append(i)
            #進入質心的迭代    
            centroid_news_list=[]
            #從之前得到的簇中計算均值,重新確定質心並進行運算
            for i in range(k):
                x_t=sum(x[j] for j in crowds[i])/len(crowds[i])
                y_t=sum(y[j] for j in crowds[i])/len(crowds[i])
                centroid_news_list.append((x_t,y_t))
            centroid_news_list.sort()
            #繪圖
            plt.figure()
            plt.title('k={0:d} k-means'.format(k))
            for i in range(k):
                #繪點
                plt.plot([x[j] for j in crowds[i]],[y[j] for j in crowds[i]],self.color[self.now])#
                #繪質心
                plt.plot(k_list[i][0],k_list[i][1],self.cencolor[self.now])#
                self.now+=1
            plt.show()
            plt.close('all')
            if centroid_news_list==k_list:
                return crowds
            else:
                self.now=0
                k_list=centroid_news_list
  • 看完程式碼之後不難發現,該程式碼非常簡單,但同樣,其處理能力也有限,因為該程式碼只是為了從原理角度淺析k-means演算法,因此不會用於處理很複雜的資料,但是是其對二元組資料處理仍然十分優秀,這邊博主已經用資料集測試過了,程式碼沒有bug可以放心參考。

總結

k-means本身作為原型聚類中最著名的劃分聚類演算法,以及最為廣泛使用的劃分聚類演算法,其優秀程度自然是不言而喻。有興趣的小夥伴可以嘗試用無序屬性的資料集,以及無序距離的計算處理複雜任務,比如物品的分類等等,實現也沒有很難,但是十分有用。