0. 社區划分簡介
0x1:非重疊社區划分方法
在一個網絡里面,每一個樣本只能是屬於一個社區的,那么這樣的問題就稱為非重疊社區划分。
在非重疊社區划分算法里面,有很多的方法:
1. 基於模塊度優化的社區划分
基本思想是將社區划分問題轉換成了模塊度函數的優化,而模塊度是對社區划分算法結果的一個很重要的衡量標准。
模塊度函數在實際求解中無法直接計算得到全局最優解析解(類似深度神經網絡對應的復雜高維非線性函數),所以通常是采用近似解法,根據求解方法不同可以分為以下幾種方法:
1. 凝聚方法(down to top): 通過不斷合並不同社區,實現對整個網絡的社區划分,典型的方法有Newman快速算法,CNM算法和MSG-MV算法; 2. 分裂方法(top to down): 通過不斷的刪除網絡的邊來實現對整個網絡的社區划分,典型的方法有GN算法; 3. 直接近似求解模塊度函數(近似等價解): 通過優化算法直接對模塊度函數進行求解,典型的方法有EO算法;
2. 基於譜分析的社區划分算法
3. 基於信息論的社區划分算法
4. 基於標簽傳播的社區划分算法
undone
Relevant Link:
https://www.cnblogs.com/LittleHann/p/9078909.html
1. Label Propagation簡介
0x1:LPA基本思想
在基本思想上,LPA 和 Kmean 本質非常類似,在 LPA 的每輪迭代中,節點被歸屬於哪個社區,取決於其鄰居中累加權重最大的label(取數量最多的節點列表對應的label是weight=1時的一種特例),而 Kmeans的則是計算和當前節點“最近”的社區,將該節點歸入哪個社區。
但是這兩個算法還是有細微的區別的:
1. 首先: Kmeans是基於歐式空間計算節點向量間的距離的,而LPA則是根據節點間的“共有關系”以及“共有關系的強弱程度”來度量度量節點間的距離; 2. 第二點: Kmeasn中節點處在歐式空間中,它假設所有節點之間都存在“一定的關系”,不同的距離體現了關系的強弱。但是 LPA 中節點間只有滿足“某種共有關系”時,才存在節點間的邊,沒有共有關系的節點是完全隔斷的,計算鄰居節點的時候也不會計算整個圖結構,而是僅僅計算和該節點有邊連接的節點,從這個角度看,LPA 的這個圖結構具有更強的社區型;
0x2:LPA算法優點
0x3:LPA算法缺點
划分結果不穩定,隨機性強是這個算法致命的缺點。具體體現在:
1. 更新順序:節點標簽更新順序隨機,但是很明顯,越重要的節點越早更新會加速收斂過程; 2. 隨機選擇:如果一個節點的出現次數最大的鄰居標簽不止一個時,隨機選擇一個標簽作為自己標簽。這種隨機性可能會帶來一個雪崩效應,即剛開始一個小小的聚類錯誤會不斷被放大。不過話也說話來,如果相似鄰居節點出現多個,可能是weight計算的邏輯有問題,需要回過頭去優化weight抽象和計算邏輯;
0x4:LPA的一個簡單例子
算法初始化:a、b、c、d各自為獨立的社區;
第一輪標簽傳播:
一開始c選擇了a,因為大家的社區標簽都是一樣的,所以隨機選擇了一個;
d也根據自己周圍的鄰居節點來確定標簽數,最多的是a,所以就是d為a了;
繼續標簽傳播:以此類推,最后就全部都是a了;
Relevant Link:
https://www.jianshu.com/p/cff65d7595f9 https://arxiv.org/pdf/0709.2938.pdf https://blog.csdn.net/Katherine_hsr/article/details/82343647 http://sighingnow.github.io/%E7%A4%BE%E4%BC%9A%E7%BD%91%E7%BB%9C/community_detection_k_means_clustering.html
2. LPA算法過程
0x1:算法過程描述
第一步:先給每個節點分配對應標簽,即節點1對應標簽1,節點i對應標簽i;
第二步:遍歷N個節點(for i=1:N),找到對應節點鄰居,獲取此節點鄰居標簽,找到出現次數最大標簽,若出現次數最多標簽不止一個,則隨機選擇一個標簽替換成此節點標簽;
第三步:若本輪標簽重標記后,節點標簽不再變化(或者達到設定的最大迭代次數),則迭代停止,否則重復第二步
0x2:邊權重計算
社區圖結構中邊的權重代表了這兩個節點之間的的“關系強弱”,這個關系的定義取決於具體的場景,例如:
1. 兩個DNS域名共享的client ip數量; 2. 兩個微博ID的共同好友數量;
0x3:標簽傳播方式
LPA標簽傳播分為兩種傳播方式,同步更新,異步更新。
1. 同步更新
同步的意思是實時,即時的意思,每個節點label更新后立即生效,其他節點在統計最近鄰社區的時候,永遠取的是當前圖結構中的最新值。
對於節點,在第 t 輪迭代時,根據其所在節點在第t-1代的標簽進行更新。也就是
,其中
表示的就是節點
在第 t 次迭代時的社區標簽。
函數表示的就是取參數節點中社區標簽最多的。
需要注意的是,這種同步更新的方法會存在一個問題,當遇到二分圖的時候,會出現標簽震盪,如下圖:
這種情況和深度學習中SGD在優化到全局最優點附近時會圍繞最優點附近進行布朗運動(震盪)的原理類似。解決的方法就是設置最大迭代次數,提前停止迭代。
2. 異步更新
異步更新方式可以理解為取了一個當前社區的快照信息,基於上一輪迭代的快照信息來進行本輪的標簽更新。
0x4: 算法代碼
1. 數據集
3列分別是:【node_out,node_in,edge_weitght】
2. 社區初始化
import matplotlib.pyplot as plt import pandas as pd import numpy as np import string def loadData(filePath): f = open(filePath) vector_dict = {} edge_dict = {} for line in f.readlines(): lines = line.strip().split(" ") for i in range(2): if lines[i] not in vector_dict: vector_dict[lines[i]] = int(lines[i]) edge_list = [] if len(lines) == 3: edge_list.append(lines[1 - i] + ":" + lines[2]) else: edge_list.append(lines[1 - i] + ":" + "1") edge_dict[lines[i]] = edge_list else: edge_list = edge_dict[lines[i]] if len(lines) == 3: edge_list.append(lines[1 - i] + ":" + lines[2]) else: edge_list.append(lines[1 - i] + ":" + "1") edge_dict[lines[i]] = edge_list return vector_dict, edge_dict if __name__ == '__main__': filePath = './label_data.txt' vector, edge = loadData(filePath) print(vector) print(edge)
初始化時,所有節點都是一個獨立的社區。
3. LPA社區聚類迭代
# -*- coding: utf-8 -*- import matplotlib.pyplot as plt import pandas as pd import numpy as np import string def loadData(filePath): f = open(filePath) vector_dict = {} edge_dict = {} for line in f.readlines(): lines = line.strip().split(" ") for i in range(2): if lines[i] not in vector_dict: vector_dict[lines[i]] = int(lines[i]) edge_list = [] if len(lines) == 3: edge_list.append(lines[1 - i] + ":" + lines[2]) else: edge_list.append(lines[1 - i] + ":" + "1") edge_dict[lines[i]] = edge_list else: edge_list = edge_dict[lines[i]] if len(lines) == 3: edge_list.append(lines[1 - i] + ":" + lines[2]) else: edge_list.append(lines[1 - i] + ":" + "1") edge_dict[lines[i]] = edge_list return vector_dict, edge_dict def get_max_community_label(vector_dict, adjacency_node_list): label_dict = {} for node in adjacency_node_list: node_id_weight = node.strip().split(":") node_id = node_id_weight[0] node_weight = int(node_id_weight[1]) # 按照label為group維度,統計每個label的weight累加和 if vector_dict[node_id] not in label_dict: label_dict[vector_dict[node_id]] = node_weight else: label_dict[vector_dict[node_id]] += node_weight sort_list = sorted(label_dict.items(), key=lambda d: d[1], reverse=True) return sort_list[0][0] def check(vector_dict, edge_dict): for node in vector_dict.keys(): adjacency_node_list = edge_dict[node] # 獲取該節點的鄰居節點 node_label = vector_dict[node] # 獲取該節點當前label label = get_max_community_label(vector_dict, adjacency_node_list) # 從鄰居節點列表中選擇weight累加和最大的label if node_label >= label: continue else: return 0 # 找到weight權重累加和更大的label return 1 def label_propagation(vector_dict, edge_dict): t = 0 print('First Label: ') while True: if (check(vector_dict, edge_dict) == 0): t = t + 1 print('iteration: ', t) # 每輪迭代都更新一遍所有節點的社區label for node in vector_dict.keys(): adjacency_node_list = edge_dict[node] vector_dict[node] = get_max_community_label(vector_dict, adjacency_node_list) else: break return vector_dict if __name__ == '__main__': filePath = './label_data.txt' vector, edge = loadData(filePath) print "load and initial the community...." #print(vector) #print(edge) print "start lpa clustering...." vector_dict = label_propagation(vector, edge) print "ending lpa clustering...." print "the finnal cluster result...." print(vector_dict) cluster_group = dict() for node in vector_dict.keys(): cluster_id = vector_dict[node] print "cluster_id, node", cluster_id, node if cluster_id not in cluster_group.keys(): cluster_group[cluster_id] = [node] else: cluster_group[cluster_id].append(node) print cluster_group
最后得到的聚類社區為:
{8: ['15', '9', '8'], 13: ['11', '10', '13', '12', '14'], 6: ['3', '7', '6'], 5: ['1', '0', '2', '5', '4']}
Relevant Link:
https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/Label%20Propagation https://www.jianshu.com/p/cff65d7595f9
3. LPA算法改進思路
0x1:標簽隨機選擇改進
給節點或邊添加權重(勢函數、模塊密度優化、LeaderRank值、局部拓撲信息的相似度、標簽從屬系數等),信息熵等描述節點的傳播優先度。
這樣,在進行鄰居節點的最大標簽統計的時候,可以將鄰居節點的weight權值等作為參考因素。
0x2:標簽初始化改進
可以提取一些較為緊密的子結構來作為標簽傳播的初始標簽(例如非重疊最小極大團提取算法),或通過初始社區划分算法先確定社區的雛形再進行傳播。
Relevant Link:
https://www.cnblogs.com/bethansy/p/6953625.html https://blog.csdn.net/zzz24512653/article/details/26151669