日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

社区发现算法——Louvain 算法

發布時間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 社区发现算法——Louvain 算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Louvain 算法

原始論文為:《Fast unfolding of communities in large networks》。

所以又被稱為Fast unfolding算法。

Louvain算法是一種基于模塊度的社區發現算法。其基本思想是網絡中節點嘗試遍歷所有鄰居的社區標簽,并選擇最大化模塊度增量的社區標簽。在最大化模塊度之后,每個社區看成一個新的節點,重復直到模塊度不再增大。

首先復習下模塊度:


這里引入了權重方便擴展到有權圖,但其實對于無權圖,可以看做所有邊權重為1,這時候就等于用節點的度計算,用度理解一樣。

算法詳述:

  • 模塊度優化階段:每個節點將自己作為自己社區標簽。每個節點遍歷自己的所有鄰居節點,嘗試將自己的社區標簽更新成鄰居節點的社區標簽,選擇模塊度增量最大(貪婪思想)的社區標簽,直到所有節點都不能通過改變社區標簽來增加模塊度

    也就是說,首先將每個節點指定到唯一的一個社區,然后按順序將節點在這些社區間進行移動。怎么移動呢?假設有一節點 i ,它有三個鄰居節點 j1, j2, j3,我們分別嘗試將節點 i 移動到 j1, j2, j3 所在的社區,并計算相應的 modularity 變化值ΔQ,哪個變化值最大就將節點 i 移動到相應的社區中去(當然,這里我們要求最大的 modularity 變化值要為正,如果變化值均為負,則節點 i 保持不動)。按照這個方法反復迭代,直到網絡中任何節點的移動都不能再改善總的 modularity 值為止。

    移動到一個社區C中所獲得的模塊化Q增益可以很容易地計算出來:

    =12m(ki,in?Σtotkim)\frac{1}{2m}(k_{i,in}-{Σ_{tot}ki\over m})2m1?(ki,in??mΣtot?ki?)

    其中ΣinΣ_{in}Σin?是在社區C內的鏈路的權重總和(權重為1時就等于度數),如果是初始情況,即一個節點作為一個社區時,它就為這個節點自己到自己的連接,這時候仍然需要起點加weight,終點加weight(即使這個時候起點和終點為同一節點,對于無環圖權為0)

    ΣtotΣ_{tot}Σtot?是關聯到C中的節點的鏈路上的權重的總和

    ki是關聯到節點i的鏈路的權重的總和

    ki,in是從節點i連接到C中的節點的鏈路的總和

    m是網絡中的所有鏈路的權重的總和

  • 網絡凝聚階段:每個社區合并為一個新的超級節點,超級節點的邊權重為原始社區內所有節點的邊權重之和,形成一個新的網絡

    將第一個階段得到的社區視為新的“節點”(一個社區對應一個),重新構造子圖,兩個新“節點”之間邊的權值為相應兩個社區之間各邊的權值的總和。如這個圖所示,如果第一個階段得到的社區有以下三個,那么第二個階段的任務就是將這三個社分別看一個新的節點,然后將任意兩個新節點之間的所有連線的權重相加得到的和,作為這兩個新節點之間的連線的權重。

  • 上述兩個階段迭代進行,直到模塊度不再增大。
    ??下圖很好的描述了這兩個階段。第一次迭代,經過模塊度優化階段,總的 modularity 值不再改變,算法將16個節點劃分成4個社區;在網絡凝聚階段,4個社區被凝聚成4個超級節點,并重新更新了邊權重。之后就進入第二次迭代中。

    算法通過引入分步迭代的思想(類比EM算法),避免陷入貪婪思想導致的局部最優。

    算法流程:

    1、初始時將每個頂點當作一個社區,社區個數與頂點個數相同。
    2、依次將每個頂點與之相鄰頂點合并在一起,計算它們最大的模塊度增益是否大于0,如果大于0,就將該結點放入模塊度增量最大的相鄰結點所在社區。
    3、迭代第二步,直至算法穩定,即所有頂點所屬社區不再變化。
    4、將各個社區所有節點壓縮成為一個結點,社區內點的權重轉化為新結點環的權重,社區間權重轉化為新結點邊的權重。
    5、重復步驟1-3,直至算法穩定。

    可以看到Louvain 算法與FN算法非常相似,我覺得最大的不同時Louvain 算法在凝聚階段是將整個社區看做一個新的超節點來看,而FN算法是以社區的觀點來凝聚。

    算法優缺點

    優點

    1、時間復雜度低,適合大規模的網絡。
    ??2、社區劃分結果穩定,有具體指標能夠評估社區劃分好壞。
    ??3、消除了模塊化分辨率的限制。由于模塊度是全局指標,最大化的時候很難發現小型的社區,很容易將小型社區合并。而算法第一次迭代是以單個節點作為社區的粒度,規避了這種問題。
    ??4、天然自帶層次化的社區劃分結果,每一次迭代的社區劃分結果都可以保留下來,作為社區劃分的中間結果,可供選擇。

    缺點

    1、社區過大,不能及時收斂。如果我們將模塊度類比為損失函數的話,Fast Unfolding在模塊度優化階段采用的貪婪思想很容易將整個社區劃分“過擬合”。因為Fast Unfolding是針對點遍歷,很容易將一些外圍的點加入到原本緊湊的社區中,導致一些錯誤的合并。這種劃分有時候在局部視角是優的,但是全局視角下會變成劣的。
    ??
    ??Python代碼如下:代碼與數據下載

    class Louvain:def __init__(self, G):self._G = Gself._m = 0 # 邊數量 圖會凝聚動態變化self._cid_vertices = {} # 需維護的關于社區的信息(社區編號,其中包含的結點編號的集合)self._vid_vertex = {} # 需維護的關于結點的信息(結點編號,相應的Vertex實例)for vid in self._G.keys():# 剛開始每個點作為一個社區self._cid_vertices[vid] = {vid}# 剛開始社區編號就是節點編號self._vid_vertex[vid] = Vertex(vid, vid, {vid})# 計算邊數 每兩個點維護一條邊self._m += sum([1 for neighbor in self._G[vid].keys() if neighbor > vid])# 模塊度優化階段def first_stage(self):mod_inc = False # 用于判斷算法是否可終止visit_sequence = self._G.keys()# 隨機訪問random.shuffle(list(visit_sequence))while True:can_stop = True # 第一階段是否可終止# 遍歷所有節點for v_vid in visit_sequence:# 獲得節點的社區編號v_cid = self._vid_vertex[v_vid]._cid# k_v節點的權重(度數) 內部與外部邊權重之和k_v = sum(self._G[v_vid].values()) + self._vid_vertex[v_vid]._kin# 存儲模塊度增益大于0的社區編號cid_Q = {}# 遍歷節點的鄰居for w_vid in self._G[v_vid].keys():# 獲得該鄰居的社區編號w_cid = self._vid_vertex[w_vid]._cidif w_cid in cid_Q:continueelse:# tot是關聯到社區C中的節點的鏈路上的權重的總和tot = sum([sum(self._G[k].values()) + self._vid_vertex[k]._kin for k in self._cid_vertices[w_cid]])if w_cid == v_cid:tot -= k_v# k_v_in是從節點i連接到C中的節點的鏈路的總和k_v_in = sum([v for k, v in self._G[v_vid].items() if k in self._cid_vertices[w_cid]])delta_Q = k_v_in - k_v * tot / self._m # 由于只需要知道delta_Q的正負,所以少乘了1/(2*self._m)cid_Q[w_cid] = delta_Q# 取得最大增益的編號cid, max_delta_Q = sorted(cid_Q.items(), key=lambda item: item[1], reverse=True)[0]if max_delta_Q > 0.0 and cid != v_cid:# 讓該節點的社區編號變為取得最大增益鄰居節點的編號self._vid_vertex[v_vid]._cid = cid# 在該社區編號下添加該節點self._cid_vertices[cid].add(v_vid)# 以前的社區中去除該節點self._cid_vertices[v_cid].remove(v_vid)# 模塊度還能增加 繼續迭代can_stop = Falsemod_inc = Trueif can_stop:breakreturn mod_inc# 網絡凝聚階段def second_stage(self):cid_vertices = {}vid_vertex = {}# 遍歷社區和社區內的節點for cid, vertices in self._cid_vertices.items():if len(vertices) == 0:continuenew_vertex = Vertex(cid, cid, set())# 將該社區內的所有點看做一個點for vid in vertices:new_vertex._nodes.update(self._vid_vertex[vid]._nodes)new_vertex._kin += self._vid_vertex[vid]._kin# k,v為鄰居和它們之間邊的權重 計算kin社區內部總權重 這里遍歷vid的每一個在社區內的鄰居 因為邊被兩點共享后面還會計算 所以權重/2for k, v in self._G[vid].items():if k in vertices:new_vertex._kin += v / 2.0# 新的社區與節點編號cid_vertices[cid] = {cid}vid_vertex[cid] = new_vertexG = collections.defaultdict(dict)# 遍歷現在不為空的社區編號 求社區之間邊的權重for cid1, vertices1 in self._cid_vertices.items():if len(vertices1) == 0:continuefor cid2, vertices2 in self._cid_vertices.items():# 找到cid后另一個不為空的社區if cid2 <= cid1 or len(vertices2) == 0:continueedge_weight = 0.0# 遍歷 cid1社區中的點for vid in vertices1:# 遍歷該點在社區2的鄰居已經之間邊的權重(即兩個社區之間邊的總權重 將多條邊看做一條邊)for k, v in self._G[vid].items():if k in vertices2:edge_weight += vif edge_weight != 0:G[cid1][cid2] = edge_weightG[cid2][cid1] = edge_weight# 更新社區和點 每個社區看做一個點self._cid_vertices = cid_verticesself._vid_vertex = vid_vertexself._G = Gdef get_communities(self):communities = []for vertices in self._cid_vertices.values():if len(vertices) != 0:c = set()for vid in vertices:c.update(self._vid_vertex[vid]._nodes)communities.append(list(c))return communitiesdef execute(self):iter_time = 1while True:iter_time += 1# 反復迭代,直到網絡中任何節點的移動都不能再改善總的 modularity 值為止mod_inc = self.first_stage()if mod_inc:self.second_stage()else:breakreturn self.get_communities()

    參考結果如下:

    社區 1 [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 16, 17, 19, 21]
    社區 2 [32, 33, 8, 14, 15, 18, 20, 22, 26, 29, 30]
    社區 3 [23, 24, 25, 27, 28, 31]
    0.388560157790927
    算法執行時間0.002000093460083008

    總結

    以上是生活随笔為你收集整理的社区发现算法——Louvain 算法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。