使用scikit-learn進行KMeans文字聚類
轉載自部落格:https://blog.razrlele.com/p/1614
K-Means 演算法簡介
中文名字叫做K-均值演算法,演算法的目的是將n個向量分別歸屬到K箇中心點裡面去。演算法首先會隨機選擇K箇中心向量,然後通過迭代計算以及重新選擇K箇中心向量,使得n個向量各自被分配到距離最近的K中心點,並且所有向量距離各自中心點的和最小。
在這裡借用Wikipedia上的K-Means條目的圖來說明
步驟一:在輸入資料集裡面隨機選擇三個向量作為初始中心點,這裡的K值為3, 也就是一開始從資料集裡面選擇了三個向量。
步驟二:將每個向量分配到離各自最近的中心點,從而將資料集分成了K個類。
步驟三:計算得到上步得到聚類中每一聚類觀測值的圖心,作為新的均值點。
步驟四:重複步驟三,直至結果收斂,這裡的收斂是指所有點到各自中心點的距離的和收斂。
K-Means演算法的原理比較簡單,但是值得注意的是,有兩個地方是需要演算法使用者去自己選擇的:第一個就是K的值,簡而言之就是資料集應該被分成多少個類,在K-Means演算法裡面是要求先給出K值的; 第二個就是距離函式,即如何計算向量和向量或者向量和中心點之間的距離,這裡也有很多選擇,最常見的自然是歐式距離,也可以用餘弦相似度來作為距離函式,還有其他的一些方法等等。以上兩個需要注意的選擇必然會對聚類結果產生影響,尤其是K值的選擇,這裡就需要演算法使用者根據自身需要來做出仔細衡量。
scikit-learn
scikit-learn 是一個基於Python的Machine Learning模組,裡面給出了很多Machine Learning相關的演算法實現,其中就包括K-Means演算法。安裝的話建議參考scikit-learn的Github Repo, 從軟體包管理裡面裝的似乎都版本比較低,會少一點功能。
在做K-Means聚類之前,我們首先需要對將文字轉化成向量的形式,轉換文字的第一步,自然是分詞,這裡可以直接使用jieba分詞,分完詞過後再將詞轉換成向量,或者說轉換成Bag-of-words模型,這裡可以採用TF-IDF演算法,TF-IDF簡單來說就是從兩個方面對文字中的詞進行加權:
- 詞在當前文字中出現的次數。
- 總文字數包含詞的數目。
前者越高越好,後者越低越好,比如說「你」,「我」這些詞,在一個文字中出現的次數很多,總文字包含這些詞的數目也會很多,那麼這些詞自然無法作為當前文字的代表性內容,如果一個文字中某個詞大量出現,但是在總文字中出現的次數又不多,那麼這個詞對於文字來說很有可能是很重要的。比如在A文字中」喬布斯「這個詞出現了很多次,然後「喬布斯」在其他文本里面又很少出現,那麼A文字主題和「喬布斯」相關的可能性就比較大,我們必然得提高「喬布斯」這個詞在A文字向量中的權重。
在scikit-learn裡面,同樣也實現了TF-IDF演算法,我們可以直接呼叫。
kmeans_demo
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!/usr/bin/env python # -*- coding: utf-8 -*- ''' Author: razrlele Email: [email protected] ''' import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans
def jieba_tokenize(text): return jieba.lcut(text)
tfidf_vectorizer = TfidfVectorizer(tokenizer=jieba_tokenize, \ lowercase=False) ''' tokenizer: 指定分詞函式 lowercase: 在分詞之前將所有的文字轉換成小寫,因為涉及到中文文字處理, 所以最好是False ''' text_list = ["今天天氣真好啊啊啊啊", "小明上了清華大學", \ "我今天拿到了Google的Offer", "清華大學在自然語言處理方面真厲害"] #需要進行聚類的文字集 tfidf_matrix = tfidf_vectorizer.fit_transform(text_list)
num_clusters = 3 km_cluster = KMeans(n_clusters=num_clusters, max_iter=300, n_init=40, \ init='k-means++',n_jobs=-1) ''' n_clusters: 指定K的值 max_iter: 對於單次初始值計算的最大迭代次數 n_init: 重新選擇初始值的次數 init: 制定初始值選擇的演算法 n_jobs: 程序個數,為-1的時候是指預設跑滿CPU 注意,這個對於單個初始值的計算始終只會使用單程序計算, 平行計算只是針對與不同初始值的計算。比如n_init=10,n_jobs=40, 伺服器上面有20個CPU可以開40個程序,最終只會開10個程序 ''' #返回各自文字的所被分配到的類索引 result = km_cluster.fit_predict(tfidf_matrix)
print "Predicting result: ", result |
程式執行結果:
ZSH
1 2 3 4 5 6 |
python2 kmeans_demo.py Building prefix dict from the default dictionary ... Loading model from cache /tmp/jieba.cache Loading model cost 0.275 seconds. Prefix dict has been built succesfully. Predicting result: [2 0 1 0] |
對擬合結果的持久化
在進行大規模文字聚類的時候,並不是每一次都需要從頭開始計算TF-IDF和K-Means的,有的時候我們只是需要根據之前的資料聚類結果預測新資料,所以我們可以考慮將計算中途的一些資料儲存下來,這樣就可以節省不少的時間,令人感到驚喜的是,我們同樣可以通過scikit-learn的joblib
模組來實現持久化這一過程。
如果有看過scikit-learn官方文件的話應該就可以發現,其實fit_transform
和fit_predict
只是將fit
和transform
,以及fit
和predicti
兩個方法寫在了一起,也就是說在上面程式碼中:
Python
1 2 3 4 5 6 7 8 9 |
tfidf_matrix = tfidf_vectorizer.fit_transform(text_list) #上面一行程式碼等價於下面兩行程式碼 tfidf_vectorizer.fit(text_list) tfidf_matrix = tfidf_vectorizer.transform(text_list)
result = km_cluster.fit_predict(tfidf_matrix) #上面一行程式碼等價於下面兩行程式碼 km_cluster.fit(tfidf_matrix) result = km_cluster.predict(tfidf_matrix) |
每一次fit
都是對資料進行擬合操作,所以我們可以直接選擇將擬合結果持久化,然後預測的時候直接載入,進而節省時間。
Python
1 2 3 4 5 6 7 8 |
from sklearn.externals import joblib
joblib.dump(tfidf_vectorizer, 'tfidf_fit_result.pkl') joblib.dump(km_cluster, 'km_cluster_fit_result.pkl')
#程式下一次則可以直接load tfidf_vectorizer = joblib.load('tfidf_fit_result.pkl') km_cluster = joblib.load('km_cluster_fit_result.pkl') |