|
1 |
| -# Building graph neural networks |
| 1 | +# Building Graph Neural Networks |
2 | 2 |
|
3 | 3 | Building GNN is as simple as building neural network in Flux. The syntax here is the same as Flux. `Chain` is used to stack layers into a GNN. A simple example is shown here:
|
4 | 4 |
|
5 | 5 | ```julia
|
6 |
| -model = Chain(GCNConv(adj_mat, feat=>h1), |
7 |
| - GCNConv(adj_mat, h1=>h2, relu)) |
| 6 | +model = Chain( |
| 7 | + GCNConv(feat=>h1), |
| 8 | + GCNConv(h1=>h2, relu), |
| 9 | +) |
8 | 10 | ```
|
9 | 11 |
|
10 |
| -In the example above, The first argument `adj_mat` is the representation of a graph in form of adjacency matrix. The feature dimension in first layer is mapped from `feat` to `h1`. In second layer, `h1` is then mapped to `h2`. Default activation function is given as identity if it is not specified by users. |
| 12 | +In the example above, the feature dimension in first layer is mapped from `feat` to `h1`. In second layer, `h1` is then mapped to `h2`. Default activation function is given as `identity` if it is not specified by users. |
11 | 13 |
|
12 |
| -The initialization function `GCNConv(...)` constructs a `GCNConv` layer. For most of the layer types in GeometricFlux, a layer can be initialized in at least two ways: |
| 14 | +The initialization function `GCNConv(...)` constructs a `GCNConv` layer. For most of the layer types in GeometricFlux, a layer can be initialized in two ways: |
13 | 15 |
|
14 |
| -* Initializing *with* a predefined adjacency matrix or `FeaturedGraph`, followed by the other parameters. For most of the layer types, this is for datasets where each input has the same graph structure. |
15 |
| -* Initializing *without* an initial graph argument, only supplying the relevant parameters. This allows the layer to accept different graph structures. |
| 16 | +* GNN layer without graph: initializing *without* a predefined graph topology. This allows the layer to accept different graph topology. |
| 17 | +* GNN layer with static graph: initializing *with* a predefined graph topology, e.g. graph wrapped in `FeaturedGraph`. This strategy is suitable for datasets where each input requires the same graph structure and it has better performance than variable graph strategy. |
16 | 18 |
|
17 |
| -# Applying layers |
| 19 | +The example above demonstrate the variable graph strategy. The equivalent GNN architecture but with static graph strategy is shown as following: |
| 20 | + |
| 21 | +```julia |
| 22 | +model = Chain( |
| 23 | + WithGraph(fg, GCNConv(feat=>h1)), |
| 24 | + WithGraph(fg, GCNConv(h1=>h2, relu)), |
| 25 | +) |
| 26 | +``` |
| 27 | + |
| 28 | +```@docs |
| 29 | +GeometricFlux.WithGraph |
| 30 | +``` |
| 31 | + |
| 32 | +## Applying Layers |
18 | 33 |
|
19 | 34 | When using GNN layers, the general guidelines are:
|
20 | 35 |
|
21 |
| -* If you pass in a ``n \times d`` matrix of node features, and the layer maps node features ``\mathbb{R}^d \rightarrow \mathbb{R}^k`` then the output will be in matrix with dimensions ``n \times k``. The same ostensibly goes for edge features but as of now no layer type supports outputting new edge features. |
22 |
| -* If you pass in a `FeaturedGraph`, the output will be also be a `FeaturedGraph` with modified node (and/or edge) features. Add `node_feature` as the following entry in the Flux chain (or simply call `node_feature()` on the output) if you wish to subsequently convert them to matrix form. |
| 36 | +* With static graph strategy: you should pass in a ``d \times n \times batch`` matrix for node features, and the layer maps node features ``\mathbb{R}^d \rightarrow \mathbb{R}^k`` then the output will be in matrix with dimensions ``k \times n \times batch``. The same ostensibly goes for edge features but as of now no layer type supports outputting new edge features. |
| 37 | +* With variable graph strategy: you should pass in a `FeaturedGraph`, the output will be also be a `FeaturedGraph` with modified node (and/or edge) features. Add `node_feature` as the following entry in the Flux chain (or simply call `node_feature()` on the output) if you wish to subsequently convert them to matrix form. |
| 38 | + |
| 39 | +## Define Your Own GNN Layer |
| 40 | + |
| 41 | +Customizing your own GNN layers are the same as defining a layer in Flux. You may want to check [Flux documentation](https://fluxml.ai/Flux.jl/stable/models/basics/#Building-Layers-1) first. |
| 42 | + |
| 43 | +To define a customized GNN layer, for example, we take a simple `GCNConv` layer as example here. |
| 44 | + |
| 45 | +```julia |
| 46 | +struct GCNConv <: AbstractGraphLayer |
| 47 | + weight |
| 48 | + bias |
| 49 | + σ |
| 50 | +end |
| 51 | + |
| 52 | +@functor GCNConv |
| 53 | +``` |
| 54 | + |
| 55 | +We first should define a `GCNConv` type and let it be the subtype of `AbstractGraphLayer`. In this type, it holds parameters that a layer operate on. Don't forget to add `@functor` macro to `GCNConv` type. |
| 56 | + |
| 57 | +```julia |
| 58 | +(l::GCNConv)(Ã::AbstractMatrix, x::AbstractMatrix) = l.σ.(l.weight * x * Ã .+ l.bias) |
| 59 | +``` |
23 | 60 |
|
24 |
| -## Create custom layers |
| 61 | +Then, we can define the operation for `GCNConv` layer. |
| 62 | + |
| 63 | +```julia |
| 64 | +function (l::GCNConv)(fg::AbstractFeaturedGraph) |
| 65 | + nf = node_feature(fg) |
| 66 | + Ã = Zygote.ignore() do |
| 67 | + GraphSignals.normalized_adjacency_matrix(fg, eltype(nf); selfloop=true) |
| 68 | + end |
| 69 | + return ConcreteFeaturedGraph(fg, nf = l(Ã, nf)) |
| 70 | +end |
| 71 | +``` |
25 | 72 |
|
26 |
| -Customizing your own GNN layers are the same as customizing layers in Flux. You may want to reference [Flux documentation](https://fluxml.ai/Flux.jl/stable/models/basics/#Building-Layers-1). |
| 73 | +Here comes to the GNN-specific behaviors. A GNN layer should accept object of subtype of `AbstractFeaturedGraph` to support variable graph strategy. A variable graph strategy should fetch node/edge/global features from `fg` and transform graph in `fg` into required form for layer operation, e.g. `GCNConv` layer needs a normalized adjacency matrix with self loop. Then, normalized adjacency matrix `Ã` and node features `nf` are pass through `GCNConv` layer `l(Ã, nf)` to give a new node feature. Finally, a `ConcreteFeaturedGraph` wrap graph in `fg` and new node features into a new object of subtype of `AbstractFeaturedGraph`. |
| 74 | + |
| 75 | +```julia |
| 76 | +layer = GCNConv(10=>5, relu) |
| 77 | +new_fg = layer(fg) |
| 78 | +gradient(() -> sum(node_feature(layer(fg))), Flux.params(layer)) |
| 79 | +``` |
| 80 | + |
| 81 | +Now we complete a simple version of `GCNConv` layer. One can test the forward pass and gradient if they work properly. |
| 82 | + |
| 83 | +```@docs |
| 84 | +GeometricFlux.AbstractGraphLayer |
| 85 | +``` |
0 commit comments