Layers and models

[1]:
from molgraph.chemistry import MolecularGraphEncoder
from molgraph.chemistry import Featurizer
from molgraph.chemistry import features

import tensorflow as tf

import numpy as np
import pandas as pd

Construct a MolecularGraphEncoder

[2]:
atom_encoder = Featurizer([
    features.Symbol({'C', 'N', 'O'}, oov_size=1),
    features.Hybridization({'SP', 'SP2', 'SP3'}, oov_size=1),
    features.HydrogenDonor(),
    features.HydrogenAcceptor(),
    features.Hetero()
])

bond_encoder = Featurizer([
    features.BondType({'SINGLE', 'DOUBLE', 'TRIPLE', 'AROMATIC'}),
    features.Rotatable(),
])

encoder = MolecularGraphEncoder(atom_encoder, bond_encoder)

Obtain dataset

[3]:
path = tf.keras.utils.get_file(
    fname='ESOL.csv',
    origin='http://deepchem.io.s3-website-us-west-1.amazonaws.com/datasets/ESOL.csv',
)
df = pd.read_csv(path)
df.head(3)
[3]:
Compound ID ESOL predicted log solubility in mols per litre Minimum Degree Molecular Weight Number of H-Bond Donors Number of Rings Number of Rotatable Bonds Polar Surface Area measured log solubility in mols per litre smiles
0 Amigdalin -0.974 1 457.432 7 3 7 202.32 -0.77 OCC3OC(OCC2OC(OC(C#N)c1ccccc1)C(O)C(O)C2O)C(O)...
1 Fenfuram -2.885 1 201.225 1 2 2 42.24 -3.30 Cc1occc1C(=O)Nc2ccccc2
2 citral -2.579 1 152.237 0 0 4 17.07 -2.06 CC(C)=CCCC(C)=CC(=O)

Obtain SMILES xand associated labels y

[4]:
x, y = df['smiles'].values, df['measured log solubility in mols per litre'].values

Obtain GraphTensor from x, via MolecularGraphEncoder

[5]:
x = encoder(x)

print(x, end='\n\n')
print('node_feature shape:', x.node_feature.shape)
print('edge_dst shape:    ', x.edge_dst.shape)
print('edge_src shape:    ', x.edge_src.shape)
print('edge_feature shape:', x.edge_feature.shape)
GraphTensor(
  sizes=<tf.Tensor: shape=(1128,), dtype=int32>,
  node_feature=<tf.Tensor: shape=(14991, 11), dtype=float32>,
  edge_src=<tf.Tensor: shape=(30856,), dtype=int32>,
  edge_dst=<tf.Tensor: shape=(30856,), dtype=int32>,
  edge_feature=<tf.Tensor: shape=(30856, 5), dtype=float32>,
  node_position=<tf.Tensor: shape=(14991, 16), dtype=float32>)

node_feature shape: (14991, 11)
edge_dst shape:     (30856,)
edge_src shape:     (30856,)
edge_feature shape: (30856, 5)

1. Import GNN layers

[6]:
from molgraph import layers

2. Use GNN layers

[7]:
layer = layers.GATConv(units=128, use_edge_features=True)

out1 = layer(x.separate())      # with nested ragged tensors
out2 = layer(x)                 # with nested tensors

print(out1, end='\n\n')
print(out2)
GraphTensor(
  sizes=<tf.Tensor: shape=(1128,), dtype=int32>,
  node_feature=<tf.RaggedTensor: shape=(1128, None, 128), dtype=float32, ragged_rank=1>,
  edge_src=<tf.RaggedTensor: shape=(1128, None), dtype=int32, ragged_rank=1>,
  edge_dst=<tf.RaggedTensor: shape=(1128, None), dtype=int32, ragged_rank=1>,
  edge_feature=<tf.RaggedTensor: shape=(1128, None, 128), dtype=float32, ragged_rank=1>,
  node_position=<tf.RaggedTensor: shape=(1128, None, 16), dtype=float32, ragged_rank=1>)

GraphTensor(
  sizes=<tf.Tensor: shape=(1128,), dtype=int32>,
  node_feature=<tf.Tensor: shape=(14991, 128), dtype=float32>,
  edge_src=<tf.Tensor: shape=(30856,), dtype=int32>,
  edge_dst=<tf.Tensor: shape=(30856,), dtype=int32>,
  edge_feature=<tf.Tensor: shape=(30856, 128), dtype=float32>,
  node_position=<tf.Tensor: shape=(14991, 16), dtype=float32>)

3. Pass GNN layers to Keras models

Split data into train/test

[8]:
random_indices = np.random.permutation(np.arange(x.shape[0]))

x_train = x[random_indices[:800]]
x_test = x[random_indices[800:]]

y_train = y[random_indices[:800]]
y_test = y[random_indices[800:]]

Option 1: Keras Sequential API

[9]:
sequential_model = tf.keras.Sequential([
    tf.keras.layers.Input(type_spec=x_train.spec),
    layers.GINConv(128),
    layers.GINConv(128),
    layers.GINConv(128),
    layers.Readout(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(1)
])

sequential_model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 gin_conv (GINConv)          (None, None, 128)         21187

 gin_conv_1 (GINConv)        (None, None, 128)         51073

 gin_conv_2 (GINConv)        (None, None, 128)         49537

 segment_pooling_readout (S  (None, 128)               0
 egmentPoolingReadout)

 dense_2 (Dense)             (None, 512)               66048

 dense_3 (Dense)             (None, 1)                 513

=================================================================
Total params: 188358 (735.77 KB)
Trainable params: 188358 (735.77 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
[10]:
sequential_model.compile('adam', 'mse', ['mae'])
sequential_model.fit(x_train, y_train, epochs=30, verbose=2)
mse, mae = sequential_model.evaluate(x_test, y_test)
print(f"{mse = :.3f}\n{mae = :.3f}")
Epoch 1/30
25/25 - 4s - loss: 7.0829 - mae: 2.0933 - 4s/epoch - 167ms/step
Epoch 2/30
25/25 - 0s - loss: 3.7235 - mae: 1.5299 - 138ms/epoch - 6ms/step
Epoch 3/30
25/25 - 0s - loss: 3.0047 - mae: 1.3911 - 138ms/epoch - 6ms/step
Epoch 4/30
25/25 - 0s - loss: 2.9660 - mae: 1.3739 - 153ms/epoch - 6ms/step
Epoch 5/30
25/25 - 0s - loss: 2.7608 - mae: 1.3286 - 152ms/epoch - 6ms/step
Epoch 6/30
25/25 - 0s - loss: 2.5752 - mae: 1.2790 - 140ms/epoch - 6ms/step
Epoch 7/30
25/25 - 0s - loss: 2.3623 - mae: 1.2191 - 123ms/epoch - 5ms/step
Epoch 8/30
25/25 - 0s - loss: 2.2896 - mae: 1.1918 - 130ms/epoch - 5ms/step
Epoch 9/30
25/25 - 0s - loss: 2.2290 - mae: 1.1800 - 128ms/epoch - 5ms/step
Epoch 10/30
25/25 - 0s - loss: 1.7473 - mae: 1.0566 - 128ms/epoch - 5ms/step
Epoch 11/30
25/25 - 0s - loss: 1.7529 - mae: 1.0464 - 138ms/epoch - 6ms/step
Epoch 12/30
25/25 - 0s - loss: 1.6870 - mae: 1.0037 - 131ms/epoch - 5ms/step
Epoch 13/30
25/25 - 0s - loss: 1.5346 - mae: 0.9839 - 133ms/epoch - 5ms/step
Epoch 14/30
25/25 - 0s - loss: 1.4849 - mae: 0.9538 - 142ms/epoch - 6ms/step
Epoch 15/30
25/25 - 0s - loss: 1.2754 - mae: 0.8829 - 133ms/epoch - 5ms/step
Epoch 16/30
25/25 - 0s - loss: 1.2463 - mae: 0.8781 - 143ms/epoch - 6ms/step
Epoch 17/30
25/25 - 0s - loss: 1.3979 - mae: 0.9110 - 134ms/epoch - 5ms/step
Epoch 18/30
25/25 - 0s - loss: 1.2012 - mae: 0.8581 - 136ms/epoch - 5ms/step
Epoch 19/30
25/25 - 0s - loss: 1.0255 - mae: 0.8057 - 144ms/epoch - 6ms/step
Epoch 20/30
25/25 - 0s - loss: 1.0765 - mae: 0.8157 - 141ms/epoch - 6ms/step
Epoch 21/30
25/25 - 0s - loss: 1.1615 - mae: 0.8261 - 132ms/epoch - 5ms/step
Epoch 22/30
25/25 - 0s - loss: 1.0205 - mae: 0.7694 - 138ms/epoch - 6ms/step
Epoch 23/30
25/25 - 0s - loss: 1.1099 - mae: 0.8121 - 139ms/epoch - 6ms/step
Epoch 24/30
25/25 - 0s - loss: 0.8393 - mae: 0.7029 - 138ms/epoch - 6ms/step
Epoch 25/30
25/25 - 0s - loss: 0.8663 - mae: 0.7098 - 143ms/epoch - 6ms/step
Epoch 26/30
25/25 - 0s - loss: 0.9310 - mae: 0.7355 - 141ms/epoch - 6ms/step
Epoch 27/30
25/25 - 0s - loss: 0.8394 - mae: 0.6957 - 140ms/epoch - 6ms/step
Epoch 28/30
25/25 - 0s - loss: 1.1528 - mae: 0.8242 - 143ms/epoch - 6ms/step
Epoch 29/30
25/25 - 0s - loss: 0.9932 - mae: 0.7647 - 129ms/epoch - 5ms/step
Epoch 30/30
25/25 - 0s - loss: 0.8268 - mae: 0.6887 - 129ms/epoch - 5ms/step
11/11 [==============================] - 0s 2ms/step - loss: 0.9816 - mae: 0.7799
mse = 0.982
mae = 0.780

Option 2: Keras Functional API

[11]:
inputs = tf.keras.layers.Input(type_spec=x_train.spec)
x = layers.GINConv(128)(inputs)
x = layers.GINConv(128)(x)
x = layers.GINConv(128)(x)
x = layers.Readout()(x)
x = tf.keras.layers.Dense(512, activation='relu')(x)
x = tf.keras.layers.Dense(1)(x)
functional_model = tf.keras.Model(inputs=inputs, outputs=x)
functional_model.summary()
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_2 (InputLayer)        [(None, None, 11)]        0

 gin_conv_3 (GINConv)        (None, None, 128)         21187

 gin_conv_4 (GINConv)        (None, None, 128)         51073

 gin_conv_5 (GINConv)        (None, None, 128)         49537

 segment_pooling_readout_1   (None, 128)               0
 (SegmentPoolingReadout)

 dense_4 (Dense)             (None, 512)               66048

 dense_5 (Dense)             (None, 1)                 513

=================================================================
Total params: 188358 (735.77 KB)
Trainable params: 188358 (735.77 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
[12]:
functional_model.compile('adam', 'mse', ['mae'])
functional_model.fit(x_train, y_train, epochs=30, verbose=2)
mse, mae = functional_model.evaluate(x_test, y_test)
print(f"{mse = :.3f}\n{mae = :.3f}")
Epoch 1/30
25/25 - 4s - loss: 6.9203 - mae: 2.0325 - 4s/epoch - 148ms/step
Epoch 2/30
25/25 - 0s - loss: 3.4010 - mae: 1.4839 - 133ms/epoch - 5ms/step
Epoch 3/30
25/25 - 0s - loss: 2.9173 - mae: 1.3693 - 148ms/epoch - 6ms/step
Epoch 4/30
25/25 - 0s - loss: 2.7455 - mae: 1.3209 - 146ms/epoch - 6ms/step
Epoch 5/30
25/25 - 0s - loss: 2.8170 - mae: 1.3229 - 140ms/epoch - 6ms/step
Epoch 6/30
25/25 - 0s - loss: 2.4958 - mae: 1.2679 - 144ms/epoch - 6ms/step
Epoch 7/30
25/25 - 0s - loss: 2.3044 - mae: 1.1992 - 144ms/epoch - 6ms/step
Epoch 8/30
25/25 - 0s - loss: 1.9932 - mae: 1.1212 - 139ms/epoch - 6ms/step
Epoch 9/30
25/25 - 0s - loss: 1.8481 - mae: 1.0773 - 141ms/epoch - 6ms/step
Epoch 10/30
25/25 - 0s - loss: 1.7411 - mae: 1.0312 - 143ms/epoch - 6ms/step
Epoch 11/30
25/25 - 0s - loss: 1.5762 - mae: 0.9794 - 140ms/epoch - 6ms/step
Epoch 12/30
25/25 - 0s - loss: 1.5218 - mae: 0.9651 - 146ms/epoch - 6ms/step
Epoch 13/30
25/25 - 0s - loss: 1.7555 - mae: 1.0286 - 143ms/epoch - 6ms/step
Epoch 14/30
25/25 - 0s - loss: 1.2763 - mae: 0.9069 - 144ms/epoch - 6ms/step
Epoch 15/30
25/25 - 0s - loss: 1.1931 - mae: 0.8552 - 136ms/epoch - 5ms/step
Epoch 16/30
25/25 - 0s - loss: 1.2232 - mae: 0.8649 - 125ms/epoch - 5ms/step
Epoch 17/30
25/25 - 0s - loss: 1.3285 - mae: 0.9049 - 122ms/epoch - 5ms/step
Epoch 18/30
25/25 - 0s - loss: 1.2162 - mae: 0.8614 - 127ms/epoch - 5ms/step
Epoch 19/30
25/25 - 0s - loss: 1.0204 - mae: 0.7888 - 132ms/epoch - 5ms/step
Epoch 20/30
25/25 - 0s - loss: 1.2871 - mae: 0.8817 - 130ms/epoch - 5ms/step
Epoch 21/30
25/25 - 0s - loss: 0.9439 - mae: 0.7681 - 133ms/epoch - 5ms/step
Epoch 22/30
25/25 - 0s - loss: 0.8779 - mae: 0.7186 - 133ms/epoch - 5ms/step
Epoch 23/30
25/25 - 0s - loss: 1.0503 - mae: 0.7944 - 136ms/epoch - 5ms/step
Epoch 24/30
25/25 - 0s - loss: 0.8557 - mae: 0.7086 - 135ms/epoch - 5ms/step
Epoch 25/30
25/25 - 0s - loss: 0.9817 - mae: 0.7609 - 140ms/epoch - 6ms/step
Epoch 26/30
25/25 - 0s - loss: 0.9477 - mae: 0.7526 - 141ms/epoch - 6ms/step
Epoch 27/30
25/25 - 0s - loss: 0.8323 - mae: 0.6940 - 139ms/epoch - 6ms/step
Epoch 28/30
25/25 - 0s - loss: 0.7691 - mae: 0.6539 - 136ms/epoch - 5ms/step
Epoch 29/30
25/25 - 0s - loss: 0.9321 - mae: 0.7294 - 139ms/epoch - 6ms/step
Epoch 30/30
25/25 - 0s - loss: 1.1205 - mae: 0.8220 - 137ms/epoch - 5ms/step
11/11 [==============================] - 0s 2ms/step - loss: 0.7704 - mae: 0.6764
mse = 0.770
mae = 0.676

Option 3: Keras Model subclassing

Creating a custom Keras model allow for more flexibility. Let perform some random skip connections.

[13]:
class MyModel(tf.keras.Model):
    def __init__(self, gnn_units=128, dense_units=512):
        super().__init__()
        self.gin_conv1 = layers.GINConv(gnn_units)
        self.gin_conv2 = layers.GINConv(gnn_units)
        self.gin_conv3 = layers.GINConv(gnn_units)
        self.readout = layers.Readout()
        self.dense_1 = tf.keras.layers.Dense(512, activation='relu')
        self.dense_2 = tf.keras.layers.Dense(1)

    def call(self, inputs):
        x0 = inputs
        x1 = self.gin_conv1(x0)
        x2 = self.gin_conv2(x1)
        x3 = self.gin_conv3(x2)
        x1 = self.readout(x1)
        x2 = self.readout(x2)
        x3 = self.readout(x3)
        x = tf.concat([x1, x2, x3], axis=1)
        x = self.dense_1(x)
        return self.dense_2(x)


my_model = MyModel()

my_model(x_train) # build

my_model.summary()
Model: "my_model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 gin_conv_6 (GINConv)        multiple                  21187

 gin_conv_7 (GINConv)        multiple                  51073

 gin_conv_8 (GINConv)        multiple                  49537

 segment_pooling_readout_2   multiple                  0
 (SegmentPoolingReadout)

 dense_6 (Dense)             multiple                  197120

 dense_7 (Dense)             multiple                  513

=================================================================
Total params: 319430 (1.22 MB)
Trainable params: 319430 (1.22 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
[14]:
my_model.compile('adam', 'mse', ['mae'])
my_model.fit(x_train, y_train, epochs=30, verbose=2)
mse, mae = my_model.evaluate(x_test, y_test)
print(f"{mse = :.3f}\n{mae = :.3f}")
Epoch 1/30
25/25 - 4s - loss: 6.9835 - mae: 2.0324 - 4s/epoch - 154ms/step
Epoch 2/30
25/25 - 0s - loss: 3.5191 - mae: 1.4901 - 130ms/epoch - 5ms/step
Epoch 3/30
25/25 - 0s - loss: 3.0028 - mae: 1.3773 - 127ms/epoch - 5ms/step
Epoch 4/30
25/25 - 0s - loss: 3.0950 - mae: 1.4046 - 127ms/epoch - 5ms/step
Epoch 5/30
25/25 - 0s - loss: 3.0432 - mae: 1.3907 - 124ms/epoch - 5ms/step
Epoch 6/30
25/25 - 0s - loss: 2.8056 - mae: 1.3432 - 134ms/epoch - 5ms/step
Epoch 7/30
25/25 - 0s - loss: 2.4625 - mae: 1.2536 - 135ms/epoch - 5ms/step
Epoch 8/30
25/25 - 0s - loss: 2.3279 - mae: 1.2204 - 134ms/epoch - 5ms/step
Epoch 9/30
25/25 - 0s - loss: 2.0883 - mae: 1.1648 - 165ms/epoch - 7ms/step
Epoch 10/30
25/25 - 0s - loss: 1.7619 - mae: 1.0528 - 214ms/epoch - 9ms/step
Epoch 11/30
25/25 - 0s - loss: 1.5656 - mae: 0.9921 - 185ms/epoch - 7ms/step
Epoch 12/30
25/25 - 0s - loss: 1.5067 - mae: 0.9590 - 163ms/epoch - 7ms/step
Epoch 13/30
25/25 - 0s - loss: 1.3769 - mae: 0.9156 - 154ms/epoch - 6ms/step
Epoch 14/30
25/25 - 0s - loss: 1.3757 - mae: 0.9190 - 125ms/epoch - 5ms/step
Epoch 15/30
25/25 - 0s - loss: 1.2272 - mae: 0.8522 - 131ms/epoch - 5ms/step
Epoch 16/30
25/25 - 0s - loss: 1.3874 - mae: 0.9079 - 127ms/epoch - 5ms/step
Epoch 17/30
25/25 - 0s - loss: 1.2847 - mae: 0.8731 - 129ms/epoch - 5ms/step
Epoch 18/30
25/25 - 0s - loss: 1.0207 - mae: 0.7711 - 126ms/epoch - 5ms/step
Epoch 19/30
25/25 - 0s - loss: 0.9642 - mae: 0.7421 - 125ms/epoch - 5ms/step
Epoch 20/30
25/25 - 0s - loss: 1.0932 - mae: 0.8059 - 128ms/epoch - 5ms/step
Epoch 21/30
25/25 - 0s - loss: 1.3735 - mae: 0.8976 - 126ms/epoch - 5ms/step
Epoch 22/30
25/25 - 0s - loss: 0.9599 - mae: 0.7704 - 124ms/epoch - 5ms/step
Epoch 23/30
25/25 - 0s - loss: 0.9566 - mae: 0.7487 - 122ms/epoch - 5ms/step
Epoch 24/30
25/25 - 0s - loss: 1.0032 - mae: 0.7642 - 127ms/epoch - 5ms/step
Epoch 25/30
25/25 - 0s - loss: 1.1856 - mae: 0.8178 - 117ms/epoch - 5ms/step
Epoch 26/30
25/25 - 0s - loss: 1.0833 - mae: 0.7991 - 125ms/epoch - 5ms/step
Epoch 27/30
25/25 - 0s - loss: 0.9256 - mae: 0.7378 - 124ms/epoch - 5ms/step
Epoch 28/30
25/25 - 0s - loss: 0.8480 - mae: 0.6993 - 126ms/epoch - 5ms/step
Epoch 29/30
25/25 - 0s - loss: 1.0073 - mae: 0.7551 - 125ms/epoch - 5ms/step
Epoch 30/30
25/25 - 0s - loss: 0.8465 - mae: 0.6929 - 126ms/epoch - 5ms/step
11/11 [==============================] - 0s 3ms/step - loss: 0.7891 - mae: 0.6715
mse = 0.789
mae = 0.672

Model with tf.data.Dataset

[15]:
ds_train = tf.data.Dataset.from_tensor_slices((x_train, y_train))
ds_train = ds_train.shuffle(800).batch(32)

ds_test = tf.data.Dataset.from_tensor_slices((x_test, y_test))
ds_test = ds_test.batch(32)
[16]:
sequential_model.compile('adam', 'mse', ['mae'])
sequential_model.fit(ds_train, epochs=30, verbose=2)
mse, mae = sequential_model.evaluate(x_test, y_test)
print(f"{mse = :.3f}\n{mae = :.3f}")
Epoch 1/30
25/25 - 4s - loss: 2.0662 - mae: 1.1023 - 4s/epoch - 169ms/step
Epoch 2/30
25/25 - 0s - loss: 0.9658 - mae: 0.7642 - 146ms/epoch - 6ms/step
Epoch 3/30
25/25 - 0s - loss: 0.7833 - mae: 0.6706 - 193ms/epoch - 8ms/step
Epoch 4/30
25/25 - 0s - loss: 0.8129 - mae: 0.6789 - 224ms/epoch - 9ms/step
Epoch 5/30
25/25 - 0s - loss: 0.7564 - mae: 0.6587 - 160ms/epoch - 6ms/step
Epoch 6/30
25/25 - 0s - loss: 0.6944 - mae: 0.6358 - 167ms/epoch - 7ms/step
Epoch 7/30
25/25 - 0s - loss: 0.7331 - mae: 0.6370 - 152ms/epoch - 6ms/step
Epoch 8/30
25/25 - 0s - loss: 0.7645 - mae: 0.6606 - 156ms/epoch - 6ms/step
Epoch 9/30
25/25 - 0s - loss: 0.7225 - mae: 0.6401 - 176ms/epoch - 7ms/step
Epoch 10/30
25/25 - 0s - loss: 0.6720 - mae: 0.6212 - 176ms/epoch - 7ms/step
Epoch 11/30
25/25 - 0s - loss: 0.6687 - mae: 0.6162 - 140ms/epoch - 6ms/step
Epoch 12/30
25/25 - 0s - loss: 0.7627 - mae: 0.6656 - 139ms/epoch - 6ms/step
Epoch 13/30
25/25 - 0s - loss: 0.8233 - mae: 0.6914 - 184ms/epoch - 7ms/step
Epoch 14/30
25/25 - 0s - loss: 0.6556 - mae: 0.6026 - 136ms/epoch - 5ms/step
Epoch 15/30
25/25 - 0s - loss: 0.7744 - mae: 0.6573 - 184ms/epoch - 7ms/step
Epoch 16/30
25/25 - 0s - loss: 0.8383 - mae: 0.6919 - 139ms/epoch - 6ms/step
Epoch 17/30
25/25 - 0s - loss: 0.7492 - mae: 0.6450 - 137ms/epoch - 5ms/step
Epoch 18/30
25/25 - 0s - loss: 0.5925 - mae: 0.5758 - 139ms/epoch - 6ms/step
Epoch 19/30
25/25 - 0s - loss: 0.5963 - mae: 0.5831 - 153ms/epoch - 6ms/step
Epoch 20/30
25/25 - 0s - loss: 0.6683 - mae: 0.6151 - 133ms/epoch - 5ms/step
Epoch 21/30
25/25 - 0s - loss: 0.7187 - mae: 0.6552 - 136ms/epoch - 5ms/step
Epoch 22/30
25/25 - 0s - loss: 0.6949 - mae: 0.6189 - 137ms/epoch - 5ms/step
Epoch 23/30
25/25 - 0s - loss: 0.6306 - mae: 0.5957 - 134ms/epoch - 5ms/step
Epoch 24/30
25/25 - 0s - loss: 0.5399 - mae: 0.5503 - 142ms/epoch - 6ms/step
Epoch 25/30
25/25 - 0s - loss: 0.5650 - mae: 0.5578 - 137ms/epoch - 5ms/step
Epoch 26/30
25/25 - 0s - loss: 0.6170 - mae: 0.5973 - 143ms/epoch - 6ms/step
Epoch 27/30
25/25 - 0s - loss: 0.5971 - mae: 0.5838 - 136ms/epoch - 5ms/step
Epoch 28/30
25/25 - 0s - loss: 0.5966 - mae: 0.5822 - 133ms/epoch - 5ms/step
Epoch 29/30
25/25 - 0s - loss: 0.5543 - mae: 0.5507 - 132ms/epoch - 5ms/step
Epoch 30/30
25/25 - 0s - loss: 0.6288 - mae: 0.5837 - 139ms/epoch - 6ms/step
11/11 [==============================] - 0s 2ms/step - loss: 0.6447 - mae: 0.6042
mse = 0.645
mae = 0.604

4. Save and load GNN model

Option 1: with tf.saved_model

[20]:
import tempfile
import shutil

file = tempfile.NamedTemporaryFile()
filename = file.name
file.close()

tf.saved_model.save(sequential_model, filename)
loaded_model = tf.saved_model.load(filename)

print(loaded_model(x_train).shape)

shutil.rmtree(filename)
(800, 1)

Option 2: with tf.keras

[24]:
import tempfile
import shutil

file = tempfile.NamedTemporaryFile()
filename = file.name
file.close()

sequential_model.save(filename)
loaded_model = tf.keras.models.load_model(filename)

loaded_model.fit(ds_train, epochs=5, verbose=2);

shutil.rmtree(filename)
Epoch 1/5
25/25 - 0s - loss: 0.5887 - mae: 0.5814 - 150ms/epoch - 6ms/step
Epoch 2/5
25/25 - 0s - loss: 0.5724 - mae: 0.5628 - 153ms/epoch - 6ms/step
Epoch 3/5
25/25 - 0s - loss: 0.5588 - mae: 0.5565 - 151ms/epoch - 6ms/step
Epoch 4/5
25/25 - 0s - loss: 0.5512 - mae: 0.5565 - 154ms/epoch - 6ms/step
Epoch 5/5
25/25 - 0s - loss: 0.4906 - mae: 0.5297 - 144ms/epoch - 6ms/step
[ ]: