对于我的项目,我需要将有向图转换为图的张量流实现,就好像它是神经网络一样。在tensorflow版本1中,我可以将所有输入定义为占位符,然后使用广度优先搜索图为输出生成数据流图。然后,我只需使用feed_dict来输入我的输入。但是,在TensorFlow v2.0中,他们决定完全放弃占位符。
如何在不使用占位符的情况下为每个接受可变数量的输入并返回可变数量的输出的图制作tf.function?
我想生成一个类似tf.function的函数,该函数适用于任意非循环有向图,以便在生成图后,我可以利用tensorflow GPU支持来连续运行图前馈数千次。
编辑代码示例:
我的图被定义为字典。每个键代表一个节点,并具有另一个字典的对应值,该字典指定具有权重的入站和出站链接。
{ "A": { "incoming": [("B", 2), ("C", -1)], "outgoing": [("D", 3)] } }
为了简洁起见,我省略了B,C和D的条目。这是我如何在tensorflow v1.0中构造我想要的代码的方式,其中输入只是键值的列表,这些键值严格是图形的输入
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]
然后,我将能够多次运行输出,因为它们只是带有占位符的图形,我将为其提供feed_dict。
显然,这不是TensorFlow v2.0中的预期方式,因为它们似乎强烈不鼓励在此新版本中使用占位符。
关键是我只需要对图形进行一次预处理,因为它返回的数据图与graph_dict定义无关。
以下是可与TF 2.0一起使用的示例代码。它依赖于可 通过以下方式访问的兼容性APItensorflow.compat.v1,并且需要禁用v2行为。我不知道它的行为是否符合您的预期。如果没有,请向我们提供更多有关您要实现的目标的说明。
tensorflow.compat.v1
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>]
尽管以上代码段有效,但仍与TF 1.0绑定。要将其迁移到TF 2.0,您必须重构一点代码。
建议您不要返回的列表,而不是返回TF 1.0中可调用的张量的列表 keras.layers.Model。
keras.layers.Model
下面是一个工作示例:
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()
这里要注意什么?
placeholder
keras.Input
keras.layers.[add|multiply]
keras.Model
这是代码的输出。
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)