1. 程式人生 > >scikit-learn中的KMeans聚類實現

scikit-learn中的KMeans聚類實現

在這篇文章中:

之前一直用R,現在開始學python之後就來嘗試用Python來實現Kmeans。 之前用R來實現kmeans的部落格:筆記︱多種常見聚類模型以及分群質量評估(聚類注意事項、使用技巧)

聚類分析在客戶細分中極為重要。有三類比較常見的聚類模型,K-mean聚類、層次(系統)聚類、最大期望EM演算法。在聚類模型建立過程中,一個比較關鍵的問題是如何評價聚類結果如何,會用一些指標來評價。 .

一、scikit-learn中的Kmeans介紹

scikit-learn 是一個基於Python的Machine Learning模組,裡面給出了很多Machine Learning相關的演算法實現,其中就包括K-Means演算法。

各個聚類的效能對比:

優點:

原理簡單
速度快
對大資料集有比較好的伸縮性

缺點:

需要指定聚類 數量K
對異常值敏感
對初始值敏感

1、相關理論

  • (1)中心點的選擇

k-meams演算法的能夠保證收斂,但不能保證收斂於全域性最優點,當初始中心點選取不好時,只能達到區域性最優點,整個聚類的效果也會比較差。可以採用以下方法:k-means中心點

選擇彼此距離儘可能遠的那些點作為中心點; 先採用層次進行初步聚類輸出k個簇,以簇的中心點的作為k-means的中心點的輸入。 多次隨機選擇中心點訓練k-means,選擇效果最好的聚類結果

  • (2)k值的選取

k-means的誤差函式有一個很大缺陷,就是隨著簇的個數增加,誤差函式趨近於0,最極端的情況是每個記錄各為一個單獨的簇,此時資料記錄的誤差為0,但是這樣聚類結果並不是我們想要的,可以引入結構風險對模型的複雜度進行懲罰:

λλ是平衡訓練誤差與簇的個數的引數,但是現在的問題又變成了如何選取λλ了,有研究[參考文獻1]指出,在資料集滿足高斯分佈時,λ=2mλ=2m,其中m是向量的維度。

另一種方法是按遞增的順序嘗試不同的k值,同時畫出其對應的誤差值,通過尋求拐點來找到一個較好的k值,詳情見下面的文字聚類的例子。

2、主函式KMeans

sklearn.cluster.KMeans(n_clusters=8,
     init='k-means++', 
    n_init=10, 
    max_iter=300, 
    tol=0.0001, 
    precompute_distances='auto', 
    verbose=0, 
    random_state=None, 
    copy_x=True, 
    n_jobs=1, 
    algorithm='auto'
    )

引數的意義:

  • n_clusters:簇的個數,即你想聚成幾類
  • init: 初始簇中心的獲取方法
  • n_init: 獲取初始簇中心的更迭次數,為了彌補初始質心的影響,演算法預設會初始10個質心,實現演算法,然後返回最好的結果。
  • max_iter: 最大迭代次數(因為kmeans演算法的實現需要迭代)
  • tol: 容忍度,即kmeans執行準則收斂的條件
  • precompute_distances:是否需要提前計算距離,這個引數會在空間和時間之間做權衡,如果是True 會把整個距離矩陣都放到記憶體中,auto 會預設在資料樣本大於featurs*samples 的數量大於12e6 的時候False,False 時核心實現的方法是利用Cpython 來實現的
  • verbose: 冗長模式(不太懂是啥意思,反正一般不去改預設值)
  • random_state: 隨機生成簇中心的狀態條件。
  • copy_x: 對是否修改資料的一個標記,如果True,即複製了就不會修改資料。bool 在scikit-learn 很多介面中都會有這個引數的,就是是否對輸入資料繼續copy 操作,以便不修改使用者的輸入資料。這個要理解Python 的記憶體機制才會比較清楚。
  • n_jobs: 並行設定
  • algorithm: kmeans的實現演算法,有:’auto’, ‘full’, ‘elkan’, 其中 ‘full’表示用EM方式實現

雖然有很多引數,但是都已經給出了預設值。所以我們一般不需要去傳入這些引數,引數的。可以根據實際需要來呼叫。

3、簡單案例一

參考部落格:python之sklearn學習筆記 本案例說明了,KMeans分析的一些類如何調取與什麼意義。

import numpy as np
from sklearn.cluster import KMeans
data = np.random.rand(100, 3) #生成一個隨機資料,樣本大小為100, 特徵數為3

#假如我要構造一個聚類數為3的聚類器
estimator = KMeans(n_clusters=3)#構造聚類器
estimator.fit(data)#聚類
label_pred = estimator.labels_ #獲取聚類標籤
centroids = estimator.cluster_centers_ #獲取聚類中心
inertia = estimator.inertia_ # 獲取聚類準則的總和

estimator初始化Kmeans聚類;estimator.fit聚類內容擬合; estimator.label_聚類標籤,這是一種方式,還有一種是predict;estimator.cluster_centers_聚類中心均值向量矩陣 estimator.inertia_代表聚類中心均值向量的總和

4、案例二

from sklearn.cluster import KMeans

num_clusters = 3
km_cluster = KMeans(n_clusters=num_clusters, max_iter=300, n_init=40, \
                    init='k-means++',n_jobs=-1)

#返回各自文字的所被分配到的類索引
result = km_cluster.fit_predict(tfidf_matrix)

print "Predicting result: ", result

km_cluster是KMeans初始化,其中用init的初始值選擇演算法用’k-means++’; km_cluster.fit_predict相當於兩個動作的合併:km_cluster.fit(data)+km_cluster.predict(data),可以一次性得到聚類預測之後的標籤,免去了中間過程。

  • n_clusters: 指定K的值
  • max_iter: 對於單次初始值計算的最大迭代次數
  • n_init: 重新選擇初始值的次數
  • init: 制定初始值選擇的演算法
  • n_jobs: 程序個數,為-1的時候是指預設跑滿CPU
  • 注意,這個對於單個初始值的計算始終只會使用單程序計算,
  • 平行計算只是針對與不同初始值的計算。比如n_init=10,n_jobs=40,
  • 伺服器上面有20個CPU可以開40個程序,最終只會開10個程序

其中:

km_cluster.labels_
km_cluster.predict(data)

這是兩種聚類結果標籤輸出的方式,結果貌似都一樣。都需要先km_cluster.fit(data),然後再呼叫。

5、案例四——Kmeans的後續分析

Kmeans演算法之後的一些分析,參考來源:用Python實現文件聚類

from sklearn.cluster import KMeans

num_clusters = 5

km = KMeans(n_clusters=num_clusters)

%time km.fit(tfidf_matrix)


clusters = km.labels_.tolist()

分為五類,同時用%time來測定執行時間,把分類標籤labels格式變為list。

  • (1)模型儲存與載入
from sklearn.externals import joblib

# 註釋語句用來儲存你的模型
joblib.dump(km,  'doc_cluster.pkl')
km = joblib.load('doc_cluster.pkl')
clusters = km.labels_.tolist()
  • (2)聚類類別統計
frame = pd.DataFrame(films, index = [clusters] , columns = ['rank', 'title', 'cluster', 'genre'])
frame['cluster'].value_counts()
  • (3)質心均值向量計算組內平方和

選擇更靠近質心的點,其中 km.cluster_centers_代表著一個 (聚類個數*維度數),也就是不同聚類、不同維度的均值。 該指標可以知道: 一個類別之中的,那些點更靠近質心; 整個類別組內平方和。

類別內的組內平方和要參考以下公式:

通過公式可以看出: 質心均值向量每一行數值-每一行均值(相當於均值的均值) 注意是平方。其中,n代表樣本量,k是聚類數量(譬如聚類5) 其中,整篇的組內平方和可以通過來獲得總量:

km.inertia_

.

二、大資料量下的Mini-Batch-KMeans演算法

部分內容參考來源:scikit-learn學習之K-means聚類演算法與 Mini Batch K-Means演算法 當資料量很大的時候,Kmeans 顯然還是很弱的,會比較耗費記憶體速度也會收到很大影響。scikit-learn 提供了MiniBatchKMeans演算法,大致思想就是對資料進行抽樣,每次不使用所有的資料來計算,這就會導致準確率的損失。

MiniBatchKmeans 繼承自Kmeans 因為MiniBathcKmeans 本質上還利用了Kmeans 的思想.從構造方法和文件大致能看到這些引數的含義,瞭解了這些引數會對使用的時候有很大的幫助。batch_size 是每次選取的用於計算的資料的樣本量,預設為100.

Mini Batch K-Means演算法是K-Means演算法的變種,採用小批量的資料子集減小計算時間,同時仍試圖優化目標函式,這裡所謂的小批量是指每次訓練演算法時所隨機抽取的資料子集,採用這些隨機產生的子集進行訓練演算法,大大減小了計算時間,與其他演算法相比,減少了k-均值的收斂時間,小批量k-均值產生的結果,一般只略差於標準演算法。

該演算法的迭代步驟有兩步: 1:從資料集中隨機抽取一些資料形成小批量,把他們分配給最近的質心 2:更新質心 與K均值演算法相比,資料的更新是在每一個小的樣本集上。對於每一個小批量,通過計算平均值得到更新質心,並把小批量裡的資料分配給該質心,隨著迭代次數的增加,這些質心的變化是逐漸減小的,直到質心穩定或者達到指定的迭代次數,停止計算 Mini Batch K-Means比K-Means有更快的 收斂速度,但同時也降低了聚類的效果,但是在實際專案中卻表現得不明顯 一張k-means和mini batch k-means的實際效果對比圖

來看一下 MiniBatchKMeans的python實現: 官網連結案例一則連結

主函式 :

MiniBatchKMeans(n_clusters=8, init=’k-means++’, max_iter=100, batch_size=100, verbose=0, compute_labels=True, random_state=None, 
tol=0.0, max_no_improvement=10, init_size=None, n_init=3, reassignment_ratio=0.01)
  • random_state: 隨機生成簇中心的狀態條件,譬如設定random_state = 9
  • tol: 容忍度,即kmeans執行準則收斂的條件
  • max_no_improvement:即連續多少個Mini Batch沒有改善聚類效果的話,就停止演算法, 和reassignment_ratio, max_iter一樣是為了控制演算法執行時間的。預設是10.一般用預設值就足夠了。
  • batch_size:即用來跑Mini Batch KMeans演算法的取樣集的大小,預設是100.如果發現數據集的類別較多或者噪音點較多,需要增加這個值以達到較好的聚類效果。
  • reassignment_ratio: 某個類別質心被重新賦值的最大次數比例,這個和max_iter一樣是為了控制演算法執行時間的。這個比例是佔樣本總數的比例, 乘以樣本總數就得到了每個類別質心可以重新賦值的次數。如果取值較高的話演算法收斂時間可能會增加,尤其是那些暫時擁有樣本數較少的質心。 預設是0.01。如果資料量不是超大的話,比如1w以下,建議使用預設值。 如果資料量超過1w,類別又比較多,可能需要適當減少這個比例值。 具體要根據訓練集來決定。
import time

import numpy as np
import matplotlib.pyplot as plt

from sklearn.cluster import MiniBatchKMeans, KMeans
from sklearn.metrics.pairwise import pairwise_distances_argmin
from sklearn.datasets.samples_generator import make_blobs

# 獲取資料
np.random.seed(0)

batch_size = 45
centers = [[1, 1], [-1, -1], [1, -1]]
n_clusters = len(centers)
X, labels_true = make_blobs(n_samples=3000, centers=centers, cluster_std=0.7)

# kmeans
# Compute clustering with Means

k_means = KMeans(init='k-means++', n_clusters=3, n_init=10)
t0 = time.time()
k_means.fit(X)
t_batch = time.time() - t0

# MiniBatchKMeans

mbk = MiniBatchKMeans(init='k-means++', n_clusters=3, batch_size=batch_size,
                      n_init=10, max_no_improvement=10, verbose=0)
t0 = time.time()
mbk.fit(X)
t_mini_batch = time.time() - t0

內容跟kmeans很像,只是一般多加一個引數,batch_size。

.

三、sklearn中的cluster進行kmeans聚類

import numpy as np
from sklearn import cluster
data = np.random.rand(100, 3) #生成一個隨機資料,樣本大小為100, 特徵數為3
k = 3 # 假如我要聚類為3個clusters
[centroid, label, inertia] = cluster.k_means(data, k)

.

延伸一:資料如何做標準化

data_zs = 1.0*(data - data.mean())/data.std() #資料標準化

.

延伸二:Kmeans視覺化案例

from sklearn.cluster import KMeans
from sklearn.externals import joblib
import numpy
import time
import matplotlib.pyplot as plt

if __name__ == '__main__':
    ## step 1: 載入資料
    print "step 1: load data..."
    dataSet = []
    fileIn = open('./data.txt')
    for line in fileIn.readlines():
        lineArr = line.strip().split(' ')
        dataSet.append([float(lineArr[0]), float(lineArr[1])])

    #設定不同k值以運算
    for k in range(2,10):
        clf = KMeans(n_clusters=k) #設定k  !!!!!!!!!!這裡就是呼叫KMeans演算法
        s = clf.fit(dataSet) #載入資料集合
        numSamples = len(dataSet) 
        centroids = clf.labels_
        print centroids,type(centroids) #顯示中心點
        print clf.inertia_  #顯示聚類效果
        mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
        #畫出所有樣例點 屬於同一分類的繪製同樣的顏色
        for i in xrange(numSamples):
            #markIndex = int(clusterAssment[i, 0])
            plt.plot(dataSet[i][0], dataSet[i][1], mark[clf.labels_[i]]) #mark[markIndex])
        mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb']
        # 畫出質點,用特殊圖型
        centroids =  clf.cluster_centers_
        for i in range(k):
            plt.plot(centroids[i][0], centroids[i][1], mark[i], markersize = 12)
            #print centroids[i, 0], centroids[i, 1]
        plt.show()

延伸三:模型儲存

from sklearn.externals import joblib
joblib.dump(km_cluster, "/..../train_model.m")
km_cluster = joblib.load(".../train_model.m")
kmeans_SSE.labels_