【问题标题】:Replacing placeholder for tensorflow v2替换 tensorflow v2 的占位符
【发布时间】:2020-03-18 01:07:30
【问题描述】:

对于我的项目,我需要将有向图转换为图的 tensorflow 实现,就好像它是一个神经网络一样。在 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 tf.function
  • @jdehesa 添加了代码示例。
  • @OmerEldar,inputsoutputs 中有什么?
  • 如果您可以包含一个示例图,例如graph_dictinputsoutputs,将会很有帮助。

标签: python python-3.x tensorflow tensorflow2.0


【解决方案1】:

让您的代码与 TF 2.0 一起工作

以下是可用于 TF 2.0 的示例代码。 它依赖于compatibility API 可以通过tensorflow.compat.v1 访问,并且需要disable v2 behaviors。 我不知道它的行为是否符合您的预期。 如果没有,请向我们提供更多关于您尝试实现的目标的说明。

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 2.0

虽然上面的 sn-p 是有效的,但它仍然与 TF 1.0 相关联。 要将其迁移到 TF 2.0,您必须重构一些代码。

我建议您返回一个张量列表,而不是返回一个张量列表,这些张量是 TF 1.0 的可调用对象。 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)

【讨论】:

  • 您的迁移解决方案正是我想要的,我不知道 keras api 对模型构建有如此好的定义。很好的答案。
  • @Alexis 脚本现在给出了这个错误ValueError: A merge layer should be called on a list of inputs.
  • @Hirak 这适用于 TF 2.0。从那时起,也许一些 API 发生了变化。就这一行错误,很难说是哪里出了问题,但我认为与迁移过程无关。
  • @AlexisBRENON 感谢您的回复,这是 ipynb 的链接,错误为 colab.research.google.com/drive/…
  • @AlexisBRENON 仅供参考,如果其他人在执行此更正时遇到同样的错误:outg[1]K.constant([outg[1]]) 将修复错误。
猜你喜欢
  • 2018-10-14
  • 1970-01-01
  • 2013-04-08
  • 2017-09-11
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多