Skip to content

Commit 65d6e5f

Browse files
update examples to explicit gradient (#301)
* update to explicit gradient * update DiffEqFlux * regenerate pluto output * change test
1 parent 532caa6 commit 65d6e5f

21 files changed

+1523
-1122
lines changed

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
- uses: actions/checkout@v3
1515
- uses: julia-actions/setup-julia@latest
1616
with:
17-
version: '1.8.2'
17+
version: '1.9.1'
1818
- name: Install dependencies
1919
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
2020
- name: Build and deploy

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Among its features:
1919
* CUDA support.
2020
* Integration with [Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl).
2121
* [Examples](https://github.com/CarloLucibello/GraphNeuralNetworks.jl/tree/master/examples) of node, edge, and graph level machine learning tasks.
22+
* Heterogeneous and temporal graphs.
2223

2324
## Installation
2425

@@ -30,7 +31,7 @@ pkg> add GraphNeuralNetworks
3031

3132
## Usage
3233

33-
Usage examples can be found in the [examples](https://github.com/CarloLucibello/GraphNeuralNetworks.jl/tree/master/examples) and in the [notebooks](https://github.com/CarloLucibello/GraphNeuralNetworks.jl/tree/master/notebooks) folder. Also, make sure to read the [documentation](https://CarloLucibello.github.io/GraphNeuralNetworks.jl/dev) for a comprehensive introduction to the library.
34+
Usage examples can be found in the [examples](https://github.com/CarloLucibello/GraphNeuralNetworks.jl/tree/master/examples) and in the [notebooks](https://github.com/CarloLucibello/GraphNeuralNetworks.jl/tree/master/notebooks) folder. Also, make sure to read the [documentation](https://CarloLucibello.github.io/GraphNeuralNetworks.jl/dev) for a comprehensive introduction to the library.
3435

3536

3637
## Citing

docs/Project.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
88
MarkdownLiteral = "736d6165-7244-6769-4267-6b50796e6954"
99
NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd"
10+
Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
11+
PlutoStaticHTML = "359b1769-a58e-495b-9770-312e911026ad"
1012
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1113
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1214
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1315

14-
1516
[compat]
16-
DemoCards = "^0.4.11"
17+
DemoCards = "0.5.0"

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Flux, NNlib, GraphNeuralNetworks, Graphs, SparseArrays
2+
using Pluto, PlutoStaticHTML # for tutorials
23
using Documenter, DemoCards
34

45
tutorials, tutorials_cb, tutorial_assets = makedemos("tutorials")

docs/pluto_output/gnn_intro_pluto.md

Lines changed: 19 additions & 20 deletions
Large diffs are not rendered by default.

docs/pluto_output/graph_classification_pluto.md

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
<!--
2626
# This information is used for caching.
2727
[PlutoStaticHTML.State]
28-
input_sha = "465a4590b716e99f2d70eaaa9ca2e5de5f8549d401b4fa432fab439ab30aa172"
29-
julia_version = "1.8.2"
28+
input_sha = "f145b80b8f1e399d4cd5686b529cf173942102c538702952fe0743defca62210"
29+
julia_version = "1.9.1"
3030
-->
3131
<pre class='language-julia'><code class='language-julia'>begin
3232
using Flux
@@ -102,18 +102,18 @@ end</code></pre>
102102
<div class="markdown"><p>We have some useful utilities for working with graph datasets, <em>e.g.</em>, we can shuffle the dataset and use the first 150 graphs as training graphs, while using the remaining ones for testing:</p></div>
103103
104104
<pre class='language-julia'><code class='language-julia'>train_data, test_data = splitobs((graphs, y), at = 150, shuffle = true) |&gt; getobs</code></pre>
105-
<pre class="code-output documenter-example-output" id="var-train_data">((GraphNeuralNetworks.GNNGraphs.GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}[GNNGraph(16, 34), GNNGraph(15, 32), GNNGraph(19, 44), GNNGraph(20, 44), GNNGraph(20, 46), GNNGraph(15, 34), GNNGraph(18, 40), GNNGraph(16, 36), GNNGraph(13, 28), GNNGraph(16, 34) … GNNGraph(23, 48), GNNGraph(20, 44), GNNGraph(28, 66), GNNGraph(25, 56), GNNGraph(13, 28), GNNGraph(16, 36), GNNGraph(12, 24), GNNGraph(22, 50), GNNGraph(25, 58), GNNGraph(19, 42)], Bool[1 1 … 0 0; 0 0 … 1 1]), (GraphNeuralNetworks.GNNGraphs.GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}[GNNGraph(12, 24), GNNGraph(11, 22), GNNGraph(15, 34), GNNGraph(19, 44), GNNGraph(22, 50), GNNGraph(17, 38), GNNGraph(17, 38), GNNGraph(17, 38), GNNGraph(19, 42), GNNGraph(13, 28) … GNNGraph(19, 40), GNNGraph(13, 28), GNNGraph(22, 50), GNNGraph(14, 28), GNNGraph(23, 54), GNNGraph(20, 46), GNNGraph(13, 28), GNNGraph(26, 60), GNNGraph(17, 38), GNNGraph(12, 26)], Bool[1 1 … 0 0; 0 0 … 1 1]))</pre>
105+
<pre class="code-output documenter-example-output" id="var-train_data">((GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}[GNNGraph(12, 24) with x: 7×12 data, GNNGraph(22, 50) with x: 7×22 data, GNNGraph(23, 54) with x: 7×23 data, GNNGraph(25, 56) with x: 7×25 data, GNNGraph(16, 36) with x: 7×16 data, GNNGraph(11, 22) with x: 7×11 data, GNNGraph(18, 38) with x: 7×18 data, GNNGraph(23, 52) with x: 7×23 data, GNNGraph(22, 50) with x: 7×22 data, GNNGraph(20, 46) with x: 7×20 data … GNNGraph(16, 34) with x: 7×16 data, GNNGraph(13, 28) with x: 7×13 data, GNNGraph(21, 44) with x: 7×21 data, GNNGraph(17, 38) with x: 7×17 data, GNNGraph(23, 54) with x: 7×23 data, GNNGraph(12, 24) with x: 7×12 data, GNNGraph(22, 50) with x: 7×22 data, GNNGraph(19, 42) with x: 7×19 data, GNNGraph(16, 34) with x: 7×16 data, GNNGraph(16, 36) with x: 7×16 data], Bool[1 0 … 1 0; 0 1 … 0 1]), (GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}[GNNGraph(21, 44) with x: 7×21 data, GNNGraph(22, 50) with x: 7×22 data, GNNGraph(16, 34) with x: 7×16 data, GNNGraph(27, 66) with x: 7×27 data, GNNGraph(13, 26) with x: 7×13 data, GNNGraph(20, 44) with x: 7×20 data, GNNGraph(19, 44) with x: 7×19 data, GNNGraph(20, 46) with x: 7×20 data, GNNGraph(16, 34) with x: 7×16 data, GNNGraph(13, 28) with x: 7×13 data … GNNGraph(11, 22) with x: 7×11 data, GNNGraph(20, 46) with x: 7×20 data, GNNGraph(16, 34) with x: 7×16 data, GNNGraph(18, 40) with x: 7×18 data, GNNGraph(13, 28) with x: 7×13 data, GNNGraph(20, 44) with x: 7×20 data, GNNGraph(14, 30) with x: 7×14 data, GNNGraph(13, 26) with x: 7×13 data, GNNGraph(21, 44) with x: 7×21 data, GNNGraph(22, 50) with x: 7×22 data], Bool[0 0 … 0 0; 1 1 … 1 1]))</pre>
106106
107107
<pre class='language-julia'><code class='language-julia'>begin
108-
train_loader = DataLoader(train_data, batchsize = 64, shuffle = true)
109-
test_loader = DataLoader(test_data, batchsize = 64, shuffle = false)
108+
train_loader = DataLoader(train_data, batchsize = 32, shuffle = true)
109+
test_loader = DataLoader(test_data, batchsize = 32, shuffle = false)
110110
end</code></pre>
111-
<pre class="code-output documenter-example-output" id="var-test_loader">1-element DataLoader(::Tuple{Vector{GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}}, OneHotArrays.OneHotMatrix{UInt32, Vector{UInt32}}}, batchsize=64)
111+
<pre class="code-output documenter-example-output" id="var-test_loader">2-element DataLoader(::Tuple{Vector{GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}}, OneHotArrays.OneHotMatrix{UInt32, Vector{UInt32}}}, batchsize=32)
112112
with first element:
113-
(38-element Vector{GraphNeuralNetworks.GNNGraphs.GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}}, 2×38 OneHotMatrix(::Vector{UInt32}) with eltype Bool,)</pre>
113+
(32-element Vector{GraphNeuralNetworks.GNNGraphs.GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}}, 2×32 OneHotMatrix(::Vector{UInt32}) with eltype Bool,)</pre>
114114
115115
116-
<div class="markdown"><p>Here, we opt for a <code>batch_size</code> of 64, leading to 3 (randomly shuffled) mini-batches, containing all <span class="tex">$2 \cdot 64+22 = 150$</span> graphs.</p></div>
116+
<div class="markdown"><p>Here, we opt for a <code>batch_size</code> of 32, leading to 5 (randomly shuffled) mini-batches, containing all <span class="tex">$4 \cdot 32+22 = 150$</span> graphs.</p></div>
117117
118118
119119
```
@@ -123,15 +123,15 @@ end</code></pre>
123123
<p>Since graphs in graph classification datasets are usually small, a good idea is to <strong>batch the graphs</strong> before inputting them into a Graph Neural Network to guarantee full GPU utilization. In the image or language domain, this procedure is typically achieved by <strong>rescaling</strong> or <strong>padding</strong> each example into a set of equally-sized shapes, and examples are then grouped in an additional dimension. The length of this dimension is then equal to the number of examples grouped in a mini-batch and is typically referred to as the <code>batchsize</code>.</p><p>However, for GNNs the two approaches described above are either not feasible or may result in a lot of unnecessary memory consumption. Therefore, GraphNeuralNetworks.jl opts for another approach to achieve parallelization across a number of examples. Here, adjacency matrices are stacked in a diagonal fashion (creating a giant graph that holds multiple isolated subgraphs), and node and target features are simply concatenated in the node dimension (the last dimension).</p><p>This procedure has some crucial advantages over other batching procedures:</p><ol><li><p>GNN operators that rely on a message passing scheme do not need to be modified since messages are not exchanged between two nodes that belong to different graphs.</p></li><li><p>There is no computational or memory overhead since adjacency matrices are saved in a sparse fashion holding only non-zero entries, <em>i.e.</em>, the edges.</p></li></ol><p>GraphNeuralNetworks.jl can <strong>batch multiple graphs into a single giant graph</strong>:</p></div>
124124
125125
<pre class='language-julia'><code class='language-julia'>vec_gs, _ = first(train_loader)</code></pre>
126-
<pre class="code-output documenter-example-output" id="var-vec_gs">(GraphNeuralNetworks.GNNGraphs.GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}[GNNGraph(11, 22), GNNGraph(16, 36), GNNGraph(16, 34), GNNGraph(22, 50), GNNGraph(18, 40), GNNGraph(19, 40), GNNGraph(24, 50), GNNGraph(23, 54), GNNGraph(24, 50), GNNGraph(12, 26) … GNNGraph(20, 44), GNNGraph(11, 22), GNNGraph(22, 50), GNNGraph(13, 26), GNNGraph(16, 34), GNNGraph(10, 20), GNNGraph(28, 66), GNNGraph(19, 44), GNNGraph(14, 30), GNNGraph(18, 38)], Bool[1 0 … 1 0; 0 1 … 0 1])</pre>
126+
<pre class="code-output documenter-example-output" id="var-vec_gs">(GNNGraph{Tuple{Vector{Int64}, Vector{Int64}, Nothing}}[GNNGraph(17, 38) with x: 7×17 data, GNNGraph(19, 42) with x: 7×19 data, GNNGraph(13, 28) with x: 7×13 data, GNNGraph(14, 30) with x: 7×14 data, GNNGraph(13, 28) with x: 7×13 data, GNNGraph(23, 54) with x: 7×23 data, GNNGraph(16, 36) with x: 7×16 data, GNNGraph(24, 50) with x: 7×24 data, GNNGraph(23, 54) with x: 7×23 data, GNNGraph(15, 34) with x: 7×15 data … GNNGraph(16, 34) with x: 7×16 data, GNNGraph(16, 34) with x: 7×16 data, GNNGraph(23, 54) with x: 7×23 data, GNNGraph(12, 26) with x: 7×12 data, GNNGraph(17, 38) with x: 7×17 data, GNNGraph(20, 44) with x: 7×20 data, GNNGraph(13, 28) with x: 7×13 data, GNNGraph(26, 60) with x: 7×26 data, GNNGraph(23, 54) with x: 7×23 data, GNNGraph(24, 50) with x: 7×24 data], Bool[0 0 … 0 0; 1 1 … 1 1])</pre>
127127
128128
<pre class='language-julia'><code class='language-julia'>MLUtils.batch(vec_gs)</code></pre>
129129
<pre class="code-output documenter-example-output" id="var-hash102363">GNNGraph:
130-
num_nodes = 1191
131-
num_edges = 2618
132-
num_graphs = 64
133-
ndata:
134-
x =&gt;1191 Matrix{Float32}</pre>
130+
num_nodes: 585
131+
num_edges: 1292
132+
num_graphs: 32
133+
ndata:
134+
x = 7×585 Matrix{Float32}</pre>
135135
136136
137137
<div class="markdown"><p>Each batched graph object is equipped with a <strong><code>graph_indicator</code> vector</strong>, which maps each node to its respective graph in the batch:</p><p class="tex">$$\textrm{graph\_indicator} = [1, \ldots, 1, 2, \ldots, 2, 3, \ldots ]$$</p></div>
@@ -177,8 +177,7 @@ end</code></pre>
177177
# device = Flux.gpu # uncomment this for GPU training
178178
device = Flux.cpu
179179
model = model |&gt; device
180-
ps = Flux.params(model)
181-
opt = Adam(1e-3)
180+
opt = Flux.setup(Adam(1e-3), model)
182181
183182
function report(epoch)
184183
train = eval_loss_accuracy(model, train_loader, device)
@@ -190,11 +189,11 @@ end</code></pre>
190189
for epoch in 1:epochs
191190
for (g, y) in train_loader
192191
g, y = MLUtils.batch(g) |&gt; device, y |&gt; device
193-
gs = Flux.gradient(ps) do
192+
grad = Flux.gradient(model) do model
194193
ŷ = model(g, g.ndata.x)
195194
logitcrossentropy(ŷ, y)
196195
end
197-
Flux.Optimise.update!(opt, ps, gs)
196+
Flux.update!(opt, model, grad[1])
198197
end
199198
epoch % infotime == 0 && report(epoch)
200199
end

0 commit comments

Comments
 (0)