From 0ce362e3cc6e777a5314f6b7314ba4dddcf3af86 Mon Sep 17 00:00:00 2001 From: Daniel Smilkov Date: Thu, 5 Sep 2019 15:33:31 -0400 Subject: [PATCH 1/5] save --- .../tf_saved_model_conversion_v2.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py index 6111944799f..34172cfa2d1 100644 --- a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py +++ b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py @@ -29,6 +29,8 @@ from tensorflow.python.grappler import cluster as gcluster from tensorflow.python.grappler import tf_optimizer from tensorflow.python.saved_model.load import load +from tensorflow.python.saved_model import loader +from tensorflow.python.tools import saved_model_utils from tensorflow.python.training.saver import export_meta_graph from google.protobuf.json_format import MessageToDict import tensorflow_hub as hub @@ -272,15 +274,20 @@ def _check_signature_in_model(saved_model, signature_name): saved_model.signatures.keys())) -def _freeze_saved_model_v1(graph, output_node_names): - frozen_graph_def = tf.compat.v1.graph_util.convert_variables_to_constants( - tf.compat.v1.Session(), graph.as_graph_def(), output_node_names) +def _freeze_saved_model_v1(saved_model_dir, saved_model_tags, + output_node_names): + input_graph_def = saved_model_utils.get_meta_graph_def( + saved_model_dir, ','.join(saved_model_tags)).graph_def + with tf.compat.v1.Session() as sess: + _ = loader.load(sess, saved_model_tags, saved_model_dir) + frozen_graph_def = tf.compat.v1.graph_util.convert_variables_to_constants( + sess, input_graph_def, output_node_names) - frozen_graph = tf.Graph() - with frozen_graph.as_default(): - tf.import_graph_def(frozen_graph_def, name='') + frozen_graph = tf.Graph() + with frozen_graph.as_default(): + tf.import_graph_def(frozen_graph_def, name='') - return frozen_graph + return frozen_graph def _freeze_saved_model_v2(concrete_func): return convert_to_constants.convert_variables_to_constants_v2( @@ -336,8 +343,8 @@ def convert_tf_saved_model(saved_model_dir, try: frozen_graph = _freeze_saved_model_v2(concrete_func) except BaseException: - frozen_graph = _freeze_saved_model_v1( - concrete_func.graph, output_node_names) + frozen_graph = _freeze_saved_model_v1(saved_model_dir, saved_model_tags, + output_node_names) optimize_graph(frozen_graph, output_node_names, output_graph, model.tensorflow_version, From d2548288509c514ea53615a11504f251a2f73fb9 Mon Sep 17 00:00:00 2001 From: Daniel Smilkov Date: Fri, 6 Sep 2019 08:48:55 -0400 Subject: [PATCH 2/5] save --- tfjs-converter/python/run-python-tests.sh | 2 +- .../tf_saved_model_conversion_v2_test.py | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/tfjs-converter/python/run-python-tests.sh b/tfjs-converter/python/run-python-tests.sh index dfd9f79534a..5759674b1c7 100755 --- a/tfjs-converter/python/run-python-tests.sh +++ b/tfjs-converter/python/run-python-tests.sh @@ -24,7 +24,7 @@ TEST_FILES="$(find "${SCRIPTS_DIR}" -name '*_test.py')" pip install virtualenv -TMP_VENV_DIR="$(mktemp -d --suffix=_venv)" +TMP_VENV_DIR="$(mktemp -d)" virtualenv -p "python" "${TMP_VENV_DIR}" source "${TMP_VENV_DIR}/bin/activate" diff --git a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py index 5144cfb87c0..106daeda3e8 100644 --- a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py +++ b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py @@ -80,6 +80,38 @@ def _create_saved_model_v1(self): builder.save() + def _create_saved_model_v1_with_reference_vars(self): + """Create a TensorFlow SavedModel V1 with reference vars for testing.""" + + graph = tf.Graph() + with graph.as_default(): + x = tf.compat.v1.constant([[37.0, -23.0], [1.0, 4.0]]) + # Use reference variable instead of resource. + w = tf.compat.v1.get_variable('w', shape=[2, 2], use_resource=False) + output = tf.compat.v1.matmul(x, w) + init_op = w.initializer + + # Create a builder. + save_dir = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + builder = tf.compat.v1.saved_model.builder.SavedModelBuilder(save_dir) + + with tf.compat.v1.Session() as sess: + # Run the initializer on `w`. + sess.run(init_op) + + builder.add_meta_graph_and_variables( + sess, [tf.compat.v1.saved_model.tag_constants.SERVING], + signature_def_map={ + "serving_default": + tf.compat.v1.saved_model \ + .signature_def_utils.predict_signature_def( + inputs={"x": x}, + outputs={"output": output}) + }, + assets_collection=None) + + builder.save() + def _create_saved_model_with_fusable_conv2d(self): """Test a basic model with fusable conv2d.""" layers = [ @@ -219,6 +251,36 @@ def test_convert_saved_model_v1(self): glob.glob( os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'group*-*'))) + def test_convert_saved_model_v1_with_reference_vars(self): + self._create_saved_model_v1_with_reference_vars() + + tf_saved_model_conversion_v2.convert_tf_saved_model( + os.path.join(self._tmp_dir, SAVED_MODEL_DIR), + os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + ) + + weights = [{ + 'paths': ['group1-shard1of1.bin'], + 'weights': [{'dtype': 'float32', 'name': 'w', 'shape': [2, 2]}]}] + + tfjs_path = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + # Check model.json and weights manifest. + with open(os.path.join(tfjs_path, 'model.json'), 'rt') as f: + model_json = json.load(f) + self.assertTrue(model_json['modelTopology']) + weights_manifest = model_json['weightsManifest'] + self.assertEqual(weights_manifest, weights) + # Check meta-data in the artifact JSON. + self.assertEqual(model_json['format'], 'graph-model') + self.assertEqual( + model_json['convertedBy'], + 'TensorFlow.js Converter v%s' % version.version) + self.assertEqual(model_json['generatedBy'], + tf.__version__) + self.assertTrue( + glob.glob( + os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'group*-*'))) + def test_convert_saved_model(self): self._create_saved_model() From bb196f6477ca85e9fa96abc14c806bd9f1bf8878 Mon Sep 17 00:00:00 2001 From: Daniel Smilkov Date: Fri, 6 Sep 2019 09:26:03 -0400 Subject: [PATCH 3/5] save --- .../tf_saved_model_conversion_v2_test.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py index 106daeda3e8..8360a9d4293 100644 --- a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py +++ b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py @@ -80,17 +80,22 @@ def _create_saved_model_v1(self): builder.save() - def _create_saved_model_v1_with_reference_vars(self): - """Create a TensorFlow SavedModel V1 with reference vars for testing.""" + def _create_saved_model_v1_with_hashtable(self): + """Create a TensorFlow SavedModel V1 with unused hash table for testing.""" graph = tf.Graph() with graph.as_default(): - x = tf.compat.v1.constant([[37.0, -23.0], [1.0, 4.0]]) - # Use reference variable instead of resource. - w = tf.compat.v1.get_variable('w', shape=[2, 2], use_resource=False) + x = tf.placeholder('float32', [2, 2]) + w = tf.compat.v1.get_variable('w', shape=[2, 2]) output = tf.compat.v1.matmul(x, w) init_op = w.initializer + # Add a hash table that is not used by the output. + keys = tf.constant(['key']) + values = tf.constant([1]) + initializer = tf.contrib.lookup.KeyValueTensorInitializer(keys, values) + table = tf.contrib.lookup.HashTable(initializer, -1) + # Create a builder. save_dir = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) builder = tf.compat.v1.saved_model.builder.SavedModelBuilder(save_dir) @@ -98,6 +103,7 @@ def _create_saved_model_v1_with_reference_vars(self): with tf.compat.v1.Session() as sess: # Run the initializer on `w`. sess.run(init_op) + table.init.run() builder.add_meta_graph_and_variables( sess, [tf.compat.v1.saved_model.tag_constants.SERVING], @@ -251,8 +257,8 @@ def test_convert_saved_model_v1(self): glob.glob( os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'group*-*'))) - def test_convert_saved_model_v1_with_reference_vars(self): - self._create_saved_model_v1_with_reference_vars() + def test_convert_saved_model_v1_with_hashtable(self): + self._create_saved_model_v1_with_hashtable() tf_saved_model_conversion_v2.convert_tf_saved_model( os.path.join(self._tmp_dir, SAVED_MODEL_DIR), From c18749ac4ee4a40ce6336e7f7177cde644b5f687 Mon Sep 17 00:00:00 2001 From: Daniel Smilkov Date: Fri, 6 Sep 2019 10:46:23 -0400 Subject: [PATCH 4/5] save --- tfjs-converter/python/run-python-tests.sh | 2 +- .../tf_saved_model_conversion_v2.py | 6 ++--- .../tf_saved_model_conversion_v2_test.py | 24 +++++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/tfjs-converter/python/run-python-tests.sh b/tfjs-converter/python/run-python-tests.sh index 5759674b1c7..7d1fd39c5c9 100755 --- a/tfjs-converter/python/run-python-tests.sh +++ b/tfjs-converter/python/run-python-tests.sh @@ -24,7 +24,7 @@ TEST_FILES="$(find "${SCRIPTS_DIR}" -name '*_test.py')" pip install virtualenv -TMP_VENV_DIR="$(mktemp -d)" +TMP_VENV_DIR="$(mktemp -u).venv" virtualenv -p "python" "${TMP_VENV_DIR}" source "${TMP_VENV_DIR}/bin/activate" diff --git a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py index 34172cfa2d1..4370a0f22f0 100644 --- a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py +++ b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2.py @@ -276,10 +276,10 @@ def _check_signature_in_model(saved_model, signature_name): def _freeze_saved_model_v1(saved_model_dir, saved_model_tags, output_node_names): - input_graph_def = saved_model_utils.get_meta_graph_def( - saved_model_dir, ','.join(saved_model_tags)).graph_def with tf.compat.v1.Session() as sess: - _ = loader.load(sess, saved_model_tags, saved_model_dir) + loader.load(sess, saved_model_tags, saved_model_dir) + input_graph_def = saved_model_utils.get_meta_graph_def( + saved_model_dir, ','.join(saved_model_tags)).graph_def frozen_graph_def = tf.compat.v1.graph_util.convert_variables_to_constants( sess, input_graph_def, output_node_names) diff --git a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py index 8360a9d4293..bd586c6e582 100644 --- a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py +++ b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py @@ -230,22 +230,24 @@ def double_module_fn(): def test_convert_saved_model_v1(self): self._create_saved_model_v1() + input_dir = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + output_dir = os.path.join(input_dir, 'js') tf_saved_model_conversion_v2.convert_tf_saved_model( - os.path.join(self._tmp_dir, SAVED_MODEL_DIR), - os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + input_dir, + output_dir ) - weights = [{ + expected_weights_manifest = [{ 'paths': ['group1-shard1of1.bin'], 'weights': [{'dtype': 'float32', 'name': 'w', 'shape': [2, 2]}]}] - tfjs_path = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + tfjs_path = os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'js') # Check model.json and weights manifest. with open(os.path.join(tfjs_path, 'model.json'), 'rt') as f: model_json = json.load(f) self.assertTrue(model_json['modelTopology']) weights_manifest = model_json['weightsManifest'] - self.assertEqual(weights_manifest, weights) + self.assertEqual(weights_manifest, expected_weights_manifest) # Check meta-data in the artifact JSON. self.assertEqual(model_json['format'], 'graph-model') self.assertEqual( @@ -260,22 +262,24 @@ def test_convert_saved_model_v1(self): def test_convert_saved_model_v1_with_hashtable(self): self._create_saved_model_v1_with_hashtable() + input_dir = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + output_dir = os.path.join(input_dir, 'js') tf_saved_model_conversion_v2.convert_tf_saved_model( - os.path.join(self._tmp_dir, SAVED_MODEL_DIR), - os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + input_dir, + output_dir ) - weights = [{ + expected_weights_manifest = [{ 'paths': ['group1-shard1of1.bin'], 'weights': [{'dtype': 'float32', 'name': 'w', 'shape': [2, 2]}]}] - tfjs_path = os.path.join(self._tmp_dir, SAVED_MODEL_DIR) + tfjs_path = os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'js') # Check model.json and weights manifest. with open(os.path.join(tfjs_path, 'model.json'), 'rt') as f: model_json = json.load(f) self.assertTrue(model_json['modelTopology']) weights_manifest = model_json['weightsManifest'] - self.assertEqual(weights_manifest, weights) + self.assertEqual(weights_manifest, expected_weights_manifest) # Check meta-data in the artifact JSON. self.assertEqual(model_json['format'], 'graph-model') self.assertEqual( From 93592defeb2cf7303e8aa9b7ce8de92840b92e1b Mon Sep 17 00:00:00 2001 From: Daniel Smilkov Date: Fri, 6 Sep 2019 10:58:37 -0400 Subject: [PATCH 5/5] save --- .../converters/tf_saved_model_conversion_v2_test.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py index bd586c6e582..21edc83388c 100644 --- a/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py +++ b/tfjs-converter/python/tensorflowjs/converters/tf_saved_model_conversion_v2_test.py @@ -255,9 +255,7 @@ def test_convert_saved_model_v1(self): 'TensorFlow.js Converter v%s' % version.version) self.assertEqual(model_json['generatedBy'], tf.__version__) - self.assertTrue( - glob.glob( - os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'group*-*'))) + self.assertTrue(glob.glob(os.path.join(output_dir, 'group*-*'))) def test_convert_saved_model_v1_with_hashtable(self): self._create_saved_model_v1_with_hashtable() @@ -287,9 +285,7 @@ def test_convert_saved_model_v1_with_hashtable(self): 'TensorFlow.js Converter v%s' % version.version) self.assertEqual(model_json['generatedBy'], tf.__version__) - self.assertTrue( - glob.glob( - os.path.join(self._tmp_dir, SAVED_MODEL_DIR, 'group*-*'))) + self.assertTrue(glob.glob(os.path.join(output_dir, 'group*-*'))) def test_convert_saved_model(self): self._create_saved_model()