機器學習基礎:kmeans算法及其優化


機器學習基礎:Kmeans算法及其優化

CONTENT

  1. 算法原理
  2. 算法流程
  3. 算法優化
    • Kmeans++
    • Elkan Kmeans
    • Mini Batch Kmeans
  4. 與KNN的區別
  5. 算法小結
  6. sklearn代碼實踐

1. 算法原理

對於給定的樣本集,按照樣本之間的距離大小,將樣本集划分為K個簇。讓簇內的點盡量緊密的連在一起,而讓簇間的距離盡量的大。即:

\[假設簇划分為(C_1,C_2,...C_k),則我們的目標是最小化平方誤差E:\\E = \sum_{i=1}^{K}\sum_{x\in C_i}\left\|x-\mu_i\right\|_2^2 \\其中μi是簇Ci的均值向量,也稱為質心,表達式為:\\\mu_i = \frac{1}{\mid C_i \mid}\sum_{x \in C_i}x \]

求解思想是:

  • 先初始k個簇的質心;
  • 然后分別求樣本中所有點到這k質心的距離,並標記每個樣本的類別為和該樣本距離最小的質心的類別;
  • 更新k個質心;
  • 重復以上兩步, 如果所有的k個質心向量都沒有發生變化,則停止。

Kmeans的思想比較簡單,用下面一組圖就可以形象的描述:

img

上圖a表達了初始的數據集,假設k=2。在圖b中,我們隨機選擇了兩個k類所對應的類別質心,即圖中的紅色質心和藍色質心,然后分別求樣本中所有點到這兩個質心的距離,並標記每個樣本的類別為和該樣本距離最小的質心的類別,如圖c所示,經過計算樣本和紅色質心和藍色質心的距離,我們得到了所有樣本點的第一輪迭代后的類別。此時我們對我們當前標記為紅色和藍色的點分別求其新的質心,如圖4所示,新的紅色質心和藍色質心的位置已經發生了變動。圖e和圖f重復了我們在圖c和圖d的過程,即將所有點的類別標記為距離最近的質心的類別並求新的質心。最終我們得到的兩個類別如圖f。當然在實際K-Mean算法中,我們一般會多次運行圖c和圖d,才能達到最終的比較優的類別。

2. 算法流程

\[假設:輸入是樣本集D={x_1,x_2,...x_m},聚類的簇樹k,最大迭代次數N,輸出是簇划分C={C_1,C_2,...C_k} \\\begin{split} &1): 從數據集D中隨機選擇k個樣本作為初始的k個質心向量: {μ_1,μ_2,...,μ_k}\\ &2): 將簇划分C初始化為C_t=\emptyset \, (t=1,2...k)\\ &3): 對於i=1,2...m,計算樣本x_i和各個質心向量μ_j(j=1,2,...k)的距離:d_{ij}=\left\|x-\mu_j\right\|_2^2,\\ &將x_i標記最小的為d_{ij}所對應的類別λ_i。此時更新C_{λ_i}=C_{λ_i}\cup{x_i} \\ &4): 對於j=1,2,...,k,對C_j中所有的樣本點重新計算新的質心μ_j=\frac{1}{|C_j|}\sum_{x∈C_j}x \\ &5): 循環2-4步,如果所有的k個質心向量都沒有發生變化,則輸出簇划分C={C_1,C_2,...C_k}\end{split} \]

3. 算法優化

3.1 kmeans++

kmeans中k個初始化的質心的位置選擇對最后的聚類結果和運行時間都有很大的影響,因此需要選擇合適的k個質心。如果僅僅是完全隨機的選擇,有可能導致算法收斂很慢。K-Means++算法就是對K-Means隨機初始化質心的方法的優化。K-Means++的對於初始化質心的優化策略如下:

\[\begin{split} &1): 從輸入的數據點集合中隨機選擇一個點作為第一個聚類中心μ_1 \\ &2): 對於數據集中的每一個點x_i,計算它與已選擇的聚類中心中最近聚類中心的距離\\ &D(x_i)=arg\,min||x_i−μ_j||_2^2\,(j=1,2,...k_{selected}) \\ &3): 選擇一個新的質心,選擇的原則是:D(x)較大的點,被選取作為聚類中心的概率較大\\ &4): 重復2和3直到選擇出k個聚類質心 \\ &5): 利用這k個質心來作為初始化質心去運行標准的Kmeans算法\end{split} \]

3.2 Elkan kmeans

在傳統的K-Means算法中,我們在每輪迭代時,要計算所有的樣本點到所有的質心的距離,這樣會比較的耗時。那么,對於距離的計算有沒有能夠簡化的地方呢?elkan K-Means算法就是從這塊入手加以改進。它的目標是減少不必要的距離的計算。主要基於以下兩種策略:

\[\begin{split} &1): 第一種規律是對於一個樣本點x和兩個質心μj1,μj2。如果我們預先計算出了這兩個質心之間的\\&距離D(j_1,j_2),則如果計算發現2D(x,j_1)≤D(j_1,j_2),我們立即就可以知道D(x,j_1)≤D(x,j_2)。\\&此時我們不需要再計算D(x,j_2),也就是說省了一步距離計算。\\ &2): 第二種規律是對於一個樣本點x和兩個質心μ_{j_1},μ_{j_2}。得到D(x,j_2)≥max\{0,D(x,j_1)−D(j_1,j_2)\}。\end{split} \]

利用上邊的兩個規律,elkan K-Means比起傳統的K-Means迭代速度有很大的提高。但是如果我們的樣本的特征是稀疏的,有缺失值的話,這個方法就不使用了,此時某些距離無法計算,則不能使用該算法。

3.3 Mini Batch kmeans

在統的K-Means算法中,要計算所有的樣本點到所有的質心的距離。如果樣本量非常大,比如達到10萬以上,特征有100以上,此時用傳統的K-Means算法非常的耗時,就算加上elkan K-Means優化也依舊。在大數據時代,這樣的場景越來越多。此時Mini Batch K-Means應運而生。

Mini Batch,也就是用樣本集中的一部分的樣本來做傳統的K-Means,這樣可以避免樣本量太大時的計算難題,算法收斂速度大大加快。當然此時的代價就是我們的聚類的精確度也會有一些降低。一般來說這個降低的幅度在可以接受的范圍之內。

一般是通過無放回的隨機采樣得到batch, 然后僅僅用batch size個樣本來做K-Means聚類,一般會多跑幾次Mini Batch K-Means算法,用得到不同的隨機采樣集來得到聚類簇,選擇其中最優的聚類簇。

4. 與KNN的區別

K-Means是無監督學習的聚類算法,沒有樣本輸出;而KNN是監督學習的分類算法,有對應的類別輸出。KNN基本不需要訓練,對測試集里面的點,只需要找到在訓練集中最近的k個點,用這最近的k個點的類別來決定測試點的類別。而K-Means則有明顯的訓練過程,找到k個類別的最佳質心,從而決定樣本的簇類別。兩者也有一些相似點,兩個算法都包含一個過程,即找出和某一個點最近的點。兩者都利用了最近鄰(nearest neighbors)的思想。

5. 算法小結

\[\begin{split}&K-Means的主要優點有:\\&1)原理比較簡單,實現也是很容易,收斂速度快。\\&2)聚類效果較優。\\&3)算法的可解釋度比較強。\\&4)主要需要調參的參數僅僅是簇數k。\\&K-Means的主要缺點有:\\&1)K值的選取不好把握\\&2)對於不是凸的數據集比較難收斂\\&3)如果各隱含類別的數據不平衡,比如各隱含類別的數據量嚴重失衡,或者各隱含類別的方差不同,則聚類效果不佳。\\&4) 采用迭代方法,得到的結果只是局部最優。\\&5) 對噪音和異常點比較的敏感。\\\end{split} \]

6. sklearn代碼實踐

在scikit-learn中,包括兩個K-Means的算法,一個是傳統的K-Means算法,對應的類是KMeans。另一個是基於采樣的Mini Batch K-Means算法,對應的類是MiniBatchKMeans。

6.1 評估方法

常見的方法有輪廓系數Silhouette CoefficientCalinski-Harabasz Index, 一般使用Calinski-Harabasz Index。其計算方法如下:

\[\begin{split}&Calinski-Harabasz分數值s的數學計算公式是:\\&s(k)=\frac{tr(B_k)}{tr(W_k)}\frac{m−k}{k−1} \\&其中m為訓練集樣本數,k為類別數。Bk為類別之間的協方差矩陣,Wk為類別內部數據的協方差矩陣。tr為矩陣的跡。\end{split} \]

類別內部數據的協方差越小越好,類別之間的協方差越大越好,這樣的Calinski-Harabasz分數會高。在scikit-learn中, Calinski-Harabasz Index對應的方法是metrics.calinski_harabaz_score

6.2 KMeans主要參數

KMeans類的主要參數有:

  • n_clusters: 即我們的k值,一般需要多試一些值以獲得較好的聚類效果。k值好壞的評估標准在下面會講。

  • max_iter: 最大的迭代次數,一般如果是凸數據集的話可以不管這個值,如果數據集不是凸的,可能很難收斂,此時可以指定最大的迭代次數讓算法可以及時退出循環。

  • n_init:用不同的初始化質心運行算法的次數。由於K-Means是結果受初始值影響的局部最優的迭代算法,因此需要多跑幾次以選擇一個較好的聚類效果,默認是10,一般不需要改。如果你的k值較大,則可以適當增大這個值。

  • init: 即初始值選擇的方式,可以為完全隨機選擇'random',優化過的'k-means++'或者自己指定初始化的k個質心。一般建議使用默認的'k-means++'。

  • algorithm:有“auto”, “full” or “elkan”三種選擇。"full"就是我們傳統的K-Means算法, “elkan”是我們原理篇講的elkan K-Means算法。默認的"auto"則會根據數據值是否是稀疏的,來決定如何選擇"full"和“elkan”。一般數據是稠密的,那么就是 “elkan”,否則就是"full"。一般來說建議直接用默認的"auto"

6.3 MiniBatchKMeans主要參數

MiniBatchKMeans類的主要參數比KMeans類稍多,主要有:

  • n_clusters: 即我們的k值,和KMeans類的n_clusters意義一樣。

  • max_iter:最大的迭代次數, 和KMeans類的max_iter意義一樣。

  • n_init:用不同的初始化質心運行算法的次數。這里和KMeans類意義稍有不同,KMeans類里的n_init是用同樣的訓練集數據來跑不同的初始化質心從而運行算法。而MiniBatchKMeans類的n_init則是每次用不一樣的采樣數據集來跑不同的初始化質心運行算法。

  • batch_size:即用來跑Mini Batch KMeans算法的采樣集的大小,默認是100.如果發現數據集的類別較多或者噪音點較多,需要增加這個值以達到較好的聚類效果。

  • init: 即初始值選擇的方式,和KMeans類的init意義一樣。

  • init_size: 用來做質心初始值候選的樣本個數,默認是batch_size的3倍,一般用默認值就可以了。

  • reassignment_ratio: 某個類別質心被重新賦值的最大次數比例,這個和max_iter一樣是為了控制算法運行時間的。這個比例是占樣本總數的比例,乘以樣本總數就得到了每個類別質心可以重新賦值的次數。如果取值較高的話算法收斂時間可能會增加,尤其是那些暫時擁有樣本數較少的質心。默認是0.01。如果數據量不是超大的話,比如1w以下,建議使用默認值。如果數據量超過1w,類別又比較多,可能需要適當減少這個比例值。具體要根據訓練集來決定。

  • max_no_improvement:即連續多少個Mini Batch沒有改善聚類效果的話,就停止算法, 和reassignment_ratio, max_iter一樣是為了控制算法運行時間的。默認是10.一般用默認值就足夠了。

6.4 實驗

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets.samples_generator import make_blobs
from sklearn.cluster import KMeans, MiniBatchKMeans
from sklearn import metrics
# X為樣本特征,Y為樣本簇類別, 共10000個樣本,每個樣本2個特征,共6個簇,簇中心在[-1,-1], [0,0],[1,1], [2,2],[3,5],[1,6] 簇方差分別為[0.4, 0.2, 0.2,0.1,0.3]
X, y = make_blobs(n_samples=10000, n_features=2, centers=[[-1,-1], [0,0], [1,1], [2,2],[3,5],[1,6]], cluster_std=[0.4, 0.2, 0.2, 0.2,0.1,0.3], 
                  random_state =9)
plt.scatter(X[:, 0], X[:, 1], marker='o')
plt.show()

def get_cluster(k, method='kmeans'):
    if method == 'kmeans':
        return KMeans(n_clusters=k, random_state=9)
    elif method == 'mini_batch_kmeans':
        return MiniBatchKMeans(n_clusters=k, batch_size = 200, random_state=9)
    else:
        print('Not valid')
        return None

def exp(method='kmeans'):
    res = []
    for k in range(2, 11):
        cluster = get_cluster(k, method)
        if not cluster:
            break
        y_pred = cluster.fit_predict(X)
        score = metrics.calinski_harabasz_score(X, y_pred)  
        res.append((k, X, y_pred, score))
    return res

def show_res(res):
    plt.figure(figsize=(26, 15))
    for i, (k, X, y_pred, score) in enumerate(res):
        plt.subplot(3,3,i+1)
        plt.scatter(X[:, 0], X[:, 1], c=y_pred)
        plt.title(f'k={k}, score:{score}')
    plt.show()
res = exp()
show_res(res)

res = exp('mini_batch_kmeans')
show_res(res)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM