• 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
16"""Generate a series of TensorFlow graphs that become tflite test cases.
17
18Usage:
19
20generate_examples <output directory>
21
22bazel run //tensorflow/lite/testing:generate_examples
23
24To more easily debug failures use (or override) the --save_graphdefs flag to
25place text proto graphdefs into the generated zip files.
26"""
27
28from __future__ import absolute_import
29from __future__ import division
30from __future__ import print_function
31
32import copy
33import datetime
34import os
35import re
36import zipfile
37
38import tensorflow.compat.v1 as tf
39
40# TODO(aselle): Disable GPU for now
41os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
42
43# pylint: disable=g-import-not-at-top
44# pylint: disable=g-multiple-import
45# pylint: disable=unused-import
46from tensorflow.lite.testing.op_tests.abs import make_abs_tests
47from tensorflow.lite.testing.op_tests.add_n import make_add_n_tests
48from tensorflow.lite.testing.op_tests.arg_min_max import make_arg_min_max_tests
49from tensorflow.lite.testing.op_tests.batch_to_space_nd import make_batch_to_space_nd_tests
50from tensorflow.lite.testing.op_tests.binary_op import make_add_tests, make_div_tests, make_sub_tests, make_mul_tests, make_pow_tests, make_floor_div_tests, make_floor_mod_tests, make_squared_difference_tests
51from tensorflow.lite.testing.op_tests.cast import make_cast_tests
52from tensorflow.lite.testing.op_tests.ceil import make_ceil_tests
53from tensorflow.lite.testing.op_tests.concat import make_concat_tests
54from tensorflow.lite.testing.op_tests.constant import make_constant_tests
55from tensorflow.lite.testing.op_tests.conv import make_conv_tests
56from tensorflow.lite.testing.op_tests.conv2d_transpose import make_conv2d_transpose_tests
57from tensorflow.lite.testing.op_tests.conv_activation import make_conv_relu_tests, make_conv_relu1_tests, make_conv_relu6_tests
58# Note: This is a regression test for a bug (b/112303004) that Toco incorrectly
59# transforms Conv into DepthwiseConv when two Conv ops share the same constant
60# weight tensor.
61from tensorflow.lite.testing.op_tests.conv_to_depthwiseconv_with_shared_weights import make_conv_to_depthwiseconv_with_shared_weights_tests
62# Note: This is a regression test for a bug (b/112436267) that Toco incorrectly
63# fuses weights when multiple Conv2D/FULLY_CONNECTED ops share the same constant
64# weight tensor.
65from tensorflow.lite.testing.op_tests.conv_with_shared_weights import make_conv_with_shared_weights_tests
66from tensorflow.lite.testing.op_tests.cos import make_cos_tests
67from tensorflow.lite.testing.op_tests.depth_to_space import make_depth_to_space_tests
68from tensorflow.lite.testing.op_tests.depthwiseconv import make_depthwiseconv_tests
69from tensorflow.lite.testing.op_tests.elementwise import make_sin_tests, make_log_tests, make_sqrt_tests, make_rsqrt_tests, make_square_tests
70from tensorflow.lite.testing.op_tests.elu import make_elu_tests
71from tensorflow.lite.testing.op_tests.embedding_lookup import make_embedding_lookup_tests
72from tensorflow.lite.testing.op_tests.equal import make_equal_tests
73from tensorflow.lite.testing.op_tests.exp import make_exp_tests
74from tensorflow.lite.testing.op_tests.expand_dims import make_expand_dims_tests
75from tensorflow.lite.testing.op_tests.eye import make_eye_tests
76from tensorflow.lite.testing.op_tests.fill import make_fill_tests
77from tensorflow.lite.testing.op_tests.floor import make_floor_tests
78from tensorflow.lite.testing.op_tests.fully_connected import make_fully_connected_tests
79from tensorflow.lite.testing.op_tests.fused_batch_norm import make_fused_batch_norm_tests
80from tensorflow.lite.testing.op_tests.gather import make_gather_tests
81from tensorflow.lite.testing.op_tests.gather_nd import make_gather_nd_tests
82from tensorflow.lite.testing.op_tests.gather_with_constant import make_gather_with_constant_tests
83from tensorflow.lite.testing.op_tests.global_batch_norm import make_global_batch_norm_tests
84from tensorflow.lite.testing.op_tests.greater import make_greater_tests
85from tensorflow.lite.testing.op_tests.greater_equal import make_greater_equal_tests
86from tensorflow.lite.testing.op_tests.hardswish import make_hardswish_tests
87from tensorflow.lite.testing.op_tests.identity import make_identity_tests
88from tensorflow.lite.testing.op_tests.l2norm import make_l2norm_tests
89# Note: This is a regression test for a bug (b/122651451) that Toco incorrectly
90# erases the reduction indices array while it's shared with other ops.
91from tensorflow.lite.testing.op_tests.l2norm_shared_epsilon import make_l2norm_shared_epsilon_tests
92from tensorflow.lite.testing.op_tests.leaky_relu import make_leaky_relu_tests
93from tensorflow.lite.testing.op_tests.less import make_less_tests
94from tensorflow.lite.testing.op_tests.less_equal import make_less_equal_tests
95from tensorflow.lite.testing.op_tests.local_response_norm import make_local_response_norm_tests
96from tensorflow.lite.testing.op_tests.log_softmax import make_log_softmax_tests
97from tensorflow.lite.testing.op_tests.logic import make_logical_or_tests, make_logical_and_tests, make_logical_xor_tests
98from tensorflow.lite.testing.op_tests.lstm import make_lstm_tests
99from tensorflow.lite.testing.op_tests.matrix_diag import make_matrix_diag_tests
100from tensorflow.lite.testing.op_tests.matrix_set_diag import make_matrix_set_diag_tests
101from tensorflow.lite.testing.op_tests.maximum import make_maximum_tests
102from tensorflow.lite.testing.op_tests.minimum import make_minimum_tests
103from tensorflow.lite.testing.op_tests.mirror_pad import make_mirror_pad_tests
104from tensorflow.lite.testing.op_tests.nearest_upsample import make_nearest_upsample_tests
105from tensorflow.lite.testing.op_tests.neg import make_neg_tests
106from tensorflow.lite.testing.op_tests.not_equal import make_not_equal_tests
107from tensorflow.lite.testing.op_tests.one_hot import make_one_hot_tests
108from tensorflow.lite.testing.op_tests.pack import make_pack_tests
109from tensorflow.lite.testing.op_tests.pad import make_pad_tests
110from tensorflow.lite.testing.op_tests.padv2 import make_padv2_tests
111from tensorflow.lite.testing.op_tests.placeholder_with_default import make_placeholder_with_default_tests
112from tensorflow.lite.testing.op_tests.pool import make_l2_pool_tests, make_avg_pool_tests, make_max_pool_tests
113from tensorflow.lite.testing.op_tests.prelu import make_prelu_tests
114from tensorflow.lite.testing.op_tests.range import make_range_tests
115from tensorflow.lite.testing.op_tests.rank import make_rank_tests
116from tensorflow.lite.testing.op_tests.reduce import make_mean_tests, make_sum_tests, make_reduce_prod_tests, make_reduce_max_tests, make_reduce_min_tests, make_reduce_any_tests
117from tensorflow.lite.testing.op_tests.relu import make_relu_tests
118from tensorflow.lite.testing.op_tests.relu1 import make_relu1_tests
119from tensorflow.lite.testing.op_tests.relu6 import make_relu6_tests
120from tensorflow.lite.testing.op_tests.reshape import make_reshape_tests
121from tensorflow.lite.testing.op_tests.resize_bilinear import make_resize_bilinear_tests
122from tensorflow.lite.testing.op_tests.resize_nearest_neighbor import make_resize_nearest_neighbor_tests
123# For verifying https://github.com/tensorflow/tensorflow/issues/23599
124from tensorflow.lite.testing.op_tests.resolve_constant_strided_slice import make_resolve_constant_strided_slice_tests
125from tensorflow.lite.testing.op_tests.reverse_sequence import make_reverse_sequence_tests
126from tensorflow.lite.testing.op_tests.reverse_v2 import make_reverse_v2_tests
127from tensorflow.lite.testing.op_tests.round import make_round_tests
128from tensorflow.lite.testing.op_tests.scatter_nd import make_scatter_nd_tests
129from tensorflow.lite.testing.op_tests.shape import make_shape_tests
130from tensorflow.lite.testing.op_tests.sigmoid import make_sigmoid_tests
131from tensorflow.lite.testing.op_tests.slice import make_slice_tests
132from tensorflow.lite.testing.op_tests.softmax import make_softmax_tests
133from tensorflow.lite.testing.op_tests.space_to_batch_nd import make_space_to_batch_nd_tests
134from tensorflow.lite.testing.op_tests.space_to_depth import make_space_to_depth_tests
135from tensorflow.lite.testing.op_tests.sparse_to_dense import make_sparse_to_dense_tests
136from tensorflow.lite.testing.op_tests.split import make_split_tests
137from tensorflow.lite.testing.op_tests.splitv import make_splitv_tests
138from tensorflow.lite.testing.op_tests.squeeze import make_squeeze_tests
139from tensorflow.lite.testing.op_tests.squeeze_transpose import make_squeeze_transpose_tests
140from tensorflow.lite.testing.op_tests.strided_slice import make_strided_slice_tests, make_strided_slice_1d_exhaustive_tests
141from tensorflow.lite.testing.op_tests.strided_slice_np_style import make_strided_slice_np_style_tests
142from tensorflow.lite.testing.op_tests.tanh import make_tanh_tests
143from tensorflow.lite.testing.op_tests.tile import make_tile_tests
144from tensorflow.lite.testing.op_tests.topk import make_topk_tests
145from tensorflow.lite.testing.op_tests.transpose import make_transpose_tests
146from tensorflow.lite.testing.op_tests.transpose_conv import make_transpose_conv_tests
147from tensorflow.lite.testing.op_tests.unfused_gru import make_unfused_gru_tests
148from tensorflow.lite.testing.op_tests.unidirectional_sequence_lstm import make_unidirectional_sequence_lstm_tests
149from tensorflow.lite.testing.op_tests.unidirectional_sequence_rnn import make_unidirectional_sequence_rnn_tests
150from tensorflow.lite.testing.op_tests.unique import make_unique_tests
151from tensorflow.lite.testing.op_tests.unpack import make_unpack_tests
152from tensorflow.lite.testing.op_tests.unroll_batch_matmul import make_unroll_batch_matmul_tests
153from tensorflow.lite.testing.op_tests.where import make_where_tests
154from tensorflow.lite.testing.op_tests.zeros_like import make_zeros_like_tests
155
156from tensorflow.lite.testing.zip_test_utils import get_test_function
157
158# A map from regular expression to bug number. Any test failure with label
159# matching the expression will be considered due to the corresponding bug.
160KNOWN_BUGS = {
161    # TOCO doesn't support scalars as input.
162    # Concat doesn't work with a single input tensor
163    r"concat.*num_tensors=1": "67378344",
164    # Softmax graphs are too complex.
165    r"softmax.*dim=0": "67749831",
166    # BatchToSpaceND only supports 4D tensors.
167    r"batch_to_space_nd.*input_shape=\[8,2,2,2,1,1\]": "70594733",
168    # Div will use floordiv.
169    r"div.*int32": "72051395",
170    # Strided slice cannot handle new_axis_mask.
171    r"strided_slice.*spec=\[None": "137470173",
172}
173
174
175class MultiGenState(object):
176  """State of multiple set generation process.
177
178  This state class stores the information needed when generating the examples
179  for multiple test set. The stored informations are open archive object to be
180  shared, information on test target for current iteration of generation,
181  accumulated generation results.
182  """
183
184  def __init__(self):
185    # Open archive.
186    self.archive = None
187    # Test name for current generation.
188    self.test_name = None
189    # Label base path containing the test name.
190    # Each of the test data path in the zip archive is derived from this path.
191    # If this path is "a/b/c/d.zip", an example of generated test data path
192    # is "a/b/c/d_input_type=tf.float32,input_shape=[2,2].inputs".
193    # The test runner interpretes the test name of this path as "d".
194    # Label base path also should finish with ".zip".
195    self.label_base_path = None
196    # Zip manifests.
197    self.zip_manifest = []
198    # Number of all parameters accumulated.
199    self.parameter_count = 0
200
201
202class Options(object):
203  """All options for example generation."""
204
205  def __init__(self):
206    # Directory where the outputs will be go.
207    self.output_path = None
208    # Particular zip to output.
209    self.zip_to_output = None
210    # Path to toco tool.
211    self.toco = None
212    # If a particular model is affected by a known bug count it as a Toco
213    # error.
214    self.known_bugs_are_errors = False
215    # Raise an exception if any converter error is encountered.
216    self.ignore_converter_errors = False
217    # Include intermediate graphdefs in the output zip files.
218    self.save_graphdefs = False
219    # Whether the TFLite Flex converter is being used.
220    self.run_with_flex = False
221    # Whether to generate test cases for edgetpu.
222    self.make_edgetpu_tests = False
223    # The function to convert a TensorFLow model to TFLite model.
224    # See the document for `toco_convert` function for its required signature.
225    self.tflite_convert_function = None
226    # A map from regular expression to bug number. Any test failure with label
227    # matching the expression will be considered due to the corresponding bug.
228    self.known_bugs = KNOWN_BUGS
229    # Make tests by setting TF forward compatibility horizon to the future.
230    self.make_forward_compat_test = False
231    # No limitation on the number of tests.
232    self.no_tests_limit = False
233    # Do not create conversion report.
234    self.no_conversion_report = False
235    # State of multiple test set generation. This stores state values those
236    # should be kept and updated while generating examples over multiple
237    # test sets.
238    # TODO(juhoha): Separate the state from the options.
239    self.multi_gen_state = None
240    self.use_experimental_converter = False
241    self.mlir_quantizer = False
242    # The list of ops' name that should exist in the converted model.
243    # This feature is currently only supported in MLIR conversion path.
244    # Example of supported ops' name:
245    # - "AVERAGE_POOL_2D" for builtin op.
246    # - "NumericVerify" for custom op.
247    self.expected_ops_in_converted_model = []
248
249
250def _prepare_dir(options):
251
252  def mkdir_if_not_exist(x):
253    if not os.path.isdir(x):
254      os.mkdir(x)
255      if not os.path.isdir(x):
256        raise RuntimeError("Failed to create dir %r" % x)
257
258  opstest_path = os.path.join(options.output_path)
259  mkdir_if_not_exist(opstest_path)
260
261
262def generate_examples(options):
263  """Generate examples for a test set.
264
265  Args:
266    options: Options containing information to generate examples.
267
268  Raises:
269    RuntimeError: if the test function cannot be found.
270  """
271  _prepare_dir(options)
272
273  out = options.zip_to_output
274  # Some zip filenames contain a postfix identifying the conversion mode. The
275  # list of valid conversion modes is defined in
276  # generated_test_conversion_modes() in build_def.bzl.
277
278  if options.multi_gen_state:
279    test_name = options.multi_gen_state.test_name
280  else:
281    # Remove suffixes to extract the test name from the output name.
282    test_name = re.sub(
283        r"(_(|toco-flex|forward-compat|edgetpu|mlir-quant))?\.zip$",
284        "",
285        out,
286        count=1)
287
288  test_function_name = "make_%s_tests" % test_name
289  test_function = get_test_function(test_function_name)
290  if test_function is None:
291    raise RuntimeError("Can't find a test function to create %r. Tried %r" %
292                       (out, test_function_name))
293  if options.make_forward_compat_test:
294    future_date = datetime.date.today() + datetime.timedelta(days=30)
295    with tf.compat.forward_compatibility_horizon(future_date.year,
296                                                 future_date.month,
297                                                 future_date.day):
298      test_function(options)
299  else:
300    test_function(options)
301
302
303def generate_multi_set_examples(options, test_sets):
304  """Generate examples for test sets.
305
306  Args:
307    options: Options containing information to generate examples.
308    test_sets: List of the name of test sets to generate examples.
309  """
310  _prepare_dir(options)
311
312  multi_gen_state = MultiGenState()
313  options.multi_gen_state = multi_gen_state
314
315  zip_path = os.path.join(options.output_path, options.zip_to_output)
316  with zipfile.PyZipFile(zip_path, "w") as archive:
317    multi_gen_state.archive = archive
318
319    for test_name in test_sets:
320      # Some generation function can change the value of the options object.
321      # To keep the original options for each run, we use shallow copy.
322      new_options = copy.copy(options)
323
324      # Remove suffix and set test_name to run proper test generation function.
325      multi_gen_state.test_name = re.sub(
326          r"(_(|toco-flex|forward-compat|mlir-quant))?$",
327          "",
328          test_name,
329          count=1)
330      # Set label base path to write test data files with proper path.
331      multi_gen_state.label_base_path = os.path.join(
332          os.path.dirname(zip_path), test_name + ".zip")
333
334      generate_examples(new_options)
335
336    archive.writestr("manifest.txt", "".join(multi_gen_state.zip_manifest),
337                     zipfile.ZIP_DEFLATED)
338