google-deepmind / graph_nets

Build Graph Nets in Tensorflow
https://arxiv.org/abs/1806.01261
Apache License 2.0
5.34k stars 783 forks source link

Why Dimension in node/edge must be equal? ValueError-- Graph_nets_basics issue #65

Closed ghost closed 5 years ago

ghost commented 5 years ago

Thanks for the great work on Graph nets. I am new to GN model, and try to work on the demo of GN_basics. Currently, have some error message as:

"ValueError: Dimension - in both shapes must be equal, but are 7 and 13. Shapes are [7] and [13]. for 'graph_network_1/edge_block/concat' (op:ConcatV2) with input shapes:[7,3],[13,3], [13,3], [13,4],[] and with computed input tensors: input[4] = <-1>."

If change to the equal number of nodes and edges, it works. BTW, can the output_edge/node/global_size be larger/smaller than the input graph's edge/node/global size? I tried to run with different sizes for input/output, it doesn't work.

The input graph:

GLOBAL_SIZE = 4
NODE_SIZE = 3
EDGE_SIZE = 3

def get_graph_data_dict(num_nodes, num_edges):
  return {
      "globals": np.random.rand(GLOBAL_SIZE).astype(np.float32),
      "nodes": np.random.rand(num_nodes, NODE_SIZE).astype(np.float32),
      "edges": np.random.rand(num_edges, EDGE_SIZE).astype(np.float32),
      "senders": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
      "receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
  }

graph_7_nodes_13_edges = get_graph_data_dict(num_nodes=7, num_edges=13)

graph_dicts = [graph_7_nodes_13_edges]`

The GN model is set as:

tf.reset_default_graph()
OUTPUT_EDGE_SIZE = 3
OUTPUT_NODE_SIZE = 3
OUTPUT_GLOBAL_SIZE = 4
graph_network = modules.GraphNetwork(
    edge_model_fn=lambda: snt.Linear(output_size=OUTPUT_EDGE_SIZE),
    node_model_fn=lambda: snt.Linear(output_size=OUTPUT_NODE_SIZE),
    global_model_fn=lambda: snt.Linear(output_size=OUTPUT_GLOBAL_SIZE))

input_graphs = utils_tf.data_dicts_to_graphs_tuple(graph_dicts)
output_graphs = graph_network(input_graphs)

print("Output edges size: {}".format(output_graphs.edges.shape[-1]))  # Equal to OUTPUT_EDGE_SIZE
print("Output nodes size: {}".format(output_graphs.nodes.shape[-1]))  # Equal to OUTPUT_NODE_SIZE
print("Output globals size: {}".format(output_graphs.globals.shape[-1]))  # Equal to OUTPUT_GLOBAL_SIZE
alvarosg commented 5 years ago

I am not sure what the problem is exactly. I just tried running your code in colaboratory and it runs fine just like you pasted it (see below). Does the GN_basics demo not run correctly for you?

You should be able to have different number of nodes and edges, as well as output feature sizes that are different than the input feature sizes (unless you plan to feed the output graph again to the same graph network, in which case of course output and output would need to be the same size).

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from graph_nets import blocks
from graph_nets import graphs
from graph_nets import modules
from graph_nets import utils_np
from graph_nets import utils_tf

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import sonnet as snt
import tensorflow as tf

GLOBAL_SIZE = 4
NODE_SIZE = 3
EDGE_SIZE = 3

def get_graph_data_dict(num_nodes, num_edges):
  return {
      "globals": np.random.rand(GLOBAL_SIZE).astype(np.float32),
      "nodes": np.random.rand(num_nodes, NODE_SIZE).astype(np.float32),
      "edges": np.random.rand(num_edges, EDGE_SIZE).astype(np.float32),
      "senders": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
      "receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
  }

graph_7_nodes_13_edges = get_graph_data_dict(num_nodes=7, num_edges=13)

graph_dicts = [graph_7_nodes_13_edges]

tf.reset_default_graph()
OUTPUT_EDGE_SIZE = 3
OUTPUT_NODE_SIZE = 7
OUTPUT_GLOBAL_SIZE = 4
graph_network = modules.GraphNetwork(
    edge_model_fn=lambda: snt.Linear(output_size=OUTPUT_EDGE_SIZE),
    node_model_fn=lambda: snt.Linear(output_size=OUTPUT_NODE_SIZE),
    global_model_fn=lambda: snt.Linear(output_size=OUTPUT_GLOBAL_SIZE))

input_graphs = utils_tf.data_dicts_to_graphs_tuple(graph_dicts)
output_graphs = graph_network(input_graphs)

print("Output edges size: {}".format(output_graphs.edges.shape[-1]))  # Equal to OUTPUT_EDGE_SIZE
print("Output nodes size: {}".format(output_graphs.nodes.shape[-1]))  # Equal to OUTPUT_NODE_SIZE
print("Output globals size: {}".format(output_graphs.globals.shape[-1]))  # Equal to OUTPUT_GLOBAL_SIZE
ghost commented 5 years ago

Thanks for the reply. updates: Found bug on my local code. should be num_edges for

"edges": np.random.rand(num_nodes, EDGE_SIZE).astype(np.float32), in the function --- def get_graph_data_dict(num_nodes, num_edges)

To clarify my previous question, in GN, for the output network, must it have the same number of nodes, edges (with same connectivity), and global with the input graph? but only can have different size of attributes that linked to nodes, edges and global?

alvarosg commented 5 years ago

Yes, currently, the graph networks cannot change the hard structure of the graph (e.g. number of edges, nodes and connectivity), but you can have different attributes sizes for each of them, and also for the output with respect to the input.

If you are interested on learning graph transformations, one way to do it is to assume a fully connected graph and represent soft connectivity as a feature in the edge indicating whether the edge actually exists or not. Then you can build a fully connected target graph with 0s in the edges that don't really exist, and 1s in the edges that exist, and do supervised learning on that.

ghost commented 5 years ago

Yes, graph transformation is one thing I am working on. Thanks for this good suggestion. One more question, how to understand the data structure/matrix for each node/edge/global block before/after training? Any documentations available? I tried to display the output_graphs after training, but doesn't work. Any function to get the graphs_tuple for output_graphs? or print the matrix of node/edge/global with attributes in details? image

alvarosg commented 5 years ago

I would recommend that you take a look at the graph nets basics demo, which explains the data structure and has examples of graph visualization and manipulation.

Hope this helps :)

(Since this is off topic from the original issue I am closing this for now.)