Initializing variables, variable scope and import_graph_def in tensorflow

泄露秘密 提交于 2020-03-02 06:23:26

问题


I have a number of related questions about tensorflow behavior when attempting to do graph surgery using import_graph_def. 2 different graph surgeries

In the image above, I represent with bold red arrows 2 different graph surgeries. On the left, there are 2 graphs, g1 and g2, and the surgery consists of replacing a node in graph g2 by a node - and everything below it - from graph g1. How to do that is explained in this post. The surgery on the right, which involves replacing nodes that belong to the same graph, I haven't been able to figure out how to perform, or even if it is at all possible. I ended up with this minimal example

with tf.Graph().as_default() as g1:
    with tf.variable_scope('foo', reuse=tf.AUTO_REUSE):
        x = tf.placeholder(dtype=tf.float64, shape=[2], name='x')
        c = tf.get_variable('c', initializer=tf.cast(1.0, tf.float64))
        y = tf.identity(2*x, 'y')

        z = tf.identity(3*x*c, 'z')

        g1_def = g1.as_graph_def()
        z1, = tf.import_graph_def(g1_def, input_map={'foo/x:0' : y}, return_elements=["foo/z:0"],
                              name='z1')
        init_op = tf.global_variables_initializer()
        print(tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='foo'))


with tf.Session(graph=g1) as sess:
    sess.run(init_op)
    print(sess.run(z, feed_dict={'foo/x:0' : np.array([1.0, 2.0])}) )
    print(sess.run(tf.report_uninitialized_variables()))
    # z1 = sess.run(z1, feed_dict={'foo/x:0' : np.array([1.0, 2.0])})

This code runs as it is. The 3 prints yield respectively:

[<tf.Variable 'foo/c:0' shape=() dtype=float64_ref>]
[ 3.  6.]
[]

In particular, the last print informs that there are no unintialized variables. However, uncommenting the last line, yields the error

FailedPreconditionError (see above for traceback): Attempting to use uninitialized value foo/z1/foo/c

Note that if I remove c from the definition of z above, this would also work. However, I would like to understand this error. To begin with, why is the variable reported as foo/z1/foo/c? Why does the scope foo appear twice? Why is nothing reported when I print the uninitialized variables? Why is only foo/c reported when I print the GLOBAL_VARIABLES collection under the scope foo?

PS: I guess that there is a simpler way to ask the question which is, what is the tensorflow analogue of

theano.clone(some_tensor, replace={input_var : replace_var})

回答1:


To begin with, why is the variable reported as foo/z1/foo/c? Why does the scope foo appear twice?

After you've called tf.import_graph_def(...), the graph got duplicated. The first graph is defined in foo score. The second subgraph has been imported under the scope foo/z1 (because name='z1', plus foo is preserved from the scope above). So the graph g1 now contains the following tensors:

foo/x
foo/y
foo/c
...
foo/z1/foo/x
foo/z1/foo/y
foo/z1/foo/c
...

The first foo/c is initialized, but the second foo/z1/foo/c is not (see below).

Why is nothing reported when I print the uninitialized variables? Why is only foo/c reported when I print the GLOBAL_VARIABLES collection under the scope foo?

Since report_uninitialized_variables() scans LOCAL_VARIABLES and GLOBAL_VARIABLES by default, this is basically the same question.

And it probably is a bug: GLOBAL_VARIABLES collection isn't updated after tf.import_graph_def call. I say probably because GLOBAL_VARIABLES was designed as a mere convenience collection. Tensorflow tries to keep it up do date, but probably doesn't guarantee it always has all variables. The fact that tf.add_to_collection exists publicly supports this idea -- one can add any value to any collection if they want it. Bottom line: this behavior may or may not change in future versions, but as of 1.5 the client is responsible to update the global variables after graph import.

In particular, the last print informs that there are no unintialized variables. However, uncommenting the last line, yields the error

To fix this error, you simply need to run the initializer for the z1 subgraph. Like this:

# note that it's defined before `g1.as_graph_def()` to be a part of graph def
init_op = tf.global_variables_initializer()

g1_def = g1.as_graph_def()
z1, = tf.import_graph_def(g1_def, input_map={'foo/x:0': y}, return_elements=["foo/z:0"],
                          name='z1')

# find the init op
z1_init_op = tf.get_default_graph().get_operation_by_name('foo/z1/foo/init')

...

sess.run(z1_init_op)

And voila! You have the duplicated graphs, just like you wanted to.




回答2:


I faced a similar issue but simply running the init operation didn't work.

I fixed it by manually running all "Assign" ops of the global variables of the imported graph.

In my scenario I want to run an encoding op 'z' with input 'patch:0' using two different input tensors.

    with tf.Session(graph=tf.get_default_graph()).as_default() as sess:

        g = tf.Graph()       
        saved_model = predictor.from_saved_model(args.export_dir, graph=g)
        variables = g.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)]

        fetch_ops = ['z:0','init']
        fetch_ops.extend([v.name.strip(":0") + "/Assign" for v in variables)

        image_graph = tf.graph_util.import_graph_def(
            g.as_graph_def(),
            input_map={'patch:0': image},
            return_elements=fetch_ops,
            name='image')

        warped_graph = tf.graph_util.import_graph_def(
            g.as_graph_def(),
            input_map={'patch:0': warped_image},
            return_elements=fetch_ops,
            name='warp')

        loss = tf.reduce_sum(tf.math.squared_difference(image_graph[0], warped_graph[0]))

        optimizer =  tf.train.GradientDescentOptimizer(learning_rate=0.0001)
        compute_gradients = optimizer.compute_gradients(
            loss,
            var_list=[dest_control_point_locations])

        apply_gradients = optimizer.apply_gradients(compute_gradients, global_step=step)

        sess.run(image_graph[1:])
        sess.run(warped_graph[1:])
        sess.run(tf.global_variables_initializer())

        gradients = sess.run(compute_gradients)

When extracting the operation and running it by feeding my tensors with feed_dict, gradient_computation doesn't work, that's why I used tf.graph_util.import_graph_def(...).

Hope this might help anyone facing the same issue.



来源:https://stackoverflow.com/questions/48715866/initializing-variables-variable-scope-and-import-graph-def-in-tensorflow

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!