前言
这个第五题本身并不难,只是我个人对这个Louvain的算法比较感兴趣。所以,就花的一周时间。可能是因为这是一篇算法型的论文吧。所以,复现难度不算太大。但是,如果不参考网上的一些已经写完的代码其实也会漏掉很多细节。包括现在也是并不确定我写的是否是一定正确。每次更新之后也会在博客里同步更新。如果是你的目标是想要实现那就从一开始慢慢看,在这里有很多实现细节的说明和我个人的理解。如果要代码就直接跳到最后 ,其实我也不能保证一定正确。毕竟这不是ACM的算法题能有一个准确的对错。
一、Louvain是什么?
Louvain是一个用于社区发现的传统算法。这个算法出现于2008年,那么什么是社区发现呢?举个例子:假设我们有一个图,在这个图中的节点是某个镇的所有的人,图中的每一条边代表的是两个人之间的说话的数量。(现实生活中这个可能难以统计)而社区发现就是在这个图中我们需要确定哪些人之间是一个团体,这个所谓的团体可能是同一个小学,也可能是同一个家庭或者是同一个小区等等。论文的原文全称是:Fast unfolding of communities in large networks 作者:Vincent D. Blondel, Jean-Loup Guillaume, Renaud Lambiotte and Etienne Lefebvre
二、算法思路
1.社区划分的合理性
作为一个认真的人,在放出公式之前先说明。论文中给出的用于衡量社区划分合理性的公式并非是最好的。毕竟,十年都过去了,应该有其他的一些公式吧(我猜的)。但是,我能确定的是最好的一种划分方式一定是真实结果(这句话看起来像句费话)。公式如下(其中的几个乘号是我自己加上去的,我想应该不会错。):
Q = 1 2 ∗ m ∑ i , j [ A i j − k i ∗ k j 2 ∗ m ] δ ( c i , c j ) Q=\frac{1}{2*m}\sum_{i,j} [A_{ij} - \frac{k_i*k_j}{2*m}]\delta(c_i,c_j) Q=2∗m1i,j∑[Aij−2∗mki∗kj]δ(ci,cj)
m m m:如果是无向图,图中所有边的权值和。如果是有向图就是图中所有边的权值和的一半。论文中原本的写法是 m = 1 2 ∑ i j A i j m = \frac{1}{2}\sum_{ij}A_{ij} m=21∑ijAij表面上看是图中所有边的一半。但是对于无向图而言需要把每一条边当作两条有向边来看。
A i j A_{ij} Aij:代表的是节点i和节点j之间的边的权值。
k i k_i ki:是所有指向节点i的边的权值和。注意:就是这一个定义当我们在处理自环的时候就需要注意一个细节。假设有一个节点的图案如下图所示:

其中权值为4的边是一个自环需要当作两条边来看,就上图的i点而言其中的 k i k_i ki的值是28。
δ ( c i , c j ) \delta(c_i,c_j) δ(ci,cj):就是判断i节点和j节点是否是同一个社区。如果是那么值就是1否则就是0
我这么写可能是有此啰嗦。但是如果你能有耐心看完那么在实现的时候就能避开很多的坑。因为我这个可不是对论文的简单翻译。
2.算法流程
其算法流程一共有两个大步骤,不断的迭代往复。
1.我们先要把每一个单一的节点都看作是一个单独的社区。对于每一个单一的节点,先要把它从它所在的社区中取出来。然后,再尝试依次加入到它相邻的社区中计算其收益 Δ Q \Delta{Q} ΔQ。( Δ Q \Delta{Q} ΔQ的计算方法之后在详细说,本文给出了三种不同的计算方法。)如果,最大的 Δ Q \Delta{Q} ΔQ小于0则将它放回到原先的社区中,否则就加入到收益最大的那一个社区中。对所有的节点都不断的循环反复的进行这一操作。直到所有节点的所处社区都不再发生变化。(不知道有没有人和我一样看原文的时候这一句没看到。)
从这一步的做法中我主观的感觉这个算法的目的是想要最大化Q的。但是,在实际情况中却又不是Q越大就越接近真实划分。详情就要参考论文中的第12篇参考文献了。(我没有看过,详细了解这个公式Q的同学可以说一下)
2. 通过第一步我们已经对图有了一个新的社区划分。于是在这一步中我们会对每一个社区都要把其中的对应的那些节点都缩成一个超节点,而这个超节点就代表了这一个社区中的若干个结点。而边的处理方式与咱们的逻辑十分相符。如果一条边连接的是同一个社区就把它变成对应超节点的一条自环边,如果一条边连接的是两个不同的社区就让这条边连接两个对应的超节点。然后计算一个新的全局Q值。如果这个Q值与之前的Q值相同那么就结束整个算法。(如果是第一次迭代那就不用考虑结束的事了)
为了能够更加形象的理解,我试试看画一个图。
(假装有图,好吧我画不出来。还是会的工具太少。)
3. Δ Q \Delta{Q} ΔQ的计算方式
在这一部分中我会一共会放三种不同的计算方式这三种方式都是基于一个大前提——我们需要尝试将一个节点加入到社区C中。其中第一种是论文中给出的,第二种是其它的一些代码中使用的版,(据说论文的初稿用的也是这个版本)第三种是我根据自己的理解推导得到的。(其正确性有待商榷,推荐使用第二种。)
1. Δ Q = [ ∑ i n + k i , i n 2 ∗ m − ( ∑ t o t + k i 2 ∗ m ) 2 ] − [ ∑ i n 2 ∗ m − ( ∑ t o t 2 ∗ m ) 2 − ( k i 2 ∗ m ) 2 ] \Delta{Q} = [\frac{\sum_{in}+k_{i,in}}{2*m} - (\frac{\sum_{tot} + k_i}{2*m})^2] - [\frac{\sum_{in}}{2*m}-(\frac{\sum_{tot}}{2*m})^2 - (\frac{k_i}{2*m})^2] ΔQ=[2∗m∑in+ki,in−(2∗m∑tot+ki)2]−[2∗m∑in−(2∗m∑tot)2−(2∗mki)2]
由于我个人对这个公式的理解并不完全,因此不对这个公式作非常详细的解读。仅仅只对其中的变量做一些简单的解释。
∑ i n \sum_{in} ∑in:社区C的内部的所有边的权值和,注意:如果是无向图则需要每一条边看作是两条有向边,所以,是所有边的权值和的两倍。
k i , i n k_{i,in} ki,in:所有从节点i指向区域C的边的权值和。
m m m:如果是无向图,图中所有边的权值和。如果是有向图就是图中所有边的权值和的一半。
∑ t o t \sum_{tot} ∑tot:所有指向区域C中的节点的边的权值和。刚开始的时候,我把这个理解成了从区域C指向外面的所有边的权值和。因此,踩了一个大坑。(这机种理解的区别就在于对自环的处理不同。)
k i k_i ki:指向节点i的所有边的权值和。(注意对自环的处理。)
在接下了我们就在这个公式的基础上进行划简:
Δ Q = [ ∑ i n 2 ∗ m + k i , i n 2 ∗ m − ( ∑ t o t 2 ∗ m ) 2 − ( 2 ∗ ∑ t o t ∗ k i 4 ∗ m 2 ) − ( k i 2 ∗ m ) 2 ] − [ ∑ i n 2 ∗ m − ( ∑ t o t 2 ∗ m ) 2 − ( k i 2 ∗ m ) 2 ] = k i , i n 2 ∗ m − 2 ∗ ∑ t o t ∗ k i 4 ∗ m 2 = 1 2 ∗ m ∗ ( k i , i n − ∑ t o t ∗ k i m ) \Delta{Q} = [\frac{\sum_{in}} {2*m} + \frac{k_{i,in}}{2*m} - (\frac{\sum_{tot}}{2*m})^2 - (\frac{2*\sum_{tot}*k_i}{4*m^2}) - (\frac{k_i}{2*m})^2] - [\frac{\sum_{in}}{2*m}-(\frac{\sum_{tot}}{2*m})^2 - (\frac{k_i}{2*m})^2] \\ = \frac{k_{i,in}}{2*m} - \frac{2*\sum_{tot}*k_i}{4*m^2} \\ =\frac{1}{2*m}*(k_{i,in} - \frac{\sum_{tot}*k_i}{m}) ΔQ=[2∗m∑in+2∗mki,in−(2∗m∑tot)2−(4∗m22∗∑tot

本文围绕Louvain算法展开,介绍其是用于社区发现的传统算法。阐述了算法思路,包括社区划分合理性的衡量公式、算法的两个大步骤及迭代过程,还给出三种ΔQ的计算方式。最后提供了代码实现,包括参考代码和作者自己实现的代码。
最低0.47元/天 解锁文章
1071





