面試手撕代碼-k-means算法

要求:

隨機生成x,y均在[0,10]範圍內的10個點,k=2,訓練一個簡單的k-means模型。

K均值算法步驟如下:

1.在訓練樣本點中隨機初始化[0,10]範圍內的k個樣本點作爲k個簇各自的中心;

2.遍歷一遍所有樣本點,將每一個樣本點分配到最近的簇中心,得到clusterDict。clusterDict的鍵爲centroidList的下標,鍵值爲屬於該類的所有樣本點。

3.計算第一次聚類迭代得到的結果的代價函數,即每一個樣本點到其簇中心的距離的平方和newVar。令oldVar = -1

4.開始迭代,直到代價函數收斂(newVar-oldVar<=0.00001):

      *對於計算每一類樣本的均值,作爲新的簇中心

      *遍歷一遍所有樣本點,將每一個樣本點分配到最近的簇中心。

 

      

import numpy as np
import matplotlib.pyplot as plt

def getCentroids(clusterDict):
    # 得到k個質心
    centroidList = []
    for key in clusterDict.keys():
        centroid = np.mean(clusterDict[key], axis=0)  # 計算每列的均值,即找到質心
        centroidList.append(centroid)
    return np.array(centroidList).tolist()

def getVar(clusterDict,centroidList):
    # 計算簇集合間的均方誤差
    # 將簇類中各個向量與質心的距離進行累加求和
    sum = 0.0
    for key in clusterDict.keys():
        vec1 = centroidList[key]
        distance = 0.0
        for item in clusterDict[key]:
            vec2 = item
            distance += calcuDistance(vec1, vec2)
        sum += distance
    return sum

def calcuDistance(vec1,vec2):
    return np.sqrt(np.sum(np.square(vec1-vec2)))

def MinDistance(dataSet,centroidList):
    clusterDict = {}
    for i in range(len(dataSet)):
        flag = 0
        min_dis = float("inf")
        for j in range(len(centroidList)):
            distance = calcuDistance(dataSet[i],centroidList[j])
            if min_dis>distance:
                flag = j
                min_dis = distance
        if flag not in clusterDict.keys():
            clusterDict[flag] = list()
        clusterDict[flag].append(dataSet[i])
    return clusterDict

def minDistance(dataSet, centroidList):
    # 對每個屬於dataSet的item,計算item與centroidList中k個質心的歐式距離,找出距離最小的,
    # 並將item加入相應的簇類中
    clusterDict = dict()  # 用dict來保存簇類結果
    for item in dataSet:
        vec1 = np.array(item)  # 轉換成array形式
        flag = 0  # 簇分類標記,記錄與相應簇距離最近的那個簇
        minDis = float("inf")  # 初始化爲最大值
        for i in range(len(centroidList)):
            vec2 = np.array(centroidList[i])
            distance = calcuDistance(vec1, vec2)  # 計算相應的歐式距離
            if distance < minDis:
                minDis = distance
                flag = i  # 循環結束時,flag保存的是與當前item距離最近的那個簇標記

        if flag not in clusterDict.keys():  # 簇標記不存在,進行初始化
            clusterDict[flag] = list()
        clusterDict[flag].append(item)  # 加入相應的類別中

    return clusterDict  # 返回新的聚類結果
def showCluster(centroidList, clusterDict):
    # 展示聚類結果
    colorMark = ['or', 'ob', 'og', 'ok', 'oy', 'ow']  # 不同簇類的標記 'or' --> 'o'代表圓,'r'代表red,'b':blue
    centroidMark = ['dr', 'db', 'dg', 'dk', 'dy', 'dw']  # 質心標記 同上'd'代表棱形
    for key in clusterDict.keys():
        plt.plot(centroidList[key][0], centroidList[key][1], centroidMark[key], markersize=10)  # 畫質心點
        for item in clusterDict[key]:
            plt.plot(item[0], item[1], colorMark[key])  # 畫簇類下的點
    plt.show()
if __name__ == '__main__':
    k = 2   #聚類種數
    dataSet = 10*np.random.random((10,2))
    centroidList = 10*np.random.random((k,2))
    clusterDict = MinDistance(dataSet,centroidList)
    newVar = getVar(clusterDict, centroidList)

    oldVar = -1  # 舊均方誤差值初始化爲-1
    # showCluster(centroidList, clusterDict)  # 展示聚類結果
    k = 2
    while abs(newVar - oldVar) >= 0.000001:  # 當連續兩次聚類結果小於0.0001時,迭代結束
        centroidList = getCentroids(clusterDict)  # 獲得新的質心
        clusterDict = MinDistance(dataSet, centroidList)  # 新的聚類結果
        oldVar = newVar
        newVar = getVar(clusterDict, centroidList)
        print('***** 第%d次迭代 *****' % k)
        print('簇類')
        for key in clusterDict.keys():
            print(key, ' --> ', clusterDict[key])
        print('k個均值向量: ', centroidList)
        print('平均均方誤差: ', newVar)
        k += 1
    showCluster(centroidList,clusterDict)   # 展示聚類結果

 

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