使用 TuGraph 图学习模块进行点分类
1.简介
GNN 是许多图上机器学习任务的强大工具。在本介绍性教程中,您将学习使用 GNN 进行点分类的基本工作流程,即预测图中点的类别。
此文档将展示如何构建一个 GNN 用于在 Cora 数据集上仅使用少量标签进行半监督点分类,这是一个以论文为点、引文为边的引文网络。任务是预测给定论文的类别。
通过完成本教程,您可以
使用 TuGraph 加载 cora 数据集。
使用 TuGraph 提供的采样算子采样,并构建 GNN 模型。
在 CPU 或 GPU 上训练用于点分类的 GNN 模型。
此文档需要对图神经网络、DGL等使用有一定经验。
2. 前置条件
TuGraph图学习模块需要TuGraph-db 3.5.1及以上版本。
TuGraph部署推荐采用Docker镜像tugraph-compile 1.2.4及以上版本:
tugraph / tugraph-compile-ubuntu18.04:latest
tugraph / tugraph-compile-centos7:latest
tugraph / tugraph-compile-centos8:latest
以上镜像均可在DockerHub上获取。 具体操作请参考快速上手。
3. Cora 数据集导入TuGraph数据库
3.1. Cora 数据集介绍
Cora 数据集由 2708 篇论文组成,分为 7 个类别。每篇论文由一个 1433 维的词袋表示,表示论文中的单词是否出现。这些词袋特征已经预处理,以从 0 到 1 的范围归一化。边表示论文之间的引用关系。
TuGraph中已经提供了Cora数据集的导入工具,用户可以直接使用。
3.2. 数据导入
Cora数据集在test/integration/data/algo目录下,包含点集cora_vertices和边集cora_edge。
首先需要将Cora数据集导入到TuGraph数据库中,详细操作可参考数据导入。
在build/output目录下执行:
cp -r ../../test/integration/data/ ./ && cp -r ../../learn/examples/* ./
该指令将数据集相关文件拷贝到build/output目录下。
然后进行数据导入:
./lgraph_import -c ./data/algo/cora.conf --dir ./coradb --overwrite 1
其中cora.conf为图schema文件,代表图数据的格式,可参考test/integration/data/algo/cora.conf。coradb为导入后的图数据文件名称,代表图数据的存储位置。
4. feature特征转换
由于Cora数据集中的feature特征为长度为1433的float类型数组,TuGraph暂不支持float数组类型加载,因此可将其按照string类型导入后,转换成char*方便后续存取,具体实现可参考feature_float.cpp文件。 具体执行过程如下:
在build目录下编译导入plugin(如果TuGraph已编译可跳过):
make feature_float_embed
在build/output目录下执行
./algo/feature_float_embed ./coradb
即可进行转换。
5. 编译采样算子
采样算子用于从数据库中获取图数据并转换成所需数据结构,具体执行过程如下(如果TuGraph已编译,可跳过此步骤):
在tugraph-db/build文件夹下执行
make -j2
或在tugraph-db/learn/procedures文件夹下执行
python3 setup.py build_ext -i
from lgraph_db_python import * # 导入tugraph-db的python接口模块
import importlib # 导入importlib模块
getdb = importlib.import_module("getdb") #获取getdb算子
getdb.Process(db, olapondb, feature_len, NodeInfo, EdgeInfo) #调用getdb算子
如代码所示,得到算子so文件后,import 导入使用。
6. 模型训练及保存
TuGraph在python层调用cython层的算子,实现图学习模型的训练。
使用 TuGraph 图学习模块使用方式介绍如下:
在build/output文件夹下执行
python3 train_full_cora.py --model_save_path ./cora_model
即可进行训练。
最终打印loss数值小于0.9,即为训练成功。至此,图模型训练完成,模型保存在cora_model文件。
训练详细过程如下:
6.1.数据加载
galaxy = PyGalaxy(args.db_path)
galaxy.SetCurrentUser(args.username, args.password)
db = galaxy.OpenGraph(args.graph_name, False)
如代码所示,根据图数据路径、用户名、密码和子图名称将数据加载到内存中。TuGraph可以载入多个子图用于图训练,在此处我们只载入一个子图。
6.2.构建采样器
训练过程中,首先使用GetDB算子从数据库中获取图数据并转换成所需数据结构,具体代码如下:
GetDB.Process(db_: lgraph_db_python.PyGraphDB, olapondb: lgraph_db_python.PyOlapOnDB, feature_num: size_t, NodeInfo: list, EdgeInfo: list)
如代码所示,结果存储在NodeInfo和EdgeInfo中。NodeInfo和EdgeInfo是python list结果,其存储的信息结果如下:
图数据 | 存储信息位置 |
---|---|
边起点 | EdgeInfo[0] |
边终点 | EdgeInfo[1] |
顶点ID | NodeInfo[0] |
顶点特征 | NodeInfo[1] |
顶点标签 | NodeInfo[2] |
然后构建采样器
batch_size = 5
count = 2708
sampler = TugraphSample(args)
dataloader = dgl.dataloading.DataLoader(fake_g,
torch.arange(count),
sampler,
batch_size=batch_size,
num_workers=0,
)
6.3.对结果进行格式转换
src = EdgeInfo[0].astype('int64')
dst = EdgeInfo[1].astype('int64')
nodes_idx = NodeInfo[0].astype('int64')
remap(src, dst, nodes_idx)
features = NodeInfo[1].astype('float32')
labels = NodeInfo[2].astype('int64')
g = dgl.graph((src, dst))
g.ndata['feat'] = torch.tensor(features)
g.ndata['label'] = torch.tensor(labels)
return g
对结果进行格式转换,使之符合训练格式
6.4.构建GCN模型
class GCN(nn.Module):
def __init__(self, in_size, hid_size, out_size):
super().__init__()
self.layers = nn.ModuleList()
# two-layer GCN
self.layers.append(dgl.nn.GraphConv(in_size, hid_size, activation=F.relu))
self.layers.append(dgl.nn.GraphConv(hid_size, out_size))
self.dropout = nn.Dropout(0.5)
def forward(self, g, features):
h = features
for i, layer in enumerate(self.layers):
if i != 0:
h = self.dropout(h)
h = layer(g, h)
return h
def build_model():
in_size = feature_len #feature_len为feature的长度,在此处为1433
out_size = classes #classes为类别数,在此处为7
model = GCN(in_size, 16, out_size) #16为隐藏层大小
return model
本教程将构建一个两层图卷积网络(GCN)。每层通过聚合邻居信息来计算新的点表示。
6.5.训练GCN模型
loss_fcn = nn.CrossEntropyLoss()
def train(graph, model, model_save_path):
optimizer = torch.optim.Adam(model.parameters(), lr=1e-2, weight_decay=5e-4)
model.train()
s = time.time()
load_time = time.time()
graph = dgl.add_self_loop(graph)
logits = model(graph, graph.ndata['feat'])
loss = loss_fcn(logits, graph.ndata['label'])
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_time = time.time()
current_loss = float(loss)
if model_save_path != "": #如果需要保存模型,则给出模型保存路径
if 'min_loss' not in train.__dict__:
train.min_loss = current_loss
elif current_loss < train.min_loss:
train.min_loss = current_loss
model_save_path = 'best_model.pth'
torch.save(model.state_dict(), model_save_path)
return current_loss
for epoch in range(50):
model.train()
total_loss = 0
loss = train(g, model)
if epoch % 5 == 0:
print('In epoch', epoch, ', loss', loss)
sys.stdout.flush()
如代码所示,根据定义好的采样器、优化器和模型进行迭代训练50次,训练后的模型保存至model_save_path路径中。
输出结果如下:
In epoch 0 , loss 1.9586775302886963
In epoch 5 , loss 1.543689250946045
In epoch 10 , loss 1.160698413848877
In epoch 15 , loss 0.8862786889076233
In epoch 20 , loss 0.6973256468772888
In epoch 25 , loss 0.5770673751831055
In epoch 30 , loss 0.5271289348602295
In epoch 35 , loss 0.45514997839927673
In epoch 40 , loss 0.43748989701271057
In epoch 45 , loss 0.3906335234642029
同时,图学习模块可采用GPU进行加速,用户如果需要再GPU上运行,需要用户自行安装相应的GPU驱动和环境。具体可参考learn/README.md。
完整代码可参考learn/examples/train_full_cora.py