Files
dmlc--dgl/examples/pytorch/multigpu/multi_gpu_graph_prediction.py
2026-07-13 13:35:51 +08:00

168 lines
5.6 KiB
Python

import argparse
import dgl
import dgl.nn as dglnn
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from dgl.data import AsGraphPredDataset
from dgl.dataloading import GraphDataLoader
from ogb.graphproppred import DglGraphPropPredDataset, Evaluator
from ogb.graphproppred.mol_encoder import AtomEncoder, BondEncoder
from tqdm import tqdm
class MLP(nn.Module):
def __init__(self, in_feats):
super().__init__()
self.mlp = nn.Sequential(
nn.Linear(in_feats, 2 * in_feats),
nn.BatchNorm1d(2 * in_feats),
nn.ReLU(),
nn.Linear(2 * in_feats, in_feats),
nn.BatchNorm1d(in_feats),
)
def forward(self, h):
return self.mlp(h)
class GIN(nn.Module):
def __init__(self, n_hidden, n_output, n_layers=5):
super().__init__()
self.node_encoder = AtomEncoder(n_hidden)
self.edge_encoders = nn.ModuleList(
[BondEncoder(n_hidden) for _ in range(n_layers)]
)
self.pool = dglnn.AvgPooling()
self.dropout = nn.Dropout(0.5)
self.layers = nn.ModuleList()
for _ in range(n_layers):
self.layers.append(dglnn.GINEConv(MLP(n_hidden), learn_eps=True))
self.predictor = nn.Linear(n_hidden, n_output)
# add virtual node
self.virtual_emb = nn.Embedding(1, n_hidden)
nn.init.constant_(self.virtual_emb.weight.data, 0)
self.virtual_layers = nn.ModuleList()
for _ in range(n_layers - 1):
self.virtual_layers.append(MLP(n_hidden))
self.virtual_pool = dglnn.SumPooling()
def forward(self, g, x, x_e):
v_emb = self.virtual_emb.weight.expand(g.batch_size, -1)
hn = self.node_encoder(x)
for i in range(len(self.layers)):
v_hn = dgl.broadcast_nodes(g, v_emb)
hn = hn + v_hn
he = self.edge_encoders[i](x_e)
hn = self.layers[i](g, hn, he)
hn = F.relu(hn)
hn = self.dropout(hn)
if i != len(self.layers) - 1:
v_emb_tmp = self.virtual_pool(g, hn) + v_emb
v_emb = self.virtual_layers[i](v_emb_tmp)
v_emb = self.dropout(F.relu(v_emb))
hn = self.pool(g, hn)
return self.predictor(hn)
@torch.no_grad()
def evaluate(dataloader, device, model, evaluator):
model.eval()
y_true = []
y_pred = []
for batched_graph, labels in tqdm(dataloader):
batched_graph, labels = batched_graph.to(device), labels.to(device)
node_feat, edge_feat = (
batched_graph.ndata["feat"],
batched_graph.edata["feat"],
)
y_hat = model(batched_graph, node_feat, edge_feat)
y_true.append(labels.view(y_hat.shape).detach().cpu())
y_pred.append(y_hat.detach().cpu())
y_true = torch.cat(y_true, dim=0).numpy()
y_pred = torch.cat(y_pred, dim=0).numpy()
input_dict = {"y_true": y_true, "y_pred": y_pred}
return evaluator.eval(input_dict)
def train(rank, world_size, dataset_name, root):
dist.init_process_group(
"nccl", "tcp://127.0.0.1:12347", world_size=world_size, rank=rank
)
torch.cuda.set_device(rank)
dataset = AsGraphPredDataset(DglGraphPropPredDataset(dataset_name, root))
evaluator = Evaluator(dataset_name)
model = GIN(300, dataset.num_tasks).to(rank)
model = nn.parallel.DistributedDataParallel(model, device_ids=[rank])
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.5)
train_dataloader = GraphDataLoader(
dataset[dataset.train_idx], batch_size=256, use_ddp=True, shuffle=True
)
valid_dataloader = GraphDataLoader(dataset[dataset.val_idx], batch_size=256)
test_dataloader = GraphDataLoader(dataset[dataset.test_idx], batch_size=256)
for epoch in range(50):
model.train()
train_dataloader.set_epoch(epoch)
for batched_graph, labels in train_dataloader:
batched_graph, labels = batched_graph.to(rank), labels.to(rank)
node_feat, edge_feat = (
batched_graph.ndata["feat"],
batched_graph.edata["feat"],
)
logits = model(batched_graph, node_feat, edge_feat)
optimizer.zero_grad()
is_labeled = labels == labels
loss = F.binary_cross_entropy_with_logits(
logits.float()[is_labeled], labels.float()[is_labeled]
)
loss.backward()
optimizer.step()
scheduler.step()
if rank == 0:
val_metric = evaluate(
valid_dataloader, rank, model.module, evaluator
)[evaluator.eval_metric]
test_metric = evaluate(
test_dataloader, rank, model.module, evaluator
)[evaluator.eval_metric]
print(
f"Epoch: {epoch:03d}, Loss: {loss:.4f}, "
f"Val: {val_metric:.4f}, Test: {test_metric:.4f}"
)
dist.destroy_process_group()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--dataset",
type=str,
default="ogbg-molhiv",
choices=["ogbg-molhiv", "ogbg-molpcba"],
help="name of dataset (default: ogbg-molhiv)",
)
dataset_name = parser.parse_args().dataset
root = "./data/OGB"
DglGraphPropPredDataset(dataset_name, root)
world_size = torch.cuda.device_count()
print("Let's use", world_size, "GPUs!")
args = (world_size, dataset_name, root)
import torch.multiprocessing as mp
mp.spawn(train, args=args, nprocs=world_size, join=True)