tensorflow: freeze_graph not initializing tables

I am not sure if this is an actual bug or if its expected but undocumented behavior.

I have a model that uses multiple lookup tables created via string_to_index. I freeze the model like so: bazel-bin/tensorflow/python/tools/freeze_graph --input_graph=/tmp/tf/graph.pbtxt --input_checkpoint=/tmp/tf/model.ckpt-0 --output_graph=/tmp/ticker_classifier.pb --output_node_names=sigmoid --initializer_nodes=init_all_tables

However when the model is reloaded and I attempt to run it I get an error “Table not initialized.” I get exactly the same resulting file whether I specify initializer_nodes or not. The behavior I was expecting was for the model to contain the lookup tables in a ready to use state for inference but I don’t know if that is an unreasonable expectation.

What related GitHub issues or StackOverflow threads have you found by searching the web for your problem?

I have not seen any issues related to this. I previously posted about this here http://stackoverflow.com/questions/42916383/how-to-properly-freeze-a-tensorflow-graph-containing-a-lookuptable

Environment info

Operating System: MacOS and Linux (CentOS 7)

Installed version of CUDA and cuDNN: None

If installed from source, provide

  1. The commit hash (git rev-parse HEAD) 07bb8ea2379bd459832b23951fb20ec47f3fdbd4
  2. Build label: 0.4.5 Build target: bazel-out/local-fastbuild/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar Build time: Thu Mar 16 12:19:38 2017 (1489666778) Build timestamp: 1489666778 Build timestamp as int: 1489666778

If possible, provide a minimal reproducible example (We usually don’t have time to read hundreds of lines of your code)

I have been unable to make a small example but I can spend more time on it if needed.

What other attempted solutions have you tried?

The workaround is to add init_all_tables to the output_nodes and then run init_all_tables before feeding the session examples for inference. This does have the side effect of needing to distribute the source files for the tables to the same path on all nodes that was originally used for training.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 5
  • Comments: 36 (19 by maintainers)

Commits related to this issue

Most upvoted comments

Adding init_all_tables to the list of names to export fixes this issue.

import os

import tensorflow as tf
from tensorflow.python.framework.graph_util import convert_variables_to_constants
from tensorflow.python.ops.lookup_ops import HashTable, KeyValueTensorInitializer

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

OUTPUT_FOLDER = '/tmp'
OUTPUT_NAME = 'hash_table.pb'
OUTPUT_NAMES = ['graph/output', 'init_all_tables']


def build_graph():
    d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
    init = KeyValueTensorInitializer(d.keys(), d.values())
    hash_table = HashTable(init, default_value=-1)
    data = tf.placeholder(tf.string, (None,), name='data')
    values = hash_table.lookup(data)
    output = tf.identity(values * 2, 'output')


def freeze_graph():
    with tf.Graph().as_default() as graph:
        with tf.name_scope('graph'):
            build_graph()

        with tf.Session(graph=graph) as sess:
            sess.run(tf.tables_initializer())
            print sess.run('graph/output:0', feed_dict={'graph/data:0': ['a', 'b', 'c', 'd', 'e']})
            frozen_graph = convert_variables_to_constants(sess, sess.graph_def, OUTPUT_NAMES)
            tf.train.write_graph(frozen_graph, OUTPUT_FOLDER, OUTPUT_NAME, as_text=False)


def load_frozen_graph():
    with open(os.path.join(OUTPUT_FOLDER, OUTPUT_NAME), 'rb') as f:
        output_graph_def = tf.GraphDef()
        output_graph_def.ParseFromString(f.read())

    with tf.Graph().as_default() as graph:
        tf.import_graph_def(output_graph_def, name='')
        with tf.Session(graph=graph) as sess:
            try:
                sess.run(graph.get_operation_by_name('init_all_tables'))
            except KeyError:
                pass
            print sess.run('graph/output:0', feed_dict={'graph/data:0': ['a', 'b', 'c', 'd', 'e']})


if __name__ == '__main__':
    freeze_graph()
    load_frozen_graph()

The call to extract_sub_graph inside convert_variables_to_constants prunes out this op and its descendants (keys, values) if you don’t include init_all_tables in output_node_names. I don’t like the idea of running an initializer op during inference and having a try/except seems hacky to me. Is there another way to do this?

@jkiske 's workaround worked for me!

We also built on top of it to make it work with tf.tables_initializer(), but it requires two other changes:

  • OUTPUT_NAMES needs to include the table initialization ops, which can be obtained with tf.get_collection(tf.GraphKeys.TABLE_INITIALIZERS).
  • The MetaGraph, instead of the Graph, needs to be what’s exported/imported. This is because tf.tables_initializer() references the tf.GraphKeys.TABLE_INITIALIZERS collection. The Graph does not contain a collection_list, but the MetaGraph does.

So here’s a solution that works for us:

import os

import tensorflow as tf
from tensorflow.python.framework.graph_util import convert_variables_to_constants
from tensorflow.python.ops.lookup_ops import HashTable, KeyValueTensorInitializer

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

OUTPUT_FOLDER = '/tmp'
OUTPUT_NAME = 'hash_table.pb'
OUTPUT_NAMES = ['graph/output']


def build_graph():
    d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
    init = KeyValueTensorInitializer(d.keys(), d.values())
    hash_table = HashTable(init, default_value=-1)
    data = tf.placeholder(tf.string, (None,), name='data')
    values = hash_table.lookup(data)
    output = tf.identity(values * 2, 'output')


def freeze_graph():
    with tf.Graph().as_default() as graph:
        with tf.name_scope('graph'):
            build_graph()

        with tf.Session(graph=graph) as sess:
            sess.run(tf.tables_initializer())
            print sess.run('graph/output:0', feed_dict={'graph/data:0': ['a', 'b', 'c', 'd', 'e']})
            for table_init_op in tf.get_collection(tf.GraphKeys.TABLE_INITIALIZERS):
                OUTPUT_NAMES.append(table_init_op.name)
            frozen_graph = convert_variables_to_constants(sess, sess.graph_def, OUTPUT_NAMES)
            tf.train.export_meta_graph(
                filename=os.path.join(OUTPUT_FOLDER, OUTPUT_NAME),
                graph_def=frozen_graph,
                collection_list=[tf.GraphKeys.TABLE_INITIALIZERS])


def load_frozen_graph():
    with tf.Graph().as_default() as graph:
        tf.train.import_meta_graph(os.path.join(OUTPUT_FOLDER, OUTPUT_NAME))
        with tf.Session(graph=graph) as sess:
            sess.run(tf.tables_initializer())
            print sess.run('graph/output:0', feed_dict={'graph/data:0': ['a', 'b', 'c', 'd', 'e']})


if __name__ == '__main__':
    freeze_graph()
    load_frozen_graph()

Nagging Assignees @petewarden, @suharshs: It has been 14 days with no activity and this issue has an assignee. Please update the label and/or status accordingly.