Using TuGraph Graph Learning Module for Node Classification
1. Introduction
GNN is a powerful tool for many machine learning tasks on graphs. In this introductory tutorial, you will learn the basic workflow of using GNN for node classification, i.e., predicting the category of nodes in a graph.
This document will show how to build a GNN for semi-supervised node classification on the Cora dataset with only a few labels, which is a citation network with papers as nodes and citations as edges. The task is to predict the category of a given paper. By completing this tutorial, you will be able to:
Load the Cora dataset.
Sample and build a GNN model using the sampling operator provided by TuGraph. Train the GNN model for node classification on CPU or GPU.
This document requires some experience in using graph neural networks and DGL.
2. Prerequisites
The TuGraph graph learning module requires TuGraph-db version 3.5.1 or above.
It is recommended to use Docker image tugraph-compile 1.2.4 or above for TuGraph deployment:
tugraph/tugraph-compile-ubuntu18.04:latest
tugraph/tugraph-compile-centos7:latest
tugraph/tugraph-compile-centos8:latest
The above images can be obtained on DockerHub. Please refer to Quick Start for specific operations.
3. Import Cora Dataset to TuGraph Database
3.1. Introduction to Cora Dataset
The Cora dataset consists of 2708 papers, classified into 7 categories. Each paper is represented by a 1433-dimensional bag-of-words feature, indicating whether a certain word appears in the paper. These bag-of-words features have been preprocessed to be normalized to a range from 0 to 1. The edges represent citation relationships between papers.
TuGraph has provided the Cora dataset import tool, which users can use directly.
3.2. Data Import
The Cora dataset is located in the test/integration/data/algo directory and contains the cora_vertices point set and the cora_edge edge set.
First, the Cora dataset needs to be imported into the TuGraph database. Please refer to Data Import.
In the build/output section below the line:
cp -r ../../test/integration/data/ ./ && cp -r ../../learn/examples/* ./
The number of commands related to the relevant document is to be built/output.
Execute the following command in the build/output directory:
./lgraph_import -c ./data/algo/cora.conf --dir ./coradb --overwrite 1
Here, cora.conf is the graph schema file representing the format of graph data, which can be found in test/integration/data/algo/cora.conf. coradb is the name of the graph data file after import, which represents the storage location of graph data.
4. Feature Conversion
Since the features in the Cora dataset are float arrays of length 1433, which are not supported by TuGraph for loading, they can be imported as strings and converted to char* for easier access later. The implementation can be referred to the feature_float.cpp file. The specific execution process is as follows:
Compile the import plugin in the build directory.(Can be skipped if TuGraph has been compiled):
make feature_float_embed
Execute the following command in the build/output directory to perform the conversion:
./algo/feature_float_embed ./coradb
5. Compile Sampling Operator
The sampling operator is used to obtain graph data from the database and convert it into the required data structure. The specific execution process is as follows:
Can be skipped if TuGraph has been compiled.
Execute the following command in the tugraph-db/build directory:
make -j2
or execute the following command in the tugraph-db/learn/procedures directory:
python3 setup.py build_ext -i
from lgraph_db_python import *
import importlib
getdb = importlib.import_module("getdb")
getdb.Process(db, olapondb, feature_len, NodeInfo, EdgeInfo)
As shown in the code, after obtaining the algorithm .so file, import and use it.
6. Model training and saving
TuGraph calls the operators of the cython layer in the python layer to realize the training of the graph learning model.
The usage of the TuGraph graph learning module is introduced as follows:
Execute under the build/output folder
python3 train_full_cora.py --model_save_path ./cora_model
You can start training.
If the final printed loss value is less than 0.9, the training is successful. So far, the graph model training is completed, and the model is saved in the cora_model file.
The detailed training process is as follows:
6.1. Data Loading
galaxy = PyGalaxy(args.db_path)
galaxy.SetCurrentUser(args.username, args.password)
db = galaxy.OpenGraph(args.graph_name, False)
As shown in the code, load the data into memory based on the path of the graph data, username, password, and subgraph name. TuGraph can load multiple subgraphs for graph training, but we only load one subgraph here.
6.2. Build Sampler
During the training process, the GetDB operator is first used to obtain the graph data from the database and convert it into the required data structure. The specific code is as follows:
GetDB.Process(db_: lgraph_db_python.PyGraphDB, olapondb: lgraph_db_python.PyOlapOnDB, feature_num: size_t, NodeInfo: list, EdgeInfo: list)
As shown in the code, the results are stored in NodeInfo and EdgeInfo. NodeInfo and EdgeInfo are python list results, and their stored information is as follows:
Graph Data |
Storage Position |
---|---|
Edge source |
EdgeInfo[0] |
Edge destination |
EdgeInfo[1] |
Vertex ID |
NodeInfo[0] |
Vertex features |
NodeInfo[1] |
Vertex label |
NodeInfo[2] |
Then a sampler is constructed:
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. Convert the results to the required format
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
The results are converted to the required format to fit the training format.
6.4. Build the GCN model
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 is the length of features, which is 1433 here
out_size = classes # classes is the number of classes, which is 7 here
model = GCN(in_size, 16, out_size) # 16 is the size of the hidden layer
return model
In this tutorial, a two-layer Graph Convolutional Network (GCN) will be built. Each layer aggregates neighbor information to compute new node representations.
6.5. Train the GCN model
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 you need to save the model, give the 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()
As shown in the code, iterative training is performed 50 times according to the defined sampler, optimizer and model, and the trained model is saved to the path model_save_path.
The output is as follows:
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
The graph learning module can be accelerated by using a GPU. If users want to run it on the GPU, they need to install the corresponding GPU driver and environment. For details, please refer to learn/README.md.
The complete code can be found in learn/examples/train_full_cora.py.