kmeans聚類算法實現

算法原理

模型

已知一組有n個樣本的數據集
{xi}i=1n,xiRt \{x_i\}_ {i=1}^n,x_i\in\mathbb R^t
其中,每個樣本xix_i都有tt個特徵,沒有標籤yiy_i。現在的目的是希望利用樣本特徵將數據集分爲k類。

算法步驟

首先要明確一些概念:類是指根據特徵的不同將數據分成不同的類別。而不同數據樣本組成的集合稱爲簇。
Step1、已知樣本數據集,數據包含特徵,樣本間的距離採用歐式距離,聚類數設定爲k,計算前需要先對數據進行標準化。之後隨機選取k個樣本點,令這k個樣本分別作爲k個簇{Ci}i=1k\{C_i\}_ {i=1}^k
Step2、計算每個簇的中心向量
x^i=1Ci(j=1Cixj(1),j=1Cixj(2),...,j=1Cixj(t)) \hat x_i=\frac{1}{|C_i|}(\sum_{j=1}^{|C_i|}x_j^{(1)},\sum_{j=1}^{|C_i|}x_j^{(2)},...,\sum_{j=1}^{|C_i|}x_j^{(t)})
其中,Ci|C_i|表示第ii個簇中的樣本個數,xj(s)x_j^{(s)}表示第ii個簇中第jj個樣本的第ss個特徵。通過上式可得到k箇中心向量,之後計算樣本數據集中每個樣本與中心向量的歐式距離,每個樣本歸屬於與它距離最小的某個中心向量所在的簇。
Step3、將所有樣本分爲k個簇後,重複第2步,直至k箇中心向量不再改變。

程序實現

kmeans算法包含3個函數,分別是計算中心向量即均值向量,劃分數據樣本集,劃分主函數。

計算均值函數

def cal_mean_vector(x):
    """
    參數
    ----
    x:已分類的樣本組,類型:list, shape:[ndarray]:{n_samples, n_features}

    返回
    ----
    x_:均值向量,類型:ndarray, shape:{n_samples, n_features}
    """
    res = []
    for i in range(len(x)):
        res.append(x[i].mean(axis=0))
    return np.array(res)

劃分數據集函數

def split_vector(x, v):
    """根據中心向量對數據集進行分組
    參數
    ----
    x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
    v:中心向量,類型:ndarray, shape:{n_samples, n_features}

    返回
    ----
    按順序進行分類的特徵向量組
    """
    label_dis = []
    # 計算每個樣本與每個中心向量的距離,並存儲每個樣本的最小中心向量索引
    for i in range(len(x)):
        distance = np.sum((x[i]-v)**2, axis=1)
        label_dis.append(np.argmin(distance))
    label_dis = np.array(label_dis)

    # 以每個中心向量爲單位,存儲數據樣本
    split_vector = []
    for i in range(len(v)):
        split_vector.append(x[label_dis==i])

    return split_vector

kmeans主函數

def kmeans(x, k):
    """
    參數
    ----
    x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
    k:聚類個數,類型:int
    返回
    ----
    x_:已分類的樣本組,類型:list,shape:[ndarray]:{n_samples, n_features}

    v_:最終的中心向量,類型:ndarray,shape:{n_samples, n_features}
    """
    # 隨機選取k個向量
    index = np.random.choice(len(x), k, replace=False)
    # 選取中心向量
    v = x[index]
    while 1:
        # 根據中心向量將數據集劃分爲若干組
        x_ = split_vector(x, v)
        v_ = cal_mean_vector(x_)
        if v_.tolist()==v.tolist():
            return x_,v_
        v = v_

演示

原始分類

color = ['red','yellow','blue']
for i in range(3):
    plt.scatter(X[y==i,1], X[y==i,2], color=color[i])


聚類結果

X_test = kmeans(X,3)[0]
for i in range(3):
    plt.scatter(X_test[i][:,1], X_test[i][:,2], color=color[i])

封裝成一個類

class KMeans:
    def __init__(self, k=5,):
        self.k = k
    # 計算均值向量
    def cal_mean_vector(self, x):
        """
        參數
        ----
        x:已分類的樣本組,類型:list, shape:[ndarray]:{n_samples, n_features}

        返回
        ----
        x_:均值向量,類型:ndarray, shape:{n_samples, n_features}
        """
        res = []
        for i in range(len(x)):
            res.append(x[i].mean(axis=0))
        return np.array(res)

    # 劃分特徵向量
    def split_vector(self, x, v):
        """根據中心向量對數據集進行分組
        參數
        ----
        x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
        v:中心向量,類型:ndarray, shape:{n_samples, n_features}

        返回
        ----
        按順序進行分類的特徵向量組
        """
        label_dis = []
        # 計算每個樣本與每個中心向量的距離,並存儲每個樣本的最小中心向量索引
        for i in range(len(x)):
            distance = np.sum((x[i]-v)**2, axis=1)
            label_dis.append(np.argmin(distance))
        label_dis = np.array(label_dis)

        # 以每個中心向量爲單位,存儲數據樣本
        split_vector = []
        for i in range(len(v)):
            split_vector.append(x[label_dis==i])

        return split_vector

    # kmeans算法主函數
    def fit(self, x):
        """
        參數
        ----
        x:特徵向量,類型:ndarray, shape:{n_samples, n_features}

        ----
        x_:已分類的樣本組,類型:list,shape:[ndarray]:{n_samples, n_features}
        
        v_:最終的中心向量,類型:ndarray,shape:{n_samples, n_features}
        """
        # 隨機選取k個向量
        index = np.random.choice(len(x), self.k, replace=False)
        # 選取中心向量
        v = x[index]
        while 1:
            # 根據中心向量將數據集劃分爲若干組
            x_ = self.split_vector(x, v)
            v_ = self.cal_mean_vector(x_)
            if v_.tolist()==v.tolist():
                self.x_ = x_
                self.v_ = v_
                return 
            v = v_
    
    def transform(self, x):
        return self.split_vector(x, self.v_)

實例化演示

clf = KMeans(k=3)
clf.fit(X)
X_test_2 = clf.transform(X)
for i in range(3):
    plt.scatter(X_test_2[i][:,1], X_test_2[i][:,2], color=color[i])


以上便是kmeans聚類算法的代碼實現,如有不對不妥之處,歡迎交流批評改正。

----end----

參考資料
周志華《機器學習》

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