Skip to content

Commit 227e3da

Browse files
authored
Merge pull request #327 from FluxML/datasets
Add demo cards to tutorials
2 parents 54ba968 + d0d65f3 commit 227e3da

File tree

14 files changed

+366
-36
lines changed

14 files changed

+366
-36
lines changed

docs/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
[deps]
2+
DemoCards = "311a05b2-6137-4a5a-b473-18580a3d38b5"
23
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
34
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
45
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
6+
GeometricFlux = "7e08b658-56d3-11e9-2997-919d5b31e4ea"
57

68
[compat]
79
Documenter = "0.27"

docs/bibliography.bib

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ @inproceedings{Satorras2021
201201
@article{Dwivedi2021,
202202
abstract = {Graph neural networks (GNNs) have become the standard learning architectures for graphs. GNNs have been applied to numerous domains ranging from quantum chemistry, recommender systems to knowledge graphs and natural language processing. A major issue with arbitrary graphs is the absence of canonical positional information of nodes, which decreases the representation power of GNNs to distinguish e.g. isomorphic nodes and other graph symmetries. An approach to tackle this issue is to introduce Positional Encoding (PE) of nodes, and inject it into the input layer, like in Transformers. Possible graph PE are Laplacian eigenvectors. In this work, we propose to decouple structural and positional representations to make easy for the network to learn these two essential properties. We introduce a novel generic architecture which we call LSPE (Learnable Structural and Positional Encodings). We investigate several sparse and fully-connected (Transformer-like) GNNs, and observe a performance increase for molecular datasets, from 2.87% up to 64.14% when considering learnable PE for both GNN classes.},
203203
author = {Vijay Prakash Dwivedi and Anh Tuan Luu and Thomas Laurent and Yoshua Bengio and Xavier Bresson},
204+
journal = {ArXiv},
204205
month = {10},
205206
title = {Graph Neural Networks with Learnable Structural and Positional Representations},
206207
url = {http://arxiv.org/abs/2110.07875},

docs/make.jl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
using Documenter
22
using DocumenterCitations
3+
using DemoCards
34
using GeometricFlux
45

6+
const ASSETS = ["assets/flux.css", "assets/favicon.ico"]
7+
58
bib = CitationBibliography(joinpath(@__DIR__, "bibliography.bib"), sorting=:nyt)
69

710
DocMeta.setdocmeta!(GeometricFlux, :DocTestSetup, :(using GeometricFlux, Flux); recursive=true)
811

12+
# DemoCards
13+
demopage, postprocess_cb, demo_assets = makedemos("tutorials")
14+
isnothing(demo_assets) || (push!(ASSETS, demo_assets))
15+
916
makedocs(
1017
bib,
1118
sitename = "GeometricFlux.jl",
1219
format = Documenter.HTML(
13-
assets = ["assets/flux.css", "assets/favicon.ico"],
20+
assets = ASSETS,
1421
canonical = "https://fluxml.ai/GeometricFlux.jl/stable/",
1522
analytics = "G-M61P0B2Y8E",
23+
edit_link = "master",
1624
),
1725
clean = false,
1826
modules = [GeometricFlux,GraphSignals],
1927
pages = ["Home" => "index.md",
20-
"Tutorials" => [
21-
"Semi-Supervised Learning with GCN" => "tutorials/semisupervised_gcn.md",
22-
"GCN with Static Graph" => "tutorials/gcn_static_graph.md",
23-
"Graph Attention Network" => "tutorials/gat.md",
24-
"DeepSet for Digit Sum" => "tutorials/deepset.md",
25-
"Variational Graph Autoencoder" => "tutorials/vgae.md",
26-
"Graph Embedding" => "tutorials/graph_embedding.md",
27-
],
28+
demopage,
2829
"Introduction" => "introduction.md",
2930
"Basics" => [
3031
"Graph Convolutions" => "basics/conv.md",
@@ -55,6 +56,9 @@ makedocs(
5556
]
5657
)
5758

59+
# callbacks of DemoCards
60+
postprocess_cb()
61+
5862
deploydocs(
5963
repo = "github.com/FluxML/GeometricFlux.jl.git",
6064
target = "build",

docs/src/tutorials/graph_embedding.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/tutorials/config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"theme": "grid"
3+
}

docs/tutorials/examples/assets/logo.svg

Lines changed: 251 additions & 0 deletions
Loading

docs/tutorials/examples/config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"theme": "grid",
3+
"description": "To begin with GeometricFlux, it is recommended to learn with following examples.",
4+
"order": [
5+
"semisupervised_gcn.md",
6+
"gcn_static_graph.md",
7+
"gat.md",
8+
"deepset.md",
9+
"vgae.md",
10+
"graph_embedding.md"
11+
]
12+
}

docs/src/tutorials/deepset.md renamed to docs/tutorials/examples/deepset.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
# Predicting Digits Sum from DeepSet model
1+
---
2+
title: Predicting Digits Sum from DeepSet Model
3+
cover: assets/logo.svg
4+
id: deepset
5+
---
6+
7+
# Predicting Digits Sum from DeepSet Model
28

39
Digits sum is a task of summing up digits in images or text. This example demonstrates summing up digits in arbitrary number of MNIST images. To accomplish such task, DeepSet model is suitable for this task. DeepSet model is excellent at the task which takes a set of objects and reduces them into single object.
410

@@ -9,8 +15,9 @@ Since a DeepSet model predicts the summation from a set of images, we have to pr
915
First, the whole dataset is loaded from MLDatasets.jl and then shuffled before generating training dataset.
1016

1117
```julia
12-
train_X, train_y = MLDatasets.MNIST.traindata(Float32)
13-
train_X, train_y = shuffle_data(train_X, train_y)
18+
train_data, test_data = MNIST(:train), MNIST(:test)
19+
train_X, train_y = shuffle_data(train_data.features, train_data.targets)
20+
test_X, test_y = shuffle_data(test_data.features, test_data.targets)
1421
```
1522

1623
The `generate_featuredgraphs` here generates a set of pairs which contains a `FeaturedGraph` and a summed number for prediction target. In a `FeaturedGraph`, an arbitrary number of MNIST images are collected as node features and corresponding nodes are collected in a graph without edges.
@@ -68,9 +75,8 @@ for epoch = 1:args.epochs
6875
@info "Epoch $(epoch)"
6976

7077
for batch in train_loader
71-
train_loss, back = Flux.pullback(ps) do
72-
model_loss(model, batch |> device)
73-
end
78+
batch = batch |> device
79+
train_loss, back = Flux.pullback(() -> model_loss(model, batch), ps)
7480
test_loss = model_loss(model, test_loader, device)
7581
grad = back(1f0)
7682
Flux.Optimise.update!(opt, ps, grad)

docs/src/tutorials/gat.md renamed to docs/tutorials/examples/gat.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
title: Graph Attention Network
3+
cover: assets/logo.svg
4+
id: gat
5+
---
6+
17
# Graph Attention Network
28

39
Graph attention network (GAT) belongs to the message-passing network family, and it queries node feature over its neighbor features and generates result as layer output.
@@ -7,18 +13,26 @@ Graph attention network (GAT) belongs to the message-passing network family, and
713
We load dataset from Planetoid dataset. Here cora dataset is used.
814

915
```julia
10-
train_X, train_y = map(x -> Matrix(x), alldata(Planetoid(), dataset, padding=true))
16+
data = dataset[1].node_data
17+
X, y = data.features, onehotbatch(data.targets, 1:7)
18+
train_idx, test_idx = data.train_mask, data.val_mask
1119
```
1220

1321
## Step 2: Batch up Features and Labels
1422

1523
Just batch up features as usual.
1624

1725
```julia
26+
s, t = dataset[1].edge_index
27+
g = Graphs.Graph(dataset[1].num_nodes)
28+
for (i, j) in zip(s, t)
29+
Graphs.add_edge!(g, i, j)
30+
end
31+
1832
add_all_self_loops!(g)
1933
fg = FeaturedGraph(g)
20-
train_data = (repeat(train_X, outer=(1,1,train_repeats)), repeat(train_y, outer=(1,1,train_repeats)))
21-
train_loader = DataLoader(train_data, batchsize=batch_size, shuffle=true)
34+
train_X, train_y = repeat(X, outer=(1,1,train_repeats)), repeat(y, outer=(1,1,train_repeats))
35+
train_loader = DataLoader((train_X, train_y), batchsize=batch_size, shuffle=true)
2236
```
2337

2438
Notably, self loop for all nodes are needed for GAT model.
@@ -66,9 +80,8 @@ for epoch = 1:args.epochs
6680
@info "Epoch $(epoch)"
6781

6882
for (X, y) in train_loader
69-
loss, back = Flux.pullback(ps) do
70-
model_loss(model, X |> device, y |> device, train_idx |> device)
71-
end
83+
X, y, device_idx = X |> device, y |> device, train_idx |> device
84+
loss, back = Flux.pullback(() -> model_loss(model, X, y, device_idx), ps)
7285
train_acc = accuracy(model, train_loader, device, train_idx)
7386
test_acc = accuracy(model, test_loader, device, test_idx)
7487
grad = back(1f0)

docs/src/tutorials/gcn_static_graph.md renamed to docs/tutorials/examples/gcn_static_graph.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
title: GCN with Static Graph
3+
cover: assets/logo.svg
4+
id: gcn_static_graph
5+
---
6+
17
# GCN with Static Graph
28

39
In the tutorial for semi-supervised learning with GCN, variable graphs are provided to GNN from `FeaturedGraph`, which contains a graph and node features. Each `FeaturedGraph` object can contain different graph and different node features, and can be train on the same GNN model. However, variable graph doesn't have the proper form of graph structure with respect to GNN layers and this lead to inefficient training/inference process. Static graph strategy can be used to train a GNN model with the same graph structure in GeometricFlux.
@@ -26,23 +32,29 @@ Since features are in the form of array, they can be batched up for batched lear
2632
Different from loading datasets in semi-supervised learning example, we use `alldata` for supervised learning here and `padding=true` is added in order to padding features from partial nodes to pseudo-full nodes. A padded features contains zeros in the nodes that are not supposed to be train on.
2733

2834
```julia
29-
train_X, train_y = map(x -> Matrix(x), alldata(Planetoid(), dataset, padding=true))
35+
data = dataset[1].node_data
36+
X, y = data.features, onehotbatch(data.targets, 1:7)
37+
train_idx, test_idx = data.train_mask, data.val_mask
38+
train_X, train_y = repeat(X, outer=(1,1,train_repeats)), repeat(y, outer=(1,1,train_repeats))
3039
```
3140

3241
We need graph and node indices for training as well.
3342

3443
```julia
35-
g = graphdata(Planetoid(), dataset)
36-
train_idx = 1:size(train_X, 2)
44+
s, t = dataset[1].edge_index
45+
g = Graphs.Graph(dataset[1].num_nodes)
46+
for (i, j) in zip(s, t)
47+
Graphs.add_edge!(g, i, j)
48+
end
49+
fg = FeaturedGraph(g)
3750
```
3851

3952
## Step 2: Batch up Features and Labels
4053

4154
In order to make batch learning available, we separate graph and node features. We don't subgraph here. Node features are batched up by repeating node features here for demonstration, since planetoid dataset doesn't have batched settings. Different repeat numbers can be specified by `train_repeats` and `train_repeats`.
4255

4356
```julia
44-
fg = FeaturedGraph(g)
45-
train_data = (repeat(train_X, outer=(1,1,train_repeats)), repeat(train_y, outer=(1,1,train_repeats)))
57+
train_loader = DataLoader((train_X, train_y), batchsize=batch_size, shuffle=true)
4658
```
4759

4860
## Step 3: Build a GCN model
@@ -99,7 +111,8 @@ for epoch = 1:args.epochs
99111
@info "Epoch $(epoch)"
100112

101113
for (X, y) in train_loader
102-
grad = gradient(() -> model_loss(model, args.λ, X |> device, y |> device, train_idx |> device), ps)
114+
X, y, device_idx = X |> device, y |> device, train_idx |> device
115+
grad = gradient(() -> model_loss(model, args.λ, X, y, device_idx), ps)
103116
Flux.Optimise.update!(opt, ps, grad)
104117
train_steps += 1
105118
end

0 commit comments

Comments
 (0)