神經網路篇——程式碼實現密度聚類DBSCAN
一提到密度聚類,腦海中立馬就能呈現出一個聚類結果圖,不自然的就感覺非常的簡單,不就是基於密度的聚類嘛,原理不用看也懂了,但是真的實現起來,彷彿又不知道從哪裡開始下手。這時候再仔細回想一下腦海中的密度聚類結果圖,好像和K-means聚類的結果圖是一樣的,那真實的密度聚類是什麼樣子的呢?看了西瓜書的虛擬碼後還是沒法實現?今天小編就帶大家解決一下密度聚類的難點。
實現一個神經網路,一定一定要先明白這個網路的結構,輸入是什麼?輸出是什麼?網路的層級結構是什麼?權值是什麼?每個節點代表的是什麼?網路的工作流程是什麼?
對於密度聚類,有兩個關鍵的要素,一個是密度的最小值,另一個是兩個樣本之間的最大距離。規定了密度最小值就規定了核心樣本鄰域包含資料的最小值,規定兩個樣本之間的最大距離就規定了兩個樣本相聚多遠才算是一類。而且,這兩個值都是需要不斷測試之後才選取的,並不是一次就那麼容易定下來的。另一個需要了解的就是,密度聚類中有核心物件、密度直達、密度可達、和密度相連
核心物件就是指的一個類的核心,滿足兩個條密度聚類的關鍵要素,初始的核心物件有很多,但是經過不斷迭代整合後,核心物件越來越少,到最後一個類形成後,核心物件就是一個抽象的概念,並不能明確的指出這個類的核心物件是哪一個,但一定是初始核心物件中的一個。初始的核心物件的鄰域中,一定包含多個核心物件。
用下圖來區分密度直達,密度可達和密度相連,假設X1為核心物件,那麼X1和X2密度直達,X1和X3是密度可達,X3和X4是密度相連。密度相連就是兩個相聚比較遠的邊緣節點了,密度直達和密度可達距離都比較近。
我們先理清一下密度聚類的過程:
-
首先要找到核心物件,即滿足周圍資料距離小於密度最小值的資料數量大於密度最小值,並且記錄下核心物件的鄰居節點。
-
隨機選取一個核心物件,找到對應的鄰居節點,即密度直達的節點,檢視鄰居節點中包含的核心物件,將這幾個節點記錄下來,並在核心物件列表中刪除包含的核心物件,然後依次遍歷這幾個核心物件和它們的鄰居,按照相同的方法,記錄下的就是密度可達的節點。在遍歷開始時,新增一個判斷條件,判斷這個節點是不是滿足核心節點的條件,如果不滿足,那麼就不再查詢它的鄰域,這些節點就是密度相連節點,也就是這一個類的邊緣節點。
下面就是整個密度聚類的程式碼:
#密度聚類 import numpy as np importrandom import time import copy np.set_printoptions(suppress=True) def euclidean_distance(x, w): # 歐式距離公式√∑(xi﹣wi)² return round(np.linalg.norm(np.subtract(x, w), axis=-1),8) def find_neighbor(j, x, eps): N = list() for i in range(x.shape[0]): temp = euclidean_distance(X[i],X[j]) # 計算歐式距離 print(str(j)+"到",str(i)+"的距離",'%.8f' % temp) if temp <= eps: N.append(i) return set(N) def DBSCAN(X, eps, min_Pts): k = -1 neighbor_list = [] # 用來儲存每個資料的鄰域 omega_list = [] # 核心物件集合 gama = set([x for x in range(len(X))]) # 初始時將所有點標記為未訪問 cluster = [-1 for _ in range(len(X))] # 聚類 for i in range(len(X)): neighbor_list.append(find_neighbor(i, X, eps)) if len(neighbor_list[-1]) + int(count_matrix[i]) >= min_Pts: #如果權值對應位置的資料樣本數量和相似權值的數量之和大於一定的數 omega_list.append(i) # 將樣本加入核心物件集合 omega_list = set(omega_list) # 轉化為集合便於操作 while len(omega_list) > 0: gama_old=copy.deepcopy(gama)#上一狀態未訪問的節點 j = random.choice(list(omega_list)) # 隨機選取一個核心物件 k = k + 1 #第幾個類別 Q = list() Q.append(j) #選出來的核心物件 gama.remove(j)#標記為訪問過 whilelen(Q)>0:#初始Q只有一個,但是後面會擴充 q = Q[0] Q.remove(q) #把遍歷完的節點刪除 #正是下面這一個if決定了密度聚類的邊緣,不滿足if語句的就是密度相連,滿足就是密度直達或者密度可達 iflen(neighbor_list[q])>=min_Pts:#驗證是不是核心物件,找出密度直達 delta=neighbor_list[q]&gama#set的交集,鄰域中包含的未訪問過的資料 deltalist=list(delta) for i in range(len(delta)): Q.append(deltalist[i])#將沒訪問過的節點新增到佇列 gama=gama-delta#節點標記為訪問 Ck=gama_old-gama#記錄這一類中的節點 Cklist = list(Ck) for i in range(len(Ck)): cluster[Cklist[i]] = k #標記這一類的資料 omega_list=omega_list-Ck#刪除核心物件 return cluster 載入資料 X=np.load("檔案位置") X=X.reshape((-1,向量維度))#修改維度 eps=0.0000002#兩個樣本之間的最大距離 min_Pts=20#樣本的最小值 C = DBSCAN(X, eps, min_Pts) C = np.array(C) np.save("classify.npy",C) print("C",C.reshape([X原來的維度]))
注意一點,密度聚類的輸入資料,不管是多少維,用這個程式碼的話都要轉換成一維資料再進行密度聚類。舉個例子,二維資料row行,loc列,那麼資料reshape成一維資料後,當前位置 i 對應的位置就是[(row*loc)+i]。如果有不懂或者有任何問題,歡迎留言討論!
公眾號“神經網路研究所”
還有更多資源在公眾號釋出!