• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""End-to-end tests for a variety of small models."""
16
17import collections
18import itertools
19
20from absl.testing import parameterized
21import numpy as np
22
23from tensorflow.python import keras
24from tensorflow.python.data.ops import dataset_ops
25from tensorflow.python.eager import context
26from tensorflow.python.framework import ops
27from tensorflow.python.keras import keras_parameterized
28from tensorflow.python.keras import testing_utils
29from tensorflow.python.ops import math_ops
30from tensorflow.python.platform import test
31
32
33def _conv2d_filter(**kwargs):
34  """Convolution with non-default strides and dilation rate is not supported."""
35  return kwargs['strides'] <= 1 or kwargs['dilation_rate'] <= 1
36
37
38# Scheme: (layer_class, data_shape, fuzz_dims, constructor_args, filter_fn)
39#   layer_class:
40#     A keras Layer class to be tested.
41#   data_shape:
42#     The shape of the input data. (not including batch dim)
43#   fuzz_dims:
44#     Dimensions which can be unspecified during model construction. For
45#     instance, if data_shape is (2, 5) and fuzz_dims is (False, True), a pass
46#     with model input shape of (2, None) will also be performed.
47#   constructor_args:
48#     An OrderedDict (to ensure consistent test names) with a key and a list
49#     of values to test. Test cases will be generated for the Cartesian product
50#     of all constructor args, so adding more fields can cause the drastically
51#     increase the testing load.
52#   filter_fn:
53#     If not None, this function will be called on each set of generated
54#     constructor args, and prevents generation of contradictory combinations.
55#     A True return value indicates a valid test.
56_LAYERS_TO_TEST = [
57    (keras.layers.Dense, (1,), (False,), collections.OrderedDict([
58        ('units', [1])]), None),
59    (keras.layers.Activation, (2, 2), (True, True), collections.OrderedDict([
60        ('activation', ['relu'])]), None),
61    (keras.layers.Dropout, (16,), (False,), collections.OrderedDict([
62        ('rate', [0.25])]), None),
63    (keras.layers.BatchNormalization, (8, 8, 3), (True, True, False),
64     collections.OrderedDict([
65         ('axis', [3]),
66         ('center', [True, False]),
67         ('scale', [True, False])
68     ]), None),
69    (keras.layers.Conv1D, (8, 8), (False, False), collections.OrderedDict([
70        ('filters', [1]),
71        ('kernel_size', [1, 3]),
72        ('strides', [1, 2]),
73        ('padding', ['valid', 'same']),
74        ('use_bias', [True]),
75        ('kernel_regularizer', ['l2']),
76        ('data_format', ['channels_last'])
77    ]), None),
78    (keras.layers.Conv2D, (8, 8, 3), (True, True, False),
79     collections.OrderedDict([
80         ('filters', [1]),
81         ('kernel_size', [1, 3]),
82         ('strides', [1, 2]),
83         ('padding', ['valid', 'same']),
84         ('use_bias', [True, False]),
85         ('kernel_regularizer', ['l2']),
86         ('dilation_rate', [1, 2]),
87         ('data_format', ['channels_last'])
88     ]), _conv2d_filter),
89    (keras.layers.LSTM, (4, 4), (False, False), collections.OrderedDict([
90        ('units', [1]),
91        ('kernel_regularizer', ['l2']),
92        ('dropout', [0, 0.5]),
93        ('stateful', [True, False]),
94        ('unroll', [True, False]),
95        ('return_sequences', [True, False])
96    ]), None),
97]
98
99
100def _gather_test_cases():
101  cases = []
102  for layer_type, inp_shape, fuzz_dims, arg_dict, filter_fn in _LAYERS_TO_TEST:
103    arg_combinations = [[(k, i) for i in v] for k, v in arg_dict.items()]  # pylint: disable=g-complex-comprehension
104    for arguments in itertools.product(*arg_combinations):
105      layer_kwargs = {k: v for k, v in arguments}
106      if filter_fn is not None and not filter_fn(**layer_kwargs):
107        continue
108
109      name = '_{}_{}'.format(layer_type.__name__,
110                             '_'.join('{}_{}'.format(*i) for i in arguments))
111      cases.append((name, layer_type, inp_shape, fuzz_dims, layer_kwargs))
112  return cases
113
114
115OUTPUT_TEST_CASES = _gather_test_cases()
116
117
118class CoreLayerIntegrationTest(keras_parameterized.TestCase):
119  """Test that layers and models produce the correct tensor types."""
120
121  # In v1 graph there are only symbolic tensors.
122  @keras_parameterized.run_all_keras_modes(always_skip_v1=True)
123  @parameterized.named_parameters(*OUTPUT_TEST_CASES)
124  def test_layer_output_type(self, layer_to_test, input_shape, _, layer_kwargs):
125    layer = layer_to_test(**layer_kwargs)
126
127    input_data = np.ones(shape=(2,) + input_shape, dtype=np.float32)
128    layer_result = layer(input_data)
129
130    inp = keras.layers.Input(shape=input_shape, batch_size=2)
131    model = keras.models.Model(inp, layer_to_test(**layer_kwargs)(inp))
132    model_result = model(input_data)
133
134    for x in [layer_result, model_result]:
135      if not isinstance(x, ops.Tensor):
136        raise ValueError('Tensor or EagerTensor expected, got type {}'
137                         .format(type(x)))
138
139      if isinstance(x, ops.EagerTensor) != context.executing_eagerly():
140        expected_type = (ops.EagerTensor if context.executing_eagerly()
141                         else ops.Tensor)
142        raise ValueError('Expected type {}, got type {}'
143                         .format(expected_type, type(x)))
144
145  def _run_fit_eval_predict(self, layer_to_test, input_shape, data_shape,
146                            layer_kwargs):
147    batch_size = 2
148    run_eagerly = testing_utils.should_run_eagerly()
149
150    def map_fn(_):
151      x = keras.backend.random_uniform(shape=data_shape)
152      y = keras.backend.random_uniform(shape=(1,))
153      return x, y
154
155    dataset = dataset_ops.DatasetV2.range(4).map(map_fn).batch(batch_size)
156
157    inp = keras.layers.Input(shape=input_shape, batch_size=batch_size)
158    layer = layer_to_test(**layer_kwargs)(inp)
159
160    # Condense the output down to a single scalar.
161    layer = keras.layers.Flatten()(layer)
162    layer = keras.layers.Lambda(
163        lambda x: math_ops.reduce_mean(x, keepdims=True))(layer)
164    layer = keras.layers.Dense(1, activation=None)(layer)
165    model = keras.models.Model(inp, layer)
166
167    model.compile(loss='mse', optimizer='sgd', run_eagerly=run_eagerly)
168    model.fit(dataset, verbose=2, epochs=2)
169
170    model.compile(loss='mse', optimizer='sgd', run_eagerly=run_eagerly)
171    model.fit(dataset.repeat(2), verbose=2, epochs=2, steps_per_epoch=2)
172
173    eval_dataset = dataset_ops.DatasetV2.range(4).map(map_fn).batch(batch_size)
174    model.evaluate(eval_dataset, verbose=2)
175
176    def pred_map_fn(_):
177      return keras.backend.random_uniform(shape=data_shape)
178
179    pred_dataset = dataset_ops.DatasetV2.range(4)
180    pred_dataset = pred_dataset.map(pred_map_fn).batch(batch_size)
181    model.predict(pred_dataset, verbose=2)
182
183  @keras_parameterized.run_all_keras_modes(always_skip_v1=False)
184  @parameterized.named_parameters(*OUTPUT_TEST_CASES)
185  def test_model_loops(self, layer_to_test, input_shape, fuzz_dims,
186                       layer_kwargs):
187    self._run_fit_eval_predict(layer_to_test, input_shape,
188                               input_shape, layer_kwargs)
189
190    if any(fuzz_dims):
191      fuzzed_shape = []
192      for dim, should_fuzz in zip(input_shape, fuzz_dims):
193        fuzzed_shape.append(None if should_fuzz else dim)
194
195      self._run_fit_eval_predict(layer_to_test, fuzzed_shape,
196                                 input_shape, layer_kwargs)
197
198
199if __name__ == '__main__':
200  test.main()
201