Shape Problem: Disjointloader and GCNConv (Node classification with multiple graphs)
See original GitHub issueHey, first of all im new in the field of Graph Neural Networks and using spektral now for like a month, i’m not sure if its really a bug, but im trying to solve these problems since weeks now and was checking the issues and the documentation of spektral.
I want to use GCN for Node classification and i have 115 Graphs (all with the same shape --> padded) and created a Dataset Object with the Dataset Class (example from the documentation). Each Graph contains 117 nodes, 322 features and 8 labels. Shape:
- Labels y: (8,8) --> one-hot-encodded
- Adjacency matrix a: (117, 117)
- Feature matrix x: (117, 322)
- e = None
I’m splitting the data into train and test and then using the Disjointloader (cause i read in an issue node classification with multiple graphs is the best way with Disjoint Mode). I was also playing with the batch size, but to make it very easy i’m trying to solve my problem with a batchsize =1. I also set the node_level argument “True” for my labels.
train_loader = DisjointLoader(data_train, batch_size=batch_size, epochs=epochs, node_level=True) test_loader = DisjointLoader(data_test, batch_size=batch_size)
For my node classification problem I want to use GCNConv (but I also tried the normal GCN class). I was trying to use the citation_gcn.py example and also the GCNConv (without boolean masks for now).
`class OCRGCN(Model): “”"Input - Node features of shape ([batch], n_nodes, n_node_features); - Modified Laplacian of shape ([batch], n_nodes, n_nodes); can be computed with spektral.utils.convolution.gcn_filter
Output
- Node features with the same shape as the input, but with the last
dimension changed to channels.
Args:
Model (_type_): _description_
"""
def __init__(self, n_hidden, n_labels):
super().__init__()
# Define the GCN layer with n_hidden layers
self.graph_conv = GCNConv(n_hidden)
# Define the global pooling layer
self.pool = GlobalSumPool()
# Define the dropout layer, initialize dropout freq. to .5 (50%)
self.dropout = Dropout(0.5)
# Define the Dense layer, with softmax activation function
self.dense = Dense(n_labels, "softmax")
def call(self, inputs):
"""Define class method to call model on input
Args:
inputs (_type_): _description_
Returns:
_type_: _description_
"""
# inputs # ohne i
out = self.graph_conv(inputs)
out = self.dropout(out)
# out # mit i --> Debugger
out = self.pool(out)
out = self.dense(out)
return out
` Later in the main:
# Build GCN model model = OCRGCN(64, n_labels=8)
#Compile the model with adam optimizer and loss function model.compile(optimizer=Adam(learning_rate), loss="categorical_crossentropy") #Now we can train! We don't need to specify a batch size, since our loader is basically a generator model.fit(train_loader.load(), steps_per_epoch=train_loader.steps_per_epoch, epochs=epochs)
It didn’t worked and i got the following error: ValueError: too many values to unpack (expected 2)
Then I checked the shape of a batch from the loader. I just found out that there is also the batch index i.
for batch in train_loader: (x, a, i), y = batch # labels have shape [batch, n_labels] if node_level=False or [n_nodes, n_labels] break
But the GCN_Conv. Input just accepts 2dim (x, a). Then I deleted the i (hard codded) for testing, but the GlobalSumPool Layer requieres (x, a, i) in disjoint mode.
I also fixed the call method in the GCNConv Class:
def call(self, inputs, mask=None): if len(inputs) == 2: x, a = inputs else: x, a, _ = inputs # So that the model can be used with DisjointLoader
But now i get another error. It seems that the shape [1, 64 is [batchsize, n_hidden] and [8, 8] is the shape of my labels.
tensorflow.python.framework.errors_impl.InvalidArgumentError: Graph execution error:
Detected at node ‘gradient_tape/ocrgcn/dense/MatMul/MatMul_1’ defined at (most recent call last): File “/root/OCR%20POC/GCN/OCR_POC/GCN_model_disjoint_mode.py”, line 410, in <module> main() File “/root/OCR%20POC/GCN/OCR_POC/GCN_model_disjoint_mode.py”, line 94, in main model.fit(train_loader.load(), steps_per_epoch=train_loader.steps_per_epoch, epochs=epochs) File “/root/paddle/lib/python3.9/site-packages/keras/utils/traceback_utils.py”, line 64, in error_handler return fn(*args, **kwargs) File “/root/paddle/lib/python3.9/site-packages/keras/engine/training.py”, line 1384, in fit tmp_logs = self.train_function(iterator) File “/root/paddle/lib/python3.9/site-packages/keras/engine/training.py”, line 1021, in train_function return step_function(self, iterator) File “/root/paddle/lib/python3.9/site-packages/keras/engine/training.py”, line 1010, in step_function outputs = model.distribute_strategy.run(run_step, args=(data,)) File “/root/paddle/lib/python3.9/site-packages/keras/engine/training.py”, line 1000, in run_step outputs = model.train_step(data) File “/root/paddle/lib/python3.9/site-packages/keras/engine/training.py”, line 863, in train_step self.optimizer.minimize(loss, self.trainable_variables, tape=tape) File “/root/paddle/lib/python3.9/site-packages/keras/optimizer_v2/optimizer_v2.py”, line 530, in minimize grads_and_vars = self._compute_gradients( File “/root/paddle/lib/python3.9/site-packages/keras/optimizer_v2/optimizer_v2.py”, line 583, in _compute_gradients grads_and_vars = self._get_gradients(tape, loss, var_list, grad_loss) File “/root/paddle/lib/python3.9/site-packages/keras/optimizer_v2/optimizer_v2.py”, line 464, in _get_gradients grads = tape.gradient(loss, var_list, grad_loss) Node: ‘gradient_tape/ocrgcn/dense/MatMul/MatMul_1’ Matrix size-incompatible: In[0]: [1,64], In[1]: [8,8] [[{{node gradient_tape/ocrgcn/dense/MatMul/MatMul_1}}]] [Op:__inference_train_function_623]
/root/OCR%20POC/GCN/OCR_POC/GCN_model_disjoint_mode.py(94)main() -> model.fit(train_loader.load(), steps_per_epoch=train_loader.steps_per_epoch, epochs=epochs) (Pdb) n –Return– /root/OCR%20POC/GCN/OCR_POC/GCN_model_disjoint_mode.py(94)main()->None -> model.fit(train_loader.load(), steps_per_epoch=train_loader.steps_per_epoch, epochs=epochs)
I guess my data shape and the GCN Model dont fit together, but i was following the instructions and examples. But at this point, I really dont know what to do and I hope that someone can help me with the problem or suggest alternative ways.
Thank you!
Ps: Sorry, for the weird format of the code snippets. I dont know why it doesnt work as supposed.
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:6 (2 by maintainers)
Top GitHub Comments
If you want to do node-level prediction, then you should remove the global pooling layer from your model. In this case, the model will output one prediction of size 9 per graph in the batch (32), hence the outputs/logits will have shape (32, 9) but the targets will have shape (n_nodes_in_batch, 9). If you remove the global pooling layer, the output of the model will be as expected.
Thanks a lot that helped!
I also had an issue with the shape of my labels! It was because of the stupid One-hot-encoder of sklearn, which returned a sparse-matrix covered by a np array! That issued another error! Since i fixed that with the flag sparse=False, the training finally started!
Thanks a lot for helping, im closing the issue!