【菜菜的sklearn】06 聚類算法

小伙伴們大家好~o( ̄▽ ̄)ブ
我是菜菜,這里是我的sklearn課堂第6期:sklearn中的聚類算法K-Means~

我的開(kāi)發(fā)環(huán)境是Jupyter lab,所用的庫(kù)和版本大家參考:
Python 3.7.1(你的版本至少要3.4以上
Scikit-learn 0.20.1 (你的版本至少要0.20
Numpy 1.15.4, Pandas 0.23.4, Matplotlib 3.0.2, SciPy 1.1.0

本文主要內(nèi)容:
?1 概述
??1.1 無(wú)監(jiān)督學(xué)習(xí)與聚類算法
??1.2 sklearn中的聚類算法
?2 KMeans
??2.1 KMeans是如何工作的
??2.2 簇內(nèi)誤差平方和的定義和解惑
?3 sklearn.cluster.KMeans
??3.1 重要參數(shù)n_clusters
???3.1.1 先進(jìn)行一次聚類看看吧
???3.1.2 聚類算法的模型評(píng)估指標(biāo)
???3.1.3 案例:基于輪廓系數(shù)來(lái)選擇n_clusters

1 概述

1.1 無(wú)監(jiān)督學(xué)習(xí)與聚類算法

在過(guò)去的五周之內(nèi),我們學(xué)習(xí)了決策樹(shù)摊崭,隨機(jī)森林操骡,邏輯回歸,他們雖然有著不同的功能藕赞,但卻都屬于“有監(jiān)督學(xué)習(xí)”的一部分,即是說(shuō)卖局,模型在訓(xùn)練的時(shí)候斧蜕,即需要特征矩陣X,也需要真實(shí)標(biāo)簽y砚偶。機(jī)器學(xué)習(xí)當(dāng)中批销,還有相當(dāng)一部分算法屬于“無(wú)監(jiān)督學(xué)習(xí)”洒闸,無(wú)監(jiān)督的算法在訓(xùn)練的時(shí)候只需要特征矩陣X,不需要標(biāo)簽均芽。我們?cè)?jīng)學(xué)過(guò)的PCA降維算法就是無(wú)監(jiān)督學(xué)習(xí)中的一種丘逸,聚類算法,也是無(wú)監(jiān)督學(xué)習(xí)的代表算法之一掀宋。

聚類算法又叫做“無(wú)監(jiān)督分類”深纲,其目的是將數(shù)據(jù)劃分成有意義或有用的組(或簇)。這種劃分可以基于我們的業(yè)務(wù)需求或建模需求來(lái)完成劲妙,也可以單純地幫助我們探索數(shù)據(jù)的自然結(jié)構(gòu)和分布湃鹊。比如在商業(yè)中,如果我們手頭有大量的當(dāng)前和潛在客戶的信息镣奋,我們可以使用聚類將客戶劃分為若干組币呵,以便進(jìn)一步分析和開(kāi)展?fàn)I銷活動(dòng),最有名的客戶價(jià)值判斷模型RFM侨颈,就常常和聚類分析共同使用余赢。再比如,聚類可以用于降維和矢量量化(vector quantization)哈垢,可以將高維特征壓縮到一列當(dāng)中妻柒,常常用于圖像,聲音耘分,視頻等非結(jié)構(gòu)化數(shù)據(jù)举塔,可以大幅度壓縮數(shù)據(jù)量。

  • 聚類vs分類
聚類 分類
核心 將數(shù)據(jù)分成多個(gè)組
探索每個(gè)組的數(shù)據(jù)是否有聯(lián)系
從已經(jīng)分組的數(shù)據(jù)中去學(xué)習(xí)
把新數(shù)據(jù)放到已經(jīng)分好的組中去
學(xué)習(xí)類型 無(wú)監(jiān)督陶贼,無(wú)需標(biāo)簽進(jìn)行訓(xùn)練 有監(jiān)督,需要標(biāo)簽進(jìn)行訓(xùn)練
典型算法 K-Means待秃,DBSCAN拜秧,層次聚類,光譜聚類 決策樹(shù)章郁,貝葉斯枉氮,邏輯回歸
算法輸出 聚類結(jié)果是不確定的
不一定總是能夠反映數(shù)據(jù)的真實(shí)分類
同樣的聚類,根據(jù)不同的業(yè)務(wù)需求
可能是一個(gè)好結(jié)果暖庄,也可能是一個(gè)壞結(jié)果
分類結(jié)果是確定的
分類的優(yōu)劣是客觀的
不是根據(jù)業(yè)務(wù)或算法需求決定

1.2 sklearn中的聚類算法

聚類算法在sklearn中有兩種表現(xiàn)形式聊替,一種是類(和我們目前為止學(xué)過(guò)的分類算法以及數(shù)據(jù)預(yù)處理方法們都一樣),需要實(shí)例化培廓,訓(xùn)練并使用接口和屬性來(lái)調(diào)用結(jié)果惹悄。另一種是函數(shù)(function),只需要輸入特征矩陣和超參數(shù)肩钠,即可返回聚類的結(jié)果和各種指標(biāo)泣港。

  • 輸入數(shù)據(jù)

需要注意的一件重要事情是暂殖,該模塊中實(shí)現(xiàn)的算法可以采用不同類型的矩陣作為輸入。 所有方法都接受形狀[n_samples当纱,n_features]的標(biāo)準(zhǔn)特征矩陣呛每,這些可以從sklearn.feature_extraction模塊中的類中獲得。對(duì)于親和力傳播坡氯,光譜聚類和DBSCAN晨横,還可以輸入形狀[n_samples,n_samples]的相似性矩陣箫柳,我們可以使用sklearn.metrics.pairwise模塊中的函數(shù)來(lái)獲取相似性矩陣手形。

2 KMeans

2.1 KMeans是如何工作的

作為聚類算法的典型代表,KMeans可以說(shuō)是最簡(jiǎn)單的聚類算法沒(méi)有之一滞时,那它是怎么完成聚類的呢叁幢?

關(guān)鍵概念:簇與質(zhì)心
KMeans算法將一組N個(gè)樣本的特征矩陣X劃分為K個(gè)無(wú)交集的簇,直觀上來(lái)看是簇是一組一組聚集在一起的數(shù)據(jù)坪稽,在一個(gè)簇中的數(shù)據(jù)就認(rèn)為是同一類曼玩。簇就是聚類的結(jié)果表現(xiàn)。 簇中所有數(shù)據(jù)的均值\mu_j通常被稱為這個(gè)簇的“質(zhì)心”(centroids)窒百。在一個(gè)二維平面中黍判,一簇?cái)?shù)據(jù)點(diǎn)的質(zhì)心的橫坐標(biāo)就是這一簇?cái)?shù)據(jù)點(diǎn)的橫坐標(biāo)的均值,質(zhì)心的縱坐標(biāo)就是這一簇?cái)?shù)據(jù)點(diǎn)的縱坐標(biāo)的均值篙梢。同理可推廣至高維空間顷帖。

在KMeans算法中,簇的個(gè)數(shù)K是一個(gè)超參數(shù)渤滞,需要我們?nèi)藶檩斎雭?lái)確定贬墩。KMeans的核心任務(wù)就是根據(jù)我們?cè)O(shè)定好的K,找出K個(gè)最優(yōu)的質(zhì)心妄呕,并將離這些質(zhì)心最近的數(shù)據(jù)分別分配到這些質(zhì)心代表的簇中去陶舞。具體過(guò)程可以總結(jié)如下:

順序 過(guò)程
1 隨機(jī)抽取K個(gè)樣本作為最初的質(zhì)心
2 開(kāi)始循環(huán):
2.1 將每個(gè)樣本點(diǎn)分配到離他們最近的質(zhì)心,生成K個(gè)簇
2.2 對(duì)于每個(gè)簇绪励,計(jì)算所有被分到該簇的樣本點(diǎn)的平均值作為新的質(zhì)心
3 當(dāng)質(zhì)心的位置不再發(fā)生變化肿孵,迭代停止,聚類完成

那什么情況下疏魏,質(zhì)心的位置會(huì)不再變化呢构回?當(dāng)我們找到一個(gè)質(zhì)心猜拾,在每次迭代中被分配到這個(gè)質(zhì)心上的樣本都是一致的武翎,即每次新生成的簇都是一致的拓瞪,所有的樣本點(diǎn)都不會(huì)再?gòu)囊粋€(gè)簇轉(zhuǎn)移到另一個(gè)簇,質(zhì)心就不會(huì)變化了。

這個(gè)過(guò)程在可以由下圖來(lái)顯示眉抬,我們規(guī)定贯吓,將數(shù)據(jù)分為4簇(K=4),其中白色X代表質(zhì)心的位置:

在數(shù)據(jù)集下多次迭代(iteration)蜀变,模型就會(huì)收斂悄谐。第六次迭代之后,基本上質(zhì)心的位置就不再改變了库北,生成的簇也變得穩(wěn)定爬舰。此時(shí)我們的聚類就完成了,我們可以明顯看出寒瓦,KMeans按照數(shù)據(jù)的分布情屹,將數(shù)據(jù)聚集成了我們規(guī)定的4類,接下來(lái)我們就可以按照我們的業(yè)務(wù)需求或者算法需求杂腰,對(duì)這四類數(shù)據(jù)進(jìn)行不同的處理垃你。

2.2 簇內(nèi)誤差平方和的定義和解惑

聚類算法聚出的類有什么含義呢?這些類有什么樣的性質(zhì)喂很?我們認(rèn)為惜颇,被分在同一個(gè)簇中的數(shù)據(jù)是有相似性的,而不同簇中的數(shù)據(jù)是不同的少辣,當(dāng)聚類完畢之后凌摄,我們就要分別去研究每個(gè)簇中的樣本都有什么樣的性質(zhì),從而根據(jù)業(yè)務(wù)需求制定不同的商業(yè)或者科技策略漓帅。這個(gè)聽(tīng)上去和我們?cè)谏现艿脑u(píng)分卡案例中講解的“分箱”概念有些類似锨亏,即我們分箱的目的是希望,一個(gè)箱內(nèi)的人有著相似的信用風(fēng)險(xiǎn)忙干,而不同箱的人的信用風(fēng)險(xiǎn)差異巨大器予,以此來(lái)區(qū)別不同信用度的人,因此我們追求“組內(nèi)差異小捐迫,組間差異大”乾翔。聚類算法也是同樣的目的,我們追求“簇內(nèi)差異小弓乙,簇外差異大”末融。而這個(gè)“差異“钧惧,由樣本點(diǎn)到其所在簇的質(zhì)心的距離來(lái)衡量暇韧。

對(duì)于一個(gè)簇來(lái)說(shuō),所有樣本點(diǎn)到質(zhì)心的距離之和越小浓瞪,我們就認(rèn)為這個(gè)簇中的樣本越相似懈玻,簇內(nèi)差異就越小。而距離的衡量方法有多種乾颁,令x表示簇中的一個(gè)樣本點(diǎn)涂乌,\mu表示該簇中的質(zhì)心艺栈,n表示每個(gè)樣本點(diǎn)中的特征數(shù)目,i表示組成點(diǎn)x的每個(gè)特征湾盒,則該樣本點(diǎn)到質(zhì)心的距離可以由以下距離來(lái)度量:

\begin{equation} \begin{aligned} 歐幾里得距離:d(x,\mu) &= \sqrt{\sum_{i=1}^{n}(x_i - \mu_i)^2}\\曼哈頓距離:d(x,\mu) &= \sum_{i=1}^{n}(|x_i - \mu|) \\ 余弦距離:cos\theta &= \frac{\sum_{1}^{n}(x_i * \mu)}{\sqrt{\sum_1^n(x_i)^2}*\sqrt{\sum_{1}^{n}(\mu)^2}} \end{aligned} \end{equation}

如我們采用歐幾里得距離湿右,則一個(gè)簇中所有樣本點(diǎn)到質(zhì)心的距離的平方和為:

\begin{aligned} Cluster\ Sum\ of\ Square(CSS) &= \sum_{j=0}^{m}\sum_{i=1}^{n}(x_i - \mu_i)^2 \\Total\ Cluster\ Sum\ of\ Square &=\sum_{l=1}^{k}CSS_l \end{aligned}

其中,m為一個(gè)簇中樣本的個(gè)數(shù)罚勾,j是每個(gè)樣本的編號(hào)毅人。這個(gè)公式被稱為簇內(nèi)平方和(cluster Sum of Square),又叫做Inertia尖殃。而將一個(gè)數(shù)據(jù)集中的所有簇的簇內(nèi)平方和相加丈莺,就得到了整體平方和(Total Cluster Sum of Square),又叫做total inertia送丰。Total Inertia越小缔俄,代表著每個(gè)簇內(nèi)樣本越相似,聚類的效果就越好器躏。因此KMeans追求的是俐载,求解能夠讓Inertia最小化的質(zhì)心。實(shí)際上邀桑,在質(zhì)心不斷變化不斷迭代的過(guò)程中瞎疼,總體平方和是越來(lái)越小的。我們可以使用數(shù)學(xué)來(lái)證明壁畸,當(dāng)整體平方和最小的時(shí)候贼急,質(zhì)心就不再發(fā)生變化了。如此捏萍,K-Means的求解過(guò)程太抓,就變成了一個(gè)最優(yōu)化問(wèn)題。

這是我們?cè)谶@個(gè)課程中第二次遇見(jiàn)最優(yōu)化問(wèn)題令杈,即需要將某個(gè)指標(biāo)最小化來(lái)求解模型中的一部分信息走敌。記得我們?cè)谶壿嫽貧w中式怎么做的嗎?我們?cè)谝粋€(gè)固定的方程y(x) = \frac{1}{1+e{\thetaTx}}中最小化損失函數(shù)來(lái)求解模型的參數(shù)向量\theta逗噩,并且基于參數(shù)向量\theta的存在去使用模型掉丽。而在KMeans中,我們?cè)谝粋€(gè)固定的簇?cái)?shù)K下异雁,最小化總體平方和來(lái)求解最佳質(zhì)心捶障,并基于質(zhì)心的存在去進(jìn)行聚類。兩個(gè)過(guò)程十分相似纲刀,并且项炼,整體距離平方和的最小值其實(shí)可以使用梯度下降來(lái)求解。因此,有許多博客和教材都這樣寫道:簇內(nèi)平方和/整體平方和是KMeans的損失函數(shù)锭部。

解惑:Kmeans有損失函數(shù)嗎暂论?
記得我們?cè)谶壿嫽貧w中曾有這樣的結(jié)論:損失函數(shù)本質(zhì)是用來(lái)衡量模型的擬合效果的,只有有著求解參數(shù)需求的算法拌禾,才會(huì)有損失函數(shù)取胎。Kmeans不求解什么參數(shù),它的模型本質(zhì)也沒(méi)有在擬合數(shù)據(jù)湃窍,而是在對(duì)數(shù)據(jù)進(jìn)行一種探索扼菠。所以如果你去問(wèn)大多數(shù)數(shù)據(jù)挖掘工程師,甚至是算法工程師坝咐,他們可能會(huì)告訴你說(shuō)循榆,K-Means不存在什么損失函數(shù),Inertia更像是Kmeans的模型評(píng)估指標(biāo)墨坚,而非損失函數(shù)秧饮。

但我們類比過(guò)了Kmeans中的Inertia和邏輯回歸中的損失函數(shù)的功能,我們發(fā)現(xiàn)它們確實(shí)非常相似泽篮。所以盗尸,從“求解模型中的某種信息,用于后續(xù)模型的使用“這樣的功能來(lái)看帽撑,我們可以認(rèn)為Inertia是Kmeans中的損失函數(shù)泼各,雖然這種說(shuō)法并不嚴(yán)謹(jǐn)。

對(duì)比來(lái)看亏拉,在決策樹(shù)中扣蜻,我們有衡量分類效果的指標(biāo)準(zhǔn)確度accuracy,準(zhǔn)確度所對(duì)應(yīng)的損失叫做泛化誤差及塘,但我們不能通過(guò)最小化泛化誤差來(lái)求解某個(gè)模型中需要的信息莽使,我們只是希望模型的效果上表現(xiàn)出來(lái)的泛化誤差很小。因此決策樹(shù)笙僚,KNN等算法芳肌,是絕對(duì)沒(méi)有損失函數(shù)的。

大家可以發(fā)現(xiàn)肋层,我們的Inertia是基于歐幾里得距離的計(jì)算公式得來(lái)的亿笤。實(shí)際上,我們也可以使用其他距離栋猖,每個(gè)距離都有自己對(duì)應(yīng)的Inertia净薛。在過(guò)去的經(jīng)驗(yàn)中,我們總結(jié)出不同距離所對(duì)應(yīng)的質(zhì)心選擇方法和Inertia掂铐,在Kmeans中罕拂,只要使用了正確的質(zhì)心和距離組合,無(wú)論使用什么樣的距離全陨,都可以達(dá)到不錯(cuò)的聚類效果:

距離度量 質(zhì)心 Inertia
歐幾里得距離 均值 最小化每個(gè)樣本點(diǎn)到質(zhì)心的歐式距離之和
曼哈頓距離 中位數(shù) 最小化每個(gè)樣本點(diǎn)到質(zhì)心的曼哈頓距離之和
余弦距離 均值 最小化每個(gè)樣本點(diǎn)到質(zhì)心的余弦距離之和

而這些組合爆班,都可以由嚴(yán)格的數(shù)學(xué)證明來(lái)推導(dǎo)。在sklearn當(dāng)中辱姨,我們無(wú)法選擇使用的距離柿菩,只能使用歐式距離。因此雨涛,我們也無(wú)需去擔(dān)憂這些距離所搭配的質(zhì)心選擇是如何得來(lái)的了枢舶。

3 sklearn.cluster.KMeans

class 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=None, algorithm=’auto’)

3.1 重要參數(shù)n_clusters

n_clusters是KMeans中的k,表示著我們告訴模型我們要分幾類替久。這是KMeans當(dāng)中唯一一個(gè)必填的參數(shù)凉泄,默認(rèn)為8類,但通常我們的聚類結(jié)果會(huì)是一個(gè)小于8的結(jié)果蚯根。通常后众,在開(kāi)始聚類之前,我們并不知道n_clusters究竟是多少颅拦,因此我們要對(duì)它進(jìn)行探索蒂誉。

3.1.1 先進(jìn)行一次聚類看看吧

當(dāng)我們拿到一個(gè)數(shù)據(jù)集,如果可能的話距帅,我們希望能夠通過(guò)繪圖先觀察一下這個(gè)數(shù)據(jù)集的數(shù)據(jù)分布右锨,以此來(lái)為我們聚類時(shí)輸入的n_clusters做一個(gè)參考。

首先碌秸,我們來(lái)自己創(chuàng)建一個(gè)數(shù)據(jù)集绍移。這樣的數(shù)據(jù)集是我們自己創(chuàng)建,所以是有標(biāo)簽的讥电。

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

#自己創(chuàng)建數(shù)據(jù)集
X, y = make_blobs(n_samples=500,n_features=2,centers=4,random_state=1)

fig, ax1 = plt.subplots(1)
ax1.scatter(X[:, 0], X[:, 1]
            ,marker='o' #點(diǎn)的形狀
            ,s=8 #點(diǎn)的大小
           )
plt.show()

#如果我們想要看見(jiàn)這個(gè)點(diǎn)的分布登夫,怎么辦?
color = ["red","pink","orange","gray"]
fig, ax1 = plt.subplots(1)

for i in range(4):
    ax1.scatter(X[y==i, 0], X[y==i, 1]
            ,marker='o' #點(diǎn)的形狀
            ,s=8 #點(diǎn)的大小
            ,c=color[i]
           )
plt.show()

基于這個(gè)分布允趟,我們來(lái)使用Kmeans進(jìn)行聚類恼策。首先,我們要猜測(cè)一下潮剪,這個(gè)數(shù)據(jù)中有幾簇涣楷?

from sklearn.cluster import KMeans

n_clusters = 3

cluster = KMeans(n_clusters=n_clusters, random_state=0).fit(X)

#重要屬性labels_,查看聚好的類別抗碰,每個(gè)樣本所對(duì)應(yīng)的類
y_pred = cluster.labels_
y_pred

#KMeans因?yàn)椴⒉恍枰⒛P突蛘哳A(yù)測(cè)結(jié)果狮斗,因此我們只需要fit就能夠得到聚類結(jié)果了
#KMeans也有接口predict和fit_predict,表示學(xué)習(xí)數(shù)據(jù)X并對(duì)X的類進(jìn)行預(yù)測(cè)
#但所得到的結(jié)果和我們不調(diào)用predict弧蝇,直接fit之后調(diào)用屬性labels一模一樣
pre = cluster.fit_predict(X)
pre == y_pred

#我們什么時(shí)候需要predict呢?當(dāng)數(shù)據(jù)量太大的時(shí)候碳褒!
#其實(shí)我們不必使用所有的數(shù)據(jù)來(lái)尋找質(zhì)心折砸,少量的數(shù)據(jù)就可以幫助我們確定質(zhì)心了
#當(dāng)我們數(shù)據(jù)量非常大的時(shí)候,我們可以使用部分?jǐn)?shù)據(jù)來(lái)幫助我們確認(rèn)質(zhì)心
#剩下的數(shù)據(jù)的聚類結(jié)果沙峻,使用predict來(lái)調(diào)用

cluster_smallsub = KMeans(n_clusters=n_clusters, random_state=0).fit(X[:200])
y_pred_ = cluster_smallsub.predict(X)
y_pred == y_pred_

#但這樣的結(jié)果睦授,肯定與直接fit全部數(shù)據(jù)會(huì)不一致。有時(shí)候摔寨,當(dāng)我們不要求那么精確去枷,或者我們的數(shù)據(jù)量實(shí)在太大,那我們可以使用這樣的方法是复。

#重要屬性cluster_centers_删顶,查看質(zhì)心
centroid = cluster.cluster_centers_
centroid

centroid.shape

#重要屬性inertia_,查看總距離平方和
inertia = cluster.inertia_
inertia

color = ["red","pink","orange","gray"]
fig, ax1 = plt.subplots(1)

for i in range(n_clusters):
    ax1.scatter(X[y_pred==i, 0], X[y_pred==i, 1]
            ,marker='o' #點(diǎn)的形狀
            ,s=8 #點(diǎn)的大小
            ,c=color[i]
           )
ax1.scatter(centroid[:,0],centroid[:,1]
           ,marker="x"
           ,s=15
           ,c="black")
plt.show()

#如果我們把猜測(cè)的簇?cái)?shù)換成4淑廊,Inertia會(huì)怎么樣逗余?

n_clusters = 4
cluster_ = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
inertia_ = cluster_.inertia_
inertia_

n_clusters = 5
cluster_ = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
inertia_ = cluster_.inertia_
inertia_

n_clusters = 6
cluster_ = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
inertia_ = cluster_.inertia_
inertia_

3.1.2 聚類算法的模型評(píng)估指標(biāo)

不同于分類模型和回歸,聚類算法的模型評(píng)估不是一件簡(jiǎn)單的事季惩。在分類中猎荠,有直接結(jié)果(標(biāo)簽)的輸出,并且分類的結(jié)果有正誤之分蜀备,所以我們使用預(yù)測(cè)的準(zhǔn)確度关摇,混淆矩陣,ROC曲線等等指標(biāo)來(lái)進(jìn)行評(píng)估碾阁,但無(wú)論如何評(píng)估输虱,都是在”模型找到正確答案“的能力。而回歸中脂凶,由于要擬合數(shù)據(jù)宪睹,我們有SSE均方誤差,有損失函數(shù)來(lái)衡量模型的擬合程度蚕钦。但這些衡量指標(biāo)都不能夠使用于聚類亭病。

面試高危問(wèn)題:如何衡量聚類算法的效果?
聚類模型的結(jié)果不是某種標(biāo)簽輸出嘶居,并且聚類的結(jié)果是不確定的罪帖,其優(yōu)劣由業(yè)務(wù)需求或者算法需求來(lái)決定,并且沒(méi)有永遠(yuǎn)的正確答案邮屁。那我們?nèi)绾魏饬烤垲惖男Ч兀?/td>

記得我們說(shuō)過(guò)整袁,KMeans的目標(biāo)是確保“簇內(nèi)差異小佑吝,簇外差異大”坐昙,我們就可以通過(guò)衡量簇內(nèi)差異來(lái)衡量聚類的效果。我們剛才說(shuō)過(guò)芋忿,Inertia是用距離來(lái)衡量簇內(nèi)差異的指標(biāo)炸客,因此疾棵,我們是否可以使用Inertia來(lái)作為聚類的衡量指標(biāo)呢?Inertia越小模型越好嘛痹仙。

可以是尔,但是這個(gè)指標(biāo)的缺點(diǎn)和極限太大。

首先蝶溶,它不是有界的。我們只知道宣渗,Inertia是越小越好抖所,是0最好,但我們不知道痕囱,一個(gè)較小的Inertia究竟有沒(méi)有達(dá)到模型的極限田轧,能否繼續(xù)提高。

第二鞍恢,它的計(jì)算太容易受到特征數(shù)目的影響傻粘,數(shù)據(jù)維度很大的時(shí)候,Inertia的計(jì)算量會(huì)陷入維度詛咒之中帮掉,計(jì)算量會(huì)爆炸弦悉,不適合用來(lái)一次次評(píng)估模型。

第三蟆炊,它會(huì)受到超參數(shù)K的影響稽莉,在我們之前的常識(shí)中其實(shí)我們已經(jīng)發(fā)現(xiàn),隨著K越大涩搓,Inertia注定會(huì)越來(lái)越小污秆,但這并不代表模型的效果越來(lái)越好了

第四,Inertia對(duì)數(shù)據(jù)的分布有假設(shè)昧甘,它假設(shè)數(shù)據(jù)滿足凸分布(即數(shù)據(jù)在二維平面圖像上看起來(lái)是一個(gè)凸函數(shù)的樣子)良拼,并且它假設(shè)數(shù)據(jù)是各向同性的(isotropic),即是說(shuō)數(shù)據(jù)的屬性在不同方向上代表著相同的含義充边。但是現(xiàn)實(shí)中的數(shù)據(jù)往往不是這樣庸推。所以使用Inertia作為評(píng)估指標(biāo),會(huì)讓聚類算法在一些細(xì)長(zhǎng)簇浇冰,環(huán)形簇予弧,或者不規(guī)則形狀的流形時(shí)表現(xiàn)不佳:

[圖片上傳失敗...(image-423a7e-1690443433820)]

那我們可以使用什么指標(biāo)呢?來(lái)使用輪廓系數(shù)湖饱。
在99%的情況下掖蛤,我們是對(duì)沒(méi)有真實(shí)標(biāo)簽的數(shù)據(jù)進(jìn)行探索,也就是對(duì)不知道真正答案的數(shù)據(jù)進(jìn)行聚類井厌。這樣的聚類蚓庭,是完全依賴于評(píng)價(jià)簇內(nèi)的稠密程度(簇內(nèi)差異兄录ァ)和簇間的離散程度(簇外差異大)來(lái)評(píng)估聚類的效果。其中輪廓系數(shù)是最常用的聚類算法的評(píng)價(jià)指標(biāo)器赞。它是對(duì)每個(gè)樣本來(lái)定義的垢袱,它能夠同時(shí)衡量:
1)樣本與其自身所在的簇中的其他樣本的相似度a,等于樣本與同一簇中所有其他點(diǎn)之間的平均距離
2)樣本與其他簇中的樣本的相似度b港柜,等于樣本與下一個(gè)最近的簇中得所有點(diǎn)之間的平均距離
根據(jù)聚類的要求”簇內(nèi)差異小请契,簇外差異大“,我們希望b永遠(yuǎn)大于a夏醉,并且大得越多越好爽锥。
單個(gè)樣本的輪廓系數(shù)計(jì)算為:
s = \frac{b-a}{max(a,b)}
這個(gè)公式可以被解析為:
\begin{equation} s = \left\{ \begin{array}{lr} 1- a/b, & if\ a < b \\ 0, & if \ a =b\\ b/a -1, & if \ a>b \end{array} \right. \end{equation}
很容易理解輪廓系數(shù)范圍是(-1,1),其中值越接近1表示樣本與自己所在的簇中的樣本很相似畔柔,并且與其他簇中的樣本不相似氯夷,當(dāng)樣本點(diǎn)與簇外的樣本更相似的時(shí)候,輪廓系數(shù)就為負(fù)靶擦。當(dāng)輪廓系數(shù)為0時(shí)腮考,則代表兩個(gè)簇中的樣本相似度一致,兩個(gè)簇本應(yīng)該是一個(gè)簇玄捕。

如果一個(gè)簇中的大多數(shù)樣本具有比較高的輪廓系數(shù)踩蔚,則簇會(huì)有較高的總輪廓系數(shù),則整個(gè)數(shù)據(jù)集的平均輪廓系數(shù)越高枚粘,則聚類是合適的寂纪。如果許多樣本點(diǎn)具有低輪廓系數(shù)甚至負(fù)值,則聚類是不合適的赌结,聚類的超參數(shù)K可能設(shè)定得太大或者太小捞蛋。

在sklearn中,我們使用模塊metrics中的類silhouette_score來(lái)計(jì)算輪廓系數(shù)柬姚,它返回的是一個(gè)數(shù)據(jù)集中拟杉,所有樣本的輪廓系數(shù)的均值。但我們還有同在metrics模塊中的silhouette_sample量承,它的參數(shù)與輪廓系數(shù)一致搬设,但返回的是數(shù)據(jù)集中每個(gè)樣本自己的輪廓系數(shù)。

我們來(lái)看看輪廓系數(shù)在我們自建的數(shù)據(jù)集上表現(xiàn)如何:

from sklearn.metrics import silhouette_score
from sklearn.metrics import silhouette_samples

X
y_pred

silhouette_score(X,y_pred)

silhouette_score(X,cluster_.labels_)

silhouette_samples(X,y_pred)

輪廓系數(shù)有很多優(yōu)點(diǎn)撕捍,它在有限空間中取值拿穴,使得我們對(duì)模型的聚類效果有一個(gè)“參考”。并且忧风,輪廓系數(shù)對(duì)數(shù)據(jù)的分布沒(méi)有假設(shè)默色,因此在很多數(shù)據(jù)集上都表現(xiàn)良好。但它在每個(gè)簇的分割比較清洗時(shí)表現(xiàn)最好狮腿。但輪廓系數(shù)也有缺陷腿宰,它在凸型的類上表現(xiàn)會(huì)虛高呕诉,比如基于密度進(jìn)行的聚類,或通過(guò)DBSCAN獲得的聚類結(jié)果吃度,如果使用輪廓系數(shù)來(lái)衡量甩挫,則會(huì)表現(xiàn)出比真實(shí)聚類效果更高的分?jǐn)?shù)。

3.1.3 案例:基于輪廓系數(shù)來(lái)選擇n_clusters

我們通常會(huì)繪制輪廓系數(shù)分布圖和聚類后的數(shù)據(jù)分布圖來(lái)選擇我們的最佳n_clusters椿每。

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score

import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

#先設(shè)定我們要分成的簇?cái)?shù)
n_clusters = 4

#創(chuàng)建一個(gè)畫布伊者,畫布上共有一行兩列兩個(gè)圖
fig, (ax1, ax2) = plt.subplots(1, 2)

#畫布尺寸
fig.set_size_inches(18, 7)

# 第一個(gè)圖是我們的輪廓系數(shù)圖像,是由各個(gè)簇的輪廓系數(shù)組成的橫向條形圖
# 橫向條形圖的橫坐標(biāo)是我們的輪廓系數(shù)取值间护,縱坐標(biāo)是我們的每個(gè)樣本亦渗,因?yàn)檩喞禂?shù)是對(duì)于每一個(gè)樣本進(jìn)行計(jì)算的

# 首先我們來(lái)設(shè)定橫坐標(biāo)
# 輪廓系數(shù)的取值范圍在[-1,1]之間,但我們至少是希望輪廓系數(shù)要大于0的
# 太長(zhǎng)的橫坐標(biāo)不利于我們的可視化兑牡,所以只設(shè)定X軸的取值在[-0.1,1]之間
ax1.set_xlim([-0.1, 1])

# 接下來(lái)設(shè)定縱坐標(biāo)央碟,通常來(lái)說(shuō)税灌,縱坐標(biāo)是從0開(kāi)始均函,最大值取到X.shape[0]的取值
# 但我們希望,每個(gè)簇能夠排在一起菱涤,不同的簇之間能夠有一定的空隙
# 以便我們看到不同的條形圖聚合成的塊苞也,理解它是對(duì)應(yīng)了哪一個(gè)簇
# 因此我們?cè)谠O(shè)定縱坐標(biāo)的取值范圍的時(shí)候,在X.shape[0]上粘秆,加上一個(gè)距離(n_clusters + 1) * 10如迟,留作間隔用
ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])

# 開(kāi)始建模,調(diào)用聚類好的標(biāo)簽
clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(X)
cluster_labels = clusterer.labels_

# 調(diào)用輪廓系數(shù)分?jǐn)?shù)攻走,注意殷勘,silhouette_score生成的是所有樣本點(diǎn)的輪廓系數(shù)均值
# 兩個(gè)需要輸入的參數(shù)是,特征矩陣X和聚類完畢后的標(biāo)簽
silhouette_avg = silhouette_score(X, cluster_labels)
#用print來(lái)報(bào)一下結(jié)果昔搂,現(xiàn)在的簇?cái)?shù)量下玲销,整體的輪廓系數(shù)究竟有多少
print("For n_clusters =", n_clusters,
      "The average silhouette_score is :", silhouette_avg)

# 調(diào)用silhouette_samples,返回每個(gè)樣本點(diǎn)的輪廓系數(shù)摘符,這就是我們的橫坐標(biāo)
sample_silhouette_values = silhouette_samples(X, cluster_labels)

#設(shè)定y軸上的初始取值
y_lower = 10

#接下來(lái)贤斜,對(duì)每一個(gè)簇進(jìn)行循環(huán)
for i in range(n_clusters):
    # 從每個(gè)樣本的輪廓系數(shù)結(jié)果中抽取出第i個(gè)簇的輪廓系數(shù),并對(duì)他進(jìn)行排序
    ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
    
    #注意, .sort()這個(gè)命令會(huì)直接改掉原數(shù)據(jù)的順序
    ith_cluster_silhouette_values.sort()
    
    #查看這一個(gè)簇中究竟有多少個(gè)樣本
    size_cluster_i = ith_cluster_silhouette_values.shape[0]
    
    #這一個(gè)簇在y軸上的取值逛裤,應(yīng)該是由初始值(y_lower)開(kāi)始瘩绒,到初始值+加上這個(gè)簇中的樣本數(shù)量結(jié)束(y_upper)
    y_upper = y_lower + size_cluster_i
    
    #colormap庫(kù)中的,使用小數(shù)來(lái)調(diào)用顏色的函數(shù)
    #在nipy_spectral([輸入任意小數(shù)來(lái)代表一個(gè)顏色])
    #在這里我們希望每個(gè)簇的顏色是不同的带族,我們需要的顏色種類剛好是循環(huán)的個(gè)數(shù)的種類
    #在這里锁荔,只要能夠確保,每次循環(huán)生成的小數(shù)是不同的蝙砌,可以使用任意方式來(lái)獲取小數(shù)
    #在這里堕战,我是用i的浮點(diǎn)數(shù)除以n_clusters坤溃,在不同的i下,自然生成不同的小數(shù)
    #以確保所有的簇會(huì)有不同的顏色
    color = cm.nipy_spectral(float(i)/n_clusters)
    
    #開(kāi)始填充子圖1中的內(nèi)容
    #fill_between是填充曲線與直角之間的空間的函數(shù)
    #fill_betweenx的直角是在縱坐標(biāo)上
    #fill_betweeny的直角是在橫坐標(biāo)上
    #fill_betweenx的參數(shù)應(yīng)該輸入(定義曲線的點(diǎn)的橫坐標(biāo)嘱丢,定義曲線的點(diǎn)的縱坐標(biāo)薪介,柱狀圖的顏色)
    ax1.fill_betweenx(np.arange(y_lower, y_upper)
                      ,ith_cluster_silhouette_values
                      ,facecolor=color
                      ,alpha=0.7
                     )

    #為每個(gè)簇的輪廓系數(shù)寫上簇的編號(hào),并且讓簇的編號(hào)顯示坐標(biāo)軸上每個(gè)條形圖的中間位置
    #text的參數(shù)為(要顯示編號(hào)的位置的橫坐標(biāo)越驻,要顯示編號(hào)的位置的縱坐標(biāo)汁政,要顯示的編號(hào)內(nèi)容)
    ax1.text(-0.05
             , y_lower + 0.5 * size_cluster_i
             , str(i))

    # 為下一個(gè)簇計(jì)算新的y軸上的初始值,是每一次迭代之后缀旁,y的上線再加上10
    #以此來(lái)保證记劈,不同的簇的圖像之間顯示有空隙
    y_lower = y_upper + 10
    
#給圖1加上標(biāo)題,橫坐標(biāo)軸并巍,縱座標(biāo)軸的標(biāo)簽
ax1.set_title("The silhouette plot for the various clusters.")
ax1.set_xlabel("The silhouette coefficient values")
ax1.set_ylabel("Cluster label")

#把整個(gè)數(shù)據(jù)集上的輪廓系數(shù)的均值以虛線的形式放入我們的圖中
ax1.axvline(x=silhouette_avg, color="red", linestyle="--")

#讓y軸不顯示任何刻度
ax1.set_yticks([])

#讓x軸上的刻度顯示為我們規(guī)定的列表
ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])

#開(kāi)始對(duì)第二個(gè)圖進(jìn)行處理目木,首先獲取新顏色,由于這里沒(méi)有循環(huán)懊渡,因此我們需要一次性生成多個(gè)小數(shù)來(lái)獲取多個(gè)顏色
colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)

ax2.scatter(X[:, 0], X[:, 1]
            ,marker='o' #點(diǎn)的形狀
            ,s=8 #點(diǎn)的大小
            ,c=colors
           )

#把生成的質(zhì)心放到圖像中去
centers = clusterer.cluster_centers_
# Draw white circles at cluster centers
ax2.scatter(centers[:, 0], centers[:, 1], marker='x',
            c="red", alpha=1, s=200)

#為圖二設(shè)置標(biāo)題刽射,橫坐標(biāo)標(biāo)題,縱坐標(biāo)標(biāo)題
ax2.set_title("The visualization of the clustered data.")
ax2.set_xlabel("Feature space for the 1st feature")
ax2.set_ylabel("Feature space for the 2nd feature")

#為整個(gè)圖設(shè)置標(biāo)題
plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
              "with n_clusters = %d" % n_clusters),
             fontsize=14, fontweight='bold')
plt.show()

將上述過(guò)程包裝成一個(gè)循環(huán)剃执,可以得到:

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score

import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

for n_clusters in [2,3,4,5,6,7]:
    n_clusters = n_clusters
    fig, (ax1, ax2) = plt.subplots(1, 2)
    fig.set_size_inches(18, 7)
    ax1.set_xlim([-0.1, 1])
    ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])
    clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(X)
    cluster_labels = clusterer.labels_
    silhouette_avg = silhouette_score(X, cluster_labels)
    print("For n_clusters =", n_clusters,
          "The average silhouette_score is :", silhouette_avg)
    sample_silhouette_values = silhouette_samples(X, cluster_labels)
    y_lower = 10
    for i in range(n_clusters):
        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
        ith_cluster_silhouette_values.sort()
        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i
        color = cm.nipy_spectral(float(i)/n_clusters)
        ax1.fill_betweenx(np.arange(y_lower, y_upper)
                          ,ith_cluster_silhouette_values
                          ,facecolor=color
                          ,alpha=0.7
                         )
        ax1.text(-0.05
                 , y_lower + 0.5 * size_cluster_i
                 , str(i))
        y_lower = y_upper + 10

    ax1.set_title("The silhouette plot for the various clusters.")
    ax1.set_xlabel("The silhouette coefficient values")
    ax1.set_ylabel("Cluster label")
    ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
    ax1.set_yticks([])
    ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])

    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
    ax2.scatter(X[:, 0], X[:, 1]
                ,marker='o'
                ,s=8
                ,c=colors
               )
    centers = clusterer.cluster_centers_
    # Draw white circles at cluster centers
    ax2.scatter(centers[:, 0], centers[:, 1], marker='x',
                c="red", alpha=1, s=200)
    
    ax2.set_title("The visualization of the clustered data.")
    ax2.set_xlabel("Feature space for the 1st feature")
    ax2.set_ylabel("Feature space for the 2nd feature")

    plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
                  "with n_clusters = %d" % n_clusters),
                 fontsize=14, fontweight='bold')
    plt.show()

完整版目錄

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末誓禁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肾档,更是在濱河造成了極大的恐慌摹恰,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怒见,死亡現(xiàn)場(chǎng)離奇詭異俗慈,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)遣耍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門闺阱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人配阵,你說(shuō)我怎么就攤上這事馏颂。” “怎么了棋傍?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵救拉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我瘫拣,道長(zhǎng)亿絮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮派昧,結(jié)果婚禮上黔姜,老公的妹妹穿的比我還像新娘。我一直安慰自己蒂萎,他們只是感情好秆吵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著五慈,像睡著了一般纳寂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泻拦,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天毙芜,我揣著相機(jī)與錄音,去河邊找鬼争拐。 笑死腋粥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的架曹。 我是一名探鬼主播隘冲,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼音瓷!你這毒婦竟也來(lái)了对嚼?” 一聲冷哼從身側(cè)響起夹抗,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绳慎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后漠烧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體杏愤,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年已脓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了珊楼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡度液,死狀恐怖厕宗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堕担,我是刑警寧澤已慢,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站霹购,受9級(jí)特大地震影響佑惠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一膜楷、第九天 我趴在偏房一處隱蔽的房頂上張望旭咽。 院中可真熱鬧,春花似錦赌厅、人聲如沸穷绵。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)请垛。三九已至,卻和暖如春洽议,著一層夾襖步出監(jiān)牢的瞬間宗收,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工亚兄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留混稽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓审胚,卻偏偏與公主長(zhǎng)得像匈勋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子膳叨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容