基于 Nebula Graph 构建图学习能力

经常看技术文章的小伙伴可能会留意到除了正在阅读的那篇文章,在文章页面的正文下方或者右侧区域会有若干同主题、同作者的文章等你阅读;经常逛淘宝、京东的小伙伴可能发现了,一旦你购买或者查看过某个商品之后,猜你喜欢的推荐商品会贴近你刚拔草的商品…这些日常生活中常遇到的事情,可能是由一个名叫图学习的“家伙”提供技术支持。
图学习,顾名思义是基于图的机器学习,按照本期项目介绍的参赛队伍——图学习兴趣小队队长杨鑫的话就是,图学习是一个通过深度学习方法,基于图的结构与属性生成一个向量作为点、边或者整个图的表征。
在之前 Nebula Hackathon 2021 年的参赛项目中,图学习兴趣小队“豪气”地说要让 Nebula Graph 具备支持图学习的能力,在本文接下来的内容中,你将了解到他们是怎么实现这一目标的。
图学习小课堂
在讲述项目实现之前,项目负责人杨鑫先给大家上了一课“图解图学习”。

以获取顶点的向量表征为例来讲解下图学习过程,第一步需要对图中顶点邻居进行采样拿到邻居的拓扑结构以及属性;第二步便是通过自定义的聚合函数聚合邻居顶点蕴含的信息进行计算;最后一步基于聚合信息得到图中各顶点的向量表示。
杨鑫表示,衡量一个图学习算法的好坏是通过生成向量中包含图中顶点属性及拓扑结构的丰富程度来判断的。
故事的开始
在选择图学习框架时,图学习兴趣小队有自己的一套选型标准:首先它是开源组件,方便二次开发,其次它支持分布式,最后框架本身和图数据库的耦合程度要低方便定制化开发。经过深入调研,由于 DGL(链接:https://www.dgl.ai/)同底层的图数据库耦合过高,PYG(链接:https://pytorch-geometric.readthedocs.io/en/latest/)不支持分布式,最终他们采用了 Euler(GitHub 地址:https://github.com/alibaba/euler),而在 Nebula Hackathon 2021 中,图学习兴趣小队的重点工作便是用 Nebula Graph 替换 Euler 原生图数据库,让社区用户可以基于 Nebula Graph 低成本尝试图学习能力。
设计思路

在方案设计上,架构分为三层:底层是 Nebula Graph 图数据库,中间层是图采样算子层,为上层 Euler 图算法提供多种采样图数据的能力。图采样算子则是他们本次工作的重点——重写 22 个 TF 采样算子、6 种 nGQL 采样语法。
高强度的开发量
以全局带权采样语法设计为例,

这里采用了预计算(离线)+ 计算下推(实时)的方式来实现全局采样,主要过程为(上图 1-4)
- graphd 提交异步构建任务给 metad;
- metad 下发任务给各个 storaged 节点进行计算;
- storaged 将计算结果上报给 metad 汇总;
- metad 通过心跳将结果同步给 graphd。
详细展开来讲,在图学习训练过程中,数据采样是一个非常高频的操作,采样性能好坏对图学习训练耗时影响很大。
在这里,杨鑫他们采用别名采样算法作为全局带权采样语法的核心算法,它的基本原理是首先对进行采样的全部数据根据各自的权重计算出一份采样表,采样表由行号(sid)、vid1、采样概率(prob)、vid2 四部分组成,其中行号是从 0 开始的编号,vid1、vid2 表示可以被采样的某个顶点的 vid;采样流程是首先在表中随机选择一行,然后再随机生成一个 0~1 之间的值 p,如果 p < prob,则本次采样的数据是 vid1,否则采样 vid2。
目前图学习是基于静态图数据训练,因此我们可以通过预计算的方式离线生成一份别名采样表,而实时采样过程简化为基于预计算的采样表做两次随机采样,时间复杂度由 O(n) 降到 O(1),可以极大地提高采样效率。
下面是基于 Nebula Graph 实现的全局带权采样具体实现,跟 Nebula Graph 的索引重建逻辑很相似。
- Graph 服务调用 Meta 服务启动一个异步构建采样表任务,并支持异步任务的状态查询。
- Meta 服务的作用是控制异步任务的执行,包括分配每个 Storage 节点需要处理的数据(根据 partition 的 leader 分布决定)、异步触发各个 Storage 节点进行采样表的计算、记录任务的执行状态、记录全局信息(点、边权重和)、通过心跳逻辑将全局信息缓存在 Graph 服务中。
- Storage 服务的任务是生成所负责数据的采样表。采样表的计算是整个流程中的核心逻辑,计算过程需要对所有点、边编号,并根据权重大小对点、边进行分类,很显然这些数据是无法全部存储在内存中的,所以我们在 Storage 中增加了一个采样统计信息 RocksDB 实例来存储采样表、点边总数、点边权重、计算中间变量等数据。以计算某一类点的采样表的过程为例:
第一步遍历全图来统计这一类点的权重和数量,同时为了给生成采样表做准备,将点的序号(点的读取顺序)、vid、权重等数据存储在了图中(B1)结构中。其中 key 的数据结构是 type + tagid + sid,type 标识了数据是 B1 类型,tagid 就是点的 tag,sid 是点的序号,跟采样表的 key 结构相同。
第二步将数据分类,根据权重值以点的总数的倒数为界将所有的点分成两部分,分别以(B2)、(B3)的结构存储,key 的组成与 (B1) 结构相比就是type 值不同。
第三步分别遍历(B2)、(B3),根据别名采样算法的理论来计算每个点的采样概率 prob。这里每个点是用 sid 来标识的,也就是说实际上是在计算第 sid 个点的采样概率,概率值作为采样表的一部分会更新到(B1)结构中。
第四步将中间变量(B2)、(B3)结构删除掉,释放磁盘空间。
利用采样表进行实时采样的过程就比较简单了。完成采样表的预计算后会将点、边权重的统计信息上报到 Meta 服务存储,然后通过心跳通知 Graph 服务将这些数据缓存在本地。根据这些数据可以计算出点、边在各 Storage 服务上所占的权重比例,将用户指定的采样数量按比例分配到各 Storage 服务,然后在 Storage 服务上通过两次的随机数操作采样到足够数量的数据后返回,最后在 Graph 服务上进行数据聚合,这样就完成了一次带权采样。
另外为了提高异步任务的健壮性,还实现了失败重试、任务重复限制、重建采样表后脏数据清理、导出采样表等功能。
提到项目设计以及重写其他算子过程中遇到的问题,图学习兴趣小队队长杨鑫表示因为 Nebula Graph 的数据都存储在磁盘中,要用 Nebula Graph 替换 Euler 原生内存图数据库,改造后的 Euler 在采样效率上肯定要低于原生 Euler,因此怎么保证改造后 Euler 的图学习训练耗时尽可能接近原生 Euler 是要解决的首要问题,至少得保证两者采样性能不能有数据级上的差距。
原生 Euler 中的图学习算法由 Python 实现,通过 TensorFlow 的 OP 机制来调用 C++ 实现的采样算子,然后在采样算子内直接调用内存图数据库获取数据,这样就完成了一次数据采样。
图兴趣小队最初的方案是将采样算子用 Python 实现,这样一次数据采样过程就变成了图学习算法直接调用采样算子,然后在采样算子内则通过 Nebula Graph 的 Python 客户端执行采样语法获取数据。这样改造后的系统会有更清晰的处理流程,但是经过测试他们发现其训练耗时要远大于原生 Euler,具体到训练过程中的每次数据采样上,其耗时是原生 Euler 的几十倍,这样的结果显然是不能满足要求的。通过分析各阶段执行耗时他们发现,数据采样的耗时要远大于采样语法的执行耗时,所以他们认为是 Python 客户端在反序列化上消耗了大量时间。因此图兴趣小队进行了第一次优化,使用 fbthrift fastproto 替换 Nebula Graph 原生客户端的序列化组件,优化后的性能提高了一倍,整体训练耗时确实有所降低,但是这样的结果却仍然无法满足他们的使用要求。
这时候用上了第二个方案,重写 C++ 版本的采样算子,在采样算子中通过 Nebula Graph 的 C++ 客户端执行采样语法获取数据,相比第一个方案多了一些适配工作,算子的重写主要是适配 C++ 客户端的输入输出,另外改造了 C++ 客户端以便采样算子的调用。
这也是他们最终的方案,这样改造后的 Euler 虽然在训练耗时上仍然高于原生 Euler,但是差距控制在了二倍以内,符合预期。
有意思的 Hackathon
当谈到本次有什么值得图学习兴趣小队留意的项目时,队长杨鑫表示比较关注的是大油条东北虎队伍的项目——如何吊打 Nebula 的深度查询,这个项目的优化思路对他们很有借鉴意义。
因为在实际使用中,图兴趣小队所在团队的业务方有很多的多跳查询需求,包括 GO 查询、FINDPATH 查询等。这本应该是 Nebula Graph 所擅长的部分,在大部分场景下也确实如此,但是在遇到出度很大的顶点的时候,查询性能会急剧恶化。其主要原因还是基于目前 Nebula Graph 的架构,多跳查询只能先将一跳的结果集汇总到 Graph 服务后才能进行下一跳计算,完全不能将多跳计算下推。而这个项目正好给他们提供了一个解决这个难题的思路。
附:图学习兴趣小队项目设计文稿:https://docs.qq.com/doc/DVnZQUVBzd1dmS0lS。
交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebula 名片,Nebula 小助手会拉你进群~~
基于 Nebula Graph 构建图学习能力的更多相关文章
- 图数据库|基于 Nebula Graph 的 BetweennessCentrality 算法
本文首发于 Nebula Graph Community 公众号 在图论中,介数(Betweenness)反应节点在整个网络中的作用和影响力.而本文主要介绍如何基于 Nebula Graph 图数据 ...
- GraphX 在图数据库 Nebula Graph 的图计算实践
不同来源的异构数据间存在着千丝万缕的关联,这种数据之间隐藏的关联关系和网络结构特性对于数据分析至关重要,图计算就是以图作为数据模型来表达问题并予以解决的过程. 一.背景 随着网络信息技术的飞速发展,数 ...
- 图数据库 Nebula Graph 的数据模型和系统架构设计
Nebula Graph:一个开源的分布式图数据库.作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,而且能够提供极高的 ...
- Nebula Graph 在微众银行数据治理业务的实践
本文为微众银行大数据平台:周可在 nMeetup 深圳场的演讲这里文字稿,演讲视频参见:B站 自我介绍下,我是微众银行大数据平台的工程师:周可,今天给大家分享一下 Nebula Graph 在微众银行 ...
- Nebula Graph 在企查查的应用
本文首发于 Nebula Graph Community 公众号 背景 企查查是企查查科技有限公司旗下的一款企业信用查询工具,旨在为用户提供快速查询企业工商信息.法院判决信息.关联企业信息.法律诉讼. ...
- Nebula Graph 在网易游戏业务中的实践
本文首发于 Nebula Graph Community 公众号 当游戏上知识图谱,网易游戏是如何应对大规模图数据的管理问题,Nebula Graph 又是如何帮助网易游戏落地游戏内复杂的图的业务呢? ...
- Nebula Graph 技术总监陈恒:图数据库怎么和深度学习框架进行结合?
引子 Nebula Graph 的技术总监在 09.24 - 09.30 期间同开源中国·高手问答的小伙伴们以「图数据库的设计和实践」为切入点展开讨论,包括:「图数据库的存储设计」.「图数据库的计算设 ...
- 使用 Docker 构建 Nebula Graph 源码
Nebula Graph 介绍 Nebula Graph 是开源的高性能分布式图数据库.项目使用 C++ 语言开发,cmake 工具构建.其中两个重要的依赖是 Facebook 的 Thrift RP ...
- 分布式图数据库 Nebula Graph 的 Index 实践
导读 索引是数据库系统中不可或缺的一个功能,数据库索引好比是书的目录,能加快数据库的查询速度,其实质是数据库管理系统中一个排序的数据结构.不同的数据库系统有不同的排序结构,目前常见的索引实现类型如 B ...
- 图数据库 Nebula Graph TTL 特性
导读 身处在现在这个大数据时代,我们处理的数据量需以 TB.PB, 甚至 EB 来计算,怎么处理庞大的数据集是从事数据库领域人员的共同问题.解决这个问题的核心在于,数据库中存储的数据是否都是有效的.有 ...
随机推荐
- 5.1 Windows驱动开发:判断驱动加载状态
在驱动开发中我们有时需要得到驱动自身是否被加载成功的状态,这个功能看似没啥用实际上在某些特殊场景中还是需要的,如下代码实现了判断当前驱动是否加载成功,如果加载成功, 则输出该驱动的详细路径信息. 该功 ...
- 如何控制Tomcat的catalina.out的大小
catalina.out文件,数据主要来源为:System.out 和 System.err 在控制台上直接输出的信息. 编码时应避免使用System.out.println()和e.printSta ...
- Protobuf之proto基础语法
目录 1.说明 2.字段类型 3.字段规则 4.字段编号 5.注释 6.类型 6.1.message 6.2.service 7.枚举enum 8.保留字段 9.import 9.1.protoc指令 ...
- 分享四个实用的vue自定义指令
v-drag 需求:鼠标拖动元素 思路: 元素偏移量 = 鼠标滑动后的坐标 - 鼠标初始点击元素时的坐标 + 初始点击时元素距离可视区域的top.left 将可视区域作为边界,限制在可视区域里面拖拽 ...
- 基于Python的用户登录和密码强度等级测试|Python小应用
前言 那么这里博主先安利一些干货满满的专栏了! 这两个都是博主在学习Linux操作系统过程中的记录,希望对大家的学习有帮助! 操作系统Operating Syshttps://blog.csdn.ne ...
- Java并发(十)----线程之守护线程
默认情况下,Java 进程需要等待所有线程都运行结束,才会结束.有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束. 例: log.debug(&q ...
- 安装Electron时卡在install.js不动的解决方案
问题来源,发现即使 源切换成淘宝的之后,安装 electron的时候还是慢死,郁闷,后来百度才发现,原来,还需要设置一个地方!!! 经过试验,果然快了 爽.... 之前在安装Electron的时候,经 ...
- Asp .Net Core 系列:Asp .Net Core 集成 NLog
简介 NLog是一个基于.NET平台编写的日志记录类库,它可以在应用程序中添加跟踪调试代码,以便在开发.测试和生产环境中对程序进行监控和故障排除.NLog具有简单.灵活和易于配置的特点,支持在任何一种 ...
- android mvvm实例解析
MVVM架构,将整个应用分为三层,View层,VM层,Model层.其中View层单向引用VM层,VM层单向引用Model层.如上图. 单向引用,而非双向引用,这是MVVM与MVP最大的区别.View ...
- 【Unity3D】Unity与Android交互
1 前言 本文主要介绍 Unity 打包发布 Android apk 流程.基于 AndroidJavaObject(或 AndroidJavaClass)实现 Unity 调用 Java 代码. ...