聚类实例
1. 有趣模式
在數據挖掘和機器學習中,一次就算會產生大量的“模式”,所謂模式可以理解為一種數據規律。
如果一個模式具備以下的特點,那么它就是有趣的
- 易于被人理解
- 在某種確信度上,對于新的或檢驗數據是有效的
- 是潛在有用的(具有一定的實際意義)
- 是新穎的
2. 層次聚類
層次聚類與人類的“自底而上”的認識事物的過程是一樣的。
從思考的角度來看有兩種思路:一種是“凝聚的層次聚類方法”,一種是“分裂的層次聚類方法”。第一種顧名思義就是在大量的樣本上自底向上找到那些距離比較近的樣本先聚合成小的群,聚合到一定的程度再由小的群聚合成更大的群,第二種既是先把所有的樣本分成若干個大群,再在每個群里各自重新進行聚類劃分。
“分裂的層次聚類方法”:可先用K-Means算法進行一次聚類,分成若干類簇,再在每個類簇中使用K-Means進行聚類。
“凝聚的層次聚類方法”:首先把待分類樣本看作一棵完整的樹,樹是所有的訓練樣本向量,而眾多樹葉就是每一個單獨的樣本。然后設計幾個觀察點,讓他們散布在整個訓練樣本中。讓這些觀察點自下向上不斷的進行類簇的合并。這種聚類也是遵循一定的原則的,即基于連接度的度量來判斷是否要向上繼續合并兩個類簇,度量由以下3種不同的策略原則:
- ward策略,讓所有類簇中的方差最小化
- Maximum策略,也叫全連接策略,力求將類簇之間的距離最大值最小化
- Average Linkage策略,力求將類簇之間的距離的平均值最小化
這里用到了Scikit-learn庫中的AgglomerativeClustering算法
# coding=utf-8 import numpy as np from matplotlib import pyplot as plt from sklearn.cluster import AgglomerativeClusteringX = [] f = open('data.txt') for v in f:X.append([float(v.split(',')[2]), float(v.split(',')[3])])#轉換成numpy array,得到的X是坐標數組 X = np.array(X) print X#類簇的數量 n_clusters = 5#現在我們把訓練數據和對應的分類數放入聚類函數中進行聚類,使用方差最小化的方法'ward' cls = AgglomerativeClustering(linkage='ward', n_clusters=n_clusters).fit(X)#X中每項所屬分類的一個列表 cls.labels_ print 'cls.labels:',cls.labels_#畫圖 markers = ['^', 'x', 'o', '*', '+'] plt.subplots() for i in range(n_clusters):my_members = cls.labels_ == iprint 'my_members:',my_members# 篩選各個類別的聚類簇plt.scatter(X[my_members, 0], X[my_members, 1], s=60, marker=markers[i], c='b', alpha=0.5)plt.title("hierarchical_clustering") plt.show() plt.savefig('hierarchical_clustering.png')運行結果:
[[ 39.55 116.24][ 26.05 119.18][ 25.58 119.31]..., [ 29.18 106.16][ 29.1 107.05][ 29.23 105.53]] cls.labels: [1 0 0 ..., 0 0 0] my_members: [False True True ..., True True True] my_members: [ True False False ..., False False False] my_members: [False False False ..., False False False] my_members: [False False False ..., False False False] my_members: [False False False ..., False False False]聚類的思路其實很簡單,可以在認識陌生的數據簇層次時比較有幫助。
此外層次聚類的思路也可以用于對人們社會活動中的一些現象進行總結,比如一個做歌曲發布的網站,如果希望做推薦算法,可以考慮一個人愛聽的歌曲進行層次化的聚類。對每首愛聽的歌曲進行向量建模,既是對一首歌的各個維度進行建模。向量化后就可以嘗試挖掘用戶喜歡歌曲的大類別,以及其下的小類別。或者研究歌曲流行風格的趨勢等。
3. 密度聚類
密度聚類很多時候用在聚類形狀不規則的情形下,如:
很顯然類別是兩個不規則的形狀,此時K-Means算法就不適合了,因為K-Means算法是用歐氏距離半徑來進行類簇劃分的,而對于這種不規則的形狀的聚類效果就沒有圓形類簇的效果好。
注意:
- K-Means的距離計算公式是可以選取的,一般用歐式距離比較簡單,或者用曼哈頓距離。
- 常用的距離度量方法包括歐式距離和余弦相似度。兩者都是評定個體間差異的大小的。歐式距離會受指標不同單位刻度的影響,所以一般要先進行標準化或者歸一化。而空間向量余弦夾角的相似度度量不會受到指標刻度的影響,余弦值落在區間[-1,+1]上。
在sklearn里有做基于密度分類的算法庫——sklearn.cluster.DBSCAN
示例:
進行密度聚類的代碼:
# coding=utf-8 import numpy as np from sklearn.cluster import DBSCAN import matplotlib.pyplot as plt#國家面積和人口 X = [[9670250, 1392358258],[2980000, 1247923065],[9629091, 317408015],[8514877, 201032714],[377873, 127270000],[7692024, 23540517],[9984670, 34591000],[17075400, 143551289],[513115, 67041000],[181035, 14805358],[99600, 50400000],[120538, 24052231]]#轉換成numpy array X = np.array(X)#歸一化 a = X[:, :1] / 17075400.0 * 10000 b = X[:, 1:] / 1392358258.0 * 10000 X = np.concatenate((a, b), axis=1)#現在我們把訓練數據和對應的分類放入分類器中進行訓練, #這里沒有噪點出現因為我們把min_samples設置成了1 cls = DBSCAN(eps=2000, min_samples=1).fit(X)#X中每項所屬分類的一個列表 print 'cls.labels_',cls.labels_#類簇的數量 n_clusters = len(set(cls.labels_)) print 'n_clusters',n_clusters#畫圖 markers = ['^', 'x', 'o', '*', '+'] for i in range(n_clusters):my_members = cls.labels_ == iprint 'my_members:', my_membersplt.scatter(X[my_members, 0], X[my_members, 1], s=60, marker=markers[i], c='b', alpha=0.5)plt.title('dbscan') plt.show() plt.savefig('dbscan.png') # 保存圖像運行結果:
cls.labels_ [0 1 2 ..., 3 3 3] n_clusters 5 my_members: [ True False False ..., False False False] my_members: [False True False ..., False False False] my_members: [False False True ..., False False False] my_members: [False False False ..., True True True] my_members: [False False False ..., False False False]- 這里要注意的是歸一化的問題:歸一化問題是為了解決由于維度量綱或者單位不同所產生的距離計算問題而進行的權重調整問題。這里是把兩個不同緯度的數據投影到以10000為最大值的正方形區域里。
- cls = DBSCAN(eps=2000, min_samples=1).fit(X)的使用:其中eps的含義是是設定一個閾值,既是在根據密度向外擴展的過程中如果發現在這個閾值距離范圍內找不到向量,那么就認為這個聚簇已經查找完畢。這里設置2000,因為歸一化以后所有的變量都落在一個10000*10000的區間單位。而min_samples的含義是聚類簇最小應該擁有多少個向量。所以不存再噪點。
4. 聚類評估
聚類的評估包括以下3個方面:
(1)估計聚類的趨勢對于給定的數據集,評估該數據集是否存在隨機結構,也就是分布不均勻的情況。即數據集中必須存在非隨機結構,聚類分析才是有意義的。
(2)確定數據集中的簇數。最好不要人為的主觀決定類簇的數量
(3)測量聚類的質量。可以用量化的方法來測量聚類的質量。
4.1 聚類趨勢
如果樣本空間里的樣本是隨機出現的,本身沒有聚類的趨勢,那么使用聚類肯定是有問題的。
我們常用霍普金斯統計量來進行量化評估。
H=∑ni=1yi∑ni=1xi+∑ni=1yi
如果整個樣本空間是一個均勻的沒有聚類趨勢的空間,那么H應該是0.5左右,反之,如果是有聚類趨勢的空間,那么H應該接近于1。
霍普金斯統計量常用計算代碼:
# coding=utf-8 import numpy as np from sklearn.cluster import KMeans#國家面積和人口 X = [[9670250, 1392358258],[2980000, 1247923065],[9629091, 317408015],[8514877, 201032714],[377873, 127270000],[7692024, 23540517],[9984670, 34591000],[17075400, 143551289],[513115, 67041000],[181035, 14805358],[99600, 50400000],[120538, 24052231]]# 轉換成numpy array X = np.array(X)#歸一化 a = X[:, :1] / 17075400.0 * 10000 b = X[:, 1:] / 1392358258.0 * 10000 X = np.concatenate((a, b), axis=1) print 'X:',Xpn = X[np.random.choice(X.shape[0], 3, replace=False), :] print 'pn:',pn#隨機選出來的三個 # [[ 221.29671926 914.06072589] # [ 70.59161132 172.74455667] # [ 10000. 1030.99391392]] xn = [] for i in pn:distance_min = 1000000for j in X:if np.array_equal(j, i): # 歐式距離continuedistance = np.linalg.norm(j - i)if distance_min > distance:distance_min = distancexn.append(distance_min) print 'xn:',xnqn = X[np.random.choice(X.shape[0], 3, replace=False), :] #隨機選出來的三個 # [[ 10000. 1030.99391392] # [ 4986.63398808 1443.82893444] # [ 221.29671926 914.06072589]] yn = [] for i in qn:distance_min = 1000000for j in X:if np.array_equal(j, i):continuedistance = np.linalg.norm(j - i)if distance_min > distance:distance_min = distanceyn.append(distance_min) print 'yn:',ynH = float(np.sum(yn)) / (np.sum(xn) + np.sum(yn)) print 'the result:', H運行結果:
X: [[ 5663.26411094 10000. ][ 1745.20069808 8962.65783486][ 5639.15984399 2279.64328273]..., [ 106.02094241 106.33296362][ 58.32952669 361.97580407][ 70.59161132 172.74455667]] pn: [[ 300.49954906 481.49245796][ 4986.63398808 1443.82893444][ 221.29671926 914.06072589]] xn: [270.05656868571629, 1060.3657941683734, 439.75947365340113] yn: [1345.003812374011, 1345.003812374011, 1060.3657941683734] the result: 0.679347139082注意:
np.linalg.norm():則表示范數,首先需要注意的是范數是對向量(或者矩陣)的度量,是一個標量(scalar)
函數形式:norm(x, ord=None, axis=None, keepdims=False)
x表示要度量的向量,ord表示范數的種類
4.2 簇數確定
確定一個樣本空間有多少簇也是很重要的,而且簇數的猜測會影響聚類的結果。
有一種經驗法:就是說對于n個樣本的空間,設置簇數p為n2??√,在期望狀態下每個簇大約有2n??√個點。
還有一種方法叫“肘方法”:嘗試把樣本空間劃分為n個類,在分成m個類簇的時候會有一個劃分方法,在這時,每個類簇的內部都有若干向量,計算它們的空間中心點,即計算這m個類簇各自的空間重心在哪里。再計算每個類簇中每個向量和該類簇重心的距離的和。最后把m個類簇各自的距離和相加得到一個函數var(n),n就是類簇數。
可以想象這個平方和最大的時候應該是分1個類,也就是不分類的時候,所有的向量到重心的距離都非常大,這時的距離和是最大的,隨著化分數的增加,這個距離和會總體上縮減。當每個類簇一個成員時,是另一種極端情況,這時最終的距離和變為了0。
其次在下降過程中會有一個拐點,這個點會讓人感覺曲線從立陡變為平滑,那么這個點就是要找的點。
但該方法時間復雜度會很高,需要根據實際情況做權衡。
代碼:
# encoding=utf-8 import numpy as np from sklearn.cluster import KMeans# 面積km2,人口 X = [[9670250, 1392358258],[2980000, 1247923065],[9629091, 317408015],[8514877, 201032714],[377873, 127270000],[7692024, 23540517],[9984670, 34591000],[17075400, 143551289],[513115, 67041000],[181035, 14805358],[99600, 50400000],[120538, 24052231]]#轉換成numpy array X = np.array(X)#歸一化 a = X[:, :1] / 17075400.0 * 10000 b = X[:, 1:] / 1392358258.0 * 10000 X = np.concatenate((a, b), axis=1)#類簇的數量 n_clusters = 1cls = KMeans(n_clusters).fit(X) #每個簇的中心點 print 'cls.cluster_centers_:',cls.cluster_centers_ #X中每個點所屬的簇 cls.labels_ print 'cls.labels_:',cls.labels_#曼哈頓距離 def manhattan_distance(x, y):return np.sum(abs(x - y))distance_sum = 0 for i in range(n_clusters):print 'i:',igroup = cls.labels_ == imembers = X[group, :] # 得到對應的樣本向量for v in members:distance_sum += manhattan_distance(np.array(v), cls.cluster_centers_) print distance_sum #結果63538.2443905運行結果:
cls.cluster_centers_: [[ 3261.92812467 2180.93620785]] cls.labels_: [0 0 0 ..., 0 0 0] i: 0 63538.2443905在使用K-Means算法,m=1時所計算的肘方法的距離值,可以通過改變聚類數,而其實這個聚類數大部分時間還是通過人為調試的。
4.3 測定聚類質量
測定聚類質量的方法一般分為“外在方法”和“內在方法”。
所謂的外在方法是一種依靠類別基準的方法,即已經有比較嚴格的類別定義時在討論聚類是不是足夠準確。這里通常使用“BCubed精度”和“BCubed召回率”來進行衡量。而聚類是一種無監督的學習,更多的是不知道基準的狀況下進行的,內在方法更實用。
“內在方法”使用輪廓系數進行度量:
s(v)=b(v)?a(v)max[a(v),b(v)]其中, a(v):是一個向量到本類簇其他點的距離的平均值
b(v):代表一個向量到其他各類簇的最小平均距離(即從其他各類簇中找到離該向量距離最小的向量,然后計算距離),求這些距離的平均值
一般輪廓系數的結果是在-1到1之間。a(v)表示的是類簇內部的緊湊型,b(v)表示該類簇和其他類簇之間的分離程度。函數值越接近1,效果越好。
代碼:
# encoding=utf-8 import numpy as np from sklearn.cluster import KMeans#面積km2,人口 X = [[9670250, 1392358258],[2980000, 1247923065],[9629091, 317408015],[8514877, 201032714],[377873, 127270000],[7692024, 23540517],[9984670, 34591000],[17075400, 143551289],[513115, 67041000],[181035, 14805358],[99600, 50400000],[120538, 24052231]]#轉換成numpy array X = np.array(X)#歸一化 a = X[:, :1] / 17075400.0 * 10000 b = X[:, 1:] / 1392358258.0 * 10000 X = np.concatenate((a, b), axis=1)#類簇的數量 n_clusters = 3cls = KMeans(n_clusters).fit(X)#每個簇的中心點 cls.cluster_centers_#X中每個點所屬的簇 cls.labels_#曼哈頓距離 def manhattan_distance(x, y):return np.sum(abs(x-y))#a(v), X[0]到其他個點的距離的平均值 distance_sum = 0 for v in X[1:]:distance_sum += manhattan_distance(np.array(X[0]), np.array(v)) av = distance_sum / len(X[1:]) print av #11971.5037823#b(v), X[0] distance_min = 100000 for i in range(n_clusters):group = cls.labels_ == imembers = X[group, :]for v in members:if np.array_equal(v, X[0]):continuedistance = manhattan_distance(np.array(v), cls.cluster_centers_)if distance_min > distance:distance_min = distance bv = distance_sum / n_clusters print bv #43895.5138683sv = float(bv - av) / max(av, bv) print sv結果:
11971.5037823 43895.5138683 0.727272727273聚類在生活中還是有很多的應用場景的,例如在向量化相對完整的前提下找出忠誠客戶的共性,找出流式客戶的共性,找出疑似在業務場景中作弊的個案等。
源碼:《白話大數據與機器學習》
總結
- 上一篇: 霸气带秦字的网名 带秦的网名推荐
- 下一篇: 遗传算法求解背包问题