|
| 1 | +# TGB-Seq tutorial |
| 2 | + |
| 3 | +[`TGB-Seq`](https://arxiv.org/abs/2502.02975) is supported in `TGM`. This tutorial provides an overview of how to set up and run experiments on `TGB-Seq`. |
| 4 | + |
| 5 | +## Running Pre-packaged examples |
| 6 | + |
| 7 | +TGM includes pre-packaged example scripts to help you get started quickly. The examples require extra dependencies, including `TGB-Seq`, beyond the core library. |
| 8 | + |
| 9 | +``` |
| 10 | +pip install -e .[examples] |
| 11 | +``` |
| 12 | + |
| 13 | +*Please advise [TGB-Seq Github](https://github.com/TGB-Seq/TGB-Seq) for further information.* |
| 14 | + |
| 15 | +After installing the dependencies, you can run any supported model on any `TGB-Seq` benchmark dataset. For instance, `EdgeBank` dynamic link prediction on [`GoogleLocal`](https://tgb-seq.github.io/datasets/#googlelocal): |
| 16 | + |
| 17 | +``` |
| 18 | +python examples/linkproppred/tgb_seq/edgebank.py --dataset GoogleLocal --device cuda |
| 19 | +``` |
| 20 | + |
| 21 | +To view the full list of available datasets, please visit the [`TGB-Seq` dataset page.](https://tgb-seq.github.io/datasets/) |
| 22 | + |
| 23 | +> \[!NOTE\] |
| 24 | +> By default, our link prediction examples on `TGB-Seq` default to `GoogleLocal`. |
| 25 | +> Examples run on CPU by default; use the `--device` flag to override this as shown above. |
| 26 | +
|
| 27 | +## `TGB-Seq` dataload |
| 28 | + |
| 29 | +`TGM` provides data loading I/O support for `TGB-Seq`. A `TGB-Seq` dataset can be loaded as follows: |
| 30 | + |
| 31 | +```python |
| 32 | +full_data= DGData.from_tgb_seq( |
| 33 | + 'GoogleLocal', root='./data' |
| 34 | +) |
| 35 | + |
| 36 | +train_data, val_data, test_data = full_data.split() |
| 37 | +``` |
| 38 | + |
| 39 | +Unlike `TGB`, which downloads dataset to `.env` by default, `TGB-Seq` requires you to explicitly specify the download destination (in this example, `./data`). |
| 40 | + |
| 41 | +## `TGB-Seq` custom negative sampler hook |
| 42 | + |
| 43 | +To evaluate to TGB-Seq on link prediction, we need a custom hook from `TGB-Seq` to sample negative edges: |
| 44 | + |
| 45 | +```python |
| 46 | +from tgm.hooks import StatelessHook |
| 47 | + |
| 48 | +class TGBSEQ_NegativeEdgeSamplerHook(StatelessHook): |
| 49 | + produces = {'neg', 'neg_time'} |
| 50 | + |
| 51 | + def __init__( |
| 52 | + self, dataset_name: str, split_mode: str, dgraph: DGraph, root: str = './data' |
| 53 | + ) -> None: |
| 54 | + self.has_precomputed_negatives = split_mode == 'test' |
| 55 | + |
| 56 | + if self.has_precomputed_negatives: |
| 57 | + from tgb_seq.LinkPred.dataloader import TGBSeqLoader |
| 58 | + |
| 59 | + self.negs = torch.from_numpy( |
| 60 | + TGBSeqLoader(dataset_name, root=root).negative_samples |
| 61 | + ) |
| 62 | + self.neg_idx = 0 |
| 63 | + else: |
| 64 | + edge_dst = dgraph.edge_dst |
| 65 | + self.low, self.high = int(edge_dst.min()), int(edge_dst.max()) |
| 66 | + self.num_negs = 100 |
| 67 | + |
| 68 | + def __call__(self, dg: DGraph, batch: DGBatch) -> DGBatch: |
| 69 | + batch_size = len(batch.edge_src) |
| 70 | + |
| 71 | + if self.has_precomputed_negatives: |
| 72 | + batch.neg = self.negs[self.neg_idx : self.neg_idx + batch_size] |
| 73 | + self.neg_idx += batch_size |
| 74 | + else: |
| 75 | + size = (self.num_negs, batch_size) |
| 76 | + batch.neg = torch.randint( |
| 77 | + self.low, self.high, size, dtype=torch.int32, device=dg.device |
| 78 | + ) |
| 79 | + |
| 80 | + batch.neg_time = batch.edge_time.clone() |
| 81 | + return batch |
| 82 | +``` |
| 83 | + |
| 84 | +## Minimal example |
| 85 | + |
| 86 | +Here’s a basic example demonstrating how to run `EdgeBank` for dynamic link property prediction on `GoogleLocal`: |
| 87 | + |
| 88 | +```python |
| 89 | +import numpy as np |
| 90 | +import torch |
| 91 | +from tgb_seq.LinkPred.evaluator import Evaluator |
| 92 | +from tqdm import tqdm |
| 93 | + |
| 94 | +from tgm import DGBatch, DGraph |
| 95 | +from tgm.data import DGData, DGDataLoader |
| 96 | +from tgm.hooks import HookManager |
| 97 | +from tgm.nn import EdgeBankPredictor |
| 98 | + |
| 99 | + |
| 100 | +def eval( |
| 101 | + loader: DGDataLoader, |
| 102 | + model: EdgeBankPredictor, |
| 103 | + evaluator: Evaluator, |
| 104 | +) -> float: |
| 105 | + perf_list = [] |
| 106 | + for batch in tqdm(loader): |
| 107 | + negs_per_pos = len(batch.neg) |
| 108 | + |
| 109 | + for idx in range(negs_per_pos): |
| 110 | + query_src = batch.edge_src[idx].repeat(negs_per_pos + 1) |
| 111 | + query_dst = torch.cat([batch.edge_dst[idx].unsqueeze(0), batch.neg[idx]]) |
| 112 | + |
| 113 | + y_pred = model(query_src, query_dst) |
| 114 | + y_pred_pos, y_pred_neg = y_pred[0].unsqueeze(0), y_pred[1:] |
| 115 | + perf_list.append(evaluator.eval(y_pred_pos, y_pred_neg)) |
| 116 | + model.update(batch.edge_src, batch.edge_dst, batch.edge_time) |
| 117 | + |
| 118 | + return float(np.mean(perf_list)) |
| 119 | + |
| 120 | +evaluator = Evaluator() |
| 121 | + |
| 122 | +train_data, val_data, test_data = DGData.from_tgb_seq( |
| 123 | + 'GoogleLocal', root='./data' |
| 124 | +).split() |
| 125 | +train_dg = DGraph(train_data) |
| 126 | +test_dg = DGraph(test_data) |
| 127 | + |
| 128 | +edge_dst = test_dg.edge_dst |
| 129 | +low, high = int(edge_dst.min()), int(edge_dst.max()) |
| 130 | + |
| 131 | +hm = HookManager(keys=['test']) |
| 132 | +hm.register( |
| 133 | + 'test', |
| 134 | + TGBSEQ_NegativeEdgeSamplerHook( |
| 135 | + 'GoogleLocal', split_mode='test', dgraph=test_dg, root='./data' |
| 136 | + ), |
| 137 | +) |
| 138 | + |
| 139 | +test_loader = DGDataLoader(test_dg, batch_size=200, hook_manager=hm, drop_last=True) |
| 140 | + |
| 141 | +train_data = train_dg.materialize(materialize_features=False) |
| 142 | +model = EdgeBankPredictor( |
| 143 | + train_data.edge_src, |
| 144 | + train_data.edge_dst, |
| 145 | + train_data.edge_time, |
| 146 | +) |
| 147 | + |
| 148 | + |
| 149 | +with hm.activate('test'): |
| 150 | + test_mrr = eval(test_loader, model, evaluator) |
| 151 | + log_metric(f'Test {METRIC_TGB_LINKPROPPRED}', test_mrr) |
| 152 | + |
| 153 | +``` |
| 154 | + |
| 155 | +## References |
| 156 | + |
| 157 | +1. [TGB-Seq Benchmark: Challenging Temporal GNNs with Complex Sequential Dynamics.](https://openreview.net/forum?id=8e2LirwiJT) |
0 commit comments