Louvain 算法原理 及设计实现

模块度:

Louvain算法是一种基于图数据的社区发现算法。原始论文为:《Fast unfolding of communities in large networks》。

算法的优化目标为最大化整个数据的模块度,模块度的计算如下:


其中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)的。

出于以上考虑,我们设计一种更高效的数据结构来存储图中的节点和边,避开使用复杂的数据结构,且在算法迭代过程中不申请多余的空间和空间的销毁操作,具体如下:

关于节点字段的说明:

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.且整个迭代过程中内存环境维持不变。

迭代过程:

1, 假设我们最开始有5个点,互相之间存在一定的关系(至于什么关系,先不管),如下:


2, 假设在进过了步骤1的充分迭代之后发现节点2,应该加入到节点1所在的社区(最开始每个点都是一个社区,而自己就是这个社区的代表),新的社区由节点1代表,如下:


此时节点3,4,5之间以及与节点1,2之间没有任何归属关系。

3, 此时应该执行步骤2,将节点1,2组合成的新社区进行折叠,折叠之后的社区看成一个单点,用节点1来代表,如下:


此时数据中共有4个节点(或者说4个社区),其中一个社区包含了两个节点,而社区3,4,5都只包含一个节点,即他们自己。

4, 重新执行步骤1,对社区1,3,4,5进行扫描,假设在充分迭代之后节点5,4,3分别先后都加入了节点1所在的社区,如下:


5, 进行步骤2,对新生成的社区进行折叠,新折叠而成的社区看成一个单点,由节点1代表,结构如下:


此时由于整个数据中只剩下1个社区,即由节点1代表的社区。

再进行步骤1时不会有任何一个节点的社区归属发生变化,此时也就不需要再执行步骤2,至此, 迭代结束。



代码实现及测试:

一个基于上述结构设计的代码实现参见:
https://github.com/liuzhiqiangruc/dml/blob/master/cls/louvain.c

在一个实际的图(70万点,200万边)上进行测试,迭代到完全收敛所需时间为:1.77秒。
实际中往往不需要迭代到每一个点都不发生变化,或者整个图中有多少比例的节点不在发生变化就退出。



  • 18
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 以下是Louvain算法的Matlab实现: ```matlab function [C,Q]=louvain(A) % Input: A: adjacency matrix of the network % Output: C: community structure % Q: modularity n=length(A); m=sum(sum(A)); k=sum(A); B=A-m'*m/(2*m); C=1:n; Q=0; while 1 [Ci,Qi]=modularity(C,B); if Qi-Q<1e-10 break end for i=1:max(Ci) nodes=find(Ci==i); B(nodes,nodes)=B(nodes,nodes)-sum(sum(A(nodes,nodes)))/(2*m); end [C,Q]=reindex(Ci,Qi); end function [C,Q]=modularity(C,B) m=length(C); mk=C'*C; Q=0; for i=1:m ki=sum(B(i,:)); if ki>0 Q=Q+(B(i,:)*C==C(i))*(1-mk(i)/(2*m))/2/ki; end end function [C,Q]=reindex(C,Q) A=unique(C); B=zeros(max(A),1); B(A)=1:length(A); C=B(C); ``` 调用方式如下: ```matlab [C,Q]=louvain(A); ``` 其中,A为邻接矩阵,C为社区划分结果,Q为模块度。这个函数使用了Matlab自带的unique函数,因此需要注意输入的邻接矩阵A必须是非负整数矩阵。 ### 回答2: Louvain算法是一种常用于社区发现的图分析算法。它通过不断优化社区结构的方式,将图中的节点划分为不同的社区。下面是Louvain算法在Matlab中的实现步骤: 1. 构建图:首先,根据图的节点和边的信息,通过Matlab中的图数据结构来表示图。 2. 初始化社区:将每个节点作为一个单独的社区,并为每个节点分配一个唯一的社区标识符。 3. 迭代优化:在每一轮迭代中,遍历图中的每个节点,计算将该节点移动到相邻节点所属社区所带来的模块度增益。 4. 社区合并:找出具有最大模块度增益的节点,并将其移动到相邻节点所属的社区中。同时,更新其他节点的社区标识符。 5. 终止条件:如果在当前轮迭代中没有任何节点发生社区变化,停止迭代。 6. 输出结果:返回最终优化后的社区划分结果。 在Matlab中,可以使用类似以下的伪代码实现Louvain算法: ``` 1. 构建图 2. 初始化社区 3. while(true) 4. for each node in graph 5. for each neighbor of node 6. calculate modularity gain by moving node to neighbor's community 7. find node with the maximum modularity gain 8. if the maximum modularity gain is non-positive 9. break 10. move the node to the community of its neighbor with the maximum modularity gain 11. update community labels for other nodes 12. end while 13. return the optimized community partition ``` 这是一个简单的Louvain算法实现,可以根据具体的需求进行优化和改进。 ### 回答3: Louvain算法是一种用于发现社区结构的层次聚类算法,可用于图数据的社区检测。这个算法的主要思想是将图中的节点进行合并,从而形成更大的社区,直到无法再合并为止,得到最终的社区划分结果。 在使用Matlab实现Louvain算法之前,我们首先需要导入图数据,可以使用Matlab的图对象来表示图数据。接下来,我们可以按照以下步骤实现Louvain算法: 1. 初始化每个节点为一个单独的社区,并计算图的模块度Q。 2. 遍历每个节点,计算将其与其邻居节点合并后的模块度增益值。 3. 找到增益值最大的节点,将其与邻居节点合并,更新社区划分。 4. 重复步骤2和步骤3,直到没有节点可以合并为止。 5. 得到最终的社区划分结果。 在实现过程中,我们需要使用一些辅助函数来进行计算,如计算社区的模块度、计算节点的度等。此外,我们还需要用到循环和条件判断语句来实现算法的迭代过程。 实现Louvain算法时,还可以通过优化算法的效率和准确性来改进算法,如限制迭代次数、添加终止条件等。 总而言之,Louvain算法的Matlab实现可以通过使用图对象、计算模块度和增益值、节点合并等步骤来实现。通过编写相应的函数和循环迭代,我们可以得到图数据的社区划分结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值