复现经典:《统计学习方法》第14章 聚类方法
第14章聚類方法
本文是李航老師的《統計學習方法》一書的代碼復現。作者:黃海廣
備注:代碼都可以在github中下載。我將陸續將代碼發布在公眾號“機器學習初學者”,可以在這個專輯在線閱讀。
1.聚類是針對給定的樣本,依據它們屬性的相似度或距離,將其歸并到若干個“類”或“簇”的數據分析問題。一個類是樣本的一個子集。直觀上,相似的樣本聚集在同類,不相似的樣本分散在不同類。
2.距離或相似度度量在聚類中起著重要作用。
常用的距離度量有閔可夫斯基距離,包括歐氏距離曼哈頓距離、切比雪夫距離、、以及馬哈拉諾比斯距離。常用的相似度度量有相關系數、夾角余弦。用距離度量相似度時,距離越小表示樣本越相似;用相關系數時,相關系數越大表示樣本越相似。
3.類是樣本的子集,比如有如下基本定義:用表示類或簇,用,;等表示類中的樣本,用表示樣本與樣本之間的距離。如果對任意的,有
則稱為一個類或簇。描述類的特征的指標有中心、直徑、散布矩陣、協方差矩陣。
4.聚類過程中用到類與類之間的距離也稱為連接類與類之間的距離包括最短距離、最長距離、中心距離、平均距離。
5.層次聚類假設類別之間存在層次結構,將樣本聚到層次化的類中層次聚類又有聚合或自下而上、分裂或自上而下兩種方法。
聚合聚類開始將每個樣本各自分到一個類;之后將相距最近的兩類合并,建立一個新的類,重復此操作直到滿足停止條件;得到層次化的類別。分裂聚類開始將所有樣本分到一個類;之后將已有類中相距最遠的樣本分到兩個新的類,重復此操作直到滿足停止條件;得到層次化的類別。
聚合聚類需要預先確定下面三個要素:
(1)距離或相似度;(2)合并規則;(3)停止條件。
根據這些概念的不同組合,就可以得到不同的聚類方法。
6.均值聚類是常用的聚類算法,有以下特點。基于劃分的聚類方法;類別數k事先指定;以歐氏距離平方表示樣本之間的距離或相似度,以中心或樣本的均值表示類別;以樣本和其所屬類的中心之間的距離的總和為優化的目標函數;得到的類別是平坦的、非層次化的;算法是迭代算法,不能保證得到全局最優。
均值聚類算法,首先選擇k個類的中心,將樣本分到與中心最近的類中,得到一個聚類結果;然后計算每個類的樣本的均值,作為類的新的中心;重復以上步驟,直到收斂為止。
層次聚類
聚合(自下而上):聚合法開始將每個樣本各自分裂到一個類,之后將相距最近的兩類合并,建立一個新的類,重復次操作知道滿足停止條件,得到層次化的類別。
分裂(自上而下):分裂法開始將所有樣本分到一個類,之后將已有類中相距最遠的樣本分到兩個新的類,重復此操作直到滿足停止條件,得到層次化的類別。
k均值聚類
k均值聚類是基于中心的聚類方法,通過迭代,將樣本分到k個類中,使得每個樣本與其所屬類的中心或均值最近,得到k個平坦的,非層次化的類別,構成對空間的劃分。
import?math import?random import?numpy?as?np from?sklearn?import?datasets,cluster import?matplotlib.pyplot?as?plt iris?=?datasets.load_iris() gt?=?iris['target'];gt array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])3類
iris['data'][:,:2].shape (150, 2) data?=?iris['data'][:,:2] x?=?data[:,0] y?=?data[:,1] plt.scatter(x,?y,?color='green') plt.xlim(4,?8) plt.ylim(1,?5) plt.show() #?定義聚類數的節點class?ClusterNode:def?__init__(self,?vec,?left=None,?right=None,?distance=-1,?id=None,?count=1):""":param?vec:?保存兩個數據聚類后形成新的中心:param?left:?左節點:param?right:??右節點:param?distance:?兩個節點的距離:param?id:?用來標記哪些節點是計算過的:param?count:?這個節點的葉子節點個數"""self.vec?=?vecself.left?=?leftself.right?=?rightself.distance?=?distanceself.id?=?idself.count?=?count def?euler_distance(point1:?np.ndarray,?point2:?list)?->?float:"""計算兩點之間的歐拉距離,支持多維"""distance?=?0.0for?a,?b?in?zip(point1,?point2):distance?+=?math.pow(a?-?b,?2)return?math.sqrt(distance) #?層次聚類(聚合法)class?Hierarchical:def?__init__(self,?k):self.k?=?kself.labels?=?Nonedef?fit(self,?x):nodes?=?[ClusterNode(vec=v,?id=i)?for?i,?v?in?enumerate(x)]distances?=?{}point_num,?feature_num?=?x.shapeself.labels?=?[-1]?*?point_numcurrentclustid?=?-1while(len(nodes))?>?self.k:min_dist?=?math.infnodes_len?=?len(nodes)closest_part?=?Nonefor?i?in?range(nodes_len?-?1):for?j?in?range(i+1,?nodes_len):d_key?=?(nodes[i].id,?nodes[j].id)if?d_key?not?in?distances:distances[d_key]?=?euler_distance(nodes[i].vec,?nodes[j].vec)d?=?distances[d_key]if?d?<?min_dist:min_dist?=?dclosest_part?=?(i,?j)part1,?part2?=?closest_partnode1,?node2?=?nodes[part1],?nodes[part2]new_vec?=?[?(node1.vec[i]?*?node1.count?+?node2.vec[i]?*?node2.count?)?/?(node1.count?+?node2.count)for?i?in?range(feature_num)]new_node?=?ClusterNode(vec=new_vec,left=node1,right=node2,distance=min_dist,id=currentclustid,count=node1.count?+?node2.count)currentclustid?-=?1del?nodes[part2],?nodes[part1]nodes.append(new_node)self.nodes?=?nodesself.calc_label()def?calc_label(self):"""調取聚類的結果"""for?i,?node?in?enumerate(self.nodes):#?將節點的所有葉子節點都分類self.leaf_traversal(node,?i)def?leaf_traversal(self,?node:?ClusterNode,?label):"""遞歸遍歷葉子節點"""if?node.left?==?None?and?node.right?==?None:self.labels[node.id]?=?labelif?node.left:self.leaf_traversal(node.left,?label)if?node.right:self.leaf_traversal(node.right,?label)#?https://zhuanlan.zhihu.com/p/32438294 my?=?Hierarchical(3) my.fit(data) labels?=?np.array(my.labels) print(labels) [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 22 2 2 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 2 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 2 0 0 0 1 0 0 1 2 1 0 1 00 0 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 00 0] #?visualize?resultcat1?=?data[np.where(labels==0)] cat2?=?data[np.where(labels==1)] cat3?=?data[np.where(labels==2)]plt.scatter(cat1[:,0],?cat1[:,1],?color='green') plt.scatter(cat2[:,0],?cat2[:,1],?color='red') plt.scatter(cat3[:,0],?cat3[:,1],?color='blue') plt.title('Hierarchical?clustering?with?k=3') plt.xlim(4,?8) plt.ylim(1,?5) plt.show() sk?=?cluster.AgglomerativeClustering(3) sk.fit(data) labels_?=?sk.labels_ print(labels_) [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 2 0 2 0 1 0 1 1 0 2 0 2 0 2 2 2 2 0 0 2 00 0 0 0 0 2 2 2 2 0 2 0 0 2 2 2 2 0 2 1 2 2 2 0 1 2 0 2 0 0 0 0 1 0 0 0 00 0 2 2 0 0 0 0 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 2 00 0] #?visualize?result?of?sklearncat1_?=?data[np.where(labels_==0)] cat2_?=?data[np.where(labels_==1)] cat3_?=?data[np.where(labels_==2)]plt.scatter(cat1_[:,0],?cat1_[:,1],?color='green') plt.scatter(cat2_[:,0],?cat2_[:,1],?color='red') plt.scatter(cat3_[:,0],?cat3_[:,1],?color='blue') plt.title('Hierarchical?clustering?with?k=3') plt.xlim(4,?8) plt.ylim(1,?5) plt.show() #?kmeansclass?MyKmeans:def?__init__(self,?k,?n=20):self.k?=?kself.n?=?ndef?fit(self,?x,?centers=None):#?第一步,隨機選擇?K?個點,?或者指定if?centers?is?None:idx?=?np.random.randint(low=0,?high=len(x),?size=self.k)centers?=?x[idx]#print(centers)inters?=?0while?inters?<?self.n:#print(inters)#print(centers)points_set?=?{key:?[]?for?key?in?range(self.k)}#?第二步,遍歷所有點?P,將?P?放入最近的聚類中心的集合中for?p?in?x:nearest_index?=?np.argmin(np.sum((centers?-?p)?**?2,?axis=1)?**?0.5)points_set[nearest_index].append(p)#?第三步,遍歷每一個點集,計算新的聚類中心for?i_k?in?range(self.k):centers[i_k]?=?sum(points_set[i_k])/len(points_set[i_k])inters?+=?1return?points_set,?centersm?=?MyKmeans(3) points_set,?centers?=?m.fit(data) centers array([[5.006 , 3.428 ],[6.81276596, 3.07446809],[5.77358491, 2.69245283]]) #?visualize?resultcat1?=?np.asarray(points_set[0]) cat2?=?np.asarray(points_set[1]) cat3?=?np.asarray(points_set[2])for?ix,?p?in?enumerate(centers):plt.scatter(p[0],?p[1],?color='C{}'.format(ix),?marker='^',?edgecolor='black',?s=256)plt.scatter(cat1_[:,0],?cat1_[:,1],?color='green') plt.scatter(cat2_[:,0],?cat2_[:,1],?color='red') plt.scatter(cat3_[:,0],?cat3_[:,1],?color='blue') plt.title('Hierarchical?clustering?with?k=3') plt.xlim(4,?8) plt.ylim(1,?5) plt.show() #?using?sklearn from?sklearn.cluster?import?KMeans kmeans?=?KMeans(n_clusters=3,?max_iter=100).fit(data) gt_labels__?=?kmeans.labels_ centers__?=?kmeans.cluster_centers_ gt_labels__ array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 0, 0, 0, 2, 0, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0,2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0,0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0,0, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2]) centers__ array([[6.81276596, 3.07446809],[5.006 , 3.428 ],[5.77358491, 2.69245283]]) #?visualize?resultcat1?=?data[gt_labels__?==?0] cat2?=?data[gt_labels__?==?1] cat3?=?data[gt_labels__?==?2]for?ix,?p?in?enumerate(centers__):plt.scatter(p[0],?p[1],?color='C{}'.format(ix),?marker='^',?edgecolor='black',?s=256)plt.scatter(cat1_[:,0],?cat1_[:,1],?color='green') plt.scatter(cat2_[:,0],?cat2_[:,1],?color='red') plt.scatter(cat3_[:,0],?cat3_[:,1],?color='blue') plt.title('kmeans?using?sklearn?with?k=3') plt.xlim(4,?8) plt.ylim(1,?5) plt.show()尋找 K 值
from?sklearn.cluster?import?KMeansloss?=?[]for?i?in?range(1,?10):kmeans?=?KMeans(n_clusters=i,?max_iter=100).fit(data)loss.append(kmeans.inertia_?/?len(data)?/?3)plt.title('K?with?loss') plt.plot(range(1,?10),?loss) plt.show()例 14.2
X?=?[[0,?2],?[0,?0],?[1,?0],?[5,?0],?[5,?2]] np.asarray(X) array([[0, 2],[0, 0],[1, 0],[5, 0],[5, 2]]) m?=?MyKmeans(2,?100) points_set,?centers?=?m.fit(np.asarray(X)) points_set {0: [array([0, 2]), array([0, 0]), array([1, 0])],1: [array([5, 0]), array([5, 2])]} centers array([[0, 0],[5, 1]]) kmeans?=?KMeans(n_clusters=2,?max_iter=100).fit(np.asarray(X)) kmeans.labels_ array([0, 0, 0, 1, 1]) kmeans.cluster_centers_ array([[0.33333333, 0.66666667],[5. , 1. ]])本章代碼來源:https://github.com/hktxt/Learn-Statistical-Learning-Method
下載地址
https://github.com/fengdu78/lihang-code
參考資料:
[1] 《統計學習方法》: https://baike.baidu.com/item/統計學習方法/10430179
[2] 黃海廣: https://github.com/fengdu78
[3] ?github: https://github.com/fengdu78/lihang-code
[4] ?wzyonggege: https://github.com/wzyonggege/statistical-learning-method
[5] ?WenDesi: https://github.com/WenDesi/lihang_book_algorithm
[6] ?火燙火燙的: https://blog.csdn.net/tudaodiaozhale
[7] ?hktxt: https://github.com/hktxt/Learn-Statistical-Learning-Method
總結
以上是生活随笔為你收集整理的复现经典:《统计学习方法》第14章 聚类方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python入门】一个有意思还有用的P
- 下一篇: 复现经典:《统计学习方法》第12章 监督