【论文笔记】Fast unfolding of communities in large networks

Louvain算法

原论文Fast unfolding of communities in large networks
2008年的

导读

  1. 自己论文要改进这个方法,在特定情况下效果更好一些。需要首先彻底了解Louvain,再去思考原本仅根据modularity的合并方式如何进行修改。
  2. 这论文多经典啊,2008年的,现在引用1w1了。速度又快,思路也很简单。简直是奥卡姆剃刀的典型代表。

虽然本文是原始用于检测社区的,但其实可以作为层次聚类的一种形式。
层次聚类这个问题我研究不深,但是常见的层次聚类例如HAC,其实并不能够给定有效的层级结构。一般都是在某个distance阈值了,选取此时还保留的几个cluster。
但是低的hierarchy层级以及有效的hierarchy结构。其实并没有获取到。i.e. 其实没有层级的cluster。
举个例子,层级聚类,我们想要的不仅是这个方法给出的最顶层层级。我们也想要他的下属层级,并且想要自动化的获取。这总不能我们设定好有几个阈值,然后分别的cut吧,因为有的时候层级比较多,有的时候层级比较少,不能自动化。

Abstract

提出一种简单在大型网络下抽取社区结构的方法,基于模块度优化的启发式方法

1 在计算时间上优于其他所有已知的社区检测方法。
2 检测的社区质量很好(通过模块度度量)

Introduction

一些当今的社交网络等等,有非常多的结点和连线。都是数以百万计。因此有效的方法包括压缩网络变成子单元,这些子单元中都是高度内部连接的结点。
笔者:在Louvain中的思想就是把社区变成小node,再进行高层的社区聚类
识别这些子单元或者社区非常重要,可以发现一些例如社区网络中的“话题”等等。可以用来可视化社区结构等扽
笔者:也就是讲述发现community是很有意义的

社区检测问题需要网络的一部分变成一个高度内部连接的社区,不同社区之间的结点可能仅仅是稀疏连接笔者:这很合理
准确的公式化这个优化问题是非常棘手的,很多算法都被提了出来,此外由于社交网络的影响力和数据集公布等等,这个研究方向也吸引很多人。

算法可以被分为:

  1. 区分算法,检测社区并且移除。
  2. 合并算法 迭代式的合并
  3. 优化最大目标函数等等

区分的质量通常被模块度来进行一个评估。
模块度介于[1,1][-1, 1]去描述社区内连线的密度与社区间连接的密度。 在加权网络中,模块度定义为:

Q=12mi,j[Aijkikj2m]δ(ci,cj), Q=\frac{1}{2 m} \sum_{i, j}\left[A_{i j}-\frac{k_{i} k_{j}}{2 m}\right] \delta\left(c_{i}, c_{j}\right),
其中

  1. AijA_{ij}代表结点ii与结点jj之间的边的权重
  2. ki=jAijk_i = \sum_{j} A_{ij}是结点jj的所有边的连接权重之和。同理kj=iAijk_{j}= \sum_{i} A_{ij}
  3. cic_i, cjc_j分别是结点iijj所属的社区
  4. δ(u,v)\delta(u, v)是一个函数,如果u=vu=v则值为1,如果uvu \neq v则值为0
  5. $ m = \frac{1}{2}\sum_{ij}A_{ij}$,也就是所有连接权重总和的1/2.

慢慢解析这个模块度公式,毕竟这个是全文的核心了(虽然这个模块度也不是louvain提出来的hhh)

  1. δ\delta函数的意思是计算两个结点时,如果其所在同一个社区,模块度才不为0,但如果不在同一社区,其模块度就为0,当在后续计算模块度增益时,这个公式就很关键。
  2. kik_ikjk_j描述的是结点的所有连接,可以简单的理解为一个结点的的数量总和。(虽然这边是有权重的,简化为无权图,就是度的总和)。首先说模块度是越大越好,代表一个社区越成一个结构,内部越紧凑。 如果一个结点的度不高,与外界的连接数量很少,那么kk就越小,作为一个被剪去的数值,模块度相应的就越高。所以对于结点来说,希望他们的社区外的连接越少越好,越少则模块度越高因为如果是社区内的连接会在后续中计算进来,而社区外的只会增加这个被剪的权重

这边尝试化整一下公式,先将δ\delta公式取出来(假设所有的点全部存在于1个社区内),把上部分m公式带入
12m[i,jAijijAijkikj2m]=14m2i,j[Ai,ji,jAi,jkikj]=14m2[i,jAi,ji,jAi,ji,jkikj] \begin{aligned} &\frac{1}{2m}[\sum_{i,j}\frac{A_{ij}\sum_{ij}A_{ij}- k_ik_j}{2m}]\\ &=\frac{1}{4m^2}\sum_{i,j}[A_{i,j}*\sum_{i,j}A_{i,j}-k_ik_j]\\ &=\frac{1}{4m^2}[\sum_{i,j}A_{i,j}*\sum_{i,j}A_{i,j}-\sum_{i,j}k_ik_j] \end{aligned}

重点强调一下其中的m=12ijAijm = \frac{1}{2}\sum_{ij}A_{ij} 由于ijAij\sum_{ij}A_{ij}是一个定值,我们可以在这里设置为值为WaW_a
然后上述公式为

14m2[i,jAi,ji,jAi,ji,jkikj]=Wa2i,jkikjWa2=1i,jkikji,jAi,ji,jAi,j \begin{aligned} &\frac{1}{4m^2}[\sum_{i,j}A_{i,j}*\sum_{i,j}A_{i,j}-\sum_{i,j}k_ik_j]\\ &=\frac{W_a^2-\sum_{i,j}k_ik_j}{W_a^2}\\ &=1 - \frac{\sum_{i,j}k_ik_j}{\sum_{i,j}A_{i,j}*\sum_{i,j}A_{i,j}} \end{aligned}
最后我还是把mm带回了原公式,可以看到模块度最大的上限为1。
最后也没能发现kik_ikjk_j存在什么特殊之处,我觉得应该就是想把两个点的一些特征带进去罢了。
出现-1的情况,想了一下应该是对于某个社区来说,如果特别差劲,内部连接全部没有,所有的连接全都在外面,就会出现-1的情况。

模块度被用于比较分块的质量,也是一个优化的目标函数。但是计算和优化都非常困难呢。贪心算法的效果也不太行。

Method

文中提出的方法可以在短时间内寻找到高模块度并且可以展开称为一个完整的层级社区结构。因此可以去解决多种社区检测问题。
比较其他方法,该方法主要的问题是存储空间的大小而不是计算时间的问题。118个百万结点仅需要152分钟。Louvain NB
Louvain图例子【论文笔记】Fast unfolding of communities in large networks
原图是点皆为灰色的情况,然后通过模块度优化(具体的优化过程后面再讲),得到了一群community。
然后将这群community化整称为一个新的node,做第二阶段的community的检测。就这样达到hierarchy的效果。

算法主要包括两个步骤,并且迭代的执行。
假设我们拥有NN个结点的加权网络,首先将每个node视作一个单独的community。
随后对每一个nodeii,考虑加入邻居结点jj所在的社区(对初始情况来说也就是加入自己,也就是1个结点的社区变为2个结点的社区)
考虑加入ii之后的jj社区模块度收益,如果收益高,就加入,如果不高就换个邻居。
文中提到的是使用最大的增益的一个结点增加进入。并且仅有增加的情况会加入。如果都是负的,就不改变了。

上述步骤对所有的结点执行,直到没有任何的结点可以进行加入了repeatly and sequentially笔者:有些算法解析会说一开始随机选择,但原论文并非如此,结果是稳定的。但是这肯定并非是最优解,因为算法依赖于结点计算的顺序,但是文中又又说了对性能影响不大,会影响计算时间。。这就是图1中Modularity Optimization结束的那个标颜色的结点图。
由于考虑邻居,某一个点可能会被重复的考虑,因此文中也指出,结点会被经常性的重复计算和考虑。
当达到最大的local模块度时笔者:我的理解是,对于任何的一个子community,都无法再添加结点进入使其增加模块度了,此时就是最大的local modularity

计算模块度增益的公式如下

ΔQ=[in+ki,in2m(tot+ki2m)2][in2m(tot2m)2(ki2m)2] \Delta Q=\left[\frac{\sum_{i n}+k_{i, i n}}{2 m}-\left(\frac{\sum_{t o t}+k_{i}}{2 m}\right)^{2}\right]-\left[\frac{\sum_{i n}}{2 m}-\left(\frac{\sum_{t o t}}{2 m}\right)^{2}-\left(\frac{k_{i}}{2 m}\right)^{2}\right]
其中:

1 ii是node,CCjj所在的community
2 in\sum_{in}CC之中所有的连接权重之和
3 tot\sum_{tot}是所有与CC内结点有连接的连接权之和
4 ki,ink_{i, in}是从iiCC中的内部结点之和
5 mm是整个网络中的连接权重之和

分析这个公式。

前部分是ii加入CC对社区CC带来的增益,后半部分是社区CC的未加ii结点的情况。

1 in+ki,in2m\frac{\sum_{i n}+k_{i, i n}}{2 m}是原本内部的连接权重,加上新增的ii与内部的连接权重
2 (tot+ki2m)2\left(\frac{\sum_{t o t}+k_{i}}{2 m}\right)^{2}部分是新增了iiCC社区增加需要减去的项。对应到原始模块度计算公式中,就是考虑了δ\delta函数的ijAij\sum_{ij}A_{ij}
3 后面部分对应上述为加ii的模块度算法解析
4 最后的(ki2m)2(\frac{k_i}{2m})^{2}代表的是ii结点单独成为一个社区的模块度影响。

来一手化神奇为腐朽把
ΔQ=[in+ki,in2m(tot+ki2m)2][in2m(tot2m)2(ki2m)2]=in+ki,in2m(tot2m)2(ki2m)2(2totki2m)in2m+(tot2m)2+(ki2m)2=in+ki,in2m(2totki2m)in2m \begin{aligned} &\Delta Q=\left[\frac{\sum_{i n}+k_{i, i n}}{2 m}-\left(\frac{\sum_{t o t}+k_{i}}{2 m}\right)^{2}\right]-\left[\frac{\sum_{i n}}{2 m}-\left(\frac{\sum_{t o t}}{2 m}\right)^{2}-\left(\frac{k_{i}}{2 m}\right)^{2}\right]\\ &=\frac{\sum_{i n}+k_{i, i n}}{2 m}-(\frac{\sum_{t o t}}{2 m})^{2} - (\frac{k_{i}}{2 m})^{2}-(\frac{2\sum_{t o t} * k_{i}}{2 m})-\frac{\sum_{i n}}{2 m} + (\frac{\sum_{t o t}}{2 m})^{2} + (\frac{k_{i}}{2 m})^{2}\\ &= \frac{\sum_{i n}+k_{i, i n}}{2 m}-(\frac{2\sum_{t o t} * k_{i}}{2 m})-\frac{\sum_{i n}}{2 m} \end{aligned}
上述项一为加入后模块度的主要增加,二为增加后社区内部想剪去的item,对应就是考虑了δ\delta函数的kikj-k_ik_j。三为原始社区的模块度非重复项考虑了δ\delta函数的ijAij\sum_{ij}A_{ij}

该公式计算的是社区加入ii,增加的模块度Q\triangle Q
当然也可以通过计算移除社区内的结点,来计算Q\triangle Q

此时第一步骤已经完结了,第二个阶段会将所有的community转换成新的node,具体:

1 同一个社区下,之间结点的link会变成自连接
2 不同社区的link 会根据其内node间的权重合并

算法有特别多的好处,容易实现,直觉的,无监督,非常非常的快速。

总结

后面实验这边就不看了,主要是探究Louvain的前世今身

总结下来有一些特点吧:
原本node变为自连接,这个需要再去看看代码
邻居的选取是顺序的
模块度的优化和计算需要再细一些。