機器學習-K-Means算法

簡介
又叫K-均值算法,是非監督學習中的聚類算法。

基本思想
k-means算法比較簡單。在k-means算法中,用cluster來表示簇;容易證明k-means算法收斂等同於所有質心不再發生變化。基本的k-means算法流程如下:

選取k個初始質心(作爲初始cluster,每個初始cluster只包含一個點);  
repeat:  
        對每個樣本點,計算得到距其最近的質心,將其類別標爲該質心所對應的cluster;  
        重新計算k個cluster對應的質心(質心是cluster中樣本點的均值);  
until 質心不再發生變化  

repeat的次數決定了算法的迭代次數。實際上,k-means的本質是最小化目標函數,目標函數爲每個點到其簇質心的距離的平方和:
在這裏插入圖片描述
N是元素個數,x表示元素,c(j)表示第j簇的質心

即:
初始化選擇 k 個質心 (隨機選擇, 或者領域知識選擇), 該選擇決定了聚類的速度與效果
爲所有點劃分簇
計算該種劃分的損失
當該損失還能減小時循環以下:
對於每個質心(k個)執行:
a. 隨機選擇一個新的點
b. 以新的點代替這個質心
c. 重新對所有數據劃分簇
d. 計算劃分的損失
e. 若 新的損失小於原先的損失, 則 用新的點代替原質心

算法複雜度
時間複雜度是O(nkt) ,其中n代表元素個數,t代表算法迭代的次數,k代表簇的數目

優缺點

優點
簡單、快速;
對大數據集有較高的效率並且是可伸縮性的;
時間複雜度近於線性,適合挖掘大規模數據集。
缺點
k-means是局部最優,因而對初始質心的選取敏感;
選擇能達到目標函數最優的k值是非常困難的。

代碼
代碼已在github上實現,這裏也貼出來

# coding:utf-8
import numpy as np
import matplotlib.pyplot as plt


def loadDataSet(fileName):
    '''
    加載測試數據集,返回一個列表,列表的元素是一個座標
    '''
    dataList = []
    with open(fileName) as fr:
        for line in fr.readlines():
            curLine = line.strip().split('\t')
            fltLine = list(map(float,curLine))
            dataList.append(fltLine)
    return dataList


def randCent(dataSet, k):
    '''
    隨機生成k個初始的質心
    '''
    n = np.shape(dataSet)[1] # n表示數據集的維度
    centroids = np.mat(np.zeros((k,n)))
    for j in range(n):
        minJ = min(dataSet[:,j])
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = np.mat(minJ + rangeJ * np.random.rand(k,1))
    return centroids


def kMeans(dataSet, k):
    '''
    KMeans算法,返回最終的質心座標和每個點所在的簇
    '''
    m = np.shape(dataSet)[0] # m表示數據集的長度(個數)
    clusterAssment = np.mat(np.zeros((m,2)))

    centroids = randCent(dataSet, k) # 保存k個初始質心的座標
    clusterChanged = True
    iterIndex=1 # 迭代次數
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = np.inf; minIndex = -1
            for j in range(k):
                distJI = np.linalg.norm(np.array(centroids[j,:])-np.array(dataSet[i,:]))
                if distJI < minDist:
                    minDist = distJI; minIndex = j
            if clusterAssment[i,0] != minIndex: clusterChanged = True
            clusterAssment[i,:] = minIndex,minDist**2
            print("第%d次迭代後%d個質心的座標:\n%s"%(iterIndex,k,centroids)) # 第一次迭代的質心座標就是初始的質心座標
            iterIndex+=1
        for cent in range(k):
            ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
            centroids[cent,:] = np.mean(ptsInClust, axis=0)
    return centroids, clusterAssment


def showCluster(dataSet, k, centroids, clusterAssment):
    '''
    數據可視化,只能畫二維的圖(若是三維的座標圖則直接返回1)
    '''
    numSamples, dim = dataSet.shape
    if dim != 2:
        return 1

    mark = ['or', 'ob', 'og', 'ok','oy','om','oc', '^r', '+r', 'sr', 'dr', '<r', 'pr']

    # draw all samples
    for i in range(numSamples):
        markIndex = int(clusterAssment[i, 0])
        plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex])

    mark = ['Pr', 'Pb', 'Pg', 'Pk','Py','Pm','Pc','^b', '+b', 'sb', 'db', '<b', 'pb']
    # draw the centroids
    for i in range(k):
        plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12)

    plt.show()



if __name__ == '__main__':

    dataMat = np.mat(loadDataSet('./testSet')) #mat是numpy中的函數,將列表轉化成矩陣

    k = 4 # 選定k值,也就是簇的個數(可以指定爲其他數)
    cent, clust = kMeans(dataMat, k)

    showCluster(dataMat, k, cent, clust)

測試地址:https://github.com/fuqiuai/datamining_algorithms/blob/master/K-means/testSet

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