聲明:未經允許禁止轉載與抄襲。
前言
k k k均值( k k k-means)聚類算法是一種經典的無監督聚類算法,本文將深入解析其理論原理,并在真是數據集上進行算法實踐,話不多說,請看下文。
算法原理
給定樣本集 D = { x 1 , x 2 , … , x m } D=\left\{\mathbf{x}_1, \mathbf{x}_2, \ldots, \mathbf{x}_m\right\} D={x1?,x2?,…,xm?},其中每個樣本 x i \mathbf{x}_i xi?都由一個向量表示,例如以周志華老師西瓜書中的西瓜數據集為例,每個樣本都包含兩個屬性密度和含糖量,這兩個屬性值組成的向量便是該樣本的向量表示。
k k k均值算法旨在將樣本集 D D D劃分為 k k k個簇,即 C = { C 1 , C 2 , … , C k } C=\left\{C_1,C_2,\ldots,C_k\right\} C={C1?,C2?,…,Ck?},使得每個樣本都被劃分到與其距離最小的簇中。用數學來刻畫就是, k k k均值算法希望能夠最小化所劃分的 k k k個簇的平方誤差,即:
argmin C ∑ i = 1 k ∑ x ∈ C i ∥ x ? μ i ∥ 2 2 \text{argmin}_{C} \sum_{i=1}^k \sum_{\mathbf{x} \in C_i}\left\|\mathbf{x}-\mathbf{\mu}_i\right\|_2^2 argminC?i=1∑k?x∈Ci?∑?∥x?μi?∥22?
其中 μ i = 1 ∣ C i ∣ ∑ x ∈ C i x \boldsymbol{\mu}_i=\frac{1}{\left|C_i\right|} \sum_{\mathbf{x} \in C_{\boldsymbol{i}}} \mathbf{x} μi?=∣Ci?∣1?∑x∈Ci??x 表示簇 C i C_i Ci? 的均值向量, x \mathbf{x} x表示簇 C i C_i Ci?中的樣本。
k k k均值算法事先設定數據集 D D D要劃分為 k k k個簇,初始化時,會先從數據集 D D D中隨機挑選 k k k個樣本作為各個簇的初始均值向量(簇中心)。然后遍歷所有樣本,分別計算各個樣本與各個簇中心的距離,并將樣本劃分到與距離最小的簇中。待到所有樣本都劃分完畢后,將各個簇樣本向量的均值向量作為新的簇均值向量,重復上述的步驟,直到上一輪所有的簇中心均值向量與本輪計算的結果相同為止。該算法的具體算法流程如下:
需要注意的是,實際計算過程中為避免計算時間過長,該算法的終止條件不可能這樣嚴苛。西瓜書中給出了兩種方案:
- 設置一個最大輪數。
- 設置均值向量的變化閾值,若新簇中心與舊簇中心之間的距離不超過該閾值即可,而不是嚴格不變。
對于 k k k均值聚類算法而言,數據集的預處理和 k k k值的選取同樣十分重要,可以參考博客【機器學習】K-means(非常詳細),限于篇幅原因,本文就不詳細展開。
算法實踐
本文在鳶尾花數據集上進行 k k k均值聚簇算法的實踐。
基于最大輪數終止的 k k k均值聚類算法實現如下所示,兩個向量之間的距離計算本文采用了余弦距離。
import numpy as np
from scipy.spatial.distance import cdistclass KMeansModel:def rand_pick(self, x, k):"""隨機選取k個簇中心"""n = x.shape[0]indices = np.random.choice(n, k, replace=False)return x[indices]def calculate_distance(self, x, centers):"""計算簇中心與數據樣本之間的余弦距離centers: 簇中心數據 (k, d)x: 樣本 (N, d)"""return cdist(x, centers, metric="cosine")def get_centers(self, k, x, y):"""根據計算結果重新計算簇中心y: 根據距離將數據集劃分的標簽數組 (N)"""centers = np.zeros((k, x.shape[1]))for label in range(k):centers[label] = np.mean(x[y == label], axis=0)return centersdef get_label(self, dis):"""根據距離矩陣將每個樣本劃分到距離最小的簇中心"""return np.argmin(dis, axis=-1)def cluster(self, x, k, times):"""進行KMeans聚類x: 數據樣本 (N, d)k: 類別數tims: 迭代次數"""# 隨機選取k個作為初始簇中心centers = self.rand_pick(x, k)for _ in range(times):# 計算各個樣本到簇中心的距離dis = self.calculate_distance(x, centers)# 根據距離矩陣將樣本進行劃分y = self.get_label(dis)# 重新計算新的簇中心centers = self.get_centers(k, x, y)return y
在鳶尾花數據集上,設置的實驗參數為 k = 3 k=3 k=3, t i m e s = 500 times=500 times=500,即將整個數據集聚為 3 3 3個簇,算法迭代 500 500 500輪終止。
算法額外對比了基于sklearn
庫實現的 k k k均值聚簇算法的效果,為更直觀的展示,本文對數據集進行PCA降維,下圖從左到右分別是真實標簽、本文模型的聚類結果、基于sklearn算法的聚類結果。從結果可以看出,本文實現的模型在鳶尾花數據集上效果還是不錯的。
結語
以上便是本文的全部內容,如果感覺不錯可以支持一下,若有任何問題敬請批評指正。