K-Means 是一種最經典和常用的聚類方法。它通過多輪迭代的方式不斷更新不同類樣本的中心,計算樣本到每個中心的距離,然後更新樣本所屬的類。最終能夠把樣本劃分到 K 個類中。本案例中,我們首先使用 Python 實現 K-Means 算法,基於一份隨機數據集,使用動畫演示聚類過程和優化目標的變化。然後將 K-Means 應用於圖像分割問題。最後我們還將使用一份中文新聞數據集,用 K-Means 算法進行自動新聞主題聚類,並使用柱狀圖和詞雲圖對聚類結果進行可視化分析。
1 實現 K-Means 算法K-Means 算法的基本運行流程為:
在整個算法中,2.1 步驟運算量最大,因為該步驟需要計算每一個樣本到
1.1 使用 iterrows 遍歷的方式實現假設我們使用歐式距離計算樣本到中心的距離。對於樣本
使用最簡單的方式來實現,先用一個函數 point_dist 計算一個樣本到中心的距離。這裡我們使用 Numpy 的線性代數模塊 linalg 中的 norm 方法。
然後使用 iterrows 方法遍歷樣本計算樣本到中心的距離,定義 k_means_iterrows 方法實現 K-Means 算法。
用一個簡單的隨機數據集來測試時間性能。Sklearn 中的 datasets 模塊的 make_blobs 函數能夠自動生成一些供測試聚類算法的隨機數據集。它能夠根據輸入的參數生成數據集和對應的類標籤。常用的參數如下表:
參數含義說明n_samples需要生成的樣本數量,默認值100n_features特徵數量,默認值是2centers數據的中心點數量,默認值3cluster_std數據集的標準差,浮點數或者浮點數序列,默認值1.0center_box中心確定之後的數據邊界,默認值(-10.0, 10.0)shuffle打亂樣本順序,默認值是Truerandom_state官網解釋是隨機生成器的種子在該數據集上用我們實現的 k_means1 方法運行 K-Means 聚類。使用 iPython 提供的魔法命令 %time 記錄運行時間。
CPU times: user 5.16 s, sys: 39.3 ms, total: 5.19 s Wall time: 5.14 s
1.2 使用 apply 遍歷的方式實現提高運算效率,可以使用 DataFrame 的 apply 函數,它可以對數據框中的每一行執行一個複雜的函數。在我們的例子中,是計算每一行與每一個中心的距離。
CPU times: user 3.24 s, sys: 19.9 ms, total: 3.26 s Wall time: 3.23 s
1.3 矩陣運算的方式實現數據集表示成
已經聚類中心,計算樣本到中心距離,並將樣本劃分到距離最小的類的流程如下圖所示。
png使用 Numpy 實現上述計算流程的代碼為:
得到樣本的類標籤後,聚類中心的更新流程為:1)根據類標籤對樣本進行分組;2)將聚類中心更新為每一組樣本的均值。Python 實現的代碼為:
現在我們更新 K-Means 算法的實現,函數名為 k_means。
CPU times: user 150 ms, sys: 0 ns, total: 150 ms Wall time: 149 ms
1.4 聚類結果可視化下面我們使用一份隨機生成的二維數據集,使用我們上一小節實現的 k_means 完成聚類,然後使用不同顏色標註不同類的樣本以及類中心。
Text(0, 0.5, '
1.5 使用動畫展示 K-Means 聚類過程要動態展示 K-Means 聚類過程,我們需要在每一步迭代中記錄每一個類的中心,以及每一個類的樣本集合。創建 k_means_steps,在完成聚類的同時,將每一步迭代的每類樣本和中心返回。
我們可以藉助 matplotlib.animation 動畫模塊來實現下面的 init_draw 函數是動畫最開始時繪製的內容,包含數據。update_draw 則是每次更新的內容。
1.6 失真度量 J 的變化我們來查看隨著迭代的進行,K-Means聚類模型的優化目標,即失真度量
在隨機數據上進行聚類,並將失真度量的變化以折線圖的形式繪製出來。
Text(0.5, 1.0, 'K-Means算法優化目標的變化')
2 使用 K-Means 算法進行圖像分割我們先加載一張測試圖片,將圖片列印出來。使用 PIL.Image.open 方法來打開圖片,然後使用 matplotlib 中的 imshow 方法將圖片可視化。
(-0.5, 599.5, 514.5, -0.5)
將一張圖片轉換成表格形式。每一行為一個像素,三列分別為像素的 R,B,G取值。獲取圖片的每一個像素
列印轉換的數據框如下:
RBG02472482431247248243224724824332472482434247248243600 515 309000 309000
使用我們在上一節實現的 K-Means 算法對像素進行聚類。
將生成的灰度圖可視化,對圖像可視化使用 plt.imshow 方法。
(-0.5, 599.5, 514.5, -0.5)
實現一個函數 img_from_labels ,將像素聚類類別標籤,轉換成一張灰度圖。
調整聚類數量
3 使用 K-Means 進行中文新聞聚類首先,我們使用 Pandas 的 read_csv 方法將中文新聞語料讀取進來, encoding 參數和 sep 參數分別設置為 "utf8" 和 "\t" 。
分類分詞文章0科技「 一路 聽 天下 」 開拓 「 無聊 經濟 」 在 納斯達克 風光 上市 的 分眾傳媒...1旅遊中國 赴美 旅遊 首發 團 6 月 1 7 日 啟程 報價 兩萬左右 六月 初 的 ...2新聞科學家 教育家 蒙特 梭利 :給 孩子 愛 與 自由 ( 圖 ) 蒙特 梭利 和 「 ...3教育7 名 大學生 作出 「 震後 恢復 建議 」 獲 國務院 肯定 大學生 災後 重建...4教育最牛 高 考生 :重慶 學生 稱 「 押 中 」 今年 作文題 最牛 鼓勁 :數學考...汽車 544
3.1 將新聞表示成向量格式
科技 511
旅遊 510
健康 492
文化 491
房地產 480
財經 469
新聞 458
體育 437
教育 437
娛樂 370
女人 322
Name: 分類, dtype: int64使用 sklearn.feature_extraction.text 模塊的 TfidfVectorizer,將詞列表格式的新聞轉換成向量形式。向量中的每一個維度代表字典中的一個詞,維度的取值代表詞在對應文檔中的 TF-IDF 取值。
3.2 使用 k_means 方法對新聞進行聚類在真實的文本應用中,文本表示成向量後,由於詞典的詞通常會很大,我們得到的是稀疏矩陣。例如上一步 news_vectors 就是一個稀疏存儲格式。
scipy.sparse.csr.csr_matrix
在本案例中,為了簡便我們直接使用 todense 方法將稀疏向量轉換成稠密矩陣。(!注意,實際中很少這麼操作!)
現在我們可以直接使用第一節實現的 k_means 方法對新聞進行聚類,這裡我們將聚類個數設置成 12 。
在 news 中新建 labels 列保存將得到的樣本聚類結果。
3.3 使用柱狀圖查看新聞聚類的主題分布在新聞數據集中,由於 分類 一列已經標註了新聞的主題類別,我們可以聚類得到的每一組新聞中的主題分布,從而定性地觀察聚類效果。將聚類結果按照聚類結果進行分組,可以使用 DataFrame 的 groupby 方法。然後使用 value_counts 方法統計每一組的不同主題的新聞數量分布。
從上圖可以看到,部分分組對應於特定主題,然而在另一上部分分組中,不同主題的新聞分布較為分散。
3.4 使用詞雲圖查看新聞聚類結果首先,使用 groupby 方法,根據聚類結果對新聞進行分組。
將 wordcloud 模塊導入詞雲類 WordCloud,新建一個詞雲對象 cloud 。為了顯示效果,構造詞雲對象時需要設置停用詞詞表。由於我們顯示的是中文,為了避免亂碼,還需要制定顯示字體。其中字體文件為 ./input/simfang.ttf 。
遍歷每一個聚類的新聞樣本,得到新聞內容的內容,然後顯示成詞雲圖。
可以看到,我們可以直接根據詞雲圖對得到的聚類進行解釋。
4 總結本案例中我們使用首先使用三種方式實現了 K-Means 算法並對不同實現的時間性能進行了對比,結果發現向量化實現能夠大大提高運行效率。然後我們使用 K-Means 算法進行圖像分割,展示了不同 K 取值下生成的灰度圖的變化。最後,我們使用 K-Means 在一份中文新聞數據集進行了主題聚類。
本案例使用的主要 Python 工具如下:
工具包用途NumPy矩陣運算Pandas數據讀取與預處理Matplotlib數據集可視化、聚類結果動畫Sklearn中文新聞的向量化Wordcloud繪製聚類結果詞雲圖