| # Copyright 2018 The TensorFlow Authors. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # ============================================================================== |
| """Tests for model_coverage_lib.py.""" |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import os |
| import tempfile |
| |
| import numpy as np |
| from tensorflow import keras |
| |
| from tensorflow.lite.python import lite |
| from tensorflow.lite.testing.model_coverage import model_coverage_lib as model_coverage |
| from tensorflow.python.client import session |
| from tensorflow.python.eager import def_function |
| from tensorflow.python.framework import constant_op |
| from tensorflow.python.framework import dtypes |
| from tensorflow.python.framework import ops |
| from tensorflow.python.ops import array_ops |
| from tensorflow.python.ops import math_ops |
| from tensorflow.python.platform import test |
| from tensorflow.python.saved_model import saved_model |
| from tensorflow.python.training.training_util import write_graph |
| |
| |
| class EvaluateFrozenGraph(test.TestCase): |
| |
| def _saveFrozenGraph(self, sess): |
| graph_def_file = os.path.join(self.get_temp_dir(), 'model.pb') |
| write_graph(sess.graph_def, '', graph_def_file, False) |
| return graph_def_file |
| |
| def testFloat(self): |
| with ops.Graph().as_default(): |
| with session.Session().as_default() as sess: |
| in_tensor = array_ops.placeholder( |
| shape=[1, 16, 16, 3], dtype=dtypes.float32) |
| _ = in_tensor + in_tensor |
| |
| filename = self._saveFrozenGraph(sess) |
| model_coverage.test_frozen_graph(filename, ['Placeholder'], ['add']) |
| |
| def testInputWithRange(self): |
| with ops.Graph().as_default(): |
| with session.Session().as_default() as sess: |
| in_tensor = array_ops.placeholder( |
| shape=[1, 16, 16, 3], dtype=dtypes.float32) |
| _ = in_tensor + in_tensor |
| |
| filename = self._saveFrozenGraph(sess) |
| model_coverage.test_frozen_graph( |
| filename, ['Placeholder'], ['add'], |
| input_data_range={'Placeholder': (0, 10)}) |
| |
| def testMultipleOutputs(self): |
| with ops.Graph().as_default(): |
| with session.Session().as_default() as sess: |
| in_tensor_1 = array_ops.placeholder( |
| shape=[1, 16], dtype=dtypes.float32, name='inputA') |
| in_tensor_2 = array_ops.placeholder( |
| shape=[1, 16], dtype=dtypes.float32, name='inputB') |
| |
| weight = constant_op.constant(-1.0, shape=[16, 16]) |
| bias = constant_op.constant(-1.0, shape=[16]) |
| layer = math_ops.matmul(in_tensor_1, weight) + bias |
| _ = math_ops.reduce_mean(math_ops.square(layer - in_tensor_2)) |
| |
| filename = self._saveFrozenGraph(sess) |
| model_coverage.test_frozen_graph(filename, ['inputA', 'inputB'], |
| ['add', 'Mean']) |
| |
| def testFunctions(self): |
| """Tests functions.""" |
| |
| @def_function.function |
| def plus_placeholder(x, placeholder): |
| return x + placeholder |
| |
| with ops.Graph().as_default(): |
| placeholder = array_ops.placeholder( |
| dtype=dtypes.float32, shape=[1], name='input') |
| variable_node = constant_op.constant(1.0, name='variable_node') |
| defun_node = plus_placeholder(variable_node, placeholder) |
| _ = math_ops.multiply(defun_node, 2.0, name='output_node') |
| |
| # Initialize variables in the model. |
| sess = session.Session() |
| |
| filename = self._saveFrozenGraph(sess) |
| model_coverage.test_frozen_graph(filename, ['input'], ['output_node']) |
| |
| def _getQuantizedModel(self): |
| np.random.seed(0) |
| with ops.Graph().as_default(): |
| with session.Session().as_default() as sess: |
| # The tensor needs to have more than 1024 elements for quantize_weights |
| # to kick in. Thus, the [33, 33] shape. |
| in_tensor_1 = array_ops.placeholder( |
| shape=[33, 33], dtype=dtypes.float32, name='inputA') |
| in_tensor_2 = constant_op.constant( |
| np.random.uniform(low=-10., high=10., size=(33, 33)), |
| shape=[33, 33], |
| dtype=dtypes.float32, |
| name='inputB') |
| _ = math_ops.matmul(in_tensor_1, in_tensor_2, name='output') |
| |
| filename = self._saveFrozenGraph(sess) |
| return filename |
| |
| def testQuantized(self): |
| filename = self._getQuantizedModel() |
| model_coverage.test_frozen_graph_quant(filename, ['inputA'], ['output']) |
| |
| def testQuantizedInputShapes(self): |
| filename = self._getQuantizedModel() |
| model_coverage.test_frozen_graph_quant( |
| filename, ['inputA'], ['output'], input_shapes={'inputA': [33, 33]}) |
| |
| def testQuantizedFlexAll(self): |
| filename = self._getQuantizedModel() |
| model_coverage.test_frozen_graph_quant( |
| filename, ['inputA'], ['output'], |
| target_ops=set([lite.OpsSet.SELECT_TF_OPS])) |
| |
| |
| class EvaluateSavedModel(test.TestCase): |
| |
| def testFloat(self): |
| saved_model_dir = os.path.join(self.get_temp_dir(), 'simple_savedmodel') |
| with ops.Graph().as_default(): |
| with session.Session().as_default() as sess: |
| in_tensor_1 = array_ops.placeholder( |
| shape=[1, 16, 16, 3], dtype=dtypes.float32, name='inputB') |
| in_tensor_2 = array_ops.placeholder( |
| shape=[1, 16, 16, 3], dtype=dtypes.float32, name='inputA') |
| out_tensor = in_tensor_1 + in_tensor_2 |
| |
| inputs = {'x': in_tensor_1, 'y': in_tensor_2} |
| outputs = {'z': out_tensor} |
| saved_model.simple_save(sess, saved_model_dir, inputs, outputs) |
| model_coverage.test_saved_model(saved_model_dir) |
| |
| def testPostTrainingQuantize16x8(self): |
| """Test for post-training quantization mode: activations/weights - int16/int8.""" |
| saved_model_dir = os.path.join(self.get_temp_dir(), 'simple_savedmodel') |
| |
| input_size = [5, 5, 3] |
| kernel_size = [3, 3, 1] |
| layer_name = 'test_conv2d' |
| input_0 = keras.layers.Input(shape=input_size) |
| layer_0 = keras.layers.Conv2D( |
| filters=kernel_size[-1], |
| kernel_size=kernel_size[0:2], |
| use_bias=False, |
| name=layer_name)( |
| input_0) |
| model = keras.models.Model(inputs=[input_0], outputs=[layer_0]) |
| keras_layer = [layer for layer in model.layers if layer.name == layer_name |
| ][0] |
| keras_layer.set_weights([ |
| np.random.rand( |
| input_size[-1], |
| kernel_size[0], |
| kernel_size[1], |
| kernel_size[2], |
| ).astype(np.float32) |
| ]) |
| |
| saved_model.save(model, saved_model_dir) |
| |
| model_coverage.test_saved_model( |
| saved_model_dir, |
| post_training_quantize_16x8=True, |
| model_input_size=input_size) |
| |
| |
| class EvaluateKerasModel(test.TestCase): |
| |
| def _getSingleInputKerasModel(self): |
| """Returns single input Sequential tf.keras model.""" |
| keras.backend.clear_session() |
| |
| xs = np.array([-1, 0, 1, 2, 3, 4]) |
| ys = np.array([-3, -1, 1, 3, 5, 7]) |
| |
| model = keras.Sequential([keras.layers.Dense(units=1, input_shape=[1])]) |
| model.compile(optimizer='sgd', loss='mean_squared_error') |
| model.train_on_batch(xs, ys) |
| return model |
| |
| def _saveKerasModel(self, model): |
| try: |
| fd, keras_file = tempfile.mkstemp('.h5') |
| model.save(keras_file) |
| finally: |
| os.close(fd) |
| return keras_file |
| |
| def testFloat(self): |
| model = self._getSingleInputKerasModel() |
| keras_file = self._saveKerasModel(model) |
| |
| model_coverage.test_keras_model(keras_file) |
| |
| def testPostTrainingQuantize(self): |
| model = self._getSingleInputKerasModel() |
| keras_file = self._saveKerasModel(model) |
| |
| model_coverage.test_keras_model(keras_file, post_training_quantize=True) |
| |
| def testTargetOps(self): |
| model = self._getSingleInputKerasModel() |
| keras_file = self._saveKerasModel(model) |
| |
| model_coverage.test_keras_model( |
| keras_file, |
| target_ops=set([lite.OpsSet.TFLITE_BUILTINS, |
| lite.OpsSet.SELECT_TF_OPS])) |
| |
| |
| if __name__ == '__main__': |
| test.main() |