因为在我最近的科研中需要用到分布式的社区检测(也称为图聚类(graph clustering))算法,专门去查找了相关文献对其进行了学习。下面我们就以这篇论文IPDPS2018的文章[1]为例介绍并行社区检测算法。

关于基本的单机/串行社区检测算法,大家可以参考我的另一篇博客《图数据挖掘:社区检测算法(一)》(链接:https://www.cnblogs.com/orion-orion/p/15662253.html)。总而言之,目前对于图的簇/社团划分,目前最广泛的测量方法是使用模块性(modularity)。然而模块性的优化是NP完全问题,目前最有名的一种启发式求解方法是基于贪心准则的Louvain方法。由于其速度和对社区检测结果的高质量,Louvain方法在串行/单机社区检测中一直是最常用的方法之一。

目前有许多方式可以对Louvain方法进行并行化。目前最快的基于共享内存多线程的Louvain方法是Grappolo软件包[2]。该软件能够在812s内在20个核上处理现实世界中大型的网络(soc-friendER; 18亿条边)。

在本篇论文中,作者将这个方法扩展到了分布式内存领域(和前面基于共享内存的领域不同)。而设计一个分布式的Louvain算法的挑战在于执行高效的邻居节点扫描(为了捕捉邻居节点所属社区状态的变化),因为在图的分布式表示的情况下,通信开销会变得非常重要。另一个关键的挑战是我们对哪个社区的状态进行查询和更新。串行算法的好处在于每轮迭代之间有同步操作,然而在分布式的环境下维护与传播最新的信息是被禁止的。此外,在分布式环境下一条边跨几个进程的处理也是一个棘手的问题。

1. 串行Louvain方法

我们这里简要描述一下串行的Louvain方法(详细描述见另一篇博客)。Louvain方法是一个多轮的迭代过程,每轮迭代分几个步骤。第一步,将每个节点视为一个社区,然后遍历每个节点——其中对于节点\(v\),计算将\(v\)移入各邻居可能导致的模块性增益\(\Delta Q\),如果最大的增益为正,那么\(v\)就会从其现在的社区移入该社区;第二步,进行图重建,即将所有划分好的社区缩为超节点。这个迭代过程一直持续直到不再获得模块性的增益(可以人工给定一个阈值\(\tau\))。

该算法的伪代码描述如下(伪代码中省略了图重建的过程):

2. 并行Louvain方法

我们开始叙述并行算法部分。我们使用\(p\)表示进程的数量,编号\(i\)表示区间\([0, p-1]\)内的任意进程编号。

首先,我们将输入的节点及它们的边集列表(edge list)(我们采用邻接表进行存储)分摊到各进程上,使每个进程接收到的边数量大致相同(这里没有用高级的图划分算法)。每个进程除了它本身拥有的节点集合之外,还存储了虽然属于其他进程、但与本进程中节点相关联的其他节点的拷贝(后面我们统一把这类节点称为影子节点)。论文采用稀疏行(CSR)的方式存储点和边集列表。

类似地,每个进程也获得一个任意集合的社区(大致使每个进程获得的社区数量相同),同时也保存了和本进程的社区之间有关联边(incident edges,即“跨边”)的社区(我们称为影子社区)。

下面的伪代码给出了并行的Louvain算法的大致框架,它包含了LouvainIteration过程(也就是前面的模块性增益最大化过程)和BuildNextPhaseGraph(对应前面的图的重构)这两个过程。注意,LouvainIteration过程的步骤我们还没有展开,其中包括了进程之间通信的过程。

接下来我们详细的看LouvainIteration过程和BuildNextPhaseGraph是怎么实现的。

(a)Louvain Iteration

下面的伪代码展示了LouvainIteration过程:

在这个过程中,每个进程都拥有节点和社区的集合,而进程之间的通信主要交换的信息也是节点和社区之间的信息。对每个局部的节点,都会存储一个社区ID。对每个局部的社区,关联度(\(a_c\))也被局部地存储。因为节点和进程编号的映射关系没轮迭代都在变化,每个进程需要存储其影子节点的列表以及这些影子节点对应的进程。此外,因为在每轮迭代开始时每个节点都在其自身的社区,故初始化的影子社区信息能够由节点信息推导出来。

下面我们解释一下LouvainIteration过程的主要步骤:

  • 2行(即过程开始),因为节点到进程的映射在每轮迭代(这里的迭代是指LouvainIteration过程外的迭代,别搞混了)会改变(由于图的缩点重构),我们会调用ExchangeGhostVertices操作执行一次通信操作来自交换影子的坐标信息。

  • 4-5行(相当于每轮迭代的开始),论文进行了每轮迭代一次的send-receive通信步骤来交换对应的影子节点信息(参照算法4),send操作指将进程的局部节点信息发往以这些节点做为影子节点的其他进程;receive操作指从其他进程获得影子节点的更新。

  • 7-9行,使用最新的节点信息来计算所有局部节点的社区分配。

  • 10-11行(相当于每轮迭代的末尾),我们需要将影子社区已更新的信息送往它们所属的进程,并且使各进程的社区接受相关更新的社区。

  • 12-13行,先新的社区状态计算当前进程子图的模块性,然后采用AllReduce操作规约得到全局模块性。

  • 如果网络的模块性增益(\(\Delta Q\))相对于前面的迭代低于需要的一个阈值下限,我们就终止迭代(否则继续迭代)。

其中,ExchangeGhost函数的定义如下:

ExchangeGhost函数中6-7行表示当\(u\)节点的所有者\(owner\)不是当前节点,就将\(u\)增加到列表 \(vmap[owner]\) 中(\(vmap\)做为一个局部缓冲区,后面在第10行将\(vmap\)发给进程\(j\),然后在第11行接受传到第\(j\)个进程的影子节点数据)。

(b)Graph reconstruction

下面我们来看图的重构过程。上一个社区划分过程中所划分的社团将在图的重构过程中被压缩为单个节点。在一个社区内部的边会形成自环,对于连接两个不同社区的“跨边”会形成连接两个节点的一条边(权重为所有"跨边"之和)。图的重建过程可以参考下图:

我们可以看到,模块性优化步骤最初始将节点\(\{0, 1, 3\}\)分配给社区\(0\),节点\(2\)分配给社区\(2\),节点\(4\)分配给社区\(4\)(节点\(2\)和\(4\)各自成为一个社区)。由于社区ID由节点ID产生,我们认为\(0-2\)号社区属于(存储在)进程\(0\),\(3-4\)号社区属于(存储在)进程\(1\)。注意,图例中[Array]表示数组,<Map>表示字典映射。<Vlocal, C> 字典中存储了节点编号和社区编号的映射关系;[Edges]数组中存储了边的权重;<Vremote, C>字典存储了影子节点编号与其所属社区的映射关系。

然后图的重建可分为7个步骤:

(1) 每个进程将其局部社区进行重编号。重编号使用一个关联原始社区ID和新社区ID的哈希表<Cold, Cnew>来实现。

(2) 每个进程检查可能已分配给远程节点(但和局部节点已无任何联系)的局部社区的ID。

(3) 基于前\(N\)个进程中局部社区的数量,为第\(N+1\)个进程的局部社区进行全局编号(并行前缀和计算)。

(4) 将最新的局部社区ID和全局社区ID编号的映射关系(<C, Cnew>) 以AllReduce的方式更新到各个进程上。

(5) 每个进程会检查其所拥有的节点并构建部分的新的边集列表。对进程所拥有的每个节点,进程会检查其邻居列表,其中和新的社区ID相关联的邻居会形成一个自环。

(6) 一旦新的“部分边集列表”生成,它们就会被重新分配到各进程,然后生成新的图划分并调整边的权重(每个进程会尽可能获得相同数量的节点)。

(7) 根据新的超节点的带权邻接列表<Cnew, <Cnew, weight>>构建新的图。

参考文献

  • [1] Ghosh S, Halappanavar M, Tumeo A, et al. Distributed louvain algorithm for graph community detection[C]//2018 IEEE international parallel and distributed processing symposium (IPDPS). IEEE, 2018: 885-895.

  • [2] Lu H, Halappanavar M, Kalyanaraman A. Parallel heuristics for scalable community detection[J]. Parallel Computing, 2015, 47: 19-37.

并行Louvain社区检测算法的更多相关文章

  1. 模块度与Louvain社区发现算法

    Louvain算法是基于模块度的社区发现算法,该算法在效率和效果上都表现较好,并且能够发现层次性的社区结构,其优化目标是最大化整个社区网络的模块度. 模块度(Modularity) 模块度是评估一个社 ...

  2. GNN 相关资料记录;GCN 与 graph embedding 相关调研;社区发现算法相关;异构信息网络相关;

    最近做了一些和gnn相关的工作,经常听到GCN 和 embedding 相关技术,感觉很是困惑,所以写下此博客,对相关知识进行索引和记录: 参考链接: https://www.toutiao.com/ ...

  3. 机器学习:异常检测算法Seasonal Hybrid ESD及R语言实现

    Twritters的异常检测算法(Anomaly Detection)做的比较好,Seasonal Hybrid ESD算法是先用STL把序列分解,考察残差项.假定这一项符合正态分布,然后就可以用Ge ...

  4. 海量数据挖掘MMDS week3:社交网络之社区检测:高级技巧

    http://blog.csdn.net/pipisorry/article/details/49052255 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...

  5. 海量数据挖掘MMDS week3:社交网络之社区检测:基本技巧

    http://blog.csdn.net/pipisorry/article/details/49052057 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...

  6. 社区发现算法 - Fast Unfolding(Louvian)算法初探

    1. 社团划分 0x1:社区是什么 在社交网络中,用户相当于每一个点,用户之间通过互相的关注关系构成了整个网络的结构. 在这样的网络中,有的用户之间的连接较为紧密,有的用户之间的连接关系较为稀疏.其中 ...

  7. Python机器学习笔记 异常点检测算法——Isolation Forest

    Isolation,意为孤立/隔离,是名词,其动词为isolate,forest是森林,合起来就是“孤立森林”了,也有叫“独异森林”,好像并没有统一的中文叫法.可能大家都习惯用其英文的名字isolat ...

  8. [转]Python机器学习笔记 异常点检测算法——Isolation Forest

    Isolation,意为孤立/隔离,是名词,其动词为isolate,forest是森林,合起来就是“孤立森林”了,也有叫“独异森林”,好像并没有统一的中文叫法.可能大家都习惯用其英文的名字isolat ...

  9. FAIR开源Detectron:整合全部顶尖目标检测算法

    昨天,Facebook AI 研究院(FAIR)开源了 Detectron,业内最佳水平的目标检测平台. 昨天,Facebook AI 研究院(FAIR)开源了 Detectron,业内最佳水平的目标 ...

随机推荐

  1. vivo统一告警平台设计与实践

    一.背景 一套监控系统检测和告警是密不可分的,检测用来发现异常,告警用来将问题信息发送给相应的人.vivo监控系统1.0时代各个监控系统分别维护一套计算.存储.检测.告警收敛逻辑,这种架构下对底层数据 ...

  2. [cf1290D]Coffee Varieties

    思路 统计数的种类数,也等价于统计有多少个数满足其之前没有与其相同的数 将序列以$\frac{k}{2}$为块大小分块,那么即会有$m=\frac{2n}{k}$个块 (关于$k=1$的情况,以1为块 ...

  3. 未能加载文件或程序集“Microsoft.CodeDom.Providers.DotNetCompilerPlatform

    "/"应用程序中的服务器错误. 未能加载文件或程序集"Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Versio ...

  4. java番外茶余饭后闲聊

    java番外茶余饭后闲聊 **本人博客网站 **IT小神 www.itxiaoshen.com 今天聊点题外话没事时可以作为平时沟通交流的谈资,接下来一起简单了解下个人知晓对Java界开发产生深远影响 ...

  5. 面向对象编程之Python学习一

    在实际的程序设计中,使用Java面向对象编程方法编写算法能够很清楚的理解其来龙去脉. 习惯了面向对象思维,学习Python也自然使用这种思维. 目前,由于Python很多软件包能够容易的获取和利用,人 ...

  6. java 装饰器模式实现代码

    目录 1.实现装饰器模式 1.1.公共接口 1.2.接口实现 1.3.装饰器 1.4.装饰构件 1.5.测试装饰器 上图展示的是io流中的一个装饰者模式的代码结构 1.实现装饰器模式 汽车厂生产汽车实 ...

  7. Git的基础入门

    Git --everything-is-local 查阅文档 Git是一个免费的开源 分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有内容. Git易于学习, 占地面积小,具有闪电般的快速 ...

  8. 记一次使用 SelectMany 的经历

    最近在改造一个功能时为了减少循环的层数,于是想着将List列表映射为一个能直接使用颗粒大小的List列表,这样一层循环就可以解决问题.     public class ConflictWordIte ...

  9. Codeforces 1411G - No Game No Life(博弈论+生成函数+FWTxor)

    Codeforces 题面传送门 & 洛谷题面传送门 一道肥肠套路的题目. 首先这题涉及博弈论.注意到这里每一个棋子的移动方式都是独立的,因此可以考虑 SG 定理.具体来说,我们先求出每个棋子 ...

  10. P7091 数上的树

    题目传送门. 首先将 \(n\) 分解质因数,用 DFS 求出 \(n\) 的所有因数,记为 \(d_1,d_2,\cdots,d_c\),跑一遍反素数那题的代码可知 \(c\leq 23327\)( ...