-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtrain.py
105 lines (85 loc) · 3.41 KB
/
train.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
103
104
105
from __future__ import print_function
from keras.layers import Input, Dropout
from keras.models import Model
from keras.optimizers import Adam
from keras.regularizers import l2
from kegra.layers.graph import GraphConvolution
from kegra.utils import *
import time
# Define parameters
DATASET = 'cora'
FILTER = 'localpool' # 'chebyshev'
MAX_DEGREE = 2 # maximum polynomial degree
SYM_NORM = True # symmetric (True) vs. left-only (False) normalization
NB_EPOCH = 500
PATIENCE = 10 # early stopping patience
# Get data
X, A, y = load_data(dataset=DATASET)
y_train, y_val, y_test, idx_train, idx_val, idx_test, train_mask = get_splits(y)
# Normalize X
X /= X.sum(1).reshape(-1, 1)
if FILTER == 'localpool':
""" Local pooling filters (see 'renormalization trick' in Kipf & Welling, arXiv 2016) """
print('Using local pooling filters...')
A_ = preprocess_adj(A, SYM_NORM)
support = 1
graph = [X, A_]
G = [Input(shape=(None, None), batch_shape=(None, None), sparse=True)]
elif FILTER == 'chebyshev':
""" Chebyshev polynomial basis filters (Defferard et al., NIPS 2016) """
print('Using Chebyshev polynomial basis filters...')
L = normalized_laplacian(A, SYM_NORM)
L_scaled = rescale_laplacian(L)
T_k = chebyshev_polynomial(L_scaled, MAX_DEGREE)
support = MAX_DEGREE + 1
graph = [X]+T_k
G = [Input(shape=(None, None), batch_shape=(None, None), sparse=True) for _ in range(support)]
else:
raise Exception('Invalid filter type.')
X_in = Input(shape=(X.shape[1],))
# Define model architecture
# NOTE: We pass arguments for graph convolutional layers as a list of tensors.
# This is somewhat hacky, more elegant options would require rewriting the Layer base class.
H = Dropout(0.5)(X_in)
H = GraphConvolution(16, support, activation='relu', kernel_regularizer=l2(5e-4))([H]+G)
H = Dropout(0.5)(H)
Y = GraphConvolution(y.shape[1], support, activation='softmax')([H]+G)
# Compile model
model = Model(inputs=[X_in]+G, outputs=Y)
model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.01))
# Helper variables for main training loop
wait = 0
preds = None
best_val_loss = 99999
# Fit
for epoch in range(1, NB_EPOCH+1):
# Log wall-clock time
t = time.time()
# Single training iteration (we mask nodes without labels for loss calculation)
model.fit(graph, y_train, sample_weight=train_mask,
batch_size=A.shape[0], epochs=1, shuffle=False, verbose=0)
# Predict on full dataset
preds = model.predict(graph, batch_size=A.shape[0])
# Train / validation scores
train_val_loss, train_val_acc = evaluate_preds(preds, [y_train, y_val],
[idx_train, idx_val])
print("Epoch: {:04d}".format(epoch),
"train_loss= {:.4f}".format(train_val_loss[0]),
"train_acc= {:.4f}".format(train_val_acc[0]),
"val_loss= {:.4f}".format(train_val_loss[1]),
"val_acc= {:.4f}".format(train_val_acc[1]),
"time= {:.4f}".format(time.time() - t))
# Early stopping
if train_val_loss[1] < best_val_loss:
best_val_loss = train_val_loss[1]
wait = 0
else:
if wait >= PATIENCE:
print('Epoch {}: early stopping'.format(epoch))
break
wait += 1
# Testing
test_loss, test_acc = evaluate_preds(preds, [y_test], [idx_test])
print("Test set results:",
"loss= {:.4f}".format(test_loss[0]),
"accuracy= {:.4f}".format(test_acc[0]))