图学习引擎#
GraphScope中的图学习引擎 (GLE) 是面向大规模图神经网络的研发和应用而设计的一款分布式框架。 它从实际问题出发,提炼和抽象了一套适合于当下图神经网络模型的编程范式, 并已经成功应用在阿里巴巴 内部的诸如搜索推荐、网络安全、知识图谱等众多场景。 GL注重可移植和可扩展,对于开发者更为友好,为了应对GNN在工业场景中的多样性和快速发展的需求。 基于GL,开发者可以实现一种GNN算法,或者面向实际场景定制化一种图算子,例如图采样。
接下来,我们通过一个入门教程介绍如何使用 GLE 来构建用户自己的CNN模型。
图学习模型#
图学习算法的实现目前主要有两种方式。第一种是直接以全图为计算对象, 原始的GCN,GAT等算法都是这种实现思路,一般会直接用邻接矩阵进行计算。 然而这种方法在大规模图上会消耗大量内存,导致无法高效训练甚至无法训练。 第二种思路是将全图分成若干子图,用深度学习里常用的批次训练方法进行训练, 每次训练一个子图,代表方法是GraphSAGE,FastGCN, GraphSAINT等。
GLE 主要面向超大规模图神经网络的开发。它由底层的一个图引擎和上层的 算法模型构成。图引擎分布式存储图的拓扑和属性信息并提供高效的图采样查询 接口,算法模型通过调用图采样和查询接口获取子图并进行计算。
GLE 提供了一个图学习算法的统一编程框架,支持常见图学习算法的开发,包括 GNNs, 知识图谱模型,图嵌入算法等,并且和主流的深度学习算法兼容,包括TensorFlow 和PyTorch。目前我们实现了基于TensorFlow的模型,基于PyTorch的模型正在开发中。
数据模型#
GLE 采用采样子图再计算的方式构建和训练模型。我们首先介绍一下基本的数据模型。
EgoGraph
是图学习算法编程的基本数据对象。它由一个batch的种子点或者边(称为’ego’)
以及他们的’感受野’(多跳邻居)组成。EgoGraph
由图采样和查询到的数据组成,我们实现
了常见的邻居采样、图遍历和负采样方法。
采样的数据组织成numpy格式的 EgoGraph
后根据不同的深度学习引擎转换成对应的tensor格式
EgoTensor
,然后用``EgoFlow`` 管理 EgoGraph
到 EgoTensor
的转换,提供训练所需要的数据。
编码器#
所有的图学习模型可以抽象为使用编码器将 EgoTensor
编码成最终的点、边或者子图的
向量。GLE 首先利用特征编码器来编码原始的点和边上的特征,然后将特征编码器编码后的
原始向量用不同的图编码器进行编码,得到最终的输出。对于大多数GNN模型,图编码器
提供了如何聚合邻居信息到自身节点或者边的抽象,用不同的图卷积层实现。
基于上面介绍的数据模型和编码器,我们可以简单快速地实现不同的图学习算法。在接下来一章里, 我们详细介绍了如何开发一个GNN模型。
自定义算法#
这篇文档我们将介绍如何用 GLE 提供的基本API配合深度学习引擎(如TensorFlow)来构建图学习算法。 我们以图神经网络里最流行的GCN模型做为示例来说明。
通常来说,实现一个算法需要下面四个步骤:
指定采样模式:用图采样、查询方法采样子图并组织成
EgoGraph
。我们抽象了4个基本的函数,
sample_seed
,positive_sample
,negative_sample
和receptive_fn
。sample_seed
用来遍历图数据产生Nodes
或者Edges
, 然后positive_sample
以这些Nodes
或者Edges
为输入产生 训练的正样本。对于无监督学习negative_sample
产生负样本。 GNNs需要聚合邻居信息, 我们抽象了receptive_fn
来采样邻居。 最后将sample_seed
产生的Nodes
、Edges
以及采样出的邻居组成EgoGraph
。构建图数据流:使用
EgoFlow
将EgoGraph
转换为EgoTensor
。GLE 算法模型基于类似TensorFlow的深度学习引擎构建。所以采样出的
EgoGraph``s 需要先转换成tensor格式 ``EgoTensor
才能使用。我们抽象了EgoFlow
来封装这一转换过程。EgoFlow
可以产生一个迭代器来进行批次训练。定义编码器:使用
EgoGraph
编码器和特征编码器来编码EgoTensor
。 得到EgoTensor
后,我们首先将原始的点、边特征用一些常见特征编码器编码成原始向量, 做为GNNs模型的特征输入。然后用图编码器处理EgoTensor
,将邻居节点特征进行汇聚并 和自身特征进行组合,得到最后的点或者边的向量。编写损失函数和训练过程:选择适当的损失函数,并编写训练过程。
GLE 内置了一些常见的损失函数和优化器,并对训练过程进行了封装,同时支持单机和分布式训练。 你也可以自定义损失函数、优化器和训练过程。
下面我们按照上面介绍的4个步骤来介绍如何实现一个GCN模型。
采样#
我们使用Cora数据集以点分类任务做为示例。我们提供了一个简单的数据转换脚本 cora.py
来
将原始Cora转换成 GLE 需要的格式。运行完这个脚本后你可以得到下面5个文件
node_table, edge_table_with_self_loop, train_table, val_table and test_table。
分别是点表、边表以及用来区分训练、验证和测试集的点表。
然后可以用下面代码来构建图:
import graphlearn as gle
g = gle.Graph()\
.node(dataset_folder + "node_table", node_type=node_type,
decoder=gle.Decoder(labeled=True,
attr_types=["float"] * 1433,
attr_delimiter=":"))\
.edge(dataset_folder + "edge_table_with_self_loop",
edge_type=(node_type, node_type, edge_type),
decoder=gle.Decoder(weighted=True), directed=False)\
.node(dataset_folder + "train_table", node_type="train",
decoder=gle.Decoder(weighted=True))\
.node(dataset_folder + "val_table", node_type="val",
decoder=gle.Decoder(weighted=True))\
.node(dataset_folder + "test_table", node_type="test",
decoder=gle.Decoder(weighted=True))
使用 g.init()
后这段代码会将图加载进内存:
class GCN(gle.LearningBasedModel):
def __init__(self,
graph,
output_dim,
features_num,
batch_size,
categorical_attrs_desc='',
hidden_dim=16,
hops_num=2,):
self.graph = graph
self.batch_size = batch_size
GCN模型继承自基本的学习模型类 LearningBasedModel
,只需要重写基类的采样,
模型构建等方法就可以完成GCN的构建。
class GCN(gle.LearningBasedModel):
# ...
def _sample_seed(self):
return self.graph.V('train').batch(self.batch_size).values()
def _positive_sample(self, t):
return gle.Edges(t.ids, self.node_type,
t.ids, self.node_type,
self.edge_type, graph=self.graph)
def _receptive_fn(self, nodes):
return self.graph.V(nodes.type, feed=nodes).alias('v') \
.outV(self.edge_type).sample().by('full').alias('v1') \
.outV(self.edge_type).sample().by('full').alias('v2') \
.emit(lambda x: gle.EgoGraph(x['v'], [ag.Layer(nodes=x['v1']), ag.Layer(nodes=x['v2'])]))
前两个函数用来采样种子节点和正样本,_receptive_fn
采样邻居并组织 EgoGraph
,
outV
回一跳邻居,因此上面代码是采样二跳邻居。这里可以选择不同的邻居采样方法,
对于原始GCN来说因为要获得每个点的所有邻居,因此选择 'full'
。采样完后将结果组织
成 EgoGraph
返回。
图数据流#
在 build
函数里我们使用封装的 EgoFlow
来把 EgoGraph
转换成对应的 EgoTensor
,
EgoFlow
包含一个数据流迭代器和若干 EgoTensor
。
class GCN(gle.LearningBasedModel):
def build(self):
ego_flow = gle.EgoFlow(self._sample_seed,
self._positive_sample,
self._receptive_fn,
self.src_ego_spec)
iterator = ego_flow.iterator
pos_src_ego_tensor = ego_flow.pos_src_ego_tensor
# ...
你可以从 EgoFlow
获取和前面 EgoGraph
对应的 EgoTensor
。
模型#
接下来,首先使用特征编码器来编码原始特征。这里我们使用 IdentityEncoder
,即返回自身即可,因为
Cora的特征已经是处理过的向量格式了。对于既有离散特征由于连续特征的情况,可以使用 WideNDeepEncoder
。
更多encoder请参考 feature encoder。
然后用 GCNConv
层构建图编码器,GCN每个节点采样全部邻居,邻居以稀疏格式组织,所以这里使用
SparseEgoGraphEncoder
, 邻居对齐的模型可以参考GraphSAGE的实现。
class GCN(gle.LearningBasedModel):
def _encoders(self):
depth = self.hops_num
feature_encoders = [gle.encoders.IdentityEncoder()] * (depth + 1)
conv_layers = []
# for input layer
conv_layers.append(gle.layers.GCNConv(self.hidden_dim))
# for hidden layer
for i in range(1, depth - 1):
conv_layers.append(gle.layers.GCNConv(self.hidden_dim))
# for output layer
conv_layers.append(gle.layers.GCNConv(self.output_dim, act=None))
encoder = gle.encoders.SparseEgoGraphEncoder(feature_encoders,
conv_layers)
return {"src": encoder, "edge": None, "dst": None}
损失函数和训练过程#
对于Cora点分类模型,我们选择对应的TensorFlow里的分类损失函数即可。
然后在 build
函数里将编码器和损失函数组织起来,最终返回一个数据迭代器和损失函数。
class GCN(gle.LearningBasedModel):
# ...
def _supervised_loss(self, emb, label):
return tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(emb, label))
def build(self):
ego_flow = gle.EgoFlow(self._sample_seed,
self._positive_sample,
self._receptive_fn,
self.src_ego_spec,
full_graph_mode=self.full_graph_mode)
iterator = ego_flow.iterator
pos_src_ego_tensor = ego_flow.pos_src_ego_tensor
src_emb = self.encoders['src'].encode(pos_src_ego_tensor)
labels = pos_src_ego_tensor.src.labels
loss = self._supervised_loss(src_emb, labels)
return loss, iterator
接着使用封装的单机训练过程 LocalTFTrainer
来进行训练。
def train(config, graph)
def model_fn():
return GCN(graph,
config['class_num'],
config['features_num'],
config['batch_size'],
...)
trainer = gle.LocalTFTrainer(model_fn, epoch=200)
trainer.train()
def main():
config = {...}
g = load_graph(config)
g.init(server_id=0, server_count=1, tracker='../../data/')
train(config, g)
这样就完成了一个GCN模型的编写。完整代码请参考 GCN example 目录。
我们实现了GCN, GAT, GraphSage, DeepWalk, LINE, TransE, Bipartite GraphSage, sample-based GCN and GAT等模型,你可以参考相似的模型代码做为开始。