K-means算法原理、代碼實現,優缺點及改進

k-Means是一種無監督的聚類算法,實現起來比較簡單,聚類效果也不錯,因此被廣泛應用。

原理

物以類聚,人以羣分。
無監督 聚類
簡單地說,就是把相似的物體聚到一個簇。同一簇內相似度儘可能大,不同簇間相似度儘可能低。採用距離度量相似程度

算法

1、初始化k箇中心點,有了k個簇
2、對所有樣本,計算每個樣本與k箇中心點的距離,將各樣本劃分到距離最近的中心點所在的簇
3、重新計算各簇的中心:爲各簇所有點的均值
4、不斷迭代2、3,直到各簇不再發生變化或者達到迭代次數

優缺點

優點:
是解決聚類問題的一種經典算法,簡單、快速;
對處理大數據集,該算法高效率;
當結果是密集的,它的效果較好。
缺點:
k值選取不好把握;
對初值敏感(初始聚類中心的選擇:改進1:k-means++,改進2:二分k-means);
對噪聲和異常點敏感(改進:離羣點檢測,去掉離羣點後再聚類,減少它們對聚類效果的影響);
只能收斂到局部最小,不適合於發現非凸形狀的簇還有,不能處理大小、密度差別很大的簇(改進:基於密度的聚類:擅於解決不規則形狀的聚類問題,能克服基於距離的算法只能發現“類圓形”的聚類的缺點。如,DBSCAN算法)

實現

import numpy as np

#文件解析,將值封裝到矩陣(實質是list裏每個元素list)裏
#dataset文件每行數字以製表符分
def loadDataset(filename):
    data = []
    f = open(filename)
    for line in f:
        line = line.strip().split('\t')
        floatline = map(float, line)
        data.append(floatline)
    return data


#歐式距離公式
def distance(vecA, vecB):
    return np.sqrt(np.sum(np.square(vecA - vecB)))


#構建質心
#隨機選擇k個樣本點做質心
def randCenter(dataMat, k):
    row, col = dataMat.shape
    centers = []
    centersIndex = np.random.randint(0, row, size=k)
    for i in centersIndex:
        centers.append(dataMat[i])
    centers = np.mat(centers)
    return centers


#輸入:樣本點集合(矩陣形式,m個樣本),聚類簇數k

# 算法:
# 隨機選擇k個樣本作爲初始質心:{u1,u2,..uk},即均值向量集合
# 1、對每一個點:
#         計算它與每一個簇的質心的距離;
#         找出最小距離;
#         劃分到最小距離所在簇
#     (所有點都被劃分了一遍)
# 2、對每一個簇:
#         重新計算質心(均值向量)
#         如果質心改變了,則更新質心;否則,不用更新
# 直到當前的所有簇的質心均未改變,算法結束【換個思路:即是如果一有改變就繼續循環1、2】
# 返回:質心、簇劃分

def kmeans(dataMat, k):
    m = dataMat.shape[0]  #也就是row,m個樣本點
    centers= randCenter(dataMat, k)

    #簇劃分 初始時爲:{0:u1, 1:u2,..., k:uk}
    clusters = {}
    for cent in range(k):
        clusters[cent]= centers[cent]

    clusterChanged = True
    while clusterChanged:
        clusterChanged= False
        for i in range(m):
            minDist= distance(dataMat[i], centers[0])
            minIndex= 0
            for j in range(k):
                distIJ= distance(dataMat[i], centers[j])
                if distIJ< minDist:
                    minDist= distIJ
                    minIndex= j
            #將樣本點劃分到最近簇
            clusters[minIndex]= np.row_stack((clusters[minIndex], dataMat[i])) #minIndex鍵下的值——矩陣增一行

        #一輪劃分完後(即所有樣本劃分一遍後),計算質心
        for cent in range(k):
            newCenter= np.mat([clusters[cent][:,0].mean(), clusters[cent][:,1].mean()])
            if newCenter[cent, 0]!= centers[cent, 0] or newCenter[cent, 1]!= centers[cent, 1]:
                clusterChanged= True
                #更新質心
                centers[cent]= newCenter 
           
        
    return centers, clusters
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章