From 44bbf04059e24f18f74a548821e12a774349fc0b Mon Sep 17 00:00:00 2001 From: William Chargin Date: Thu, 15 Aug 2019 18:49:21 -0700 Subject: [PATCH 1/2] =?UTF-8?q?Make=20audio-related=20functionality=20and?= =?UTF-8?q?=20tests=20TF=202.x=E2=80=93compatible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: These tests used `tf.contrib.ffmpeg.encode_audio`, but they can actually just use `tf.audio.encode_wav`, which has existed since TensorFlow 1.14. This also makes the encoder itself TF 2.x–compatible. Makes progress toward #1705. Test Plan: Tests pass in both TF 1.x and TF 2.x, and the `run_v1_only` decorators have been removed: ``` $ git grep run_v1_only '*encoder*_test.py' '*audio*_test.py' | wc -l 0 ``` wchargin-branch: audio-tf2x --- tensorboard/plugins/audio/BUILD | 1 - .../plugins/audio/audio_plugin_test.py | 65 ++++++++++--------- tensorboard/plugins/audio/summary.py | 5 +- tensorboard/plugins/audio/summary_test.py | 5 +- tensorboard/util/BUILD | 1 - tensorboard/util/encoder.py | 6 +- tensorboard/util/encoder_test.py | 2 - 7 files changed, 37 insertions(+), 48 deletions(-) diff --git a/tensorboard/plugins/audio/BUILD b/tensorboard/plugins/audio/BUILD index d3d69d330e..256b815b96 100644 --- a/tensorboard/plugins/audio/BUILD +++ b/tensorboard/plugins/audio/BUILD @@ -108,7 +108,6 @@ py_test( "//tensorboard:expect_tensorflow_installed", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:tensor_util", - "//tensorboard/util:test_util", ], ) diff --git a/tensorboard/plugins/audio/audio_plugin_test.py b/tensorboard/plugins/audio/audio_plugin_test.py index 93e1967d24..31ebe53403 100644 --- a/tensorboard/plugins/audio/audio_plugin_test.py +++ b/tensorboard/plugins/audio/audio_plugin_test.py @@ -39,7 +39,6 @@ from tensorboard.util import test_util -@test_util.run_v1_only('Uses tf.contrib in setUp via audio.summary') class AudioPluginTest(tf.test.TestCase): def setUp(self): @@ -51,40 +50,42 @@ def setUp(self): # Create old-style audio summaries for run "foo". tf.compat.v1.reset_default_graph() - sess = tf.compat.v1.Session() - placeholder = tf.compat.v1.placeholder(tf.float32) - tf.compat.v1.summary.audio(name="baz", tensor=placeholder, sample_rate=44100) - merged_summary_op = tf.compat.v1.summary.merge_all() - foo_directory = os.path.join(self.log_dir, "foo") - with test_util.FileWriterCache.get(foo_directory) as writer: - writer.add_graph(sess.graph) - for step in xrange(2): - # The floats (sample data) range from -1 to 1. - writer.add_summary(sess.run(merged_summary_op, feed_dict={ - placeholder: numpy.random.rand(42, 22050) * 2 - 1 - }), global_step=step) + with tf.compat.v1.Graph().as_default(): + sess = tf.compat.v1.Session() + placeholder = tf.compat.v1.placeholder(tf.float32) + tf.compat.v1.summary.audio(name="baz", tensor=placeholder, sample_rate=44100) + merged_summary_op = tf.compat.v1.summary.merge_all() + foo_directory = os.path.join(self.log_dir, "foo") + with test_util.FileWriterCache.get(foo_directory) as writer: + writer.add_graph(sess.graph) + for step in xrange(2): + # The floats (sample data) range from -1 to 1. + writer.add_summary(sess.run(merged_summary_op, feed_dict={ + placeholder: numpy.random.rand(42, 22050) * 2 - 1 + }), global_step=step) # Create new-style audio summaries for run "bar". tf.compat.v1.reset_default_graph() - sess = tf.compat.v1.Session() - audio_placeholder = tf.compat.v1.placeholder(tf.float32) - labels_placeholder = tf.compat.v1.placeholder(tf.string) - summary.op("quux", audio_placeholder, sample_rate=44100, - labels=labels_placeholder, - description="how do you pronounce that, anyway?") - merged_summary_op = tf.compat.v1.summary.merge_all() - bar_directory = os.path.join(self.log_dir, "bar") - with test_util.FileWriterCache.get(bar_directory) as writer: - writer.add_graph(sess.graph) - for step in xrange(2): - # The floats (sample data) range from -1 to 1. - writer.add_summary(sess.run(merged_summary_op, feed_dict={ - audio_placeholder: numpy.random.rand(42, 11025, 1) * 2 - 1, - labels_placeholder: [ - tf.compat.as_bytes('step **%s**, sample %s' % (step, sample)) - for sample in xrange(42) - ], - }), global_step=step) + with tf.compat.v1.Graph().as_default(): + sess = tf.compat.v1.Session() + audio_placeholder = tf.compat.v1.placeholder(tf.float32) + labels_placeholder = tf.compat.v1.placeholder(tf.string) + summary.op("quux", audio_placeholder, sample_rate=44100, + labels=labels_placeholder, + description="how do you pronounce that, anyway?") + merged_summary_op = tf.compat.v1.summary.merge_all() + bar_directory = os.path.join(self.log_dir, "bar") + with test_util.FileWriterCache.get(bar_directory) as writer: + writer.add_graph(sess.graph) + for step in xrange(2): + # The floats (sample data) range from -1 to 1. + writer.add_summary(sess.run(merged_summary_op, feed_dict={ + audio_placeholder: numpy.random.rand(42, 11025, 1) * 2 - 1, + labels_placeholder: [ + tf.compat.as_bytes('step **%s**, sample %s' % (step, sample)) + for sample in xrange(42) + ], + }), global_step=step) # Start a server with the plugin. multiplexer = event_multiplexer.EventMultiplexer({ diff --git a/tensorboard/plugins/audio/summary.py b/tensorboard/plugins/audio/summary.py index a0ecf8952d..6498623dde 100644 --- a/tensorboard/plugins/audio/summary.py +++ b/tensorboard/plugins/audio/summary.py @@ -91,7 +91,6 @@ def op(name, file format explicitly. """ # TODO(nickfelt): remove on-demand imports once dep situation is fixed. - import tensorflow # for contrib import tensorflow.compat.v1 as tf if display_name is None: @@ -101,9 +100,7 @@ def op(name, if encoding == 'wav': encoding = metadata.Encoding.Value('WAV') - encoder = functools.partial(tensorflow.contrib.ffmpeg.encode_audio, - samples_per_second=sample_rate, - file_format='wav') + encoder = functools.partial(tf.audio.encode_wav, sample_rate=sample_rate) else: raise ValueError('Unknown encoding: %r' % encoding) diff --git a/tensorboard/plugins/audio/summary_test.py b/tensorboard/plugins/audio/summary_test.py index f1547032b2..25fabdd54e 100644 --- a/tensorboard/plugins/audio/summary_test.py +++ b/tensorboard/plugins/audio/summary_test.py @@ -30,7 +30,6 @@ from tensorboard.plugins.audio import metadata from tensorboard.plugins.audio import summary from tensorboard.util import tensor_util -from tensorboard.util import test_util try: @@ -149,7 +148,6 @@ def test_requires_wav(self): self.audio('k488', data, 44100, encoding='pptx') -@test_util.run_v1_only('Uses tf.contrib') class SummaryV1PbTest(SummaryBaseTest, tf.test.TestCase): def setUp(self): super(SummaryV1PbTest, self).setUp() @@ -168,13 +166,12 @@ def test_requires_nonnegative_max_outputs(self): self.skipTest('summary V1 pb does not actually enforce this') -@test_util.run_v1_only('Uses tf.contrib') class SummaryV1OpTest(SummaryBaseTest, tf.test.TestCase): def setUp(self): super(SummaryV1OpTest, self).setUp() def audio(self, *args, **kwargs): - return tf.Summary.FromString(summary.op(*args, **kwargs).numpy()) + return tf.compat.v1.Summary.FromString(summary.op(*args, **kwargs).numpy()) def test_tag(self): data = np.array(1, np.float32, ndmin=3) diff --git a/tensorboard/util/BUILD b/tensorboard/util/BUILD index e322c1a477..3db15d49f0 100644 --- a/tensorboard/util/BUILD +++ b/tensorboard/util/BUILD @@ -23,7 +23,6 @@ py_test( srcs_version = "PY2AND3", deps = [ ":encoder", - ":test_util", "//tensorboard:expect_numpy_installed", "//tensorboard:expect_tensorflow_installed", "@org_pythonhosted_six", diff --git a/tensorboard/util/encoder.py b/tensorboard/util/encoder.py index 31ba34993d..130d1632d4 100644 --- a/tensorboard/util/encoder.py +++ b/tensorboard/util/encoder.py @@ -84,16 +84,14 @@ def __init__(self): def initialize_graph(self): # TODO(nickfelt): remove on-demand imports once dep situation is fixed. - import tensorflow # for contrib import tensorflow.compat.v1 as tf self._audio_placeholder = tf.placeholder( dtype=tf.float32, name='image_to_encode') self._samples_per_second_placeholder = tf.placeholder( dtype=tf.int32, name='samples_per_second') - self._encode_op = tensorflow.contrib.ffmpeg.encode_audio( + self._encode_op = tf.audio.encode_wav( self._audio_placeholder, - file_format='wav', - samples_per_second=self._samples_per_second_placeholder) + sample_rate=self._samples_per_second_placeholder) def run(self, audio, samples_per_second): # pylint: disable=arguments-differ if not isinstance(audio, np.ndarray): diff --git a/tensorboard/util/encoder_test.py b/tensorboard/util/encoder_test.py index 53c999af7a..c58916e196 100644 --- a/tensorboard/util/encoder_test.py +++ b/tensorboard/util/encoder_test.py @@ -21,7 +21,6 @@ import tensorflow as tf from tensorboard.util import encoder -from tensorboard.util import test_util class TensorFlowPngEncoderTest(tf.test.TestCase): @@ -56,7 +55,6 @@ def test_encodes_png_with_alpha(self): self._check_png(data) -@test_util.run_v1_only('Uses contrib') class TensorFlowWavEncoderTest(tf.test.TestCase): def setUp(self): From b2da803ceaae36364573780c09c52eaf07d489d9 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Thu, 15 Aug 2019 19:08:14 -0700 Subject: [PATCH 2/2] =?UTF-8?q?Make=20`plugin=5Fevent=5Faccumulator=5Ftest?= =?UTF-8?q?`=20TF=202.x=E2=80=93compatible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Depends on #2557 for compatibility of underlying audio functionality. Other than that, this test just needs to execute a bunch of things in graph mode. Parts could be converted to use more idiomatic TF 2.x style; this is a minimal fix. Makes progress toward #1705. Test Plan: Tests pass in both TF 1.x and TF 2.x, and the test file no longer uses `run_v1_only`. wchargin-branch: pea-tests-tf2x --- .../plugin_event_accumulator_test.py | 139 +++++++++--------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/tensorboard/backend/event_processing/plugin_event_accumulator_test.py b/tensorboard/backend/event_processing/plugin_event_accumulator_test.py index 4961fccf1a..99e2fe8089 100644 --- a/tensorboard/backend/event_processing/plugin_event_accumulator_test.py +++ b/tensorboard/backend/event_processing/plugin_event_accumulator_test.py @@ -121,7 +121,6 @@ def assertTagsEqual(self, actual, expected): self.assertEqual(actual[key], expected_value) -@test_util.run_v1_only('Uses v1 SummaryWriter and v1 audio summary') class MockingEventAccumulatorTest(EventAccumulatorTest): def setUp(self): @@ -358,15 +357,16 @@ def testNewStyleScalarSummary(self): event_sink = _EventGenerator(self, zero_out_timestamps=True) writer = test_util.FileWriter(self.get_temp_dir()) writer.event_writer = event_sink - with self.test_session() as sess: - step = tf.compat.v1.placeholder(tf.float32, shape=[]) - scalar_summary.op('accuracy', 1.0 - 1.0 / (step + tf.constant(1.0))) - scalar_summary.op('xent', 1.0 / (step + tf.constant(1.0))) - merged = tf.compat.v1.summary.merge_all() - writer.add_graph(sess.graph) - for i in xrange(10): - summ = sess.run(merged, feed_dict={step: float(i)}) - writer.add_summary(summ, global_step=i) + with tf.compat.v1.Graph().as_default(): + with self.test_session() as sess: + step = tf.compat.v1.placeholder(tf.float32, shape=[]) + scalar_summary.op('accuracy', 1.0 - 1.0 / (step + tf.constant(1.0))) + scalar_summary.op('xent', 1.0 / (step + tf.constant(1.0))) + merged = tf.compat.v1.summary.merge_all() + writer.add_graph(sess.graph) + for i in xrange(10): + summ = sess.run(merged, feed_dict={step: float(i)}) + writer.add_summary(summ, global_step=i) accumulator = ea.EventAccumulator(event_sink) accumulator.Reload() @@ -387,19 +387,20 @@ def testNewStyleAudioSummary(self): event_sink = _EventGenerator(self, zero_out_timestamps=True) writer = test_util.FileWriter(self.get_temp_dir()) writer.event_writer = event_sink - with self.test_session() as sess: - ipt = tf.random.normal(shape=[5, 441, 2]) - with tf.name_scope('1'): - audio_summary.op('one', ipt, sample_rate=44100, max_outputs=1) - with tf.name_scope('2'): - audio_summary.op('two', ipt, sample_rate=44100, max_outputs=2) - with tf.name_scope('3'): - audio_summary.op('three', ipt, sample_rate=44100, max_outputs=3) - merged = tf.compat.v1.summary.merge_all() - writer.add_graph(sess.graph) - for i in xrange(10): - summ = sess.run(merged) - writer.add_summary(summ, global_step=i) + with tf.compat.v1.Graph().as_default(): + with self.test_session() as sess: + ipt = tf.random.normal(shape=[5, 441, 2]) + with tf.name_scope('1'): + audio_summary.op('one', ipt, sample_rate=44100, max_outputs=1) + with tf.name_scope('2'): + audio_summary.op('two', ipt, sample_rate=44100, max_outputs=2) + with tf.name_scope('3'): + audio_summary.op('three', ipt, sample_rate=44100, max_outputs=3) + merged = tf.compat.v1.summary.merge_all() + writer.add_graph(sess.graph) + for i in xrange(10): + summ = sess.run(merged) + writer.add_summary(summ, global_step=i) accumulator = ea.EventAccumulator(event_sink) accumulator.Reload() @@ -421,23 +422,24 @@ def testNewStyleImageSummary(self): event_sink = _EventGenerator(self, zero_out_timestamps=True) writer = test_util.FileWriter(self.get_temp_dir()) writer.event_writer = event_sink - with self.test_session() as sess: - ipt = tf.ones([10, 4, 4, 3], tf.uint8) - # This is an interesting example, because the old tf.image_summary op - # would throw an error here, because it would be tag reuse. - # Using the tf node name instead allows argument re-use to the image - # summary. - with tf.name_scope('1'): - image_summary.op('images', ipt, max_outputs=1) - with tf.name_scope('2'): - image_summary.op('images', ipt, max_outputs=2) - with tf.name_scope('3'): - image_summary.op('images', ipt, max_outputs=3) - merged = tf.compat.v1.summary.merge_all() - writer.add_graph(sess.graph) - for i in xrange(10): - summ = sess.run(merged) - writer.add_summary(summ, global_step=i) + with tf.compat.v1.Graph().as_default(): + with self.test_session() as sess: + ipt = tf.ones([10, 4, 4, 3], tf.uint8) + # This is an interesting example, because the old tf.image_summary op + # would throw an error here, because it would be tag reuse. + # Using the tf node name instead allows argument re-use to the image + # summary. + with tf.name_scope('1'): + image_summary.op('images', ipt, max_outputs=1) + with tf.name_scope('2'): + image_summary.op('images', ipt, max_outputs=2) + with tf.name_scope('3'): + image_summary.op('images', ipt, max_outputs=3) + merged = tf.compat.v1.summary.merge_all() + writer.add_graph(sess.graph) + for i in xrange(10): + summ = sess.run(merged) + writer.add_summary(summ, global_step=i) accumulator = ea.EventAccumulator(event_sink) accumulator.Reload() @@ -459,13 +461,15 @@ def testTFSummaryTensor(self): event_sink = _EventGenerator(self, zero_out_timestamps=True) writer = test_util.FileWriter(self.get_temp_dir()) writer.event_writer = event_sink - with self.test_session() as sess: - tf.compat.v1.summary.tensor_summary('scalar', tf.constant(1.0)) - tf.compat.v1.summary.tensor_summary('vector', tf.constant([1.0, 2.0, 3.0])) - tf.compat.v1.summary.tensor_summary('string', tf.constant(six.b('foobar'))) - merged = tf.compat.v1.summary.merge_all() - summ = sess.run(merged) - writer.add_summary(summ, 0) + with tf.compat.v1.Graph().as_default(): + with self.test_session() as sess: + tensor_summary = tf.compat.v1.summary.tensor_summary + tensor_summary('scalar', tf.constant(1.0)) + tensor_summary('vector', tf.constant([1.0, 2.0, 3.0])) + tensor_summary('string', tf.constant(six.b('foobar'))) + merged = tf.compat.v1.summary.merge_all() + summ = sess.run(merged) + writer.add_summary(summ, 0) accumulator = ea.EventAccumulator(event_sink) accumulator.Reload() @@ -493,15 +497,16 @@ def _testTFSummaryTensor_SizeGuidance(self, event_sink = _EventGenerator(self, zero_out_timestamps=True) writer = test_util.FileWriter(self.get_temp_dir()) writer.event_writer = event_sink - with self.test_session() as sess: - summary_metadata = summary_pb2.SummaryMetadata( - plugin_data=summary_pb2.SummaryMetadata.PluginData( - plugin_name=plugin_name, content=b'{}')) - tf.compat.v1.summary.tensor_summary('scalar', tf.constant(1.0), - summary_metadata=summary_metadata) - merged = tf.compat.v1.summary.merge_all() - for step in xrange(steps): - writer.add_summary(sess.run(merged), global_step=step) + with tf.compat.v1.Graph().as_default(): + with self.test_session() as sess: + summary_metadata = summary_pb2.SummaryMetadata( + plugin_data=summary_pb2.SummaryMetadata.PluginData( + plugin_name=plugin_name, content=b'{}')) + tf.compat.v1.summary.tensor_summary('scalar', tf.constant(1.0), + summary_metadata=summary_metadata) + merged = tf.compat.v1.summary.merge_all() + for step in xrange(steps): + writer.add_summary(sess.run(merged), global_step=step) accumulator = ea.EventAccumulator( @@ -548,7 +553,6 @@ def testTFSummaryTensor_SizeGuidance_IgnoreIrrelevantGuidances(self): expected_count=size_small) -@test_util.run_v1_only('Uses contrib and v1 SummaryWriter') class RealisticEventAccumulatorTest(EventAccumulatorTest): def testTensorsRealistically(self): @@ -568,11 +572,11 @@ def FakeScalarSummary(tag, value): with tf.Graph().as_default() as graph: _ = tf.constant([2.0, 1.0]) - # Add a graph to the summary writer. - writer.add_graph(graph) - meta_graph_def = tf.compat.v1.train.export_meta_graph(graph_def=graph.as_graph_def( - add_shapes=True)) - writer.add_meta_graph(meta_graph_def) + # Add a graph to the summary writer. + writer.add_graph(graph) + graph_def = graph.as_graph_def(add_shapes=True) + meta_graph_def = tf.compat.v1.train.export_meta_graph(graph_def=graph_def) + writer.add_meta_graph(meta_graph_def) run_metadata = config_pb2.RunMetadata() device_stats = run_metadata.step_stats.dev_stats.add() @@ -646,12 +650,11 @@ def testGraphFromMetaGraphBecomesAvailable(self): with tf.Graph().as_default() as graph: _ = tf.constant([2.0, 1.0]) - # Add a graph to the summary writer. - meta_graph_def = tf.compat.v1.train.export_meta_graph(graph_def=graph.as_graph_def( - add_shapes=True)) - writer.add_meta_graph(meta_graph_def) - - writer.flush() + # Add a graph to the summary writer. + graph_def = graph.as_graph_def(add_shapes=True) + meta_graph_def = tf.compat.v1.train.export_meta_graph(graph_def=graph_def) + writer.add_meta_graph(meta_graph_def) + writer.flush() # Verify that we can load those events properly acc = ea.EventAccumulator(directory)