转:模块度(Modularity)与Fast Newman算法讲解与代码实现

https://blog.csdn.net/marywbrown/article/details/62059231

一、背景介绍
  Modularity(模块度), 这个概念是2003年一个叫Newman的人提出的。这个人先后发表了很多关于社区划分的论文,包括2002年发表的著名的Girvan-Newman(G-N)算法,和2004发表的Fast Newman(F-B)算法,Modularity就是F-B算法中提出的(03年提出的概念,04年确认发表)。
  在2006年的时候Newman重新定义了Modularity,实际上只是对原来的公式做了变换,使其适用于Spectral Optimization Algorithms。
  早期的算法不能够很好的确认什么样的社区划分是最优的。Modularity这个概念就是为了定义一个对于社区划分结果优劣的评判。
二、Modularity
  我们先从06年的定义讲起:
 
Q=12m∑vw[Avw−kvkw2m]δ(cv,cw)(1)(1)Q=12m∑vw[Avw−kvkw2m]δ(cv,cw)
  Q就是模块度,模块度越大则表明社区划分效果越好。Q值的范围在[-0.5,1),论文表示当Q值在0.3~0.7之间时,说明聚类的效果很好。
  我们现在假设有xx个节点,每个节点都代表一个输入,并且我们已经将这些输入划分为了NN个社区,节点彼此之间共有mm个连接。vv和ww是xx中的任意两个节点,当两个节点直接相连时Avw=1Avw=1,否则Avw=0Avw=0。kvkv代表的是节点vv的度,度是图论的基础知识,从一个节点出发有几个边,我们就说这个节点的度是多少。很容易理解,这里的2m2m实际就是整个图中的度(每个节点都计算一次度,那么每条边对应两个节点,所以要乘以2)。δ(cv,cw)δ(cv,cw)是用来判断节点vv和ww是否在同一个社区内,在同一个社区内`δ(cv,cw)=1δ(cv,cw)=1,否则δ(cv,cw)=0δ(cv,cw)=0。 
  下面我们举个栗子来看一下这个公式是怎么计算的:
 
 
  如上图所示,共有A、B、C三个节点,其中节点A和C在社区2,节点B在社区1。可以表达为矩阵形式:  
 
⎡⎣⎢011100100⎤⎦⎥[212][011100100][212]
   左边是邻接矩阵,右边是社区划分,我们也称之为label。
 
Q=12×2[(0−2×22×2)×1+0+(1−2×12×2)×1]+12×2[0+(0−1×12×2)×1+0]+12×2[(0−1×22×2)×1+0+(1−1×12×2)×1]=−18Q=12×2[(0−2×22×2)×1+0+(1−2×12×2)×1]+12×2[0+(0−1×12×2)×1+0]+12×2[(0−1×22×2)×1+0+(1−1×12×2)×1]=−18
   下面我们来谈一下对于公式的理解,这个概念首先是在04年提出的,06年进行了数学上的推导。因此通过04年的公式,我们会非常清楚的看懂这个概念的含义。我们先给出公式的划归:
 
Q=12m∑vw[Avw−kvkw2m]δ(cv,cw)=∑ci=1(eii−a2i)(2)(2)Q=12m∑vw[Avw−kvkw2m]δ(cv,cw)=∑i=1c(eii−ai2)
 
eij=∑vwAvw2m(3)(3)eij=∑vwAvw2m
 
ai=ki2m=∑jeij(4)(4)ai=ki2m=∑jeij
   eijeij表示一个节点在社区ii内,另一个节点在社区jj内的边。那么eiieii就表示在社区ii内所有的边与整个网络所有的边的一个比值(因为这个的A计算了两次,所以下边变数m也乘上了2,可以理解为一个社区内部的度比上整个网络的度)。而aiai则表示i社区内的节点的度(包含了一点在社区i内一点在社区i外的边的度)占整个网络的度比值。
  可以理解为在一个社区网络结构中,社区内连边数与随机期望的一个差值。当实际的连边越高于随机的期望时,这个社区的节点就存在集中于几个社区内的一个趋势。对于这个理解,也有的用下面的公式来描述:
 
Q=∑c(∑in2m−(∑tot2m)2)(5)(5)Q=∑c(∑in2m−(∑tot2m)2)
 
Q=∑c(Im−(2I+O2m)2)(6)(6)Q=∑c(Im−(2I+O2m)2)
  上式中,II表示两个端点均在同一社区中的边的数目,OO表示其中一个端点在社区中,而另一个端点不在相同社区中的边的数目。
  (2)(5)(6)这三个式子其实是完全等价的,只不过最后一个公式是用边去计量,前面两个则是用度,但是很容易理解,边与度是可以相互转化的。
  下面我用04年的定义来重新计算上面的简单社区。
 
Q=[22×2−(32×2)2]+[02×2−(12×2)2]=−18Q=[22×2−(32×2)2]+[02×2−(12×2)2]=−18
  可见两种定义确实是等价的。但是在实际的计算过程中我们可以发现,两层遍历使得算法非常的耗时,因此我们下面讲如何将这样的算法等价到矩阵形式上面去减少计算的时长。
三、Modularity 的矩阵形式
  首先我们定义一个n*r的矩阵SS,Sir=1Sir=1表示第ii个节点属于第rr社区。则我们有:
 
Q=12m∑vw[Avw−kvkw2m]δ(cv,cw)=12mTr(STBS)Q=12m∑vw[Avw−kvkw2m]δ(cv,cw)=12mTr(STBS)
 
Bij=Aij−kikj2mBij=Aij−kikj2m
  矩阵形式的简洁美啊,让我感叹一句,哈哈。
四、关于权重
  在进行社区划分的时候我们往往要考虑两个节点之间的重要性,也就是边的权重,计算方法是一样的,在理解上,可以这样理解:
 
五、算法实现
下面给出我的代码:
% Matlab
A = [0,1,1;1,0,0;1,0,0]; %邻接矩阵定义,与上面的例子是一致的
S = [0,1;1,0;0,1]; %label定义
m = sum(sum(A))/2;
k = sum(A,2);
B = A - (repmat(k,[1,size(A,1)]) .* repmat(k',[size(A,1),1])) / (2*m);
Q = 1/(2*m) .* trace(S'*B*S);1234567
  没错就是这么简单,一共只有四行代码(前两行是输入定义),貌似网上还没有这么短的代码 → →,这就是矩阵的魅力了,通过计算我们得到:
 
Q=-0.12501
和上面计算出来的结果−18−18是完全一致的。
六、感言
  首先真的要特别感谢我的林老师,其实一直有写博客的打算,但是因为平时太忙,总觉得写博客很浪费时间,所以迟迟没有动笔。林老师的课程要求我们每周写一篇LeetCode的题解,间接性的带动了我写博客的欲望,也确实能够让我对整个知识有一个梳理。
  关于Fast-Newman算法,我在网上并没有找到比较可信的资料,因为说法众多,讲的也不全面,所以就写了这篇博文和大家分享交流。如果你有疑问可以留言,我们相互探讨一下,我会尽量回复,不过平时确实比较忙碌,如有疏忽,也请见谅。   
  学海无涯,水滴石穿。
七、参考 
Paper:
Newmana M E J. Fast algorithm for detecting community structure in network[J]. Physical Review E, 2004: 69.
https://arxiv.org/pdf/cond-mat/0309508.pdf
Newman M E J. Modularity and community structure in networks[J]. Proceedings of the national academy of sciences, 2006, 103(23): 8577-8582.
http://www.pnas.org/content/103/23/8577.full.pdf
---------------------
版权声明:本文为CSDN博主「marywbrown」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/marywbrown/article/details/62059231

转载于:https://www.cnblogs.com/ruogu2019/p/11364312.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Fast Newman算法的核心是贪心算法,它通过不断合并社区来优化模块。下面提供一份Python代码实现Fast Newman算法。 ```python def fast_newman_algorithm(graph): # 初始化每个节点为一个社区 communities = [[node] for node in graph.nodes()] # 计算当前社区的模块 modularity = compute_modularity(graph, communities) # 标记社区是否合并 community_merged = True while community_merged: community_merged = False for i in range(len(communities)): for j in range(i+1, len(communities)): # 计算合并社区后的模块 merged_communities = communities.copy() merged_communities[i] += merged_communities[j] del merged_communities[j] new_modularity = compute_modularity(graph, merged_communities) if new_modularity > modularity: # 合并社区 communities = merged_communities modularity = new_modularity community_merged = True break if community_merged: break return communities def compute_modularity(graph, communities): # 计算图的总边数 m = graph.number_of_edges() # 计算每个社区的数 degrees = dict(graph.degree()) # 计算每个社区内部的边数 internal_degree = 0 for community in communities: for i in range(len(community)): for j in range(i+1, len(community)): if graph.has_edge(community[i], community[j]): internal_degree += 1 # 计算每个社区的模块 modularity = 0 for community in communities: for node in community: ki = degrees[node] ki_in = 0 for j in community: if graph.has_edge(node, j): ki_in += 1 modularity += (ki_in / m) - ((ki / (2*m)) ** 2) return modularity ``` 上述代码中,`fast_newman_algorithm`函数实现Fast Newman算法,它接受一个图作为输入,返回每个社区的集合。`compute_modularity`函数用于计算当前社区的模块。在实现过程中,我们使用了图的邻接矩阵来表示图。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值