1. 程式人生 > >用scikit-learn學習K-Means聚類

用scikit-learn學習K-Means聚類

    在K-Means聚類演算法原理中,我們對K-Means的原理做了總結,本文我們就來討論用scikit-learn來學習K-Means聚類。重點講述如何選擇合適的k值。

1. K-Means類概述

    在scikit-learn中,包括兩個K-Means的演算法,一個是傳統的K-Means演算法,對應的類是KMeans。另一個是基於取樣的Mini Batch K-Means演算法,對應的類是MiniBatchKMeans。一般來說,使用K-Means的演算法調參是比較簡單的。

    用KMeans類的話,一般要注意的僅僅就是k值的選擇,即引數n_clusters;如果是用MiniBatchKMeans的話,也僅僅多了需要注意調參的引數batch_size,即我們的Mini Batch的大小。

    當然KMeans類和MiniBatchKMeans類可以選擇的引數還有不少,但是大多不需要怎麼去調參。下面我們就看看KMeans類和MiniBatchKMeans類的一些主要引數。

2. KMeans類主要引數

    KMeans類的主要引數有:

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

    2)max_iter: 最大的迭代次數,一般如果是凸資料集的話可以不管這個值,如果資料集不是凸的,可能很難收斂,此時可以指定最大的迭代次數讓演算法可以及時退出迴圈。

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

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

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

3. MiniBatchKMeans類主要引數

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

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

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

    3)n_init:用不同的初始化質心執行演算法的次數。這裡和KMeans類意義稍有不同,KMeans類裡的n_init是用同樣的訓練集資料來跑不同的初始化質心從而執行演算法。而MiniBatchKMeans類的n_init則是每次用不一樣的取樣資料集來跑不同的初始化質心執行演算法。

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

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

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

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

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

4. K值的評估標準

    不像監督學習的分類問題和迴歸問題,我們的無監督聚類沒有樣本輸出,也就沒有比較直接的聚類評估方法。但是我們可以從簇內的稠密程度和簇間的離散程度來評估聚類的效果。常見的方法有輪廓係數Silhouette Coefficient和Calinski-Harabasz Index。個人比較喜歡Calinski-Harabasz Index,這個計算簡單直接,得到的Calinski-Harabasz分數值$s$越大則聚類效果越好。

    Calinski-Harabasz分數值$s$的數學計算公式是:$$s(k) = \frac{tr(B_k)}{tr(W_k)} \frac{m-k}{k-1}$$

    其中m為訓練集樣本數,k為類別數。$B_k$為類別之間的協方差矩陣,$W_k$為類別內部資料的協方差矩陣。$tr$為矩陣的跡。

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

5. K-Means應用例項

    下面用一個例項來講解用KMeans類和MiniBatchKMeans類來聚類。我們觀察在不同的k值下Calinski-Harabasz分數。

    完整的程式碼參見我的github: https://github.com/ljpzzz/machinelearning/blob/master/classic-machine-learning/kmeans_cluster.ipynb

    首先我們隨機建立一些二維資料作為訓練集,選擇二維特徵資料,主要是方便視覺化。程式碼如下:

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

    從輸出圖可以我們看看我們建立的資料如下:

   現在我們來用K-Means聚類方法來做聚類,首先選擇k=2,程式碼如下:

from sklearn.cluster import KMeans
y_pred = KMeans(n_clusters=2, random_state=9).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()

    k=2聚類的效果圖輸出如下:

    現在我們來看看我們用Calinski-Harabasz Index評估的聚類分數:

from sklearn import metrics
metrics.calinski_harabaz_score(X, y_pred)  

    輸出如下:

3116.1706763322227

    現在k=3來看看聚類效果,程式碼如下:

from sklearn.cluster import KMeans
y_pred = KMeans(n_clusters=3, random_state=9).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()  

    k=3的聚類的效果圖輸出如下:

    現在我們來看看我們用Calinski-Harabaz Index評估的k=3時候聚類分數:

metrics.calinski_harabaz_score(X, y_pred)  

    輸出如下:

2931.625030199556

    可見此時k=3的聚類分數比k=2還差。

    現在我們看看k=4時候的聚類效果:

from sklearn.cluster import KMeans
y_pred = KMeans(n_clusters=4, random_state=9).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()

    k=4的聚類的效果圖輸出如下:

    現在我們來看看我們用Calinski-Harabasz Index評估的k=4時候聚類分數:

metrics.calinski_harabaz_score(X, y_pred)  

    輸出如下:

5924.050613480169

    可見k=4的聚類分數比k=2和k=3都要高,這也符合我們的預期,我們的隨機資料集也就是4個簇。當特徵維度大於2,我們無法直接視覺化聚類效果來肉眼觀察時,用Calinski-Harabaz Index評估是一個很實用的方法。

    現在我們再看看用MiniBatchKMeans的效果,我們將batch size設定為200. 由於我們的4個簇都是凸的,所以其實batch size的值只要不是非常的小,對聚類的效果影響不大。

for index, k in enumerate((2,3,4,5)):
    plt.subplot(2,2,index+1)
    y_pred = MiniBatchKMeans(n_clusters=k, batch_size = 200, random_state=9).fit_predict(X)
    score= metrics.calinski_harabaz_score(X, y_pred)  
    plt.scatter(X[:, 0], X[:, 1], c=y_pred)
    plt.text(.99, .01, ('k=%d, score: %.2f' % (k,score)),
                 transform=plt.gca().transAxes, size=10,
                 horizontalalignment='right')
plt.show()

   對於k=2,3,4,5對應的輸出圖為:

    可見使用MiniBatchKMeans的聚類效果也不錯,當然由於使用Mini Batch的原因,同樣是k=4最優,KMeans類的Calinski-Harabasz Index分數為5924.05,而MiniBatchKMeans的分數稍微低一些,為5921.45。這個差異損耗並不大。

(歡迎轉載,轉載請註明出處。歡迎溝通交流: [email protected]