From 32176933097225bd1579b2039a6929029409e32f Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Sun, 31 May 2020 17:36:54 +0100 Subject: [PATCH 01/10] Add INT16 support in modify_model_interface * Added support for 16bit quantized models in modify_model_interface.cc * Added extra arguments for inputing 16bit quantized models in modify_model_interface_main.cc * Modified 2 of the unit tests to be parametrized and covered 16bit models in modify_model_interface_test.cc Signed-off-by: Tamas Nyiri --- .../tools/optimize/modify_model_interface.cc | 107 ++++++--- .../tools/optimize/modify_model_interface.h | 2 +- .../optimize/modify_model_interface_main.cc | 26 ++- .../optimize/modify_model_interface_test.cc | 203 +++++++++--------- 4 files changed, 183 insertions(+), 155 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index 9451483b79d..cadb0708cf4 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -46,7 +46,8 @@ struct TensorOpTensor { // Finds float tensors that are model inputs and is consumed by a quantize Op. // The returned TensorOpTensor should have reverse order. std::vector GetInputTensors(ModelT* model, - ErrorReporter* error_reporter) { + ErrorReporter* error_reporter, + const TensorType& input_type) { std::vector result; // Get all input tensors. for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); @@ -71,7 +72,7 @@ std::vector GetInputTensors(ModelT* model, continue; } if (op_code != BuiltinOperator_QUANTIZE) { - // Current only support INT8 quantized models. + // Currently only supports INT8 and INT16 quantized models. TF_LITE_REPORT_ERROR( error_reporter, "modify_model_interface called on a model without quant/dequant."); @@ -85,10 +86,16 @@ std::vector GetInputTensors(ModelT* model, } const int model_input_index = input_tensors[input_tensor]; TensorT* quant_output = subgraph->tensors[op->outputs[0]].get(); - if (quant_output->type != TensorType_INT8) { + if (quant_output->type != TensorType_INT8 && quant_output->type != TensorType_INT16) { TF_LITE_REPORT_ERROR(error_reporter, - "modify_model_interface currently only support " - "int8 quantized models."); + "modify_model_interface currently only supports " + "int8 and int16 quantized models."); + } + if (quant_output->type != input_type) { + if (!(quant_output->type == TensorType_INT8 && input_type == TensorType_UINT8)) { + TF_LITE_REPORT_ERROR(error_reporter, + "Model's type incompatible with output type argument."); + } } if (quant_output->quantization == nullptr) { continue; @@ -103,7 +110,8 @@ std::vector GetInputTensors(ModelT* model, // Finds float tensors that are model output and is consumed by a dequantize Op. // The returned TensorOpTensor should have reverse order. std::vector GetOutputTensors(ModelT* model, - ErrorReporter* error_reporter) { + ErrorReporter* error_reporter, + const TensorType& output_type) { std::vector result; // Get all output tensors. for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); @@ -128,7 +136,7 @@ std::vector GetOutputTensors(ModelT* model, continue; } if (op_code != BuiltinOperator_DEQUANTIZE) { - // Current only support INT8 quantized models. + // Currently only supports INT8 and INT16 quantized models. TF_LITE_REPORT_ERROR( error_reporter, "modify_model_interface called on a model without quant/dequant."); @@ -142,13 +150,19 @@ std::vector GetOutputTensors(ModelT* model, } const int model_output_index = output_tensors[output_tensor]; TensorT* dequant_input = subgraph->tensors[op->inputs[0]].get(); - if (dequant_input->type != TensorType_INT8) { - // Current only support INT8 quantized models. + if (dequant_input->type != TensorType_INT8 && dequant_input->type != TensorType_INT16) { + // Currently only supports INT8 and INT16 quantized models. TF_LITE_REPORT_ERROR(error_reporter, - "modify_model_interface currently only support " - "int8 quantized models."); + "modify_model_interface currently only supports " + "int8 and int16 quantized models."); return {}; } + if (dequant_input->type != output_type) { + if (!(dequant_input->type == TensorType_INT8 && output_type == TensorType_UINT8)) { + TF_LITE_REPORT_ERROR(error_reporter, + "Model's type incompatible with output type argument."); + } + } if (dequant_input->quantization == nullptr) { continue; } @@ -302,28 +316,36 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, tflite::StderrReporter error_reporter; const int original_number_tensors = GetOriginalNumberOfTensors(model, &error_reporter); - // Find float tensors that are model output and is consumed by a float to int8 + // Finds float tensors that are model output and are consumed by a float to int8/int16 // quantize Op. // Do output first since the tensors are added into input first., std::vector outputs = - GetOutputTensors(model, &error_reporter); - if (output_type == TensorType_UINT8) { - SetOutputTypeToUINT8(model, outputs); - } else if (output_type == TensorType_INT8) { - RemoveOutputTensor(model, outputs, original_number_tensors); - } else { - return kTfLiteError; + GetOutputTensors(model, &error_reporter, output_type); + switch (output_type) { + case TensorType_UINT8: + SetOutputTypeToUINT8(model, outputs); + break; + case TensorType_INT8: + case TensorType_INT16: + RemoveOutputTensor(model, outputs); + break; + default: + return kTfLiteError; } - // Find float tensors that are model input and is consumed by a float to int8 + // Find float tensors that are model input and is consumed by a float to int8/int16 // quantize Op. - std::vector inputs = GetInputTensors(model, &error_reporter); - if (input_type == TensorType_UINT8) { - SetInputTypeToUINT8(model, inputs); - } else if (input_type == TensorType_INT8) { - RemoveInputTensor(model, inputs, original_number_tensors); - } else { - return kTfLiteError; + std::vector inputs = GetInputTensors(model, &error_reporter, input_type); + switch (input_type) { + case TensorType_UINT8: + SetInputTypeToUINT8(model, inputs); + break; + case TensorType_INT8: + case TensorType_INT16: + RemoveInputTensor(model, inputs); + break; + default: + return kTfLiteError; } // Write to builder. @@ -340,11 +362,13 @@ TfLiteStatus ModifyModelInterface(const string& input_file, const TensorType& output_type) { // Sanity Check if (input_type != tflite::TensorType_INT8 && - input_type != tflite::TensorType_UINT8) { + input_type != tflite::TensorType_UINT8 && + input_type != tflite::TensorType_INT16) { return kTfLiteError; } if (output_type != tflite::TensorType_INT8 && - output_type != tflite::TensorType_UINT8) { + output_type != tflite::TensorType_UINT8 && + output_type != tflite::TensorType_INT16) { return kTfLiteError; } @@ -357,13 +381,26 @@ TfLiteStatus ModifyModelInterface(const string& input_file, absl::make_unique(); flatbuffers::FlatBufferBuilder builder; - tflite::TensorType input_override_type = tflite::TensorType_INT8; - if (input_type == tflite::TensorType_UINT8) { - input_override_type = tflite::TensorType_UINT8; + tflite::TensorType input_override_type; + tflite::TensorType output_override_type; + + switch (input_type) { + case tflite::TensorType_UINT8: + case tflite::TensorType_INT8: + case tflite::TensorType_INT16: + input_override_type = input_type; + break; + default: + return kTfLiteError; } - tflite::TensorType output_override_type = tflite::TensorType_INT8; - if (output_type == tflite::TensorType_UINT8) { - output_override_type = tflite::TensorType_UINT8; + switch (output_type) { + case tflite::TensorType_UINT8: + case tflite::TensorType_INT8: + case tflite::TensorType_INT16: + output_override_type = output_type; + break; + default: + return kTfLiteError; } auto status = ModifyModelInterface(&builder, tflite_model.get(), diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.h b/tensorflow/lite/tools/optimize/modify_model_interface.h index 170e0e73a67..b3a39d63801 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.h +++ b/tensorflow/lite/tools/optimize/modify_model_interface.h @@ -24,7 +24,7 @@ namespace optimize { // Changes the interface of a quantized model. This method allows the users to // replace float interface with other types. // This populates the builder with the new model. -// Currently only int8 and unit8 are supported. +// Currently only int8, int16 and unit8 are supported. // // Note: This is a private API, subject to change. TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_main.cc b/tensorflow/lite/tools/optimize/modify_model_interface_main.cc index 24674a1b341..18e210332f0 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_main.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_main.cc @@ -25,24 +25,22 @@ int main(int argc, char** argv) { return 1; } - if (strcmp(argv[3], "uint8") && strcmp(argv[3], "int8")) { - printf("Only support uint8 and int8 for input interface"); - return 1; - } - - if (strcmp(argv[4], "uint8") && strcmp(argv[4], "int8")) { - printf("Only support uint8 and int8 for output interface"); - return 1; - } + const std::unordered_map supported_types + { + { "uint8", tflite::TensorType_UINT8 }, + { "int8", tflite::TensorType_INT8 }, + { "int16", tflite::TensorType_INT16 } + }; tflite::TensorType input = tflite::TensorType_INT8; tflite::TensorType output = tflite::TensorType_INT8; - if (!strcmp(argv[3], "uint8")) { - input = tflite::TensorType_UINT8; - } - if (!strcmp(argv[4], "uint8")) { - output = tflite::TensorType_UINT8; + try { + input = supported_types.at(argv[3]); + output = supported_types.at(argv[4]); + } catch (const std::out_of_range&) { + printf("Only supports uint8, int8 and int16 for input and output interfaces"); + return 1; } tflite::optimize::ModifyModelInterface(argv[1], argv[2], input, output); diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc index 5a04f28f638..ca0699bcea2 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc @@ -18,6 +18,7 @@ limitations under the License. #include #include + #include "absl/memory/memory.h" #include "tensorflow/lite/model.h" #include "tensorflow/lite/schema/schema_generated.h" @@ -29,7 +30,7 @@ namespace { using ::testing::ElementsAreArray; // Create a model with 1 quant, 1 FC, 1 dequant -std::unique_ptr CreateModelSingleInputOutput() { +std::unique_ptr CreateModelSingleInputOutput(const TensorType& quantization_type) { auto model = absl::make_unique(); auto subgraph = absl::make_unique(); auto buffer = absl::make_unique(); @@ -85,7 +86,7 @@ std::unique_ptr CreateModelSingleInputOutput() { tensor_0->quantization->zero_point.push_back(28); tensor_0->name = "tensor_0"; tensor_0->shape = {}; - tensor_0->type = TensorType_INT8; + tensor_0->type = quantization_type; auto tensor_1 = absl::make_unique(); tensor_1->quantization = absl::make_unique(); @@ -93,7 +94,7 @@ std::unique_ptr CreateModelSingleInputOutput() { tensor_1->quantization->zero_point.push_back(50); tensor_1->name = "tensor_1"; tensor_1->shape = {}; - tensor_1->type = TensorType_INT8; + tensor_1->type = quantization_type; auto tensor_2 = absl::make_unique(); tensor_2->name = "tensor_2"; @@ -118,7 +119,7 @@ std::unique_ptr CreateModelSingleInputOutput() { // Create a model with 2 quant, 1 FC, 2 dequant // The model mimics the behavior of the quantize_model.cc. -std::unique_ptr CreateModelMultipleInputOutput() { +std::unique_ptr CreateModelMultipleInputOutput(const TensorType& quantization_type) { auto model = absl::make_unique(); auto subgraph = absl::make_unique(); auto buffer = absl::make_unique(); @@ -183,7 +184,7 @@ std::unique_ptr CreateModelMultipleInputOutput() { tensor_0->quantization->zero_point.push_back(28); tensor_0->name = "tensor_0"; tensor_0->shape = {}; - tensor_0->type = TensorType_INT8; + tensor_0->type = quantization_type; auto tensor_1 = absl::make_unique(); tensor_1->quantization = absl::make_unique(); @@ -191,7 +192,7 @@ std::unique_ptr CreateModelMultipleInputOutput() { tensor_1->quantization->zero_point.push_back(50); tensor_1->name = "tensor_1"; tensor_1->shape = {}; - tensor_1->type = TensorType_INT8; + tensor_1->type = quantization_type; auto tensor_2 = absl::make_unique(); tensor_2->quantization = absl::make_unique(); @@ -199,7 +200,7 @@ std::unique_ptr CreateModelMultipleInputOutput() { tensor_2->quantization->zero_point.push_back(28); tensor_2->name = "tensor_2"; tensor_2->shape = {}; - tensor_2->type = TensorType_INT8; + tensor_2->type = quantization_type; auto tensor_3 = absl::make_unique(); tensor_3->quantization = absl::make_unique(); @@ -207,7 +208,7 @@ std::unique_ptr CreateModelMultipleInputOutput() { tensor_3->quantization->zero_point.push_back(50); tensor_3->name = "tensor_3"; tensor_3->shape = {}; - tensor_3->type = TensorType_INT8; + tensor_3->type = quantization_type; auto tensor_4 = absl::make_unique(); tensor_4->name = "tensor_4"; @@ -290,8 +291,95 @@ std::unique_ptr CreateFloatModel() { return model; } +struct ModelInterface: + ::testing::TestWithParam {}; + +TEST_P(ModelInterface, SingleInputOutput) { + TensorType quantization_type = GetParam(); + + auto model = CreateModelSingleInputOutput(quantization_type); + + // Change model type. + flatbuffers::FlatBufferBuilder builder; + EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type, + quantization_type), + kTfLiteOk); + + // Verify results. + EXPECT_EQ(model->operator_codes.size(), 3); + EXPECT_EQ(model->subgraphs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->operators.size(), 1); + EXPECT_EQ(model->subgraphs[0]->tensors.size(), 2); + EXPECT_EQ(model->buffers.size(), 1); + + EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->inputs[0], 0); + EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->outputs[0], 1); +} + +TEST_P(ModelInterface, MutipleInputOutput) { + + TensorType quantization_type = GetParam(); + + auto model = CreateModelMultipleInputOutput(quantization_type); + + // Change model type. + flatbuffers::FlatBufferBuilder builder; + EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type, + quantization_type), + kTfLiteOk); + + // Verify results. + EXPECT_EQ(model->operator_codes.size(), 3); + EXPECT_EQ(model->subgraphs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->operators.size(), 1); + EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4); + EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2); + EXPECT_EQ(model->subgraphs[0]->inputs[0], 0); + EXPECT_EQ(model->subgraphs[0]->inputs[1], 1); + EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2); + EXPECT_EQ(model->subgraphs[0]->outputs[0], 2); + EXPECT_EQ(model->subgraphs[0]->outputs[1], 3); + EXPECT_EQ(model->buffers.size(), 1); + + // Tensors, + EXPECT_EQ(model->subgraphs[0]->tensors[0]->name, "tensor_0"); + EXPECT_EQ(model->subgraphs[0]->tensors[0]->type, quantization_type); + EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[0]->quantization->scale[0], + 0.35); + EXPECT_EQ(model->subgraphs[0]->tensors[0]->quantization->zero_point[0], 28); + + EXPECT_EQ(model->subgraphs[0]->tensors[1]->name, "tensor_1"); + EXPECT_EQ(model->subgraphs[0]->tensors[1]->type, quantization_type); + EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[1]->quantization->scale[0], + 0.12); + EXPECT_EQ(model->subgraphs[0]->tensors[1]->quantization->zero_point[0], 50); + + EXPECT_EQ(model->subgraphs[0]->tensors[2]->name, "tensor_2"); + EXPECT_EQ(model->subgraphs[0]->tensors[2]->type, quantization_type); + EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[2]->quantization->scale[0], + 0.45); + EXPECT_EQ(model->subgraphs[0]->tensors[2]->quantization->zero_point[0], 28); + + EXPECT_EQ(model->subgraphs[0]->tensors[3]->name, "tensor_3"); + EXPECT_EQ(model->subgraphs[0]->tensors[3]->type, quantization_type); + EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[3]->quantization->scale[0], + 0.22); + EXPECT_EQ(model->subgraphs[0]->tensors[3]->quantization->zero_point[0], 50); + + // Ops. + EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1); +} + +INSTANTIATE_TEST_SUITE_P( + MultipleInputOutputTests, + ModelInterface, + ::testing::Values(TensorType_INT8, TensorType_INT16) +); + TEST(ModelInterface, Uint8SingleInputOutput) { - auto model = CreateModelSingleInputOutput(); + auto model = CreateModelSingleInputOutput(TensorType_INT8); // Ops. EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0); @@ -329,52 +417,8 @@ TEST(ModelInterface, Uint8SingleInputOutput) { EXPECT_EQ(model->subgraphs[0]->operators[2]->opcode_index, 0); } -TEST(ModelInterface, Int8SingleInputOutput) { - auto model = CreateModelSingleInputOutput(); - - // Change model type. - flatbuffers::FlatBufferBuilder builder; - EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_INT8, - TensorType_INT8), - kTfLiteOk); - - // Verify results. - EXPECT_EQ(model->operator_codes.size(), 3); - EXPECT_EQ(model->subgraphs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->operators.size(), 1); - EXPECT_EQ(model->subgraphs[0]->tensors.size(), 2); - EXPECT_EQ(model->buffers.size(), 1); - - EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->inputs[0], 0); - EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->outputs[0], 1); -} - -TEST(ModelInterface, MixedTypeSingleInputOutput) { - auto model = CreateModelSingleInputOutput(); - - // Change model type. - flatbuffers::FlatBufferBuilder builder; - EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_UINT8, - TensorType_INT8), - kTfLiteOk); - - // Verify results. - EXPECT_EQ(model->operator_codes.size(), 3); - EXPECT_EQ(model->subgraphs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->operators.size(), 2); - EXPECT_EQ(model->subgraphs[0]->tensors.size(), 3); - EXPECT_EQ(model->buffers.size(), 1); - - EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->inputs[0], 2); - EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->outputs[0], 1); -} - TEST(ModelInterface, Uint8MutipleInputOutput) { - auto model = CreateModelMultipleInputOutput(); + auto model = CreateModelMultipleInputOutput(TensorType_INT8); // Ops. EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0); @@ -436,57 +480,6 @@ TEST(ModelInterface, Uint8MutipleInputOutput) { EXPECT_EQ(model->subgraphs[0]->operators[4]->opcode_index, 0); } -TEST(ModelInterface, Int8MutipleInputOutput) { - auto model = CreateModelMultipleInputOutput(); - - // Change model type. - flatbuffers::FlatBufferBuilder builder; - EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_INT8, - TensorType_INT8), - kTfLiteOk); - - // Verify results. - EXPECT_EQ(model->operator_codes.size(), 3); - EXPECT_EQ(model->subgraphs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->operators.size(), 1); - EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4); - EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2); - EXPECT_EQ(model->subgraphs[0]->inputs[0], 0); - EXPECT_EQ(model->subgraphs[0]->inputs[1], 1); - EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2); - EXPECT_EQ(model->subgraphs[0]->outputs[0], 2); - EXPECT_EQ(model->subgraphs[0]->outputs[1], 3); - EXPECT_EQ(model->buffers.size(), 1); - - // Tensors, - EXPECT_EQ(model->subgraphs[0]->tensors[0]->name, "tensor_0"); - EXPECT_EQ(model->subgraphs[0]->tensors[0]->type, TensorType_INT8); - EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[0]->quantization->scale[0], - 0.35); - EXPECT_EQ(model->subgraphs[0]->tensors[0]->quantization->zero_point[0], 28); - - EXPECT_EQ(model->subgraphs[0]->tensors[1]->name, "tensor_1"); - EXPECT_EQ(model->subgraphs[0]->tensors[1]->type, TensorType_INT8); - EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[1]->quantization->scale[0], - 0.12); - EXPECT_EQ(model->subgraphs[0]->tensors[1]->quantization->zero_point[0], 50); - - EXPECT_EQ(model->subgraphs[0]->tensors[2]->name, "tensor_2"); - EXPECT_EQ(model->subgraphs[0]->tensors[2]->type, TensorType_INT8); - EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[2]->quantization->scale[0], - 0.45); - EXPECT_EQ(model->subgraphs[0]->tensors[2]->quantization->zero_point[0], 28); - - EXPECT_EQ(model->subgraphs[0]->tensors[3]->name, "tensor_3"); - EXPECT_EQ(model->subgraphs[0]->tensors[3]->type, TensorType_INT8); - EXPECT_FLOAT_EQ(model->subgraphs[0]->tensors[3]->quantization->scale[0], - 0.22); - EXPECT_EQ(model->subgraphs[0]->tensors[3]->quantization->zero_point[0], 50); - - // Ops. - EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1); -} - TEST(ModelInterface, Float) { // Create the model. std::unique_ptr input_model_t = CreateFloatModel(); From 2e4184f8294110748787c635a8a545bbef95725e Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Wed, 10 Jun 2020 00:20:34 +0100 Subject: [PATCH 02/10] Refactored to reflect changes in upstream --- .../tools/optimize/modify_model_interface.cc | 12 +- .../optimize/modify_model_interface_test.cc | 198 +++++++++--------- 2 files changed, 108 insertions(+), 102 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index cadb0708cf4..eead4999ca6 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -302,9 +302,9 @@ std::unique_ptr CreateMutableModelFromFile( return copied_model; } -int GetOriginalNumberOfTensors(ModelT* model, ErrorReporter* error_reporter) { - std::vector outputs = GetOutputTensors(model, error_reporter); - std::vector inputs = GetInputTensors(model, error_reporter); +int GetOriginalNumberOfTensors(ModelT* model, ErrorReporter* error_reporter, const TensorType& input_type, const TensorType& output_type) { + std::vector outputs = GetOutputTensors(model, error_reporter, output_type); + std::vector inputs = GetInputTensors(model, error_reporter, input_type); return model->subgraphs[0]->tensors.size() - outputs.size() - inputs.size(); } @@ -315,7 +315,7 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, const TensorType& output_type) { tflite::StderrReporter error_reporter; const int original_number_tensors = - GetOriginalNumberOfTensors(model, &error_reporter); + GetOriginalNumberOfTensors(model, &error_reporter, input_type, output_type); // Finds float tensors that are model output and are consumed by a float to int8/int16 // quantize Op. // Do output first since the tensors are added into input first., @@ -327,7 +327,7 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, break; case TensorType_INT8: case TensorType_INT16: - RemoveOutputTensor(model, outputs); + RemoveOutputTensor(model, outputs, original_number_tensors); break; default: return kTfLiteError; @@ -342,7 +342,7 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, break; case TensorType_INT8: case TensorType_INT16: - RemoveInputTensor(model, inputs); + RemoveInputTensor(model, inputs, original_number_tensors); break; default: return kTfLiteError; diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc index 1df0a36c727..acf9bf1a02d 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc @@ -298,46 +298,6 @@ TEST_P(ModelInterface, SingleInputOutput) { auto model = CreateQuantizedModelSingleInputOutput(quantization_type); - // Change model type. - flatbuffers::FlatBufferBuilder builder; - EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type, - quantization_type), - kTfLiteOk); - - // Verify results. - EXPECT_EQ(model->subgraphs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4); - EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->inputs[0], 0); - EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1); - EXPECT_EQ(model->subgraphs[0]->outputs[0], 3); - EXPECT_EQ(model->operator_codes.size(), 3); - EXPECT_EQ(model->subgraphs[0]->operators.size(), 3); - EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0); - EXPECT_EQ(model->subgraphs[0]->operators[1]->opcode_index, 1); - EXPECT_EQ(model->subgraphs[0]->operators[2]->opcode_index, 0); - - auto input_quant_op = model->subgraphs[0]->operators[0].get(); - auto input = model->subgraphs[0]->tensors[input_quant_op->inputs[0]].get(); - EXPECT_EQ(input->name, "tensor_0"); - EXPECT_EQ(input->type, TensorType_UINT8); - EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.35); - EXPECT_EQ(input->quantization->zero_point[0], 156); - - auto output_quant_op = model->subgraphs[0]->operators[2].get(); - auto output = model->subgraphs[0]->tensors[output_quant_op->outputs[0]].get(); - EXPECT_EQ(output->name, "tensor_3"); - EXPECT_EQ(output->type, TensorType_UINT8); - EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.12); - EXPECT_EQ(output->quantization->zero_point[0], 178); -} - -TEST_P(ModelInterface, MutipleInputOutput) { - - TensorType quantization_type = GetParam(); - - auto model = CreateModelMultipleInputOutput(quantization_type); - // Change model type. flatbuffers::FlatBufferBuilder builder; EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type, @@ -361,24 +321,84 @@ TEST_P(ModelInterface, MutipleInputOutput) { auto input = model->subgraphs[0]->tensors[fc_op->inputs[0]].get(); EXPECT_EQ(input->name, "tensor_1"); - EXPECT_EQ(input->type, TensorType_INT8); + EXPECT_EQ(input->type, quantization_type); EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.35); EXPECT_EQ(input->quantization->zero_point[0], 28); auto output = model->subgraphs[0]->tensors[fc_op->outputs[0]].get(); EXPECT_EQ(output->name, "tensor_2"); - EXPECT_EQ(output->type, TensorType_INT8); + EXPECT_EQ(output->type, quantization_type); EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.12); EXPECT_EQ(output->quantization->zero_point[0], 50); } +TEST_P(ModelInterface, MutipleInputOutput) { + + TensorType quantization_type = GetParam(); + + auto model = CreateQuantizedModelMultipleInputOutput(quantization_type); + + // Change model type. + flatbuffers::FlatBufferBuilder builder; + EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type, + quantization_type), + kTfLiteOk); + + // Verify results. + EXPECT_EQ(model->subgraphs.size(), 1); + // TODO (b/158254056): Remove unused inputs and outputs from tensor list + // EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4); + EXPECT_EQ(model->subgraphs[0]->tensors.size(), 6); + EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2); + EXPECT_EQ(model->subgraphs[0]->inputs[0], 2); + EXPECT_EQ(model->subgraphs[0]->inputs[1], 3); + EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2); + EXPECT_EQ(model->subgraphs[0]->outputs[0], 4); + EXPECT_EQ(model->subgraphs[0]->outputs[1], 5); + EXPECT_EQ(model->operator_codes.size(), 3); + EXPECT_EQ(model->subgraphs[0]->operators.size(), 1); + EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1); + + auto fc_op = model->subgraphs[0]->operators[0].get(); + + auto input_1 = model->subgraphs[0]->tensors[fc_op->inputs[0]].get(); + EXPECT_EQ(input_1->name, "tensor_2"); + EXPECT_EQ(input_1->type, quantization_type); + EXPECT_FLOAT_EQ(input_1->quantization->scale[0], 0.35); + EXPECT_EQ(input_1->quantization->zero_point[0], 28); + + auto input_2 = model->subgraphs[0]->tensors[fc_op->inputs[1]].get(); + EXPECT_EQ(input_2->name, "tensor_3"); + EXPECT_EQ(input_2->type, quantization_type); + EXPECT_FLOAT_EQ(input_2->quantization->scale[0], 0.12); + EXPECT_EQ(input_2->quantization->zero_point[0], 50); + + auto output_1 = model->subgraphs[0]->tensors[fc_op->outputs[0]].get(); + EXPECT_EQ(output_1->name, "tensor_4"); + EXPECT_EQ(output_1->type, quantization_type); + EXPECT_FLOAT_EQ(output_1->quantization->scale[0], 0.45); + EXPECT_EQ(output_1->quantization->zero_point[0], 28); + + auto output_2 = model->subgraphs[0]->tensors[fc_op->outputs[1]].get(); + EXPECT_EQ(output_2->name, "tensor_5"); + EXPECT_EQ(output_2->type, quantization_type); + EXPECT_FLOAT_EQ(output_2->quantization->scale[0], 0.22); + EXPECT_EQ(output_2->quantization->zero_point[0], 50); +} + +INSTANTIATE_TEST_SUITE_P( + MultipleInputOutputTests, + ModelInterface, + ::testing::Values(TensorType_INT8, TensorType_INT16) +); + TEST(ModelInterface, MixedTypeSingleInputOutput) { - auto model = CreateQuantizedModelSingleInputOutput(); + auto model = CreateQuantizedModelSingleInputOutput(TensorType_INT8); // Change model type. flatbuffers::FlatBufferBuilder builder; EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_UINT8, - TensorType_UINT8), + TensorType_INT8), kTfLiteOk); // Verify results. @@ -408,8 +428,45 @@ TEST(ModelInterface, MixedTypeSingleInputOutput) { EXPECT_EQ(output->quantization->zero_point[0], 50); } +TEST(ModelInterface, Uint8SingleInputOutput) { + auto model = CreateQuantizedModelSingleInputOutput(TensorType_INT8); + + // Change model type. + flatbuffers::FlatBufferBuilder builder; + EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_UINT8, + TensorType_UINT8), + kTfLiteOk); + + // Verify results. + EXPECT_EQ(model->subgraphs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4); + EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->inputs[0], 0); + EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1); + EXPECT_EQ(model->subgraphs[0]->outputs[0], 3); + EXPECT_EQ(model->operator_codes.size(), 3); + EXPECT_EQ(model->subgraphs[0]->operators.size(), 3); + EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0); + EXPECT_EQ(model->subgraphs[0]->operators[1]->opcode_index, 1); + EXPECT_EQ(model->subgraphs[0]->operators[2]->opcode_index, 0); + + auto input_quant_op = model->subgraphs[0]->operators[0].get(); + auto input = model->subgraphs[0]->tensors[input_quant_op->inputs[0]].get(); + EXPECT_EQ(input->name, "tensor_0"); + EXPECT_EQ(input->type, TensorType_UINT8); + EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.35); + EXPECT_EQ(input->quantization->zero_point[0], 156); + + auto output_quant_op = model->subgraphs[0]->operators[2].get(); + auto output = model->subgraphs[0]->tensors[output_quant_op->outputs[0]].get(); + EXPECT_EQ(output->name, "tensor_3"); + EXPECT_EQ(output->type, TensorType_UINT8); + EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.12); + EXPECT_EQ(output->quantization->zero_point[0], 178); +} + TEST(ModelInterface, Uint8MutipleInputOutput) { - auto model = CreateQuantizedModelMultipleInputOutput(); + auto model = CreateQuantizedModelMultipleInputOutput(TensorType_INT8); // Change model type. flatbuffers::FlatBufferBuilder builder; @@ -465,57 +522,6 @@ TEST(ModelInterface, Uint8MutipleInputOutput) { EXPECT_EQ(output_2->quantization->zero_point[0], 178); } -TEST(ModelInterface, Int8MutipleInputOutput) { - auto model = CreateQuantizedModelMultipleInputOutput(); - - // Change model type. - flatbuffers::FlatBufferBuilder builder; - EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_INT8, - TensorType_INT8), - kTfLiteOk); - - // Verify results. - EXPECT_EQ(model->subgraphs.size(), 1); - // TODO (b/158254056): Remove unused inputs and outputs from tensor list - // EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4); - EXPECT_EQ(model->subgraphs[0]->tensors.size(), 6); - EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2); - EXPECT_EQ(model->subgraphs[0]->inputs[0], 2); - EXPECT_EQ(model->subgraphs[0]->inputs[1], 3); - EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2); - EXPECT_EQ(model->subgraphs[0]->outputs[0], 4); - EXPECT_EQ(model->subgraphs[0]->outputs[1], 5); - EXPECT_EQ(model->operator_codes.size(), 3); - EXPECT_EQ(model->subgraphs[0]->operators.size(), 1); - EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1); - - auto fc_op = model->subgraphs[0]->operators[0].get(); - - auto input_1 = model->subgraphs[0]->tensors[fc_op->inputs[0]].get(); - EXPECT_EQ(input_1->name, "tensor_2"); - EXPECT_EQ(input_1->type, TensorType_INT8); - EXPECT_FLOAT_EQ(input_1->quantization->scale[0], 0.35); - EXPECT_EQ(input_1->quantization->zero_point[0], 28); - - auto input_2 = model->subgraphs[0]->tensors[fc_op->inputs[1]].get(); - EXPECT_EQ(input_2->name, "tensor_3"); - EXPECT_EQ(input_2->type, TensorType_INT8); - EXPECT_FLOAT_EQ(input_2->quantization->scale[0], 0.12); - EXPECT_EQ(input_2->quantization->zero_point[0], 50); - - auto output_1 = model->subgraphs[0]->tensors[fc_op->outputs[0]].get(); - EXPECT_EQ(output_1->name, "tensor_4"); - EXPECT_EQ(output_1->type, TensorType_INT8); - EXPECT_FLOAT_EQ(output_1->quantization->scale[0], 0.45); - EXPECT_EQ(output_1->quantization->zero_point[0], 28); - - auto output_2 = model->subgraphs[0]->tensors[fc_op->outputs[1]].get(); - EXPECT_EQ(output_2->name, "tensor_5"); - EXPECT_EQ(output_2->type, TensorType_INT8); - EXPECT_FLOAT_EQ(output_2->quantization->scale[0], 0.22); - EXPECT_EQ(output_2->quantization->zero_point[0], 50); -} - TEST(ModelInterface, Float) { // Create the model. std::unique_ptr input_model_t = CreateFloatModel(); From 426c9839aafcccccb390842407da3465711e3fa5 Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Wed, 10 Jun 2020 10:27:54 +0100 Subject: [PATCH 03/10] added comment and wqminor stylistic changes --- .../tools/optimize/modify_model_interface.cc | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index eead4999ca6..9e76257296e 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -45,9 +45,9 @@ struct TensorOpTensor { // Finds float tensors that are model inputs and is consumed by a quantize Op. // The returned TensorOpTensor should have reverse order. -std::vector GetInputTensors(ModelT* model, - ErrorReporter* error_reporter, - const TensorType& input_type) { +std::vector GetInputTensors(const TensorType& input_type, + ModelT* model, + ErrorReporter* error_reporter) { std::vector result; // Get all input tensors. for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); @@ -91,7 +91,10 @@ std::vector GetInputTensors(ModelT* model, "modify_model_interface currently only supports " "int8 and int16 quantized models."); } + // Usually the input model has to have the same quantization layers as the ones + // we're trying to remove. if (quant_output->type != input_type) { + //An exception from this is when we are setting the input or output type to UINT8. if (!(quant_output->type == TensorType_INT8 && input_type == TensorType_UINT8)) { TF_LITE_REPORT_ERROR(error_reporter, "Model's type incompatible with output type argument."); @@ -109,9 +112,9 @@ std::vector GetInputTensors(ModelT* model, // Finds float tensors that are model output and is consumed by a dequantize Op. // The returned TensorOpTensor should have reverse order. -std::vector GetOutputTensors(ModelT* model, - ErrorReporter* error_reporter, - const TensorType& output_type) { +std::vector GetOutputTensors(const TensorType& output_type, + ModelT* model, + ErrorReporter* error_reporter) { std::vector result; // Get all output tensors. for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); @@ -302,9 +305,9 @@ std::unique_ptr CreateMutableModelFromFile( return copied_model; } -int GetOriginalNumberOfTensors(ModelT* model, ErrorReporter* error_reporter, const TensorType& input_type, const TensorType& output_type) { - std::vector outputs = GetOutputTensors(model, error_reporter, output_type); - std::vector inputs = GetInputTensors(model, error_reporter, input_type); +int GetOriginalNumberOfTensors(const TensorType& input_type, const TensorType& output_type, ModelT* model, ErrorReporter* error_reporter) { + std::vector outputs = GetOutputTensors(output_type, model, error_reporter); + std::vector inputs = GetInputTensors(input_type, model, error_reporter); return model->subgraphs[0]->tensors.size() - outputs.size() - inputs.size(); } @@ -315,12 +318,12 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, const TensorType& output_type) { tflite::StderrReporter error_reporter; const int original_number_tensors = - GetOriginalNumberOfTensors(model, &error_reporter, input_type, output_type); + GetOriginalNumberOfTensors(input_type, output_type, model, &error_reporter); // Finds float tensors that are model output and are consumed by a float to int8/int16 // quantize Op. // Do output first since the tensors are added into input first., std::vector outputs = - GetOutputTensors(model, &error_reporter, output_type); + GetOutputTensors(output_type, model, &error_reporter); switch (output_type) { case TensorType_UINT8: SetOutputTypeToUINT8(model, outputs); @@ -335,7 +338,7 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, // Find float tensors that are model input and is consumed by a float to int8/int16 // quantize Op. - std::vector inputs = GetInputTensors(model, &error_reporter, input_type); + std::vector inputs = GetInputTensors(input_type, model, &error_reporter); switch (input_type) { case TensorType_UINT8: SetInputTypeToUINT8(model, inputs); From 1efc6cc1353cd97780a726f6ea37ba6d2f2753fd Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Wed, 10 Jun 2020 10:27:54 +0100 Subject: [PATCH 04/10] Added comment and minor stylistic changes --- .../tools/optimize/modify_model_interface.cc | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index eead4999ca6..9e76257296e 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -45,9 +45,9 @@ struct TensorOpTensor { // Finds float tensors that are model inputs and is consumed by a quantize Op. // The returned TensorOpTensor should have reverse order. -std::vector GetInputTensors(ModelT* model, - ErrorReporter* error_reporter, - const TensorType& input_type) { +std::vector GetInputTensors(const TensorType& input_type, + ModelT* model, + ErrorReporter* error_reporter) { std::vector result; // Get all input tensors. for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); @@ -91,7 +91,10 @@ std::vector GetInputTensors(ModelT* model, "modify_model_interface currently only supports " "int8 and int16 quantized models."); } + // Usually the input model has to have the same quantization layers as the ones + // we're trying to remove. if (quant_output->type != input_type) { + //An exception from this is when we are setting the input or output type to UINT8. if (!(quant_output->type == TensorType_INT8 && input_type == TensorType_UINT8)) { TF_LITE_REPORT_ERROR(error_reporter, "Model's type incompatible with output type argument."); @@ -109,9 +112,9 @@ std::vector GetInputTensors(ModelT* model, // Finds float tensors that are model output and is consumed by a dequantize Op. // The returned TensorOpTensor should have reverse order. -std::vector GetOutputTensors(ModelT* model, - ErrorReporter* error_reporter, - const TensorType& output_type) { +std::vector GetOutputTensors(const TensorType& output_type, + ModelT* model, + ErrorReporter* error_reporter) { std::vector result; // Get all output tensors. for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); @@ -302,9 +305,9 @@ std::unique_ptr CreateMutableModelFromFile( return copied_model; } -int GetOriginalNumberOfTensors(ModelT* model, ErrorReporter* error_reporter, const TensorType& input_type, const TensorType& output_type) { - std::vector outputs = GetOutputTensors(model, error_reporter, output_type); - std::vector inputs = GetInputTensors(model, error_reporter, input_type); +int GetOriginalNumberOfTensors(const TensorType& input_type, const TensorType& output_type, ModelT* model, ErrorReporter* error_reporter) { + std::vector outputs = GetOutputTensors(output_type, model, error_reporter); + std::vector inputs = GetInputTensors(input_type, model, error_reporter); return model->subgraphs[0]->tensors.size() - outputs.size() - inputs.size(); } @@ -315,12 +318,12 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, const TensorType& output_type) { tflite::StderrReporter error_reporter; const int original_number_tensors = - GetOriginalNumberOfTensors(model, &error_reporter, input_type, output_type); + GetOriginalNumberOfTensors(input_type, output_type, model, &error_reporter); // Finds float tensors that are model output and are consumed by a float to int8/int16 // quantize Op. // Do output first since the tensors are added into input first., std::vector outputs = - GetOutputTensors(model, &error_reporter, output_type); + GetOutputTensors(output_type, model, &error_reporter); switch (output_type) { case TensorType_UINT8: SetOutputTypeToUINT8(model, outputs); @@ -335,7 +338,7 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, // Find float tensors that are model input and is consumed by a float to int8/int16 // quantize Op. - std::vector inputs = GetInputTensors(model, &error_reporter, input_type); + std::vector inputs = GetInputTensors(input_type, model, &error_reporter); switch (input_type) { case TensorType_UINT8: SetInputTypeToUINT8(model, inputs); From 5f1c1ae431f6b230a4f7c680589ce4ad79cf6cf9 Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Wed, 10 Jun 2020 13:05:21 +0100 Subject: [PATCH 05/10] applied clang-format --- .../tools/optimize/modify_model_interface.cc | 58 +++++++++++-------- .../optimize/modify_model_interface_main.cc | 13 ++--- .../optimize/modify_model_interface_test.cc | 25 ++++---- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index 9e76257296e..e8e9229b1bb 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -19,8 +19,8 @@ limitations under the License. #include #include -#include "flatbuffers/flexbuffers.h" #include "absl/memory/memory.h" +#include "flatbuffers/flexbuffers.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/error_reporter.h" #include "tensorflow/lite/kernels/internal/compatibility.h" @@ -86,18 +86,22 @@ std::vector GetInputTensors(const TensorType& input_type, } const int model_input_index = input_tensors[input_tensor]; TensorT* quant_output = subgraph->tensors[op->outputs[0]].get(); - if (quant_output->type != TensorType_INT8 && quant_output->type != TensorType_INT16) { + if (quant_output->type != TensorType_INT8 && + quant_output->type != TensorType_INT16) { TF_LITE_REPORT_ERROR(error_reporter, "modify_model_interface currently only supports " "int8 and int16 quantized models."); } - // Usually the input model has to have the same quantization layers as the ones - // we're trying to remove. + // Usually the input model has to have the same quantization layers as the + // ones we're trying to remove. if (quant_output->type != input_type) { - //An exception from this is when we are setting the input or output type to UINT8. - if (!(quant_output->type == TensorType_INT8 && input_type == TensorType_UINT8)) { - TF_LITE_REPORT_ERROR(error_reporter, - "Model's type incompatible with output type argument."); + // An exception from this is when we are setting the input or output + // type to UINT8. + if (!(quant_output->type == TensorType_INT8 && + input_type == TensorType_UINT8)) { + TF_LITE_REPORT_ERROR( + error_reporter, + "Model's type incompatible with output type argument."); } } if (quant_output->quantization == nullptr) { @@ -153,7 +157,8 @@ std::vector GetOutputTensors(const TensorType& output_type, } const int model_output_index = output_tensors[output_tensor]; TensorT* dequant_input = subgraph->tensors[op->inputs[0]].get(); - if (dequant_input->type != TensorType_INT8 && dequant_input->type != TensorType_INT16) { + if (dequant_input->type != TensorType_INT8 && + dequant_input->type != TensorType_INT16) { // Currently only supports INT8 and INT16 quantized models. TF_LITE_REPORT_ERROR(error_reporter, "modify_model_interface currently only supports " @@ -161,9 +166,11 @@ std::vector GetOutputTensors(const TensorType& output_type, return {}; } if (dequant_input->type != output_type) { - if (!(dequant_input->type == TensorType_INT8 && output_type == TensorType_UINT8)) { - TF_LITE_REPORT_ERROR(error_reporter, - "Model's type incompatible with output type argument."); + if (!(dequant_input->type == TensorType_INT8 && + output_type == TensorType_UINT8)) { + TF_LITE_REPORT_ERROR( + error_reporter, + "Model's type incompatible with output type argument."); } } if (dequant_input->quantization == nullptr) { @@ -305,9 +312,13 @@ std::unique_ptr CreateMutableModelFromFile( return copied_model; } -int GetOriginalNumberOfTensors(const TensorType& input_type, const TensorType& output_type, ModelT* model, ErrorReporter* error_reporter) { - std::vector outputs = GetOutputTensors(output_type, model, error_reporter); - std::vector inputs = GetInputTensors(input_type, model, error_reporter); +int GetOriginalNumberOfTensors(const TensorType& input_type, + const TensorType& output_type, ModelT* model, + ErrorReporter* error_reporter) { + std::vector outputs = + GetOutputTensors(output_type, model, error_reporter); + std::vector inputs = + GetInputTensors(input_type, model, error_reporter); return model->subgraphs[0]->tensors.size() - outputs.size() - inputs.size(); } @@ -317,11 +328,11 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, ModelT* model, const TensorType& input_type, const TensorType& output_type) { tflite::StderrReporter error_reporter; - const int original_number_tensors = - GetOriginalNumberOfTensors(input_type, output_type, model, &error_reporter); - // Finds float tensors that are model output and are consumed by a float to int8/int16 - // quantize Op. - // Do output first since the tensors are added into input first., + const int original_number_tensors = GetOriginalNumberOfTensors( + input_type, output_type, model, &error_reporter); + // Finds float tensors that are model output and are consumed by a float to + // int8/int16 quantize Op. Do output first since the tensors are added into + // input first., std::vector outputs = GetOutputTensors(output_type, model, &error_reporter); switch (output_type) { @@ -336,9 +347,10 @@ TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, return kTfLiteError; } - // Find float tensors that are model input and is consumed by a float to int8/int16 - // quantize Op. - std::vector inputs = GetInputTensors(input_type, model, &error_reporter); + // Find float tensors that are model input and is consumed by a float to + // int8/int16 quantize Op. + std::vector inputs = + GetInputTensors(input_type, model, &error_reporter); switch (input_type) { case TensorType_UINT8: SetInputTypeToUINT8(model, inputs); diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_main.cc b/tensorflow/lite/tools/optimize/modify_model_interface_main.cc index 18e210332f0..b22c2baccaa 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_main.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_main.cc @@ -25,12 +25,10 @@ int main(int argc, char** argv) { return 1; } - const std::unordered_map supported_types - { - { "uint8", tflite::TensorType_UINT8 }, - { "int8", tflite::TensorType_INT8 }, - { "int16", tflite::TensorType_INT16 } - }; + const std::unordered_map supported_types{ + {"uint8", tflite::TensorType_UINT8}, + {"int8", tflite::TensorType_INT8}, + {"int16", tflite::TensorType_INT16}}; tflite::TensorType input = tflite::TensorType_INT8; tflite::TensorType output = tflite::TensorType_INT8; @@ -39,7 +37,8 @@ int main(int argc, char** argv) { input = supported_types.at(argv[3]); output = supported_types.at(argv[4]); } catch (const std::out_of_range&) { - printf("Only supports uint8, int8 and int16 for input and output interfaces"); + printf( + "Only supports uint8, int8 and int16 for input and output interfaces"); return 1; } diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc index acf9bf1a02d..88613c3c2be 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc @@ -14,11 +14,11 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/lite/tools/optimize/modify_model_interface.h" -#include - #include #include +#include + #include "absl/memory/memory.h" #include "tensorflow/lite/model.h" #include "tensorflow/lite/schema/schema_generated.h" @@ -30,7 +30,8 @@ namespace { using ::testing::ElementsAreArray; // Create a model with 1 quant, 1 FC, 1 dequant -std::unique_ptr CreateQuantizedModelSingleInputOutput(const TensorType& quantization_type) { +std::unique_ptr CreateQuantizedModelSingleInputOutput( + const TensorType& quantization_type) { auto model = absl::make_unique(); auto subgraph = absl::make_unique(); auto buffer = absl::make_unique(); @@ -118,7 +119,8 @@ std::unique_ptr CreateQuantizedModelSingleInputOutput(const TensorType& // Create a model with 2 quant, 1 FC, 2 dequant // The model mimics the behavior of the quantize_model.cc. -std::unique_ptr CreateQuantizedModelMultipleInputOutput(const TensorType& quantization_type) { +std::unique_ptr CreateQuantizedModelMultipleInputOutput( + const TensorType& quantization_type) { auto model = absl::make_unique(); auto subgraph = absl::make_unique(); auto buffer = absl::make_unique(); @@ -290,8 +292,7 @@ std::unique_ptr CreateFloatModel() { return model; } -struct ModelInterface: - ::testing::TestWithParam {}; +struct ModelInterface : ::testing::TestWithParam {}; TEST_P(ModelInterface, SingleInputOutput) { TensorType quantization_type = GetParam(); @@ -333,15 +334,14 @@ TEST_P(ModelInterface, SingleInputOutput) { } TEST_P(ModelInterface, MutipleInputOutput) { - TensorType quantization_type = GetParam(); auto model = CreateQuantizedModelMultipleInputOutput(quantization_type); // Change model type. - flatbuffers::FlatBufferBuilder builder; + flatbuffers::FlatBufferBuilder builder; EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type, - quantization_type), + quantization_type), kTfLiteOk); // Verify results. @@ -386,11 +386,8 @@ TEST_P(ModelInterface, MutipleInputOutput) { EXPECT_EQ(output_2->quantization->zero_point[0], 50); } -INSTANTIATE_TEST_SUITE_P( - MultipleInputOutputTests, - ModelInterface, - ::testing::Values(TensorType_INT8, TensorType_INT16) -); +INSTANTIATE_TEST_SUITE_P(MultipleInputOutputTests, ModelInterface, + ::testing::Values(TensorType_INT8, TensorType_INT16)); TEST(ModelInterface, MixedTypeSingleInputOutput) { auto model = CreateQuantizedModelSingleInputOutput(TensorType_INT8); From 8c10f56c9272a9d8246aee6382ca7dbe126b54d4 Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Thu, 11 Jun 2020 01:27:54 +0100 Subject: [PATCH 06/10] Stylistic changes --- tensorflow/lite/tools/optimize/modify_model_interface.cc | 8 ++++---- tensorflow/lite/tools/optimize/modify_model_interface.h | 2 +- .../lite/tools/optimize/modify_model_interface_main.cc | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index e8e9229b1bb..e7cd09b0006 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -72,7 +72,7 @@ std::vector GetInputTensors(const TensorType& input_type, continue; } if (op_code != BuiltinOperator_QUANTIZE) { - // Currently only supports INT8 and INT16 quantized models. + // Currently only supports int8 and int16 quantized models. TF_LITE_REPORT_ERROR( error_reporter, "modify_model_interface called on a model without quant/dequant."); @@ -96,7 +96,7 @@ std::vector GetInputTensors(const TensorType& input_type, // ones we're trying to remove. if (quant_output->type != input_type) { // An exception from this is when we are setting the input or output - // type to UINT8. + // type to uint8. if (!(quant_output->type == TensorType_INT8 && input_type == TensorType_UINT8)) { TF_LITE_REPORT_ERROR( @@ -143,7 +143,7 @@ std::vector GetOutputTensors(const TensorType& output_type, continue; } if (op_code != BuiltinOperator_DEQUANTIZE) { - // Currently only supports INT8 and INT16 quantized models. + // Currently only supports int8 and int16 quantized models. TF_LITE_REPORT_ERROR( error_reporter, "modify_model_interface called on a model without quant/dequant."); @@ -159,7 +159,7 @@ std::vector GetOutputTensors(const TensorType& output_type, TensorT* dequant_input = subgraph->tensors[op->inputs[0]].get(); if (dequant_input->type != TensorType_INT8 && dequant_input->type != TensorType_INT16) { - // Currently only supports INT8 and INT16 quantized models. + // Currently only supports int8 and int16 quantized models. TF_LITE_REPORT_ERROR(error_reporter, "modify_model_interface currently only supports " "int8 and int16 quantized models."); diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.h b/tensorflow/lite/tools/optimize/modify_model_interface.h index b3a39d63801..5711a615812 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.h +++ b/tensorflow/lite/tools/optimize/modify_model_interface.h @@ -24,7 +24,7 @@ namespace optimize { // Changes the interface of a quantized model. This method allows the users to // replace float interface with other types. // This populates the builder with the new model. -// Currently only int8, int16 and unit8 are supported. +// Currently only int8, int16 and uint8 are supported. // // Note: This is a private API, subject to change. TfLiteStatus ModifyModelInterface(flatbuffers::FlatBufferBuilder* builder, diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_main.cc b/tensorflow/lite/tools/optimize/modify_model_interface_main.cc index b22c2baccaa..6272ef30777 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_main.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_main.cc @@ -38,7 +38,7 @@ int main(int argc, char** argv) { output = supported_types.at(argv[4]); } catch (const std::out_of_range&) { printf( - "Only supports uint8, int8 and int16 for input and output interfaces"); + "Only supports uint8, int8 and int16 for input and output types"); return 1; } From bb027421700c1fed19c7951ce38bd6d68e4d99c3 Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Mon, 15 Jun 2020 09:57:38 +0100 Subject: [PATCH 07/10] upgraded error message to show more information --- .../tools/optimize/modify_model_interface.cc | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index e7cd09b0006..785abc2ae97 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -18,6 +18,7 @@ limitations under the License. #include #include #include +#include #include "absl/memory/memory.h" #include "flatbuffers/flexbuffers.h" @@ -92,16 +93,19 @@ std::vector GetInputTensors(const TensorType& input_type, "modify_model_interface currently only supports " "int8 and int16 quantized models."); } - // Usually the input model has to have the same quantization layers as the - // ones we're trying to remove. - if (quant_output->type != input_type) { - // An exception from this is when we are setting the input or output - // type to uint8. - if (!(quant_output->type == TensorType_INT8 && - input_type == TensorType_UINT8)) { + + // The input type must be the same as the model quantization type + if (input_type != quant_output->type) { + // An exception, allow for UINT8 input type for INT8 quantized model. + if (!(input_type == TensorType_UINT8 && + quant_output->type == TensorType_INT8)) { TF_LITE_REPORT_ERROR( - error_reporter, - "Model's type incompatible with output type argument."); + error_reporter, + "The %s input type is incompatible with %s quantized models. " + "To resolve this error, change the input_type to a compatible one. " + "See: modify_model_interface.cc", + EnumNameTensorType(input_type), + EnumNameTensorType(quant_output->type)); } } if (quant_output->quantization == nullptr) { @@ -165,12 +169,17 @@ std::vector GetOutputTensors(const TensorType& output_type, "int8 and int16 quantized models."); return {}; } - if (dequant_input->type != output_type) { - if (!(dequant_input->type == TensorType_INT8 && - output_type == TensorType_UINT8)) { + if (output_type != dequant_input->type) { + // An exception, allow for UINT8 input type for INT8 quantized model. + if (!(output_type == TensorType_UINT8 && + dequant_input->type == TensorType_INT8)) { TF_LITE_REPORT_ERROR( - error_reporter, - "Model's type incompatible with output type argument."); + error_reporter, + "The %s output type is incompatible with %s quantized models. " + "To resolve this error, change the output_type to a compatible one. " + "See: modify_model_interface.cc", + EnumNameTensorType(output_type), + EnumNameTensorType(dequant_input->type)); } } if (dequant_input->quantization == nullptr) { From 240fd79798f82184aaf60d88e94caf360c7ca56d Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Mon, 15 Jun 2020 10:23:18 +0100 Subject: [PATCH 08/10] removed unnecessary override type --- .../tools/optimize/modify_model_interface.cc | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index 785abc2ae97..643e9493ce7 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -405,30 +405,8 @@ TfLiteStatus ModifyModelInterface(const string& input_file, absl::make_unique(); flatbuffers::FlatBufferBuilder builder; - tflite::TensorType input_override_type; - tflite::TensorType output_override_type; - - switch (input_type) { - case tflite::TensorType_UINT8: - case tflite::TensorType_INT8: - case tflite::TensorType_INT16: - input_override_type = input_type; - break; - default: - return kTfLiteError; - } - switch (output_type) { - case tflite::TensorType_UINT8: - case tflite::TensorType_INT8: - case tflite::TensorType_INT16: - output_override_type = output_type; - break; - default: - return kTfLiteError; - } - auto status = ModifyModelInterface(&builder, tflite_model.get(), - input_override_type, output_override_type); + input_type, output_type); TFLITE_DCHECK_EQ(status, kTfLiteOk); WriteFile(output_file, builder.GetBufferPointer(), builder.GetSize()); From ed94f8da21118af331687cb7b7b8b02c043d0a68 Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Wed, 1 Jul 2020 14:19:20 +0100 Subject: [PATCH 09/10] removed unnecessary import --- tensorflow/lite/tools/optimize/modify_model_interface.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface.cc b/tensorflow/lite/tools/optimize/modify_model_interface.cc index efd062642f8..b9779ee7adf 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface.cc @@ -18,7 +18,6 @@ limitations under the License. #include #include #include -#include #include "absl/memory/memory.h" #include "flatbuffers/flexbuffers.h" From f03a29b93d89f5cfd6828d13884677c8ac12bf87 Mon Sep 17 00:00:00 2001 From: Tamas Nyiri Date: Fri, 10 Jul 2020 17:40:01 +0100 Subject: [PATCH 10/10] added missing argument --- tensorflow/lite/tools/optimize/modify_model_interface_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc index a1153fd8c9b..fd267575d68 100644 --- a/tensorflow/lite/tools/optimize/modify_model_interface_test.cc +++ b/tensorflow/lite/tools/optimize/modify_model_interface_test.cc @@ -520,7 +520,7 @@ TEST(ModelInterface, Uint8MutipleInputOutput) { } TEST(ModelInterface, Int8MutipleInputOutput) { - auto model = CreateQuantizedModelMultipleInputOutput(); + auto model = CreateQuantizedModelMultipleInputOutput(TensorType_INT8); // Change model type. flatbuffers::FlatBufferBuilder builder;