Python數模筆記-Sklearn(2)樣本聚類分析

youcans發表於2021-05-10

1、分類的分類

  分類的分類?沒錯,分類也有不同的種類,而且在數學建模、機器學習領域常常被混淆。

  首先我們談談有監督學習(Supervised learning)和無監督學習(Unsupervised learning),是指有沒有老師,有沒有紀委嗎?差不多。有老師,就有正確解法,就有標準答案;有紀委,就會樹學習榜樣,還有反面教材。

  有監督學習,是指樣本資料已經給出了正確的分類,我們通過對正確分類的樣本資料進行學習,從中總結規律,獲取知識,付諸應用。所以,監督學習的樣本資料,既提供了特徵值又提供了目標值,通過迴歸(Regression)、分類(Classification)學習特徵與目標之間的關係。迴歸是針對連續變數、連續資料,分類是針對離散變數和布林變數(0-1)。

  無監督學習,是指樣本資料沒有提供確定的分類屬性,沒有老師,沒有標準答案,樣本資料中只有樣本的特徵值而沒有目標值,只能通過樣本資料自身的特徵一邊摸索一邊自我學習,通過聚類(Clustering)方法來尋找和認識物件的相似性。

  所以,我們說到分類時,其實有時是指分類(Classification),有時則是指聚類(Clustering)。

  有監督學習有老師,就有正確答案。雖然有時也會有模糊地帶,但總體說來還是有判定標準、有是非對錯的,只要與標準答案不一致就會被認為判斷錯誤。

  無監督學習則不同,可以有不同的分類方法、不同的分類結果,通常只有相對的好壞而沒有絕對的對錯。甚至連分類結果的好壞也是相對的,要根據實際需要實際情況進行綜合考慮,才能評價分類結果的好壞。誰能說人應該分幾類,怎麼分更合理呢?


2、聚類分析

2.1 聚類的分類

  聚類是從資料分析的角度,對大量的、多維的、無標記的樣本資料集,按照樣本資料自身的相似性對資料集進行分類。大量,是指樣本的數量大;多維,是指每個樣本都有很多特徵值;無標記,是指樣本資料對於每個樣本沒有指定的類別屬性。

  需要說明的是,有時樣本資料帶有一個或多個分類屬性,但那並不是我們所要研究的類別屬性,才會被稱為無監督學習。比如說,體能訓練資料集中每個樣本都有很多特徵資料,包括性別、年齡,也包括體重、腰圍、心率和血壓。性別、年齡顯然都是樣本的屬性,我們也可以根據性別屬性把樣本集分為男性、女性兩類,這當然是有監督學習;但是,如果我們是打算研究這些樣本的生理變化與鍛鍊的關係,這是性別就不定是唯一的分類屬性,甚至不一定是相關的屬性了,從這個意義上說,樣本資料中並沒有給出我們所要進行分類的類別屬性。

  至於聚類的分類,是針對研究物件的不同來說的。把樣本集的行(rows)作為物件,考察樣本的相似度,將樣本集分成若干類,稱為 Q型聚類分析,屬於樣本分類。把樣本集的列(columns)作為物件,考察各個特徵變數之間的關聯程度,按照變數的相關性聚合為若干類,稱為 R型聚類分析,屬於因子分析。

2.2 Q型聚類分析(樣本聚類)

  Q 型聚類分析考察樣本的相似度,將樣本集分成若干類。我們需要綜合考慮樣本各種特徵變數的數值或型別,找到一種分類方法將樣本集分為若干類,使每一類的樣本之間具有較大的相似性,又與其它類的樣本具有較大的差異性。通常是根據不同樣本之間的距離遠近進行劃分,距離近者分為一類,距離遠者分成不同類,以達到“同類相似,異類相異”。
  按照相似性分類,首先就要定義什麼是相似。對於任意兩個樣本,很容易想到以樣本間的距離作為衡量相似性的指標。在一維空間中兩點間的距離是絕對值:d(a,b)=abs[x(a)-x(b)];二維空間中兩點間的距離,我們最熟悉的是歐幾里德(Euclid)距離:d(a,b)=sqrt[(x1(a)-x1(b))**2+(x2(a)-x2(b))**2],歐式距離也可以擴充到多維空間。
  除了歐式距離之外,還有其它度量樣本間距的方案,例如閔可夫斯基距離(Minkowski)、切比雪夫距離(Chebyshev)、馬氏距離(Mahalanobis)等。這些距離的定義、公式和使用條件,本文就不具體介紹了。世界是豐富多彩的,問題是多種多樣的,對於特殊問題有時就要針對其特點採用特殊的解決方案。
  進而,對於兩組樣本G1、G2,也需要度量類與類之間的相似性程度。常用的方法有最短距離法(Nearest Neighbor or Single Linkage Method)、最長距離法(Farthest Neighbor or Complete Linkage Method)、重心法(Centroid Method)、類均值法(Group Average Method)、離差平方和法(Sum of Squares Method)。
  另外,處理實際問題時,在計算距離之前要對資料進行標準化、歸一化,解決不同特徵之間的統一量綱、均衡權重。


3、SKlearn 中的聚類方法

  SKlearn 工具包提供了多種聚類分析演算法:原型聚類方法(Prototype)、密度聚類方法(Density)、層次聚類方法(Hierarchical)、模型聚類(Model),等等,原型聚類方法又包括 k均值演算法(K-Means)、學習向量量化演算法(LVQ)、高斯混合演算法(Gaussian Mixture)。詳見下表。

  為什麼會有這麼多方法和演算法呢?因為特殊問題需要針對其特點採用特殊的解決方案。看看下面這張圖,就能理解這句話了,也能理解各種演算法都是針對哪種問題的。SKlearn 還提供了十多個聚類評價指標,本文就不再展開介紹了。


4、K-均值(K-Means)聚類演算法

  K-均值聚類演算法,是最基礎的、應用最廣泛的聚類演算法,也是最快速的聚類演算法之一。

4.1 原理和過程

  K-均值聚類演算法以最小化誤差函式為目標將樣本資料集分為 K類。

  K-均值聚類演算法的計算過程如下:

  • 設定 K 個類別的中心的初值;
  • 計算每個樣本到 K箇中心的距離,按最近距離進行分類;
  • 以每個類別中樣本的均值,更新該類別的中心;
  • 重複迭代以上步驟,直到達到終止條件(迭代次數、最小平方誤差、簇中心點變化率)。

  K-均值聚類演算法的優點是原理簡單、演算法簡單,速度快,聚類效果極好,對大資料集具有很好的伸縮性。這些優點特別有利於初學者、常見問題。其缺點是需要給定 K值,對一些特殊情況(如非凸簇、特殊值、簇的大小差別大)的效能不太好。怎麼看這些缺點?需要給定 K值的問題是有解決方法的;至於特殊情況,已經跟我們沒什麼關係了。

4.2 SKlearn 中 K-均值演算法的使用

  sklearn.cluster.KMeans 類是 K-均值演算法的具體實現,官網介紹詳見:https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#sklearn.cluster.KMeans
  

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

KMeans 的主要引數:

  • n_clusters: int,default=8  K值,給定的分類數量,預設值 8。
  • init:{‘k-means++’, ‘random’}  初始中心的選擇方式,預設'K-means++'是優化值,也可以隨機選擇或自行指定。
  • n_init:int, default=10  以不同的中心初值多次執行,以降低初值對演算法的影響。預設值 10。
  • max_iter:int, default=300  最大迭代次數。預設值 300。
  • algorithm:{“auto”, “full”, “elkan”}, default=”auto”  演算法選擇,"full"是經典的 EM演算法,"elkan"能快速處理定義良好的簇,預設值 “auto"目前採用"elkan"。

KMeans 的主要屬性:

  • clustercenters:每個聚類中心的座標
  • labels_: 每個樣本的分類結果
  • inertia_: 每個點到所屬聚類中心的距離之和。

4.3 sklearn.cluster.KMeans 用法例項

from sklearn.cluster import KMeans  # 匯入 sklearn.cluster.KMeans 類
import numpy as np
X = np.array([[1,2], [1,4], [1,0], [10,2], [10,4], [10,0]])
kmCluster = KMeans(n_clusters=2).fit(X)  # 建立模型並進行聚類,設定 K=2
print(kmCluster.cluster_centers_)  # 返回每個聚類中心的座標
#[[10., 2.], [ 1., 2.]]  # print 顯示聚類中心座標
print(kmCluster.labels_)  # 返回樣本集的分類結果
#[1, 1, 1, 0, 0, 0]  # print 顯示分類結果
print(kmCluster.predict([[0, 0], [12, 3]]))  # 根據模型聚類結果進行預測判斷
#[1, 0]  # print顯示判斷結果:樣本屬於哪個類別

  例程很簡單,又給了詳細註釋,就不再解讀了。核心程式就是下面這句:

  kMeanModel = KMeans(n_clusters=2).fit(X)

4.4 針對大樣本集的改進演算法:Mini Batch K-Means

  對於樣本集巨大的問題,例如樣本量大於 10萬、特徵變數大於100,K-Means演算法耗費的速度和記憶體很大。SKlearn 提供了針對大樣本集的改進演算法 Mini Batch K-Means,並不使用全部樣本資料,而是每次抽樣選取小樣本集進行 K-Means聚類,進行迴圈迭代。Mini Batch K-Means 雖然效能略有降低,但極大的提高了執行速度和記憶體佔用。    
  class sklearn.cluster.MiniBatchKMeans 類是演算法的具體實現,官網介紹詳見:https://scikit-learn.org/stable/modules/generated/sklearn.cluster.MiniBatchKMeans.html#sklearn.cluster.MiniBatchKMeans
  

class sklearn.cluster.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)

MiniBatchKMeans 與 KMeans不同的主要引數是:

  • batch_size: int, default=100   抽樣集的大小。預設值 100。

  Mini Batch K-Means 的用法例項如下:

from sklearn.cluster import MiniBatchKMeans # 匯入 .MiniBatchKMeans 類
import numpy as np
X = np.array([[1,2], [1,4], [1,0], [4,2], [4,0], [4,4],
              [4,5], [0,1], [2,2],[3,2], [5,5], [1,-1]])
# fit on the whole data
mbkmCluster = MiniBatchKMeans(n_clusters=2,batch_size=6,max_iter=10).fit(X)
print(mbkmCluster.cluster_centers_) # 返回每個聚類中心的座標
# [[3.96,2.41], [1.12,1.39]] # print 顯示內容
print(mbkmCluster.labels_)  # 返回樣本集的分類結果
#[1 1 1 0 0 0 0 1 1 0 0 1]  # print 顯示內容
print(mbkmCluster.predict([[0,0], [4,5]]))  # 根據模型聚類結果進行預測判斷
#[1, 0]  # 顯示判斷結果:樣本屬於哪個類別

5、K-均值演算法例程

5.1 問題描述

-  聚類分析案例—我國各地區普通高等教育發展狀況分析,本問題及資料來自:司守奎、孫兆亮,數學建模演算法與應用(第2版),國防工業出版社。
  問題的原始資料來自《中國統計年鑑,1995》和《中國教育統計年鑑,1995》,給出了各地區10 項教育發展資料。我國各地區普通高等教育的發展狀況存在較大的差異,請根據資料對我國各地區普通高等教育的發展狀況進行聚類分析。

5.2 Python 程式


# Kmeans_sklearn_v1d.py
# K-Means cluster by scikit-learn for problem "education2015"
# v1.0d: K-Means 聚類演算法(SKlearn)求解:各地區高等教育發展狀況-2015 問題
# 日期:2021-05-10

#  -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans, MiniBatchKMeans

# 主程式
def main():
    # 讀取資料檔案
    readPath = "../data/education2015.xlsx"  # 資料檔案的地址和檔名
    dfFile = pd.read_excel(readPath, header=0)  # 首行為標題行
    dfFile = dfFile.dropna()  # 刪除含有缺失值的資料
    # print(dfFile.dtypes)  # 檢視 df 各列的資料型別
    # print(dfFile.shape)  # 檢視 df 的行數和列數
    print(dfFile.head())

    # 資料準備
    z_scaler = lambda x:(x-np.mean(x))/np.std(x)  # 定義資料標準化函式
    dfScaler = dfFile[['x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']].apply(z_scaler)  # 資料歸一化
    dfData = pd.concat([dfFile[['地區']], dfScaler], axis=1)  # 列級別合併
    df = dfData.loc[:,['x1','x2','x3','x4','x5','x6','x7','x8','x9','x10']]  # 基於全部 10個特徵聚類分析
    # df = dfData.loc[:,['x1','x2','x7','x8','x9','x10']]  # 降維後選取 6個特徵聚類分析
    X = np.array(df)  # 準備 sklearn.cluster.KMeans 模型資料
    print("Shape of cluster data:", X.shape)

    # KMeans 聚類分析(sklearn.cluster.KMeans)
    nCluster = 4
    kmCluster = KMeans(n_clusters=nCluster).fit(X)  # 建立模型並進行聚類,設定 K=2
    print("Cluster centers:\n", kmCluster.cluster_centers_)  # 返回每個聚類中心的座標
    print("Cluster results:\n", kmCluster.labels_)  # 返回樣本集的分類結果

    # 整理聚類結果
    listName = dfData['地區'].tolist()  # 將 dfData 的首列 '地區' 轉換為 listName
    dictCluster = dict(zip(listName,kmCluster.labels_))  # 將 listName 與聚類結果關聯,組成字典
    listCluster = [[] for k in range(nCluster)]
    for v in range(0, len(dictCluster)):
        k = list(dictCluster.values())[v]  # 第v個城市的分類是 k
        listCluster[k].append(list(dictCluster.keys())[v])  # 將第v個城市新增到 第k類
    print("\n聚類分析結果(分為{}類):".format(nCluster))  # 返回樣本集的分類結果
    for k in range(nCluster):
        print("第 {} 類:{}".format(k, listCluster[k]))  # 顯示第 k 類的結果

    return

if __name__ == '__main__':
    main()

5.3 程式執行結果

   地區    x1   x2   x3    x4   x5   x6     x7    x8    x9    x10
0  北京  5.96  310  461  1557  931  319  44.36  2615  2.20  13631
1  上海  3.39  234  308  1035  498  161  35.02  3052  0.90  12665
2  天津  2.35  157  229   713  295  109  38.40  3031  0.86   9385
3  陝西  1.35   81  111   364  150   58  30.45  2699  1.22   7881
4  遼寧  1.50   88  128   421  144   58  34.30  2808  0.54   7733
Shape of cluster data: (30, 10)
Cluster centers:
 [[ 1.52987871  2.10479182  1.97836141  1.92037518  1.54974999  1.50344182
   1.13526879  1.13595799  0.83939748  1.38149832]
 [-0.32558635 -0.28230636 -0.28071191 -0.27988803 -0.28228409 -0.28494074
   0.01965142  0.09458383 -0.26439737 -0.31101153]
 [ 4.44318512  3.9725159   4.16079449  4.20994153  4.61768098  4.65296699
   2.45321197  0.4021476   4.22779099  2.44672575]
 [ 0.31835808 -0.56222029 -0.54985976 -0.52674552 -0.33003935 -0.26816609
  -2.60751756 -2.51932966  0.35167418  1.28278289]]
Cluster results:
 [2 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 3]

聚類分析結果(分為4類):
第 0 類:['上海', '天津']
第 1 類:['陝西', '遼寧', '吉林', '黑龍江', '湖北', '江蘇', '廣東', '四川', '山東', '甘肅', '湖南', '浙江', '新疆', '福建', '山西', '河北', '安徽', '雲南', '江西', '海南', '內蒙古', '河南', '廣西', '寧夏', '貴州']
第 2 類:['北京']
第 3 類:['西藏', '青海']

版權說明:
本文中案例問題來自:司守奎、孫兆亮,數學建模演算法與應用(第2版),國防工業出版社。
本文內容及例程為作者原創,並非轉載書籍或網路內容。

YouCans 原創作品
Copyright 2021 YouCans, XUPT
Crated:2021-05-09

相關文章