Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing placeholder for tensorflow v2

Tags:

For my project, I need to convert a directed graph into a tensorflow implementation of the graph as if it was a neural network. In tensorflow version 1, I could just define all of my inputs as placeholders and then just generate the dataflow graph for the outputs using a breadthfirst search of the graph. Then I would just feed in my inputs using a feed_dict. However, in TensorFlow v2.0 they have decided to do away with placeholders entirely.

How would I make a tf.function for each graph that takes in a variable amount of inputs and returns a variable amount of outputs without using a placeholder?

I want to generate a tf.function like this that works for an arbitrary acyclic directed graph so that I can take advantage of tensorflow GPU support to run the graph feed forward a few thousand times in a row after I have generated it.


Edit for code example:

My graph is defined as a dictionary. Each key represents a node and has a corresponding value of another dictionary specifying incoming and outgoing links with weights.

{     "A": {         "incoming": [("B", 2), ("C", -1)],         "outgoing": [("D", 3)]     } } 

I have omitted the entries for B,C, and D for brevity. Here is how I would construct the code I want in tensorflow v1.0 where inputs is just a list of key values that are strictly inputs to the graph

def construct_graph(graph_dict, inputs, outputs):     queue = inputs[:]     make_dict = {}     for key, val in graph_dict.items():         if key in inputs:             make_dict[key] = tf.placeholder(tf.float32, name=key)         else:             make_dict[key] = None     # Breadth-First search of graph starting from inputs     while len(queue) != 0:         cur = graph_dict[queue[0]]         for outg in cur["outgoing"]:             if make_dict[outg[0]]: # If discovered node, do add/multiply operation                 make_dict[outg[0]] = tf.add(make_dict[outg[0]], tf.multiply(outg[1], make_dict[queue[0]]))             else: # If undiscovered node, input is just coming in multiplied and add outgoing to queue                 make_dict[outg[0]] = tf.multiply(make_dict[queue[0]], outg[1])                 for outgo in graph_dict[outg[0]]["outgoing"]:                     queue.append(outgo[0])         queue.pop(0)     # Returns one data graph for each output     return [make_dict[x] for x in outputs] 

I would then be able to run the outputs many times as they are simply graphs with placeholders that I would provide a feed_dict for.

Obviously, this is not the intended way in TensorFlow v2.0 as they seem to strongly discourage the use of placeholders in this new version.

The point is that I only have to do this preprocessing for a graph once, as it returns a datagraph which is independent of the graph_dict definition.

like image 816
Em Eldar Avatar asked Nov 22 '19 01:11

Em Eldar


People also ask

What is the difference between TF variable and TF placeholder?

tf. placeholder is used for inputs that will be provided externally to the computation at run-time (e.g. training data). tf. Variable is used for inputs that are part of the computation and are going to be modified by the computation (e.g. weights of a neural network).

What is TensorFlow placeholder used for?

A placeholder is a variable in Tensorflow to which data will be assigned sometime later on. It enables us to create processes or operations without the requirement for data. Data is fed into the placeholder as the session starts, and the session is run. We can feed in data into tensorflow graphs using placeholders.

What does TF compat v1 placeholder () do?

Inserts a placeholder for a tensor that will be always fed.

What is Feed_dict in TensorFlow?

The feed_dict argument is used in TensorFlow to feed values to these placeholders, to avoid getting an error that prompts you to feed a value for placeholders in the TensorFlow.


1 Answers

Make your code work with TF 2.0

Below is a sample code which you can use with TF 2.0. It relies on the compatibility API that is accessible as tensorflow.compat.v1, and requires to disable v2 behaviors. I don't know if it behaves as you expected. If not, then provide us more explanation of what you try to achieve.

import tensorflow.compat.v1 as tf tf.disable_v2_behavior()  @tf.function def construct_graph(graph_dict, inputs, outputs):     queue = inputs[:]     make_dict = {}     for key, val in graph_dict.items():         if key in inputs:             make_dict[key] = tf.placeholder(tf.float32, name=key)         else:             make_dict[key] = None     # Breadth-First search of graph starting from inputs     while len(queue) != 0:         cur = graph_dict[queue[0]]         for outg in cur["outgoing"]:             if make_dict[outg[0]]: # If discovered node, do add/multiply operation                 make_dict[outg[0]] = tf.add(make_dict[outg[0]], tf.multiply(outg[1], make_dict[queue[0]]))             else: # If undiscovered node, input is just coming in multiplied and add outgoing to queue                 make_dict[outg[0]] = tf.multiply(make_dict[queue[0]], outg[1])                 for outgo in graph_dict[outg[0]]["outgoing"]:                     queue.append(outgo[0])         queue.pop(0)     # Returns one data graph for each output     return [make_dict[x] for x in outputs]  def main():     graph_def = {         "B": {             "incoming": [],             "outgoing": [("A", 1.0)]         },         "C": {             "incoming": [],             "outgoing": [("A", 1.0)]         },         "A": {             "incoming": [("B", 2.0), ("C", -1.0)],             "outgoing": [("D", 3.0)]         },         "D": {             "incoming": [("A", 2.0)],             "outgoing": []         }     }     outputs = construct_graph(graph_def, ["B", "C"], ["A"])     print(outputs)  if __name__ == "__main__":     main() 
[<tf.Tensor 'PartitionedCall:0' shape=<unknown> dtype=float32>] 

 Migrate your code to TF 2.0

While the above snippet is valid, it is still tied to TF 1.0. To migrate it to TF 2.0 you have to refactor a little bit your code.

Instead of returning a list of tensors, which were callables with TF 1.0, I advise you to return a list of keras.layers.Model.

Below is a working example:

import tensorflow as tf  def construct_graph(graph_dict, inputs, outputs):     queue = inputs[:]     make_dict = {}     for key, val in graph_dict.items():         if key in inputs:             # Use keras.Input instead of placeholders             make_dict[key] = tf.keras.Input(name=key, shape=(), dtype=tf.dtypes.float32)         else:             make_dict[key] = None     # Breadth-First search of graph starting from inputs     while len(queue) != 0:         cur = graph_dict[queue[0]]         for outg in cur["outgoing"]:             if make_dict[outg[0]] is not None: # If discovered node, do add/multiply operation                 make_dict[outg[0]] = tf.keras.layers.add([                     make_dict[outg[0]],                     tf.keras.layers.multiply(                         [[outg[1]], make_dict[queue[0]]],                     )],                 )             else: # If undiscovered node, input is just coming in multiplied and add outgoing to queue                 make_dict[outg[0]] = tf.keras.layers.multiply(                     [make_dict[queue[0]], [outg[1]]]                 )                 for outgo in graph_dict[outg[0]]["outgoing"]:                     queue.append(outgo[0])         queue.pop(0)     # Returns one data graph for each output     model_inputs = [make_dict[key] for key in inputs]     model_outputs = [make_dict[key] for key in outputs]     return [tf.keras.Model(inputs=model_inputs, outputs=o) for o in model_outputs]  def main():     graph_def = {         "B": {             "incoming": [],             "outgoing": [("A", 1.0)]         },         "C": {             "incoming": [],             "outgoing": [("A", 1.0)]         },         "A": {             "incoming": [("B", 2.0), ("C", -1.0)],             "outgoing": [("D", 3.0)]         },         "D": {             "incoming": [("A", 2.0)],             "outgoing": []         }     }     outputs = construct_graph(graph_def, ["B", "C"], ["A"])     print("Builded models:", outputs)     for o in outputs:         o.summary(120)         print("Output:", o((1.0, 1.0)))  if __name__ == "__main__":     main() 

What to notice here?

  • Change from placeholder to keras.Input, requiring to set the shape of the input.
  • Use keras.layers.[add|multiply] for computation. This is probably not required, but stick to one interface. However, it requires to wrap factors inside a list (to handle batching)
  • Build keras.Model to return
  • Call your model with a tuple of values (not a dictionary anymore)

Here is the output of the code.

Builded models: [<tensorflow.python.keras.engine.training.Model object at 0x7fa0b49f0f50>] Model: "model" ________________________________________________________________________________________________________________________ Layer (type)                           Output Shape               Param #       Connected to                             ======================================================================================================================== B (InputLayer)                         [(None,)]                  0                                                      ________________________________________________________________________________________________________________________ C (InputLayer)                         [(None,)]                  0                                                      ________________________________________________________________________________________________________________________ tf_op_layer_mul (TensorFlowOpLayer)    [(None,)]                  0             B[0][0]                                  ________________________________________________________________________________________________________________________ tf_op_layer_mul_1 (TensorFlowOpLayer)  [(None,)]                  0             C[0][0]                                  ________________________________________________________________________________________________________________________ add (Add)                              (None,)                    0             tf_op_layer_mul[0][0]                                                                                                    tf_op_layer_mul_1[0][0]                  ======================================================================================================================== Total params: 0 Trainable params: 0 Non-trainable params: 0 ________________________________________________________________________________________________________________________ Output: tf.Tensor([2.], shape=(1,), dtype=float32) 
like image 121
AlexisBRENON Avatar answered Oct 12 '22 14:10

AlexisBRENON