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, make_reduce_all_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.unique import make_unique_tests 149from tensorflow.lite.testing.op_tests.unpack import make_unpack_tests 150from tensorflow.lite.testing.op_tests.unroll_batch_matmul import make_unroll_batch_matmul_tests 151from tensorflow.lite.testing.op_tests.where import make_where_tests 152from tensorflow.lite.testing.op_tests.zeros_like import make_zeros_like_tests 153 154from tensorflow.lite.testing.zip_test_utils import get_test_function 155 156# A map from regular expression to bug number. Any test failure with label 157# matching the expression will be considered due to the corresponding bug. 158KNOWN_BUGS = { 159 # TOCO doesn't support scalars as input. 160 # Concat doesn't work with a single input tensor 161 r"concat.*num_tensors=1": "67378344", 162 # Softmax graphs are too complex. 163 r"softmax.*dim=0": "67749831", 164 # BatchToSpaceND only supports 4D tensors. 165 r"batch_to_space_nd.*input_shape=\[8,2,2,2,1,1\]": "70594733", 166 # Div will use floordiv. 167 r"div.*int32": "72051395", 168 # Strided slice cannot handle new_axis_mask. 169 r"strided_slice.*spec=\[None": "137470173", 170} 171 172 173class MultiGenState(object): 174 """State of multiple set generation process. 175 176 This state class stores the information needed when generating the examples 177 for multiple test set. The stored informations are open archive object to be 178 shared, information on test target for current iteration of generation, 179 accumulated generation results. 180 """ 181 182 def __init__(self): 183 # Open archive. 184 self.archive = None 185 # Test name for current generation. 186 self.test_name = None 187 # Label base path containing the test name. 188 # Each of the test data path in the zip archive is derived from this path. 189 # If this path is "a/b/c/d.zip", an example of generated test data path 190 # is "a/b/c/d_input_type=tf.float32,input_shape=[2,2].inputs". 191 # The test runner interpretes the test name of this path as "d". 192 # Label base path also should finish with ".zip". 193 self.label_base_path = None 194 # Zip manifests. 195 self.zip_manifest = [] 196 # Number of all parameters accumulated. 197 self.parameter_count = 0 198 199 200class Options(object): 201 """All options for example generation.""" 202 203 def __init__(self): 204 # Directory where the outputs will be go. 205 self.output_path = None 206 # Particular zip to output. 207 self.zip_to_output = None 208 # Path to toco tool. 209 self.toco = None 210 # If a particular model is affected by a known bug count it as a Toco 211 # error. 212 self.known_bugs_are_errors = False 213 # Raise an exception if any converter error is encountered. 214 self.ignore_converter_errors = False 215 # Include intermediate graphdefs in the output zip files. 216 self.save_graphdefs = False 217 # Whether the TFLite Flex converter is being used. 218 self.run_with_flex = False 219 # Whether to generate test cases for edgetpu. 220 self.make_edgetpu_tests = False 221 # The function to convert a TensorFLow model to TFLite model. 222 # See the document for `toco_convert` function for its required signature. 223 self.tflite_convert_function = None 224 # A map from regular expression to bug number. Any test failure with label 225 # matching the expression will be considered due to the corresponding bug. 226 self.known_bugs = KNOWN_BUGS 227 # Make tests by setting TF forward compatibility horizon to the future. 228 self.make_forward_compat_test = False 229 # No limitation on the number of tests. 230 self.no_tests_limit = False 231 # Do not create conversion report. 232 self.no_conversion_report = False 233 # State of multiple test set generation. This stores state values those 234 # should be kept and updated while generating examples over multiple 235 # test sets. 236 # TODO(juhoha): Separate the state from the options. 237 self.multi_gen_state = None 238 self.use_experimental_converter = False 239 self.mlir_quantizer = False 240 # The list of ops' name that should exist in the converted model. 241 # This feature is currently only supported in MLIR conversion path. 242 # Example of supported ops' name: 243 # - "AVERAGE_POOL_2D" for builtin op. 244 # - "NumericVerify" for custom op. 245 self.expected_ops_in_converted_model = [] 246 247 248def _prepare_dir(options): 249 250 def mkdir_if_not_exist(x): 251 if not os.path.isdir(x): 252 os.mkdir(x) 253 if not os.path.isdir(x): 254 raise RuntimeError("Failed to create dir %r" % x) 255 256 opstest_path = os.path.join(options.output_path) 257 mkdir_if_not_exist(opstest_path) 258 259 260def generate_examples(options): 261 """Generate examples for a test set. 262 263 Args: 264 options: Options containing information to generate examples. 265 266 Raises: 267 RuntimeError: if the test function cannot be found. 268 """ 269 _prepare_dir(options) 270 271 out = options.zip_to_output 272 # Some zip filenames contain a postfix identifying the conversion mode. The 273 # list of valid conversion modes is defined in 274 # generated_test_conversion_modes() in build_def.bzl. 275 276 if options.multi_gen_state: 277 test_name = options.multi_gen_state.test_name 278 else: 279 # Remove suffixes to extract the test name from the output name. 280 test_name = re.sub( 281 r"(_(|toco-flex|forward-compat|edgetpu|mlir-quant))?\.zip$", 282 "", 283 out, 284 count=1) 285 286 test_function_name = "make_%s_tests" % test_name 287 test_function = get_test_function(test_function_name) 288 if test_function is None: 289 raise RuntimeError("Can't find a test function to create %r. Tried %r" % 290 (out, test_function_name)) 291 if options.make_forward_compat_test: 292 future_date = datetime.date.today() + datetime.timedelta(days=30) 293 with tf.compat.forward_compatibility_horizon(future_date.year, 294 future_date.month, 295 future_date.day): 296 test_function(options) 297 else: 298 test_function(options) 299 300 301def generate_multi_set_examples(options, test_sets): 302 """Generate examples for test sets. 303 304 Args: 305 options: Options containing information to generate examples. 306 test_sets: List of the name of test sets to generate examples. 307 """ 308 _prepare_dir(options) 309 310 multi_gen_state = MultiGenState() 311 options.multi_gen_state = multi_gen_state 312 313 zip_path = os.path.join(options.output_path, options.zip_to_output) 314 with zipfile.PyZipFile(zip_path, "w") as archive: 315 multi_gen_state.archive = archive 316 317 for test_name in test_sets: 318 # Some generation function can change the value of the options object. 319 # To keep the original options for each run, we use shallow copy. 320 new_options = copy.copy(options) 321 322 # Remove suffix and set test_name to run proper test generation function. 323 multi_gen_state.test_name = re.sub( 324 r"(_(|toco-flex|forward-compat|mlir-quant))?$", 325 "", 326 test_name, 327 count=1) 328 # Set label base path to write test data files with proper path. 329 multi_gen_state.label_base_path = os.path.join( 330 os.path.dirname(zip_path), test_name + ".zip") 331 332 generate_examples(new_options) 333 334 zipinfo = zipfile.ZipInfo("manifest.txt") 335 archive.writestr(zipinfo, "".join(multi_gen_state.zip_manifest), 336 zipfile.ZIP_DEFLATED) 337