Models API Reference
The k3_node.models package contains complete, production-ready Graph Neural Network architectures implemented natively in Keras 3.
Standard GNN Architectures
BasicGNN
k3_node.models.BasicGNN
Bases: K3NodeHubMixin, Model
An abstract base class for implementing basic GNN models.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int or tuple
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Size of each hidden sample. |
required |
num_layers
|
int
|
Number of message passing layers. |
required |
out_channels
|
int
|
If not set to :obj: |
None
|
dropout
|
float
|
Dropout probability. (default: :obj: |
0.0
|
act
|
str or Callable
|
The non-linear activation function to
use. (default: :obj: |
'relu'
|
act_first
|
bool
|
If set to :obj: |
False
|
act_kwargs
|
Dict[str, Any]
|
Arguments passed to the
respective activation function defined by :obj: |
None
|
norm
|
str or Callable
|
The normalization function to
use. (default: :obj: |
None
|
norm_kwargs
|
Dict[str, Any]
|
Arguments passed to the
respective normalization function defined by :obj: |
None
|
jk
|
str
|
The Jumping Knowledge mode. If specified, the model
will additionally apply a final linear transformation to transform
node embeddings to the expected output feature dimensionality.
(:obj: |
None
|
**kwargs
|
optional
|
Additional arguments of the underlying
:class: |
{}
|
Call arguments: x, edge_index and optionally edge_weight, edge_attr and
batch. With mini-batches from :class:~k3_node.loader.NeighborLoader, also pass
num_sampled_nodes_per_hop and num_sampled_edges_per_hop (the batch's
num_sampled_nodes / num_sampled_edges) to skip the nodes and edges that no longer
affect the seed nodes after each layer, as in PyG. This trimming needs concrete sizes, so
compile the model with run_eagerly=True when using it.
reset_parameters()
Resets all learnable parameters of the module.
GCN
k3_node.models.GCN
Bases: BasicGNN
The Graph Neural Network from the "Semi-supervised
Classification with Graph Convolutional Networks"
<https://arxiv.org/abs/1609.02907>_ paper, using the
:class:~k3_node.layers.conv.GCNConv operator for message passing.
Example
import numpy as np
from k3_node.models import GCN
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=4)
out = model(x, edge_index) # e.g. logits for 4 classes per node
print(tuple(out.shape)) # (10, 4)
GraphSAGE
k3_node.models.GraphSAGE
Bases: BasicGNN
The Graph Neural Network from the "Inductive Representation Learning
on Large Graphs" <https://arxiv.org/abs/1706.02216>_ paper, using the
:class:~k3_node.layers.conv.SAGEConv operator for message passing.
Example
import numpy as np
from k3_node.models import GraphSAGE
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = GraphSAGE(in_channels=8, hidden_channels=16, num_layers=2, out_channels=4)
out = model(x, edge_index) # e.g. logits for 4 classes per node
print(tuple(out.shape)) # (10, 4)
GIN
k3_node.models.GIN
Bases: BasicGNN
The Graph Neural Network from the "How Powerful are Graph Neural
Networks?" <https://arxiv.org/abs/1810.00826>_ paper, using the
:class:~k3_node.layers.conv.GINConv operator for message passing.
Example
import numpy as np
from k3_node.models import GIN
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = GIN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=4)
out = model(x, edge_index) # e.g. logits for 4 classes per node
print(tuple(out.shape)) # (10, 4)
GAT
k3_node.models.GAT
Bases: BasicGNN
The Graph Neural Network from "Graph Attention Networks"
<https://arxiv.org/abs/1710.10903> or "How Attentive are Graph Attention
Networks?" <https://arxiv.org/abs/2105.14491> papers, using the
:class:~k3_node.layers.conv.GATConv or
:class:~k3_node.layers.conv.GATv2Conv operator for message passing.
Example
import numpy as np
from k3_node.models import GAT
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = GAT(in_channels=8, hidden_channels=16, num_layers=2, out_channels=4, heads=2)
out = model(x, edge_index) # e.g. logits for 4 classes per node
print(tuple(out.shape)) # (10, 4)
PNA
k3_node.models.PNA
Bases: BasicGNN
The Graph Neural Network from the "Principal Neighbourhood Aggregation
for Graph Nets" <https://arxiv.org/abs/2004.05718>_ paper, using the
:class:~k3_node.layers.conv.PNAConv operator for message passing.
Example
import numpy as np
from k3_node.models import PNA
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
deg = np.array([0, 2, 4, 3, 1]) # in-degree histogram of the training graphs
model = PNA(in_channels=8, hidden_channels=16, num_layers=2, out_channels=4,
aggregators=["mean", "min", "max", "std"],
scalers=["identity", "amplification", "attenuation"], deg=deg)
out = model(x, edge_index)
print(tuple(out.shape)) # (10, 4)
EdgeCNN
k3_node.models.EdgeCNN
Bases: BasicGNN
The Graph Neural Network from the "Dynamic Graph CNN for Learning on
Point Clouds" <https://arxiv.org/abs/1801.07829>_ paper, using the
:class:~k3_node.layers.conv.EdgeConv operator for message passing.
Example
import numpy as np
from k3_node.models import EdgeCNN
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = EdgeCNN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=4)
out = model(x, edge_index) # e.g. logits for 4 classes per node
print(tuple(out.shape)) # (10, 4)
MLP
k3_node.models.MLP
Bases: Model
A Multi-Layer Perceptron (MLP) model.
There exists two ways to instantiate an MLP:
-
By specifying explicit channel sizes, e.g.,
MLP([16, 32, 64, 128])creates a three-layer MLP with differently sized hidden layers. -
By specifying fixed hidden channel sizes over a number of layers, e.g.,
MLP(in_channels=16, hidden_channels=32, out_channels=128, num_layers=3)creates a three-layer MLP with equally sized hidden layers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_list
|
List[int] or int
|
List of input,
intermediate and output channels such that
|
None
|
in_channels
|
int
|
Size of each input sample. Will
override |
None
|
hidden_channels
|
int
|
Size of each hidden sample. Will
override |
None
|
out_channels
|
int
|
Size of each output sample. Will
override |
None
|
num_layers
|
int
|
The number of layers. Will override
|
None
|
dropout
|
float or List[float]
|
Dropout probability of
each hidden embedding. (default: |
0.0
|
act
|
str or Callable
|
The non-linear activation function
to use. (default: |
'relu'
|
act_first
|
bool
|
If set to |
False
|
act_kwargs
|
dict
|
Arguments passed to the activation function, e.g.
|
None
|
norm
|
str or Callable
|
The normalization function to
use. (default: |
'batch_norm'
|
norm_kwargs
|
dict
|
Arguments passed to the respective
normalization function. (default: |
None
|
plain_last
|
bool
|
If set to |
True
|
bias
|
bool or List[bool]
|
If set to |
True
|
Example
import numpy as np
from k3_node.models import MLP
x = np.random.rand(10, 16).astype("float32")
mlp = MLP([16, 32, 32, 4]) # channel sizes: input, hidden, hidden, output
print(tuple(mlp(x).shape)) # (10, 4)
mlp = MLP(in_channels=16, hidden_channels=32, out_channels=4, num_layers=3, dropout=0.1)
print(tuple(mlp(x).shape)) # (10, 4)
num_layers
property
The number of layers.
reset_parameters()
Resets all learnable parameters of the module.
Graph Autoencoders & Self-Supervised Models
GAE
k3_node.models.GAE
The Graph Auto-Encoder model from the
"Variational Graph Auto-Encoders" <https://arxiv.org/abs/1611.07308>_
paper based on user-defined encoder and decoder models.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
The encoder module. |
required | |
decoder
|
optional
|
The decoder module. If set to |
None
|
__call__(*args, **kwargs)
Alias for encode.
compile(optimizer, discriminator_optimizer=None, discriminator_steps=5)
Sets the optimizers used by :meth:fit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
optimizer
|
Optimizer
|
Trains the encoder (and decoder). |
required |
discriminator_optimizer
|
Optimizer
|
Trains the
discriminator of adversarial models (:class: |
None
|
discriminator_steps
|
int
|
Discriminator updates per encoder update. (default: |
5
|
decode(*args, **kwargs)
Runs the decoder and computes edge probabilities.
embed(data)
Returns the node embeddings of data (without sampling noise).
encode(*args, **kwargs)
Runs the encoder and computes node-wise latent variables.
evaluate(data)
Returns the link prediction AUC and average precision on data (which needs
pos_edge_label_index and neg_edge_label_index).
fit(data, epochs=1, validation_data=None, verbose=1)
Trains the model on one graph for epochs full-graph steps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Data
|
The training graph with node features |
required |
epochs
|
int
|
The number of training steps. (default: |
1
|
validation_data
|
Data
|
A graph with |
None
|
verbose
|
int
|
|
1
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
The loss (and validation metrics) of every epoch. |
recon_loss(z, pos_edge_index, neg_edge_index=None)
Given latent variables z, computes the binary cross entropy
loss for positive edges pos_edge_index and negative sampled
edges.
reset_parameters()
Resets all learnable parameters of the module.
test(z, pos_edge_index, neg_edge_index)
Given latent variables z, positive edges pos_edge_index and
negative edges neg_edge_index, computes area under the ROC curve
(AUC) and average precision (AP) scores.
train_step(data)
Runs one training step on data and returns the loss.
VGAE
k3_node.models.VGAE
Bases: GAE
The Variational Graph Auto-Encoder model from the
"Variational Graph Auto-Encoders" <https://arxiv.org/abs/1611.07308>_
paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
The encoder module to compute :math: |
required | |
decoder
|
optional
|
The decoder module. If set to |
None
|
kl_loss(mu=None, logstd=None)
Computes the KL loss, either for the passed arguments mu and
logstd, or based on latent variables from last encoding.
ARGA
k3_node.models.ARGA
Bases: GAE
The Adversarially Regularized Graph Auto-Encoder model from the
"Adversarially Regularized Graph Autoencoder for Graph Embedding"
<https://arxiv.org/abs/1802.04407>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
The encoder module. |
required | |
discriminator
|
The discriminator module. |
required | |
decoder
|
optional
|
The decoder module. If set to |
None
|
discriminator_loss(z)
Computes the loss of the discriminator.
reg_loss(z)
Computes the regularization loss of the encoder.
ARGVA
k3_node.models.ARGVA
Bases: ARGA
The Adversarially Regularized Variational Graph Auto-Encoder model
from the "Adversarially Regularized Graph Autoencoder for Graph
Embedding" <https://arxiv.org/abs/1802.04407>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
The encoder module to compute :math: |
required | |
discriminator
|
The discriminator module. |
required | |
decoder
|
optional
|
The decoder module. If set to |
None
|
InnerProductDecoder
k3_node.models.InnerProductDecoder
The inner product decoder from the "Variational Graph Auto-Encoders"
<https://arxiv.org/abs/1611.07308>_ paper.
.. math:: \sigma(\mathbf{Z}\mathbf{Z}^{\top})
where :math:\mathbf{Z} \in \mathbb{R}^{N \times d} denotes the latent
space produced by the encoder.
__call__(z, edge_index, sigmoid=True)
Decodes the latent variables z into edge probabilities for
the given node-pairs edge_index.
forward_all(z, sigmoid=True)
Decodes the latent variables z into a probabilistic dense
adjacency matrix.
DeepGraphInfomax
k3_node.models.DeepGraphInfomax
Bases: Model
The Deep Graph Infomax model from the
"Deep Graph Infomax" <https://arxiv.org/abs/1809.10341>_ paper.
Example
import numpy as np
import keras
from k3_node.layers import GCNConv
from k3_node.models import DeepGraphInfomax
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
encoder = GCNConv(8, 16)
model = DeepGraphInfomax(
hidden_channels=16,
encoder=lambda x, edge_index: encoder(x, edge_index),
summary=lambda z, *args: keras.ops.sigmoid(keras.ops.mean(z, axis=0)),
corruption=lambda x, edge_index: (keras.random.shuffle(x), edge_index), # permute node features
)
pos_z, neg_z, summary = model(x, edge_index)
print(tuple(pos_z.shape), tuple(neg_z.shape), tuple(summary.shape)) # (10, 16) (10, 16) (16,)
print(tuple(model.loss(pos_z, neg_z, summary).shape)) # (): scalar contrastive loss
call(*args, **kwargs)
Returns the latent space for the input arguments, their corruptions and their summary representation.
discriminate(z, summary, sigmoid=True)
Given the patch-summary pair z and summary, computes the
probability scores assigned to this patch-summary pair.
loss_fn(pos_z, neg_z, summary)
Computes the mutual information maximization objective.
test(train_z, train_y, test_z, test_y, solver='lbfgs', *args, **kwargs)
Evaluates latent space quality via a logistic regression downstream task.
Scalable Graph Transformers
Polynormer
k3_node.models.Polynormer
Bases: Layer
The Polynormer module from the "Polynormer: polynomial-expressive
graph transformer in linear time"
<https://arxiv.org/abs/2403.01232>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Input channels. |
required |
hidden_channels
|
int
|
Hidden channels. |
required |
out_channels
|
int
|
Output channels. |
required |
local_layers
|
int
|
The number of local attention layers.
(default: :obj: |
7
|
global_layers
|
int
|
The number of global attention layers.
(default: :obj: |
2
|
in_dropout
|
float
|
Input dropout rate.
(default: :obj: |
0.15
|
dropout
|
float
|
Dropout rate.
(default: :obj: |
0.5
|
global_dropout
|
float
|
Global dropout rate.
(default: :obj: |
0.5
|
heads
|
int
|
The number of heads.
(default: :obj: |
1
|
beta
|
float
|
Aggregate type.
(default: :obj: |
0.9
|
qk_shared
|
bool
|
Whether weight of query and key are shared.
(default: :obj: |
False
|
pre_ln
|
bool
|
Pre layer normalization.
(default: :obj: |
False
|
post_bn
|
bool
|
Post batch normalization.
(default: :obj: |
True
|
local_attn
|
bool
|
Whether use local attention (GATConv vs GCNConv).
(default: :obj: |
False
|
Example
import numpy as np
from k3_node.models import Polynormer
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
batch = np.repeat([0, 1], 5) # two graphs with 5 nodes each
model = Polynormer(in_channels=8, hidden_channels=32, out_channels=4,
local_layers=2, global_layers=1, heads=2)
out = model(x, edge_index, batch)
print(tuple(out.shape)) # (10, 4)
call(x, edge_index, batch=None, training=None)
Forward pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
The input node features. |
required |
edge_index
|
Tensor
|
The edge indices. |
required |
batch
|
Tensor
|
The batch vector assigning each node to
a graph. (default: :obj: |
None
|
training
|
bool
|
Whether in training mode.
(default: :obj: |
None
|
reset_parameters()
Resets all learnable parameters of the module.
SGFormer
k3_node.models.SGFormer
Bases: Model
The sgformer module from the
"SGFormer: Simplifying and Empowering Transformers for
Large-Graph Representations"
<https://arxiv.org/abs/2306.10759>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Input channels. |
required |
hidden_channels
|
int
|
Hidden channels. |
required |
out_channels
|
int
|
Output channels. |
required |
trans_num_layers
|
int
|
The number of layers for all-pair attention.
(default: :obj: |
2
|
trans_num_heads
|
int
|
The number of heads for attention.
(default: :obj: |
1
|
trans_dropout
|
float
|
Global dropout rate.
(default: :obj: |
0.5
|
gnn_num_layers
|
int
|
The number of layers for GNN.
(default: :obj: |
3
|
gnn_dropout
|
float
|
GNN dropout rate.
(default: :obj: |
0.5
|
graph_weight
|
float
|
The weight balance global and gnn module.
(default: :obj: |
0.5
|
aggregate
|
str
|
Aggregate type (:obj: |
'add'
|
Example
import numpy as np
from k3_node.models import SGFormer
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = SGFormer(in_channels=8, hidden_channels=32, out_channels=4, trans_num_layers=1,
trans_num_heads=1, gnn_num_layers=1)
print(tuple(model(x, edge_index).shape)) # (10, 4)
LPFormer
k3_node.models.LPFormer
Bases: Model
The LPFormer model from the "LPFormer: An Adaptive Graph Transformer for Link Prediction"
<https://arxiv.org/abs/2310.11009>_ paper, as in PyG.
For every candidate link it attends over the common neighbors, the one-hop neighbors and the
other nodes with a high personalized PageRank (PPR) from both endpoints (ppr_thresholds
for the three kinds), using their PPR scores as relative positional encodings, and combines
this with counts of each kind of node and with the GCN embeddings of the two endpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Input feature dimension. |
required |
hidden_channels
|
int
|
Hidden dimension. |
required |
num_gnn_layers
|
int
|
Number of GCN layers. (default: |
2
|
gnn_dropout
|
float
|
GCN dropout. (default: |
0.1
|
num_transformer_layers
|
int
|
Number of attention layers. (default: |
1
|
num_heads
|
int
|
Number of attention heads. (default: |
1
|
transformer_dropout
|
float
|
Attention dropout; during training this share of
the selected nodes is also dropped. (default: |
0.1
|
ppr_thresholds
|
list
|
Minimum PPR of common neighbors, one-hop neighbors and
other nodes. (default: |
None
|
Call arguments: batch (the [2, num_links] candidate links), x (node features),
edge_index (the graph) and the keyword ppr_matrix (from :meth:calc_sparse_ppr). Returns one logit
per link. The node selection runs on the host, so train with run_eagerly=True or
:func:~k3_node.training.gradient_step.
Example
import numpy as np
from k3_node.models import LPFormer
x = np.random.rand(10, 16).astype("float32") # 10 nodes with 16 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = LPFormer(in_channels=16, hidden_channels=16)
ppr = model.calc_sparse_ppr(edge_index, num_nodes=10)
target_links = np.array([[0, 1], [2, 3]]) # the (source, target) pairs to score
print(tuple(model(target_links, x, edge_index, ppr_matrix=ppr).shape)) # (2,): one logit per link
calc_sparse_ppr(edge_index, num_nodes, alpha=0.15, eps=5e-05)
staticmethod
The personalized PageRank matrix LPFormer needs (see :func:get_ppr).
compute_node_mask(u, v, adj, ppr, training)
For the links (u, v): (pair index, node, PPR from u, PPR from v) of their common
neighbors, one-hop neighbors and other high-PPR nodes.
GPSE
k3_node.models.GPSE
Bases: Layer
The Graph Positional and Structural Encoder (GPSE) model from the
"Graph Positional and Structural Encoder"
<https://arxiv.org/abs/2307.07107>_ paper.
Example
import numpy as np
from k3_node.models import GPSE
x = np.random.rand(6, 16).astype("float32")
edge_index = np.array([[0, 1, 2, 3, 4, 5], [1, 2, 0, 4, 5, 3]])
model = GPSE(dim_in=16, dim_inner=32, layers_pre_mp=1, layers_mp=2, layers_post_mp=1, use_repr=True)
print(tuple(model(x, edge_index).shape)) # (6, 32): positional/structural encodings per node
Molecular & Physical Models
AttentiveFP
k3_node.models.AttentiveFP
Bases: Model
The Attentive FP model for molecular representation learning from the
"Pushing the Boundaries of Molecular Representation for Drug Discovery
with the Graph Attention Mechanism"
<https://pubs.acs.org/doi/10.1021/acs.jmedchem.9b00959>_ paper, based on
graph attention mechanisms.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Hidden node feature dimensionality. |
required |
out_channels
|
int
|
Size of each output sample. |
required |
edge_dim
|
int
|
Edge feature dimensionality. |
required |
num_layers
|
int
|
Number of GNN layers. |
required |
num_timesteps
|
int
|
Number of iterative refinement steps for global readout. |
required |
dropout
|
float
|
Dropout probability. (default: |
0.0
|
batch_size
|
int
|
Fixed batch size (number of graphs) for JAX/XLA
static shape compatibility. (default: |
None
|
Example
import numpy as np
from k3_node.models import AttentiveFP
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
edge_attr = np.random.rand(30, 3).astype("float32") # bond features
batch = np.repeat([0, 1], 5) # two molecules with 5 atoms each
model = AttentiveFP(in_channels=8, hidden_channels=16, out_channels=1, edge_dim=3,
num_layers=2, num_timesteps=2)
out = model(x, edge_index, edge_attr, batch) # one prediction per molecule
print(tuple(out.shape)) # (2, 1)
NeuralFingerprint
k3_node.models.NeuralFingerprint
Bases: Layer
The Neural Fingerprint model from the
"Convolutional Networks on Graphs for Learning Molecular Fingerprints"
<https://arxiv.org/abs/1509.09292>__ paper to generate fingerprints
of molecules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Size of each hidden sample. |
required |
out_channels
|
int
|
Size of each output fingerprint. |
required |
num_layers
|
int
|
Number of layers. |
required |
**kwargs
|
optional
|
Additional arguments of
:class: |
{}
|
Example
import numpy as np
from k3_node.models import NeuralFingerprint
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
batch = np.repeat([0, 1], 5) # two molecules with 5 atoms each
model = NeuralFingerprint(in_channels=8, hidden_channels=32, out_channels=16, num_layers=3)
fingerprint = model(x, edge_index, batch) # one learned fingerprint per molecule
print(tuple(fingerprint.shape)) # (2, 16)
reset_parameters()
Resets all learnable parameters of the module.
SchNet
k3_node.models.SchNet
Bases: K3NodeHubMixin, Model
The continuous-filter convolutional neural network SchNet from the
"SchNet: A Continuous-filter Convolutional Neural Network for Modeling
Quantum Interactions" <https://arxiv.org/abs/1706.08566>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_channels
|
int
|
Hidden embedding size. (default: 128) |
128
|
num_filters
|
int
|
The number of filters to use. (default: 128) |
128
|
num_interactions
|
int
|
The number of interaction blocks. (default: 6) |
6
|
num_gaussians
|
int
|
The number of gaussians. (default: 50) |
50
|
cutoff
|
float
|
Cutoff distance. (default: 10.0) |
10.0
|
interaction_graph
|
callable
|
Interaction graph builder. (default: None) |
None
|
max_num_neighbors
|
int
|
Maximum neighbors per atom. (default: 32) |
32
|
readout
|
str
|
Readout pooling (add, sum, mean). (default: "add") |
'add'
|
dipole
|
bool
|
Predict dipole moment magnitude. (default: False) |
False
|
mean
|
float
|
Mean of target property. (default: None) |
None
|
std
|
float
|
Standard deviation of target property. (default: None) |
None
|
atomref
|
tensor
|
Reference atomic values. (default: None) |
None
|
Example
import numpy as np
from k3_node.models import SchNet
z = np.array([6, 8, 1, 1, 1]) # atomic numbers of a small molecule
pos = np.random.rand(5, 3).astype("float32") * 2.0 # 3D coordinates (Angstrom)
batch = np.array([0, 0, 0, 1, 1]) # two molecules: atoms 0-2 and atoms 3-4
model = SchNet(hidden_channels=16, num_filters=16, num_interactions=2, num_gaussians=10, cutoff=5.0)
energy = model(z, pos, batch=batch)
print(tuple(energy.shape)) # (2, 1)
DimeNet
k3_node.models.DimeNet
Bases: Layer
The directional message passing neural network (DimeNet) from the
"Directional Message Passing for Molecular Graphs"
<https://arxiv.org/abs/2003.03123>_ paper.
Example
import numpy as np
from k3_node.models import DimeNet
z = np.array([6, 8, 1, 1, 1]) # atomic numbers of a small molecule
pos = np.random.rand(5, 3).astype("float32") * 2.0 # 3D coordinates (Angstrom)
batch = np.array([0, 0, 0, 1, 1]) # two molecules: atoms 0-2 and atoms 3-4
model = DimeNet(hidden_channels=16, out_channels=1, num_blocks=2, num_bilinear=8,
num_spherical=3, num_radial=6, cutoff=5.0)
energy = model(z, pos, batch=batch) # one prediction per molecule
print(tuple(energy.shape)) # (2, 1)
DimeNetPlusPlus
k3_node.models.DimeNetPlusPlus
Bases: DimeNet
The DimeNet++ from the "Fast and Uncertainty-Aware
Directional Message Passing for Non-Equilibrium Molecules"
<https://arxiv.org/abs/2011.14115>_ paper.
Example
import numpy as np
from k3_node.models import DimeNetPlusPlus
z = np.array([6, 8, 1, 1, 1]) # atomic numbers of a small molecule
pos = np.random.rand(5, 3).astype("float32") * 2.0 # 3D coordinates (Angstrom)
batch = np.array([0, 0, 0, 1, 1]) # two molecules: atoms 0-2 and atoms 3-4
model = DimeNetPlusPlus(hidden_channels=16, out_channels=1, num_blocks=2, int_emb_size=8,
basis_emb_size=8, out_emb_channels=16, num_spherical=3, num_radial=6, cutoff=5.0)
energy = model(z, pos, batch=batch)
print(tuple(energy.shape)) # (2, 1)
GNNFF
k3_node.models.GNNFF
Bases: Layer
The Graph Neural Network Force Field (GNNFF) from the
"Accurate and scalable graph neural network force field and molecular
dynamics with direct force architecture"
<https://www.nature.com/articles/s41524-021-00543-3>_ paper.
:class:GNNFF directly predicts atomic forces from automatically
extracted features of the local atomic environment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_node_channels
|
int
|
Hidden node embedding size. |
required |
hidden_edge_channels
|
int
|
Hidden edge embedding size. |
required |
num_layers
|
int
|
Number of message passing blocks. |
required |
cutoff
|
float
|
Cutoff distance. (default: 5.0) |
5.0
|
max_num_neighbors
|
int
|
Maximum neighbors per node. (default: 32) |
32
|
Example
import numpy as np
from k3_node.models import GNNFF
z = np.array([6, 8, 1, 1, 1]) # atomic numbers of a small molecule
pos = np.random.rand(5, 3).astype("float32") * 2.0 # 3D coordinates (Angstrom)
model = GNNFF(hidden_node_channels=16, hidden_edge_channels=16, num_layers=2)
forces = model(z, pos) # predicts a 3D force vector for every atom
print(tuple(forces.shape)) # (5, 3)
ViSNet
k3_node.models.ViSNet
Bases: Layer
The equivariant vector-scalar interactive graph neural network (ViSNet)
from the "Enhancing Geometric Representations for Molecules with Equivariant
Vector-Scalar Interactive Message Passing" <https://arxiv.org/abs/2210.16518>_ paper.
Example
import numpy as np
from k3_node.models import ViSNet
z = np.array([6, 8, 1, 1, 1]) # atomic numbers of a small molecule
pos = np.random.rand(5, 3).astype("float32") * 2.0 # 3D coordinates (Angstrom)
batch = np.array([0, 0, 0, 1, 1]) # two molecules: atoms 0-2 and atoms 3-4
model = ViSNet(lmax=1, num_heads=2, num_layers=2, hidden_channels=16, num_rbf=8, cutoff=5.0)
energy, forces = model(z, pos, batch=batch) # forces are None unless derivative=True
print(tuple(energy.shape)) # (2, 1)
Semi-Supervised & Non-Homophilous Models
LabelPropagation
k3_node.models.LabelPropagation
Bases: MessagePassing
The label propagation operator, firstly introduced in the
"Learning from Labeled and Unlabeled Data with Label Propagation"
<http://mlg.eng.cam.ac.uk/zoubin/papers/CMU-CALD-02-107.pdf>_ paper.
.. math:: \mathbf{Y}^{\prime} = \alpha \cdot \mathbf{D}^{-1/2} \mathbf{A} \mathbf{D}^{-1/2} \mathbf{Y} + (1 - \alpha) \mathbf{Y},
where unlabeled data is inferred by labeled data via propagation.
This concrete implementation here is derived from the "Combining Label
Propagation And Simple Models Out-performs Graph Neural Networks"
<https://arxiv.org/abs/2010.13993>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_layers
|
int
|
The number of propagations. |
required |
alpha
|
float
|
The :math: |
required |
Example
import numpy as np
from k3_node.models import LabelPropagation
y = np.array([0, 1, 2, 0, 1, 2]) # node labels
train_mask = np.array([True, True, True, False, False, False]) # labels known for 3 nodes
edge_index = np.array([[0, 1, 2, 3, 4, 5], [3, 4, 5, 0, 1, 2]])
model = LabelPropagation(num_layers=3, alpha=0.9)
out = model(y, edge_index, train_mask) # soft labels for every node
print(tuple(out.shape)) # (6, 3)
CorrectAndSmooth
k3_node.models.CorrectAndSmooth
Bases: Layer
The correct and smooth (C&S) post-processing model from the
"Combining Label Propagation And Simple Models Out-performs Graph Neural
Networks" <https://arxiv.org/abs/2010.13993>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_correction_layers
|
int
|
The number of propagations :math: |
required |
correction_alpha
|
float
|
The :math: |
required |
num_smoothing_layers
|
int
|
The number of propagations :math: |
required |
smoothing_alpha
|
float
|
The :math: |
required |
autoscale
|
bool
|
If set to :obj: |
True
|
scale
|
float
|
The scaling factor :math: |
1.0
|
Example
import numpy as np
from k3_node.models import CorrectAndSmooth
y_soft = np.random.rand(6, 3).astype("float32") # base model's class probabilities
y_true = np.array([1, 0, 0, 2, 1, 1])
train_mask = np.array([True, False, True, False, True, False])
edge_index = np.array([[0, 1, 1, 2, 4, 5], [1, 0, 2, 1, 5, 4]])
model = CorrectAndSmooth(num_correction_layers=2, correction_alpha=0.5,
num_smoothing_layers=2, smoothing_alpha=0.5)
y_soft = model.correct(y_soft, y_true[train_mask], train_mask, edge_index) # propagate residual errors
y_soft = model.smooth(y_soft, y_true[train_mask], train_mask, edge_index) # propagate predictions
print(tuple(y_soft.shape)) # (6, 3)
RECT_L
k3_node.models.RECT_L
Bases: Layer
The RECT model, i.e. its supervised RECT-L part, from the
"Network Embedding with Completely-imbalanced Labels"
<https://arxiv.org/abs/2007.03545>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Intermediate size of each sample. |
required |
normalize
|
bool
|
Whether to add self-loops and compute
symmetric normalization coefficients on-the-fly.
(default: :obj: |
True
|
dropout
|
float
|
The dropout probability.
(default: :obj: |
0.0
|
Example
import numpy as np
from k3_node.models import RECT_L
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = RECT_L(in_channels=8, hidden_channels=16)
out = model(x, edge_index) # reconstructs the (semantic) input features
print(tuple(out.shape)) # (10, 8)
print(tuple(model.embed(x, edge_index).shape)) # (10, 16): node embeddings
get_semantic_labels(x, y, mask)
Replaces the original labels by their class-centers.
reset_parameters()
Resets all learnable parameters of the module.
PMLP
k3_node.models.PMLP
Bases: Model
The P(ropagational)MLP model from the "Graph Neural Networks are
Inherently Good Generalizers: Insights by Bridging GNNs and MLPs"
<https://arxiv.org/abs/2212.09034>_ paper.
:class:PMLP is identical to a standard MLP during training, but then
adopts a GNN architecture during testing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Size of each hidden sample. |
required |
out_channels
|
int
|
Size of each output sample. |
required |
num_layers
|
int
|
The number of layers. |
required |
dropout
|
float
|
Dropout probability of each hidden
embedding. (default: :obj: |
0.0
|
norm
|
bool
|
If set to :obj: |
True
|
bias
|
bool
|
If set to :obj: |
True
|
Example
import numpy as np
from k3_node.models import PMLP
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = PMLP(in_channels=8, hidden_channels=32, out_channels=4, num_layers=2)
print(tuple(model(x, edge_index).shape)) # (10, 4): message passing is used at inference
call(x, edge_index=None, training=None)
Forward pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
The node features of shape |
required |
edge_index
|
Tensor
|
The edge indices. Required during
inference. (default: :obj: |
None
|
training
|
bool
|
Override the instance-level
|
None
|
reset_parameters()
Resets all learnable parameters of the module.
LINKX
k3_node.models.LINKX
Bases: Model
The LINKX model from the "Large Scale Learning on Non-Homophilous
Graphs: New Benchmarks and Strong Simple Methods"
<https://arxiv.org/abs/2110.14446>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_nodes
|
int
|
The number of nodes in the graph. |
required |
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Size of each hidden sample. |
required |
out_channels
|
int
|
Size of each output sample. |
required |
num_layers
|
int
|
Number of layers of :math: |
required |
num_edge_layers
|
int
|
Number of layers of
:math: |
1
|
num_node_layers
|
int
|
Number of layers of
:math: |
1
|
dropout
|
float
|
Dropout probability. (default: :obj: |
0.0
|
Example
import numpy as np
from k3_node.models import LINKX
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
# LINKX learns from node features and adjacency separately, which suits heterophilic graphs
model = LINKX(num_nodes=10, in_channels=8, hidden_channels=32, out_channels=4, num_layers=2)
out = model(x, edge_index)
print(tuple(out.shape)) # (10, 4)
reset_parameters()
Resets all learnable parameters of the module.
ARLinkPredictor
k3_node.models.ARLinkPredictor
Bases: Layer
Link predictor using Attract-Repel embeddings from the paper
"Pseudo-Euclidean Attract-Repel Embeddings for Undirected Graphs"
<https://arxiv.org/abs/2106.09671>_.
This model splits node embeddings into: attract and repel. The edge prediction score is computed as the dot product of attract components minus the dot product of repel components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Size of hidden embeddings. |
required |
out_channels
|
int
|
Size of output embeddings. If set to
|
None
|
num_layers
|
int
|
Number of message passing layers. (default: |
2
|
dropout
|
float
|
Dropout probability. (default: |
0.0
|
attract_ratio
|
float
|
Ratio to use for attract component. Must be
between 0 and 1. (default: |
0.5
|
Example
import numpy as np
from k3_node.models import ARLinkPredictor
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
model = ARLinkPredictor(in_channels=8, hidden_channels=16, num_layers=2)
scores = model(x, edge_index) # one link score per edge in edge_index
print(tuple(scores.shape)) # (30,)
calculate_r_fraction(attract_z, repel_z)
Calculate the R-fraction (proportion of energy in repel space).
decode(attract_z, repel_z, edge_index)
Decode edge scores from attract-repel embeddings.
encode(x, *args, training=None, **kwargs)
Encode node features into attract-repel embeddings.
Recommendation & Temporal Models
LightGCN
k3_node.models.LightGCN
Bases: Model
The LightGCN model from the "LightGCN: Simplifying and Powering
Graph Convolution Network for Recommendation"
<https://arxiv.org/abs/2002.02126>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_nodes
|
int
|
The number of nodes in the graph. |
required |
embedding_dim
|
int
|
The dimensionality of node embeddings. |
required |
num_layers
|
int
|
The number of :class: |
required |
alpha
|
float or Tensor
|
The scalar or vector specifying
the re-weighting coefficients for aggregating the final embedding.
(default: :obj: |
None
|
Example
import numpy as np
from k3_node.models import LightGCN
edge_index = np.array([[0, 1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7, 0]]) # user-item interactions
edge_label_index = np.array([[0, 1, 2, 3], [4, 5, 6, 7]]) # pairs to score
model = LightGCN(num_nodes=50, embedding_dim=16, num_layers=2)
scores = model(edge_index, edge_label_index)
print(tuple(scores.shape)) # (4,)
print(tuple(model.recommend(edge_index, k=2).shape)) # (50, 2): top-2 recommendations per node
call(edge_index, edge_label_index=None, edge_weight=None)
Computes rankings for pairs of nodes.
get_embedding(edge_index, edge_weight=None)
Returns the embedding of nodes in the graph.
reset_parameters()
Resets all learnable parameters of the module.
BPRLoss
k3_node.models.BPRLoss
The Bayesian Personalized Ranking (BPR) loss.
SignedGCN
k3_node.models.SignedGCN
Bases: Layer
The signed graph convolutional network model from the "Signed Graph
Convolutional Network" <https://arxiv.org/abs/1808.06354>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Size of each input sample. |
required |
hidden_channels
|
int
|
Size of each hidden sample. |
required |
num_layers
|
int
|
Number of layers. |
required |
lamb
|
float
|
Balances the contributions of the overall
objective. (default: :obj: |
5.0
|
bias
|
bool
|
If set to :obj: |
True
|
Example
import numpy as np
from k3_node.models import SignedGCN
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
pos_edge_index, neg_edge_index = edge_index[:, :15], edge_index[:, 15:] # positive / negative links
model = SignedGCN(in_channels=8, hidden_channels=16, num_layers=2)
z = model(x, pos_edge_index, neg_edge_index)
print(tuple(z.shape)) # (10, 16)
print(tuple(model.discriminate(z, edge_index).shape)) # (30, 3): scores for (positive, negative, no link)
create_spectral_features(pos_edge_index, neg_edge_index, num_nodes=None)
Creates in_channels spectral node features from the positive and negative edges:
a truncated SVD of the signed, symmetric adjacency matrix.
TGNMemory
k3_node.models.TGNMemory
Bases: Layer
The Temporal Graph Network (TGN) memory model from the
"Temporal Graph Networks for Deep Learning on Dynamic Graphs"
<https://arxiv.org/abs/2006.10637>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_nodes
|
int
|
The number of nodes to save memories for. |
required |
raw_msg_dim
|
int
|
The raw message dimensionality. |
required |
memory_dim
|
int
|
The hidden memory dimensionality. |
required |
time_dim
|
int
|
The time encoding dimensionality. |
required |
message_module
|
Callable
|
Function combining source and destination node memory, raw message, and time encoding. |
required |
aggregator_module
|
Callable
|
Function aggregating messages to the same destination into a single representation. |
required |
Example
import numpy as np
from k3_node.models import IdentityMessage, LastAggregator, TGNMemory
memory = TGNMemory(
num_nodes=5, raw_msg_dim=8, memory_dim=16, time_dim=16,
message_module=IdentityMessage(raw_msg_dim=8, memory_dim=16, time_dim=16),
aggregator_module=LastAggregator(),
)
src, dst = np.array([0, 1]), np.array([1, 2]) # two interaction events
t = np.array([1.0, 2.0], dtype="float32")
raw_msg = np.random.rand(2, 8).astype("float32")
memory.update_state(src, dst, t, raw_msg) # update the memory of the involved nodes
mem, last_update = memory(np.array([0, 1, 2]))
print(tuple(mem.shape), tuple(last_update.shape)) # (3, 16) (3,)
call(n_id)
Returns the memory and last update time of the nodes n_id.
detach()
Kept for PyG compatibility: the stored memory is never differentiated.
reset_state()
Starts again from an empty memory and message stores.
update_state(src, dst, t, raw_msg)
Updates the memory with the new events (src, dst, t, raw_msg).
RENet
k3_node.models.RENet
Bases: Model
The Recurrent Event Network model from the "Recurrent Event Network
for Reasoning over Temporal Knowledge Graphs"
<https://arxiv.org/abs/1904.05530>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_nodes
|
int
|
The number of nodes in the knowledge graph. |
required |
num_rels
|
int
|
The number of relations in the knowledge graph. |
required |
hidden_channels
|
int
|
Hidden size of node and relation embeddings. |
required |
seq_len
|
int
|
The sequence length of past events. |
required |
num_layers
|
int
|
The number of recurrent layers.
(default: :obj: |
1
|
dropout
|
float
|
Dropout rate before final prediction.
(default: :obj: |
0.0
|
bias
|
bool
|
If set to :obj: |
True
|
Example
import numpy as np
from k3_node.models import RENet
model = RENet(num_nodes=5, num_rels=4, hidden_channels=16, seq_len=3)
sub, rel, obj = np.array([0, 1]), np.array([0, 1]), np.array([2, 3]) # queries at time t
# Neighbor histories of subjects and objects: neighbor id, timestep and query index
h_sub, h_sub_t, h_sub_batch = np.array([0, 1, 2]), np.array([0, 1, 0]), np.array([0, 0, 1])
h_obj, h_obj_t, h_obj_batch = np.array([1, 2, 3]), np.array([1, 2, 0]), np.array([0, 0, 1])
log_prob_obj, log_prob_sub = model(sub, rel, obj, h_sub, h_sub_t, h_sub_batch, h_obj, h_obj_t, h_obj_batch)
print(tuple(log_prob_obj.shape)) # (2, 5): scores over all entities for each query
pre_transform(seq_len)
staticmethod
Returns a pre-transform that adds to every event (processed in time order) the history
of its subject and object: the entities they were linked to by the same relation in each of
the last seq_len time steps (h_sub / h_obj, with the step in h_sub_t /
h_obj_t), as in PyG.
test(logits, y)
Given ground-truth :obj:y, computes Mean Reciprocal Rank (MRR)
and Hits at 1/3/10.
Graph Embedding Models
Node2Vec
k3_node.models.Node2Vec
Bases: Layer
The Node2Vec model from the
"node2vec: Scalable Feature Learning for Networks"
<https://arxiv.org/abs/1607.00653>_ paper where random walks of
length :obj:walk_length are sampled in a given graph, and node embeddings
are learned via negative sampling optimization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
edge_index
|
The edge indices. |
required | |
embedding_dim
|
int
|
The size of each embedding vector. |
required |
walk_length
|
int
|
The walk length. |
required |
context_size
|
int
|
The actual context size which is considered for positive samples. |
required |
walks_per_node
|
int
|
The number of walks to sample for each
node. (default: :obj: |
1
|
p
|
float
|
Likelihood of immediately revisiting a node in the
walk. (default: :obj: |
1.0
|
q
|
float
|
Control parameter to interpolate between
breadth-first strategy and depth-first strategy. (default: :obj: |
1.0
|
num_negative_samples
|
int
|
The number of negative samples to
use for each positive sample. (default: :obj: |
1
|
num_nodes
|
int
|
The number of nodes. (default: :obj: |
None
|
Example
import numpy as np
from k3_node.models import Node2Vec
edge_index = np.array([[0, 1, 2, 3, 0, 2], [1, 2, 3, 0, 2, 0]])
model = Node2Vec(edge_index, embedding_dim=16, walk_length=4, context_size=3, walks_per_node=2)
print(tuple(model().shape)) # (4, 16): embeddings of all nodes
batch = np.array([0, 1])
loss = model.loss(model.pos_sample(batch), model.neg_sample(batch)) # skip-gram loss on random walks
print(tuple(loss.shape)) # (): a scalar
call(batch=None)
Returns the embeddings for the nodes in :obj:batch.
compile(optimizer)
Sets the optimizer used by :meth:fit.
fit(epochs=1, batch_size=128, verbose=1)
Trains the embeddings on freshly sampled random walks for epochs passes over
all nodes; returns the mean loss of every epoch.
loader(batch_size=128, shuffle=True)
Yields (pos_rw, neg_rw) random-walk batches, starting from batch_size nodes
at a time, as PyG's Node2Vec.loader.
loss(pos_rw, neg_rw)
Computes the loss given positive and negative random walks.
test(train_z, train_y, test_z, test_y, solver='lbfgs', *args, **kwargs)
Evaluates the embeddings with a logistic regression classifier; returns its accuracy.
MetaPath2Vec
k3_node.models.MetaPath2Vec
Bases: Layer
The MetaPath2Vec model from the "metapath2vec: Scalable Representation
Learning for Heterogeneous Networks"
<https://ericdongyx.github.io/papers/
KDD17-dong-chawla-swami-metapath2vec.pdf>_ paper where random walks based
on a given :obj:metapath are sampled in a heterogeneous graph, and node
embeddings are learned via negative sampling optimization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
edge_index_dict
|
Dict[Tuple[str, str, str], Tensor]
|
Dictionary holding edge indices for each edge type. |
required |
embedding_dim
|
int
|
The size of each embedding vector. |
required |
metapath
|
List[Tuple[str, str, str]]
|
The sequence of edge types denoting the metapath. |
required |
walk_length
|
int
|
The walk length. |
required |
context_size
|
int
|
The context size considered for positive samples. |
required |
walks_per_node
|
int
|
The number of walks to sample for each node.
(default: :obj: |
1
|
num_negative_samples
|
int
|
The number of negative samples.
(default: :obj: |
1
|
num_nodes_dict
|
Dict[str, int]
|
The number of nodes for each
node type. (default: :obj: |
None
|
Example
import numpy as np
from k3_node.models import MetaPath2Vec
edge_index_dict = {
("author", "writes", "paper"): np.array([[0, 1, 1], [0, 0, 1]]),
("paper", "written_by", "author"): np.array([[0, 0, 1], [0, 1, 1]]),
}
metapath = [("author", "writes", "paper"), ("paper", "written_by", "author")]
model = MetaPath2Vec(edge_index_dict, embedding_dim=16, metapath=metapath,
walk_length=2, context_size=2, walks_per_node=2)
print(tuple(model("author").shape)) # (2, 16): embeddings of all authors
print(tuple(model("paper").shape)) # (2, 16)
call(node_type, batch=None)
Returns the embeddings for the nodes in :obj:batch of type
:obj:node_type.
loss(pos_rw, neg_rw)
Computes the loss given positive and negative random walks.
Architectural Blocks
DeepGCNLayer
k3_node.models.DeepGCNLayer
Bases: Layer
The skip connection operations from the
"DeepGCNs: Can GCNs Go as Deep as CNNs?"
<https://arxiv.org/abs/1904.03751> and "All You Need to Train Deeper
GCNs" <https://arxiv.org/abs/2006.07739> papers.
The implemented skip connections includes the pre-activation residual
connection ("res+"), the residual connection ("res"), the dense
connection ("dense") and no connections ("plain").
- Res+ (
"res+"):
.. math:: \text{Normalization}\to\text{Activation}\to\text{Dropout}\to \text{GraphConv}\to\text{Res}
- Res (
"res") / Dense ("dense") / Plain ("plain"):
.. math:: \text{GraphConv}\to\text{Normalization}\to\text{Activation}\to \text{Res/Dense/Plain}\to\text{Dropout}
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conv
|
optional
|
the GCN operator. (default: |
None
|
norm
|
optional
|
the normalization layer. (default: |
None
|
act
|
optional
|
the activation layer. (default: |
None
|
block
|
str
|
The skip connection operation to use
( |
'res+'
|
dropout
|
float
|
Whether to apply dropout. (default: |
0.0
|
ckpt_grad
|
bool
|
Kept for API compatibility; a no-op,
since there is no gradient-checkpointing API shared uniformly
across Keras backends. (default: |
False
|
Example
import numpy as np
import keras
from k3_node.layers import GENConv, LayerNorm
from k3_node.models import DeepGCNLayer
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
x = np.random.rand(10, 16).astype("float32")
layer = DeepGCNLayer(
conv=GENConv(16, 16),
norm=LayerNorm(16),
act=keras.layers.ReLU(),
block="res+", # pre-activation residual block (DeeperGCN)
)
out = layer(x, edge_index)
print(tuple(out.shape)) # (10, 16)
reset_parameters()
Resets all learnable parameters of the module.
GroupAddRev
k3_node.models.GroupAddRev
Bases: Layer
The Grouped Reversible GNN module from the "Graph Neural Networks with
1000 Layers" <https://arxiv.org/abs/2106.07476>_ paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conv
|
Layer or List[Layer]
|
A seed GNN layer or list of GNN layers. |
required |
split_dim
|
int
|
The dimension across which to split groups.
(default: :obj: |
-1
|
num_groups
|
int
|
The number of groups. (default: :obj: |
2
|
Example
import numpy as np
from k3_node.layers import GCNConv
from k3_node.models import GroupAddRev
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
x = np.random.rand(10, 32).astype("float32")
model = GroupAddRev([GCNConv(16, 16), GCNConv(16, 16)]) # channels are split into 2 groups
out = model(x, edge_index=edge_index)
print(tuple(out.shape)) # (10, 32)
print(tuple(model.inverse(out, edge_index=edge_index).shape)) # (10, 32): reversible: recover the input
JumpingKnowledge
k3_node.models.JumpingKnowledge
Bases: Layer
The Jumping Knowledge layer aggregation module from the
"Representation Learning on Graphs with Jumping Knowledge Networks"
<https://arxiv.org/abs/1806.03536>_ paper.
Jumping knowledge is performed based on either concatenation
(:obj:"cat")
.. math::
\mathbf{x}_v^{(1)} \, \Vert \, \ldots \, \Vert \, \mathbf{x}_v^{(T)},
max pooling (:obj:"max")
.. math::
\max \left( \mathbf{x}_v^{(1)}, \ldots, \mathbf{x}_v^{(T)} \right),
or weighted summation
.. math::
\sum_{t=1}^T \alpha_v^{(t)} \mathbf{x}_v^{(t)}
with attention scores :math:\alpha_v^{(t)} obtained from a bi-directional
LSTM (:obj:"lstm").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode
|
str
|
The aggregation scheme to use
(:obj: |
required |
channels
|
int
|
The number of channels per representation.
Needs to be only set for LSTM-style aggregation.
(default: :obj: |
None
|
num_layers
|
int
|
The number of layers to aggregate. Needs to
be only set for LSTM-style aggregation. (default: :obj: |
None
|
Example
import numpy as np
from k3_node.models import JumpingKnowledge
# Node representations from 3 GNN layers
xs = [np.random.rand(10, 16).astype("float32") for _ in range(3)]
print(tuple(JumpingKnowledge("cat")(xs).shape)) # (10, 48): concatenate all layers
print(tuple(JumpingKnowledge("max")(xs).shape)) # (10, 16): element-wise max
print(tuple(JumpingKnowledge("lstm", channels=16, num_layers=3)(xs).shape)) # (10, 16): attention over layers
call(xs)
Forward pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
xs
|
List[Tensor]
|
List containing the layer-wise representations. |
required |
reset_parameters()
Resets all learnable parameters of the module.
MetaLayer
k3_node.models.MetaLayer
Bases: Layer
A meta layer for building any kind of graph network, inspired by the
"Relational Inductive Biases, Deep Learning, and Graph Networks"
<https://arxiv.org/abs/1806.01261>_ paper.
A graph network takes a graph as input and returns an updated graph as
output (with same connectivity). The input graph has node features :obj:x,
edge features :obj:edge_attr as well as graph-level features :obj:u.
The output graph has the same structure, but updated features.
Edge features, node features as well as global features are updated by
calling the modules :obj:edge_model, :obj:node_model and
:obj:global_model, respectively.
To allow for batch-wise graph processing, all callable functions take an
additional argument :obj:batch, which determines the assignment of
edges or nodes to their specific graphs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
edge_model
|
callable
|
A callable which updates a graph's
edge features based on its source and target node features, its
current edge features and its global features.
(default: :obj: |
None
|
node_model
|
callable
|
A callable which updates a graph's
node features based on its current node features, its graph
connectivity, its edge features and its global features.
(default: :obj: |
None
|
global_model
|
callable
|
A callable which updates a graph's
global features based on its node features, its graph connectivity,
its edge features and its current global features.
(default: :obj: |
None
|
Example
import numpy as np
import keras
from k3_node.models import MetaLayer
x = np.random.rand(10, 8).astype("float32") # 10 nodes with 8 features each
edge_index = np.random.randint(0, 10, size=(2, 30)) # 30 random edges
edge_attr = np.random.rand(30, 4).astype("float32")
edge_mlp = keras.layers.Dense(8)
node_mlp = keras.layers.Dense(8)
def edge_model(src, dst, edge_attr, u, batch): # update every edge from its endpoints
return edge_mlp(keras.ops.concatenate([src, dst, edge_attr], axis=-1))
def node_model(x, edge_index, edge_attr, u, batch): # update nodes from incoming edges
incoming = keras.ops.segment_sum(edge_attr, edge_index[1], num_segments=10)
return node_mlp(keras.ops.concatenate([x, incoming], axis=-1))
model = MetaLayer(edge_model=edge_model, node_model=node_model)
x_out, edge_attr_out, u_out = model(x, edge_index, edge_attr=edge_attr)
print(tuple(x_out.shape), tuple(edge_attr_out.shape)) # (10, 8) (30, 8)
call(x, edge_index, edge_attr=None, u=None, batch=None)
Forward pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
The node features of shape |
required |
edge_index
|
Tensor
|
The edge indices of shape |
required |
edge_attr
|
Tensor
|
The edge features of shape
|
None
|
u
|
Tensor
|
The global graph features of shape
|
None
|
batch
|
Tensor
|
The batch vector
:math: |
None
|
reset_parameters()
Resets all learnable parameters of the module.