因为在我最近的科研中需要用到分布式的社区检测(也称为图聚类(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. Python知识整理(一)

    一.Python交互模式(终端上进行) python # 进入到Python交互模式,提示符是 >>> exit() # 退出Python交互模式 python xxx.py # 执 ...

  2. halcon基础算子介绍(窗口创建,算子运行时长,是否启用更新函数)

    前言 halcon有有大约1500个算子,我总结一些简单大家用得到的算子,比如创建窗口的方式有3种,接下来结束这方式,及其异同点等! 1.窗口创建的三种方式 1.1使用dev_open_window算 ...

  3. 史上最俗的MODBUS介绍

    如今网购正深深地改变着人们的生活,以前买东西要逛商场,先找楼层导购,再逛到相应柜台,接着愉快购物,选好东西后经过一番讨价还价,最后付钱拿货走人,这些都是稀松平常的场景.可是,如果没有实际看见东西,只在 ...

  4. System.Web.Optimization

    项目中引用了 System.Web.Optimization 这个程序集,缺少程序集会报错: 命名空间"System.Web"中不存在类型或命名空间名"Optimizat ...

  5. DirectX12 3D 游戏开发与实战第七章内容(下)

    利用Direct3D绘制几何体(续) 学习目标 学会一种无须每帧都要刷新命令队列的渲染流程,由此来优化程序的性能 了解另外两种跟签名参数类型:根描述符和根常量 探索如何在程序中生成和绘制常见的几何体, ...

  6. Linux服务器查看个人硬盘配额

    quota -uvs

  7. 【Python小试】去除核酸特定长度的接头序列

    输入 input.txt ATTCGATTATAAGCTCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATC ATTCGATTATAAGCACTGATCGATCGATCG ...

  8. Docker-原理解析

    容器! Linux容器是与系统其他部分隔离开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件.容器提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有 ...

  9. grep -r

    今晚改脚本 我发现了一个很有趣的事情,一共56个配置文件 1 # 注意:对一些参数一致的多个文件可以用此方法 2 # grep -r 查找文件内容,其中PARALLEL=2就是我要替换的内容 3 4 ...

  10. Excel-vlookup(查找值,区域范围,列序号,0)如何固定住列序列号,这样即使区域范围变动也不受影响

    突然,发现VLOOKUP的列序列号并不会随着区域范围的改变而自动调节改变,只是傻瓜的一个数,导致V错值.所有,就想实现随表格自动变化的列序号. 方法一:在列序号那里,用函数得出永远想要的那个列在区域范 ...