From 6a365818d427f1778b1154a0507a963a3e8f8b58 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 24 Jun 2016 09:41:12 -0800 Subject: [PATCH] Add repeat meta-layer. Change: 125794105 --- .../contrib/layers/python/layers/layers.py | 53 +++++++++++++++++-- .../layers/python/layers/layers_test.py | 22 ++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/tensorflow/contrib/layers/python/layers/layers.py b/tensorflow/contrib/layers/python/layers/layers.py index 4d013c66003..aa50daf3b0a 100644 --- a/tensorflow/contrib/layers/python/layers/layers.py +++ b/tensorflow/contrib/layers/python/layers/layers.py @@ -53,6 +53,7 @@ __all__ = ['avg_pool2d', 'one_hot_encoding', 'relu', 'relu6', + 'repeat', 'stack', 'legacy_fully_connected', 'legacy_linear', @@ -630,6 +631,51 @@ def _apply_activation(y, activation_fn, output_collections): return y +def repeat(inputs, repetitions, layer, *args, **kwargs): + """Applies the same layer with the same arguments repeatedly. + + ```python + y = repeat(x, 3, conv2d, 64, [3, 3], scope='conv1') + # It is equivalent to: + + x = conv2d(x, 64, [3, 3], scope='conv1/conv1_1') + x = conv2d(x, 64, [3, 3], scope='conv1/conv1_2') + y = conv2d(x, 64, [3, 3], scope='conv1/conv1_3') + ``` + + If the `scope` argument is not given in `kwargs`, it is set to + `layer.__name__`, or `layer.func.__name__` (for `functools.partial` + objects). If neither `__name__` nor `func.__name__` is available, the + layers are called with `scope='stack'`. + + Args: + inputs: A `Tensor` suitable for layer. + repetitions: Int, number of repetitions. + layer: A layer with arguments `(inputs, *args, **kwargs)` + *args: Extra args for the layer. + **kwargs: Extra kwargs for the layer. + + Returns: + a tensor result of applying the layer, repetitions times. + Raises: + ValueError: if the op is unknown or wrong. + """ + scope = kwargs.pop('scope', None) + with variable_scope.variable_op_scope([inputs], scope, 'Repeat'): + outputs = inputs + if scope is None: + if hasattr(layer, '__name__'): + scope = layer.__name__ + elif hasattr(layer, 'func') and hasattr(layer.func, '__name__'): + scope = layer.func.__name__ # In case layer is a functools.partial. + else: + scope = 'repeat' + for i in range(repetitions): + kwargs['scope'] = scope + '_' + str(i+1) + outputs = layer(outputs, *args, **kwargs) + return outputs + + def stack(inputs, layer, stack_args, **kwargs): """Builds a stack of layers by applying layer repeatedly using stack_args. @@ -638,15 +684,15 @@ def stack(inputs, layer, stack_args, **kwargs): a new scope appended with an increasing number. For example: ```python - stack(x, fully_connected, [32, 64, 128], scope='fc') + y = stack(x, fully_connected, [32, 64, 128], scope='fc') # It is equivalent to: x = fully_connected(x, 32, scope='fc/fc_1') x = fully_connected(x, 64, scope='fc/fc_2') - x = fully_connected(x, 128, scope='fc/fc_3') + y = fully_connected(x, 128, scope='fc/fc_3') ``` - If the `scope` argument is not given in `stack_args`, it is set to + If the `scope` argument is not given in `kwargs`, it is set to `layer.__name__`, or `layer.func.__name__` (for `functools.partial` objects). If neither `__name__` nor `func.__name__` is available, the layers are called with `scope='stack'`. @@ -668,7 +714,6 @@ def stack(inputs, layer, stack_args, **kwargs): raise ValueError('stack_args need to be a list or tuple') with variable_scope.variable_op_scope([inputs], scope, 'Stack'): outputs = inputs - scope = scope if scope is None: if hasattr(layer, '__name__'): scope = layer.__name__ diff --git a/tensorflow/contrib/layers/python/layers/layers_test.py b/tensorflow/contrib/layers/python/layers/layers_test.py index 36eddf4f4e3..095ab8252cf 100644 --- a/tensorflow/contrib/layers/python/layers/layers_test.py +++ b/tensorflow/contrib/layers/python/layers/layers_test.py @@ -897,6 +897,28 @@ class OneHotEncodingTest(tf.test.TestCase): self.assertAllClose(output.eval(), one_hot_labels.eval()) +class RepeatTests(tf.test.TestCase): + + def testRepeat(self): + height, width = 3, 3 + with self.test_session(): + images = tf.random_uniform((5, height, width, 3), seed=1, name='images') + output = tf.contrib.layers.repeat(images, 3, + tf.contrib.layers.conv2d, 32, [3, 3]) + self.assertEquals(output.op.name, 'Repeat/convolution2d_3/Relu') + self.assertListEqual(output.get_shape().as_list(), [5, 3, 3, 32]) + + def testRepeatWithScope(self): + height, width = 3, 3 + with self.test_session(): + images = tf.random_uniform((5, height, width, 3), seed=1, name='images') + output = tf.contrib.layers.repeat(images, 3, + tf.contrib.layers.conv2d, 32, [3, 3], + scope='conv1') + self.assertEquals(output.op.name, 'conv1/conv1_3/Relu') + self.assertListEqual(output.get_shape().as_list(), [5, 3, 3, 32]) + + class StackTests(tf.test.TestCase): def testStackFullyConnected(self): -- GitLab