小伙伴們大家好~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)度量:
如我們采用歐幾里得距離湿右,則一個(gè)簇中所有樣本點(diǎn)到質(zhì)心的距離的平方和為:
其中,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ì)算為:
這個(gè)公式可以被解析為:
很容易理解輪廓系數(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()
完整版目錄