-
Notifications
You must be signed in to change notification settings - Fork 334
/
Copy pathqm9_ecc.py
102 lines (84 loc) · 3.44 KB
/
qm9_ecc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"""
This example shows how to perform regression of molecular properties with the
QM9 database, using a simple GNN in disjoint mode.
"""
import numpy as np
import tensorflow as tf
from tensorflow.keras import Model
from tensorflow.keras.layers import Dense
from tensorflow.keras.losses import MeanSquaredError
from tensorflow.keras.optimizers import Adam
from spektral.data import DisjointLoader
from spektral.datasets import QM9
from spektral.layers import ECCConv, GlobalSumPool
################################################################################
# Config
################################################################################
learning_rate = 1e-3 # Learning rate
epochs = 10 # Number of training epochs
batch_size = 32 # Batch size
################################################################################
# Load data
################################################################################
dataset = QM9(amount=1000) # Set amount=None to train on whole dataset
# Parameters
F = dataset.n_node_features # Dimension of node features
S = dataset.n_edge_features # Dimension of edge features
n_out = dataset.n_labels # Dimension of the target
# Train/test split
idxs = np.random.permutation(len(dataset))
split = int(0.9 * len(dataset))
idx_tr, idx_te = np.split(idxs, [split])
dataset_tr, dataset_te = dataset[idx_tr], dataset[idx_te]
loader_tr = DisjointLoader(dataset_tr, batch_size=batch_size, epochs=epochs)
loader_te = DisjointLoader(dataset_te, batch_size=batch_size, epochs=1)
################################################################################
# Build model
################################################################################
class Net(Model):
def __init__(self):
super().__init__()
self.conv1 = ECCConv(32, activation="relu")
self.conv2 = ECCConv(32, activation="relu")
self.global_pool = GlobalSumPool()
self.dense = Dense(n_out)
def call(self, inputs):
x, a, e, i = inputs
x = self.conv1([x, a, e])
x = self.conv2([x, a, e])
output = self.global_pool([x, i])
output = self.dense(output)
return output
model = Net()
optimizer = Adam(learning_rate)
loss_fn = MeanSquaredError()
################################################################################
# Fit model
################################################################################
@tf.function(input_signature=loader_tr.tf_signature(), experimental_relax_shapes=True)
def train_step(inputs, target):
with tf.GradientTape() as tape:
predictions = model(inputs, training=True)
loss = loss_fn(target, predictions) + sum(model.losses)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
return loss
step = loss = 0
for batch in loader_tr:
step += 1
loss += train_step(*batch)
if step == loader_tr.steps_per_epoch:
step = 0
print("Loss: {}".format(loss / loader_tr.steps_per_epoch))
loss = 0
################################################################################
# Evaluate model
################################################################################
print("Testing model")
loss = 0
for batch in loader_te:
inputs, target = batch
predictions = model(inputs, training=False)
loss += loss_fn(target, predictions)
loss /= loader_te.steps_per_epoch
print("Done. Test loss: {}".format(loss))