【火炉炼AI】机器学习023-使用层次聚类算法构建模型
【火爐煉AI】機(jī)器學(xué)習(xí)023-使用層次聚類(lèi)算法構(gòu)建模型
(本文所使用的Python庫(kù)和版本號(hào): Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )
聚類(lèi)的算法有很多種,前面我們講解了k-means算法和均值漂移算法,此處我們繼續(xù)講解層次聚類(lèi)算法。
k-means是一種分散性聚類(lèi)算法,以空間中K個(gè)點(diǎn)為中心進(jìn)行聚類(lèi),將最靠近他們的樣本收歸門(mén)下。k-means的優(yōu)勢(shì)在于簡(jiǎn)單快速,對(duì)于大數(shù)據(jù)集,該算法仍然可以保持可伸縮性和高效率,對(duì)于密集型的數(shù)據(jù)集,效果非常好。缺點(diǎn)也很明顯:必須事先給出K值,而且對(duì)初值敏感,對(duì)于不同的初始值,可能會(huì)產(chǎn)生不同結(jié)果;對(duì)于非密集型數(shù)據(jù)集,比如非凸形狀的簇或者大小差別很大的簇,可能不太適用;而且k-means對(duì)噪聲和孤立點(diǎn)數(shù)據(jù)比較敏感。
正是因?yàn)閗-means有上述優(yōu)點(diǎn),所以它才能夠經(jīng)久不衰,也正是因?yàn)樗猩鲜鋈秉c(diǎn),所以它才沒(méi)有一統(tǒng)天下。
1. 層次聚類(lèi)算法簡(jiǎn)介
層次聚類(lèi),顧名思義,就是一層一層的進(jìn)行聚類(lèi),通常,我們可以把整個(gè)數(shù)據(jù)集看成一顆樹(shù),每一個(gè)數(shù)據(jù)點(diǎn)就是該樹(shù)的葉子,而一個(gè)節(jié)點(diǎn)就是該節(jié)點(diǎn)下的葉子總數(shù)。故而,對(duì)這個(gè)數(shù)據(jù)集進(jìn)行聚類(lèi)分析,就相當(dāng)于找到各種葉子對(duì)應(yīng)的節(jié)點(diǎn),這種尋找方式就是層次聚類(lèi)算法。
一般的,層次聚類(lèi)算法有兩種:自下而上的算法和自上而下的算法。在自下而上的算法中,剛開(kāi)始每個(gè)數(shù)據(jù)點(diǎn)(即每個(gè)葉子)都被看成一個(gè)單獨(dú)的集群,然后將這些集群不斷的合并,直到所有的集群都合并成一個(gè)巨型集群,這種自下而上的合并算法也叫做凝聚層次聚類(lèi)算法。
而相反的,在自上而下的算法中,剛開(kāi)始所有的葉子被當(dāng)做一個(gè)巨型集群,然后對(duì)這個(gè)集群進(jìn)行不斷的分解,直到所有的集群都變成一個(gè)個(gè)單獨(dú)的數(shù)據(jù)點(diǎn),即巨型集群被分解成單獨(dú)的葉子節(jié)點(diǎn),這種自上而下的的分解算法也叫做分裂層次聚類(lèi)算法。
目前應(yīng)用最多的還是凝聚層次聚類(lèi)算法,故而下面只對(duì)該算法進(jìn)行介紹。
凝聚層次聚類(lèi)算法的核心思想如上圖所示,由葉子逐步合并成根節(jié)點(diǎn)。
凝聚法的具體計(jì)算過(guò)程可以描述為:
1,將數(shù)據(jù)集中的所有的數(shù)據(jù)點(diǎn)都當(dāng)做一個(gè)獨(dú)立的集群
2,計(jì)算兩兩之間的距離,找到距離最小的兩個(gè)集群,并合并這兩個(gè)集群為一個(gè)集群,認(rèn)為距離越小,兩者之間的相似度越大,越有可能是一個(gè)集群。
3,重復(fù)上面的步驟2,直到聚類(lèi)的數(shù)目達(dá)到設(shè)定的條件,表示聚類(lèi)過(guò)程完成。
上面的計(jì)算過(guò)程看似簡(jiǎn)單,但有一個(gè)關(guān)鍵的難點(diǎn)在于:數(shù)據(jù)點(diǎn)或集群之間的距離計(jì)算,這種集群間距離的計(jì)算方法有很多種,下面幾種比較常見(jiàn)的計(jì)算方法:
1,SingleLinkage: 又叫做nearest-neighbor,其本質(zhì)就是取兩個(gè)集群中距離最近的兩個(gè)樣本的距離作為這兩個(gè)集群的距離。這種方式有一個(gè)缺點(diǎn),會(huì)造成一種Chaning的效果,即明明兩個(gè)集群相距甚遠(yuǎn),但由于其中個(gè)別點(diǎn)的距離比較近而把他們計(jì)算成距離比較近。
2,CompleteLinkage: 這種計(jì)算方式是上面的SingleLinkage算法的反面,即取兩個(gè)集群中距離最遠(yuǎn)的兩個(gè)點(diǎn)的距離作為這兩個(gè)集群的距離,所以它的缺點(diǎn)也和上面的算法類(lèi)似。
3,AverageLinkage: 由于上面兩種算法都存在一定的問(wèn)題,都會(huì)被離群點(diǎn)帶到溝里去,所以本算法就考慮整體的平均值,即把兩個(gè)集群中的點(diǎn)兩兩的距離都計(jì)算出來(lái)后求平均值,作為兩個(gè)集群的距離。有的時(shí)候,并不是計(jì)算平均值,而是取中值,其背后的邏輯也是類(lèi)似的,但取中值更加能夠避免個(gè)別離群數(shù)據(jù)點(diǎn)對(duì)結(jié)果的干擾。
一旦我們通過(guò)上面的公式計(jì)算出來(lái)兩個(gè)集群的相似度,我們就可以對(duì)這兩個(gè)集群進(jìn)行合并。
上面的部分內(nèi)容來(lái)源于聚類(lèi)系列-層次聚類(lèi) 和Kmeans聚類(lèi)與層次聚類(lèi), 聚類(lèi)(2)——層次聚類(lèi),在此表示感謝。
2. 準(zhǔn)備數(shù)據(jù)集
下面我們自己構(gòu)建一個(gè)數(shù)據(jù)集,我們定義了一個(gè)函數(shù)prepare_dataset()專(zhuān)門(mén)用來(lái)準(zhǔn)備數(shù)據(jù)集,此處的數(shù)據(jù)集是分布比較特殊的樣本點(diǎn),如下為這個(gè)準(zhǔn)備數(shù)據(jù)集的函數(shù)的代碼
# 準(zhǔn)備數(shù)據(jù)集 def prepare_dataset(sample_num,data_type,noise_amplitude):'''prepare special kinds of dataset,params:sample_num: sample numbers in this prepared dataset,data_type: must be one of ['rose','spiral','hypotrochoid'],noise_amplitude: how much noise add to the dataset. normally, range from 0-0.5return:the prepared dataset in numpy.ndarray format'''def add_noise(x, y, amplitude):X = np.concatenate((x, y))X += amplitude * np.random.randn(2, X.shape[1])return X.Tdef get_spiral(t, noise_amplitude=0.5):r = tx = r * np.cos(t)y = r * np.sin(t)return add_noise(x, y, noise_amplitude)def get_rose(t, noise_amplitude=0.02):k = 5 r = np.cos(k*t) + 0.25 x = r * np.cos(t)y = r * np.sin(t)return add_noise(x, y, noise_amplitude)def get_hypotrochoid(t, noise_amplitude=0):a, b, h = 10.0, 2.0, 4.0x = (a - b) * np.cos(t) + h * np.cos((a - b) / b * t) y = (a - b) * np.sin(t) - h * np.sin((a - b) / b * t) return add_noise(x, y, 0)X=2.5*np.pi*(1+2*np.random.rand(1,sample_num))if data_type=='hypotrochoid':return get_hypotrochoid(X,noise_amplitude)elif data_type=='spiral':return get_spiral(X,noise_amplitude)else:return get_rose(X,noise_amplitude)下面我們用這個(gè)函數(shù)準(zhǔn)備三個(gè)數(shù)據(jù)集,分別是spiral_dataset, rose_dataset, hypo_dataset,其代碼為:
spiral_dataset=prepare_dataset(600,'spiral',0.5) rose_dataset=prepare_dataset(600,'rose',0.02) hypo_dataset=prepare_dataset(600,'hypotrochoid',0)然后我們用前面文章中提到的函數(shù)visual_2D_dataset_dist()可以查看無(wú)標(biāo)簽數(shù)據(jù)集的分布情況,如下圖所示為這三個(gè)數(shù)據(jù)集的二維分布情況。
########################小**********結(jié)###############################
1,此處我們自己創(chuàng)建了一些具有特殊分布的數(shù)據(jù)集,想用這些特殊分布的數(shù)據(jù)集來(lái)考察凝聚層次聚類(lèi)算法的優(yōu)虐。
2,數(shù)據(jù)集的產(chǎn)生是通過(guò)一些數(shù)學(xué)函數(shù)來(lái)實(shí)現(xiàn)的,這些函數(shù)在網(wǎng)上可以找到成熟的數(shù)學(xué)公式。
#################################################################
3. 使用凝聚層次聚類(lèi)算法構(gòu)建模型
為了更好地比較模型在不同數(shù)據(jù)集上的效果,我們將模型的構(gòu)建函數(shù)和模型在不同數(shù)據(jù)集上的表現(xiàn)都整合到一個(gè)函數(shù)中,該函數(shù)名稱(chēng)為:perform_plot_clustering(),在以后只需要調(diào)用該函數(shù)就可以分別對(duì)這三個(gè)數(shù)據(jù)集進(jìn)行訓(xùn)練和繪制訓(xùn)練后效果圖。如下為代碼:
from sklearn.cluster import AgglomerativeClustering from sklearn.neighbors import kneighbors_graph# 建立一個(gè)函數(shù),用來(lái)構(gòu)建聚類(lèi)模型,并繪圖展示聚類(lèi)效果 def perform_plot_clustering(dataset,none_cluster_num=3,kneighbors_num=10):assert dataset.shape[1]==2,'only support dataset with 2 features'# 構(gòu)建凝聚層次聚類(lèi)模型,并用數(shù)據(jù)集對(duì)其進(jìn)行訓(xùn)練none_model=AgglomerativeClustering(n_clusters=none_cluster_num)none_model.fit(dataset) # 構(gòu)建無(wú)connectivity的modelconnectivity = kneighbors_graph(dataset, kneighbors_num, include_self=False)conn_model=AgglomerativeClustering(n_clusters=kneighbors_num,connectivity=connectivity)conn_model.fit(dataset)# 構(gòu)建kneighbors_graph connectivity的modeldef visual_2D_dataset(plt,dataset_X,dataset_y):'''將二維數(shù)據(jù)集dataset_X和對(duì)應(yīng)的類(lèi)別dataset_y顯示在散點(diǎn)圖中'''assert dataset_X.shape[1]==2,'only support dataset with 2 features'classes=list(set(dataset_y)) markers=['.',',','o','v','^','<','>','1','2','3','4','8','s','p','*','h','H','+','x','D','d','|']# colors=['b','c','g','k','m','w','r','y']colors=['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan']for class_id in classes:one_class=np.array([feature for (feature,label) in zip(dataset_X,dataset_y) if label==class_id])plt.scatter(one_class[:,0],one_class[:,1],marker=markers[class_id%len(markers)],c=colors[class_id%len(colors)],label='class_'+str(class_id))plt.legend()# 以下是繪圖def plot_model_graph(plt,model,title):labels=model.labels_# 將數(shù)據(jù)集繪制到圖表中visual_2D_dataset(plt,dataset,labels)plt.title(title)plt.xlabel('feature_0')plt.ylabel('feature_1')return pltplt.figure(12,figsize=(25,10))plt.subplot(121)plot_model_graph(plt,none_model,'none_connectivity')plt.subplot(122)plot_model_graph(plt,conn_model,'kneighbors_connectivity')plt.show()使用這個(gè)函數(shù)來(lái)查看凝聚層次聚類(lèi)算法在這三個(gè)數(shù)據(jù)集上的效果,可以得到如下結(jié)果圖:
從上面這三幅圖中可以看出,在沒(méi)有使用kneighbors connectivity的圖中,凝聚層次算法傾向于把位置相近的一些點(diǎn)聚集到一個(gè)類(lèi)別,而不考慮這些數(shù)據(jù)點(diǎn)之間是否連續(xù),是否有數(shù)據(jù)之間的連接。而用kneighbors作connectivity之后,算法傾向于把有連接的一些數(shù)據(jù)劃分為一類(lèi),而沒(méi)有連接的劃分為另外一類(lèi),這樣的劃分方法更具有邏輯性。
也有人會(huì)問(wèn),是不是因?yàn)樵诘谝环鶊D中我們把類(lèi)別設(shè)置為3類(lèi),所以算法在劃分時(shí)將很多距離較遠(yuǎn)的沒(méi)有連接的樣本劃分為同一個(gè)類(lèi)了?那么我們可以試一下同樣的將none_connectivity的類(lèi)別設(shè)置為10,再對(duì)比以下兩者的效果,如下為結(jié)果圖:
########################小**********結(jié)###############################
1,凝聚層次聚類(lèi)算法的構(gòu)建和訓(xùn)練比較簡(jiǎn)單,和其他聚類(lèi)算法沒(méi)有太大區(qū)別,直接構(gòu)建AgglomerativeClustering對(duì)象并訓(xùn)練即可。
2,在構(gòu)建凝聚層次聚類(lèi)算法時(shí),有一個(gè)connectivity的參數(shù)是選擇的難點(diǎn),此處我比較了不使用connectivity參數(shù)和使用kneighbors connectivity參數(shù)的區(qū)別,發(fā)現(xiàn)兩者在聚類(lèi)時(shí)有一些不同。
3,在不使用connectivity參數(shù)時(shí),凝聚層次算法傾向于把位置相近的一些點(diǎn)聚集到一個(gè)類(lèi)別,而不考慮這些數(shù)據(jù)點(diǎn)之間是否連續(xù),是否有數(shù)據(jù)之間的連接。
4,而用kneighbors作connectivity之后,算法傾向于把有連接的一些數(shù)據(jù)劃分為一類(lèi),而沒(méi)有連接的劃分為另外一類(lèi)。在實(shí)際項(xiàng)目中,應(yīng)該怎么使用這個(gè)connectivity參數(shù),應(yīng)該根據(jù)具體的數(shù)據(jù)集特性來(lái)選擇。
#################################################################
注:本部分代碼已經(jīng)全部上傳到(我的github)上,歡迎下載。
參考資料:
1, Python機(jī)器學(xué)習(xí)經(jīng)典實(shí)例,Prateek Joshi著,陶俊杰,陳小莉譯
總結(jié)
以上是生活随笔為你收集整理的【火炉炼AI】机器学习023-使用层次聚类算法构建模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 我是如何零基础开始能写爬虫的?
- 下一篇: CSS修饰的浮动