Louvain 算法原理及設計實現

奇技指南 在海量的信息流中,經過精準的算法給用戶推薦其感興趣的內容,已經成爲了一個產品吸引用戶,獲取收益的極其重要的方式。
本篇爲算法系列文章,將爲你們分享360的算法團隊在實踐中積累的算法知識及經驗,歡迎你們交流分享~

Louvain算法是一種基於多層次優化Modularity的算法,具備快速、準確的優勢,在效率和效果上都表現比較好,而且可以發現層次性的社區結構,被認爲是性能最好的社區發現算法之一。git

模塊度

Louvain算法是一種基於圖數據的社區發現算法。
原始論文爲:
《Fast unfolding of communities in large networks》。github

Louvain算法的優化目標爲最大化整個數據的模塊度,算法

模塊度的計算以下:
圖片描述
其中m爲圖中邊的總數量,k_i表示全部指向節點i的連邊權重之和,k_j同理。A_{i,j} 表示節點i,j之間的連邊權重。網絡

有一點要搞清楚,模塊度的概念不是Louvain算法發明的,而Louvain算法只是一種優化關係圖模塊度目標的一種實現而已。數據結構

Louvain算法的兩步迭代設計

最開始,每一個原始節點都當作一個獨立的社區,社區內的連邊權重爲0
步驟1
算法掃描數據中的全部節點,針對每一個節點遍歷該節點的全部鄰居節點,衡量把該節點加入其鄰居節點所在的社區所帶來的模塊度的收益。並選擇對應最大收益的鄰居節點,加入其所在的社區。這一過程化重複進行指導每個節點的社區歸屬都不在發生變化。
步驟2
對步驟1中造成的社區進行摺疊,把每一個社區摺疊成一個單點,分別計算這些新生成的「社區點」之間的連邊權重,以及社區內的全部點之間的連邊權重之和。用於下一輪的步驟1。性能

該算法的最大優點就是速度很快,步驟1的每次迭代的時間複雜度爲O(N),N爲輸入數據中的邊的數量。步驟2 的時間複雜度爲O(M + N), M爲本輪迭代中點的個數。學習

算法實現

數據結構設計

算法數據結構的設計主要有兩方面的考慮:測試

  1. 如何高效地存儲圖中的節點和節點之間的關係
  2. 如何在設計的數據結構上高效地掃描數據、進行算法迭代。

當前一些開源的算法實現主要經過hash表或set的結構來存儲節點和節點之間的關係。主要有兩個缺點:優化

  1. 維護hash 或 集合結構自己就須要很多內存開銷
  2. 遍歷過程當中須要不斷地建立、銷燬、清空對應的Hash 或 Set 結構,尤爲是在遍歷不一樣的節點的鄰居節點以及社區這點時。

並且,在遍歷過程當中,結構對元素的訪問也並非嚴格O(1)的。spa

出於以上考慮,咱們設計一種更高效的數據結構來存儲圖中的節點和邊,避開使用複雜的數據結構,且在算法迭代過程當中不申請多餘的空間和空間的銷燬操做,具體以下:
圖片描述

關於節點字段的說明:

  • count:社區內的節點個數
  • clsid:節點歸屬社區的表明節點ID
  • next:步驟1迭代中下一個屬於同一個臨時社區的節點
  • prev:步驟1迭代中上一個屬於同一個臨時社區的節點
  • first:屬於同一個社區的,除表明節點外的第一個節點,該節點有步驟2 社區摺疊的時候生成
  • kin:穩定社區內部節點之間的互相鏈接權重之和
  • kout:穩定社區外部,指向本身社區的權重之和
  • clskin:臨時社區內部節點之間的互相鏈接權重之和
  • clstot:穩定社區全部內外部指向本身的鏈接權重之和
  • eindex:節點鄰居鏈表的第一個指針,該鏈表下的全部left,都是本節點本身

關於邊數據結構的字段就顧名思義便可。

基於上述結構設計,在給定了一個M個節點,N調邊的圖所需的空間爲:60 M + 24 N.
例如:給定1000萬給點,2000萬邊的數據,則須要空間約爲:10000000 60 + 20000000 24 = 1080M.且整個迭代過程當中內存環境維持不變。

迭代過程

一、假設咱們最開始有5個點,互相之間存在必定的關係(至於什麼關係,先無論),以下:
圖片描述
二、假設在進過了步驟1的充分迭代以後發現節點2,應該加入到節點1所在的社區(最開始每一個點都是一個社區,而本身就是這個社區的表明),新的社區由節點1表明,以下:
圖片描述
此時節點3,4,5之間以及與節點1,2之間沒有任何歸屬關係。

三、此時應該執行步驟2,將節點1,2組合成的新社區進行摺疊,摺疊以後的社區當作一個單點,用節點1來表明,以下:
圖片描述
此時數據中共有4個節點(或者說4個社區),其中一個社區包含了兩個節點,而社區3,4,5都只包含一個節點,即他們本身。

四、從新執行步驟1,對社區1,3,4,5進行掃描,假設在充分迭代以後節點5,4,3分別前後都加入了節點1所在的社區,以下:

圖片描述
五、進行步驟2,對新生成的社區進行摺疊,新摺疊而成的社區當作一個單點,由節點1表明,結構以下:
圖片描述
此時因爲整個數據中只剩下1個社區,即由節點1表明的社區。

再進行步驟1時不會有任何一個節點的社區歸屬發生變化,此時也就不須要再執行步驟2,至此, 迭代結束。

代碼實現及測試

一個基於上述結構設計的代碼實現參見:
https://github.com/liuzhiqian...

在一個實際的圖(70萬點,200萬邊)上進行測試,迭代到徹底收斂所需時間爲:1.77秒。
實際中每每不須要迭代到每個點都不發生變化,或者整個圖中有多少比例的節點不在發生變化就退出。

本篇爲算法系列文章的第3篇,爲你們分享了Louvain算法的原理及設計實現。
本文來自360視頻信息流算法團隊投稿,咱們將每週爲你們推送一篇算法相關的文章,歡迎你們一塊兒交流學習。

相關推薦

深度殘差網絡的一波兩折
淺談 梯度降低法/Gradient descent

本文爲360技術原創內容,轉載請務必註明出處並保留文末二維碼,謝謝~
圖片描述

相關文章
相關標籤/搜索