技术分享 | GraphStore & FeatureStore

文摘   2024-08-05 08:42   上海  

GraphStore & FeatureStore 技术分享

1.引言

图结构数据由两种类型的数据组成:

  • 图拓扑:图中的节点以及它们之间的连边
  • 节点和边的特征:对应于图中节点和边的属性的密集向量

GNN使用图结构数据进行训练,当图数据过大时,通常需要用采样的方式获得mini-batch,在mini-batch上进行训练。但是mini-batch训练仍然需要将图拓扑和特征放在内存上,无法扩展到规模比内存大的图。因此,想要将GNN扩展到超出单台机器内存要求的大规模图,需要将图拓扑和特征移出内存。

2.GraphStore & FeatureStore

将图拓扑和特征移出内存的一种方案是将图数据存入数据库,通过与数据库交互提取图拓扑和相应的特征。GammaGL通过 GraphStore 和 FeatureStore 接口来实现这个功能,GraphStore 存取图拓扑,FeatureStore 存取节点特征。

通常情况下图数据中特征的数据量往往比图拓扑大好几个数量级,所以特征存储是大图训练时的主要存储瓶颈。FeatureStore 抽象出了对图特征进行增删改查的操作,可以根据使用的数据库,主要重写_put_tensor()_get_tensor()_remove_tensor() 方法实现相应数据库对节点特征进行增删改查的操作。同时,FeatureStore提供了简便直观的接口操作FeatureStore中的数据,如下:

feature_store = MyFeatureStore()

paper_features = ... # [num_papers, num_paper_features]

# Add features
feature_store['paper', 'attribute', [1,2,3]] = paper_features

# Access features
assert torch.equal(feature_store['paper', 'attribute'], paper_features)

# Del features
del feature_store['paper', 'attribute', [1,2,3]]

GraphStore 抽象出了对图拓扑进行增删改查的操作,可以根据使用的数据库,主要重写 _put_edge_index()_get_edge_index()  和 _remove_edge_index() 方法实现相应数据库对边索引进行增删改查的操作。GraphStore 支持 coo、csc、csr 格式的边,并提供进行类型转换的接口。同样的,GraphStore也可以通过简便的接口操作,如下:

graph_store = MyGraphStore()

edge = ... # [2, num_edges]

# Add edges
graph_store['cite', 'coo'] = edge

# Access edges
assert torch.equal(graph_store['cite', 'coo'], edge)

# Del edges
del graph_store['cite', 'coo']

3.数据库实例

GammaGL提供了使用 graphstore 和 featurestore 存储reddit数据集并将数据库中的数据用于训练的实例。

我们基于 neo4j 和 NebulaGraph 两个数据库分别实现了相应的 featureftore 和 graphstore,训练时先从 graphstore 中取出图拓扑,利用采样器对大图采样得到子图,然后再根据子图的 id 从 featurestore 中取出对应的节点特征。这样就可以避免把用不到的节点特征取入内存,极大的减少了内存占用。

Neo4jGraphStore 核心代码如下:

class Neo4jGraphStore(GraphStore):
def __init__(self, uri: str, user_name: str, password: str, **kwargs):
super().__init__()
self.driver = GraphDatabase.driver(uri, auth=(user_name, password))

def __del__(self):
self.driver.close()

def _put_edge_index(self, edge_index, edge_attr: EdgeAttr) -> bool:
batch_size = 128
with self.driver.session() as session:
for i in range(0, edge_index[0].size(0), batch_size):
batch_queries = []
for j in range(i, min(i + batch_size, edge_index[0].size(0))):
batch_queries.append({
"src_id": edge_index[0][j].item(),
"dst_id": edge_index[1][j].item()
})

session.run(
"""
UNWIND $rels AS rel
MERGE (n {ID: rel.src_id})
MERGE (m {ID: rel.dst_id})
"""
+ f"MERGE (n)-[r:{edge_attr.edge_type}]->(m)",
rels=batch_queries
)
return True

def _get_edge_index(self, edge_attr: EdgeAttr) -> EdgeTensorType:
with self.driver.session() as session:
result = session.run(
f"MATCH (n)-[r:{edge_attr.edge_type}]->(m) RETURN n.ID AS src_id, m.ID AS dst_id",
)
src_ids = []
dst_ids = []
for record in result:
src_ids.append(int(record['src_id']))
dst_ids.append(int(record['dst_id']))
edge_index = tlx.convert_to_tensor([src_ids,dst_ids])
return edge_index

def _remove_edge_index(self, edge_attr: EdgeAttr) -> bool:
with self.driver.session() as session:
session.run(f"MATCH (n)-[r:{edge_attr.edge_type}]->(m) DELETE r")
return True

def get_all_edge_attrs(self) -> List[EdgeAttr]:
with self.driver.session() as session:
result = session.run("CALL db.relationshipTypes() YIELD relationshipType RETURN relationshipType")
return [EdgeAttr(record['relationshipType'], EdgeLayout.COO) for record in result]

更多的实现可以在 https://github.com/xy-Ji/gdbi 查看。

我们的实现只是示例,用户可以在更多数据库上实现 featurestore 和 graphstore,也可以添加更多的功能或提供更高效的实现。如在键值数据库上实现featurestore,我们的示例实现是在图数据库上同时实现了graphstore和featurestore,事实上完全可以将它们分别在图数据库和键值数据库上实现,键值数据库根据 id 查询属性的效率远大于图数据库,因此基于键值数据库实现featurestore可以极大的提升性能。

实验室算法库简介

两算法库均获得OpenI启智社区“社区优秀孵化项目奖”

GammaGL:

GammaGL是一个基于TensorLayerX实现的开源的多框架的图神经网络(GNN)算法平台,该框架抽象出图数据、消息传递、采样等组件,已经实现了二十余种图数据集和六十余种术界经典GNN算法。GammaGL发布0.5版本 https://github.com/BUPT-GAMMA/GammaGL

OpenHGNN:

OpenHGNN是一个基于深度学习框架PyTorch和图神经网络框架DGL作为底层框架的异质图神经网络(HGNN)工具包。该工具包专注于HGNN的算法模型,抽象出了数据集管理、算法流程、任务评测等模块,提供了超参数调优组件和最优的超参数设置,发布了HGNN设计空间和Benchmark等。OpenHGNN发布0.7版本OpenHGNN:https://github.com/BUPT-GAMMA/OpenHGNN


北邮 GAMMA Lab
北邮图数据挖掘与机器学习实验室
 最新文章