Skip to content

Commit 3eda848

Browse files
ntgbaooBảo Ngô
andauthored
Tutorials for THGL and TGB-seq (#385)
* Added placeholder * Added tgb_seq tutorial * Added draft for thgl tutorial * Added tutorial for thgl * Updated tutorial --------- Co-authored-by: Bảo Ngô <ngot1@myumanitoba.ca>
1 parent fbffd69 commit 3eda848

3 files changed

Lines changed: 392 additions & 2 deletions

File tree

docs/tutorials/dgraph_tutorial.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ You can build datasets in multiple ways. Let's look at each.
8080

8181
This is most likely all you need. The [Temporal Graph Benchmark (TGB)](https://tgb.complexdatalab.com/) provides a suite of temporal graph datasets with diverse scales and properties. We natively support direct construction from all the `tgbl-` and `tgbn-` in TGM.
8282

83-
> **Note**: Temporal knowledge graph (TKG) and temporal hypergraph (THG) are not yet supported in TGM.
83+
> **Note**: Temporal heterogeneous graph (THG) is supported in TGM. Check out [THG tutorial](./thgl_tutorial.md)
84+
85+
> **Note**: Temporal knowledge graph (TKG) is under construction and not yet supported in TGM.
8486
8587
> **Note**: To load a TGB dataset, you must have the `py-tgb` package in your python env.
8688
@@ -163,7 +165,7 @@ static_node_df = pd.DataFrame({
163165
'static_node_feat': [torch.rand(11).tolist() for _ in range(9)]
164166
})
165167

166-
dg = DGraph.from_pandas(
168+
dg = DGData.from_pandas(
167169
edge_df=edge_df,
168170
edge_src_col='src',
169171
edge_dst_col='dst',

docs/tutorials/tgb_seq_tutorial.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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

Comments
 (0)