From da56f874ef781aad87508871b04e2a9dffbc0052 Mon Sep 17 00:00:00 2001 From: Jian Li Date: Thu, 18 Apr 2019 16:46:31 -0700 Subject: [PATCH] Add version to op property and set version during quantization. PiperOrigin-RevId: 244284444 --- tensorflow/lite/tools/optimize/BUILD | 1 + tensorflow/lite/tools/optimize/model_utils.cc | 14 + tensorflow/lite/tools/optimize/model_utils.h | 3 + .../lite/tools/optimize/operator_property.cc | 288 ++++++++++++------ .../lite/tools/optimize/operator_property.h | 3 + .../lite/tools/optimize/quantize_model.cc | 2 + .../tools/optimize/quantize_model_test.cc | 67 +++- 7 files changed, 276 insertions(+), 102 deletions(-) diff --git a/tensorflow/lite/tools/optimize/BUILD b/tensorflow/lite/tools/optimize/BUILD index ad9c992f790..4db1eee439a 100644 --- a/tensorflow/lite/tools/optimize/BUILD +++ b/tensorflow/lite/tools/optimize/BUILD @@ -31,6 +31,7 @@ cc_library( srcs = ["model_utils.cc"], hdrs = ["model_utils.h"], deps = [ + ":operator_property", "//tensorflow/lite:framework", "//tensorflow/lite/kernels/internal:tensor_utils", "//tensorflow/lite/kernels/internal:types", diff --git a/tensorflow/lite/tools/optimize/model_utils.cc b/tensorflow/lite/tools/optimize/model_utils.cc index 0243671a962..96e42eafb32 100644 --- a/tensorflow/lite/tools/optimize/model_utils.cc +++ b/tensorflow/lite/tools/optimize/model_utils.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include "tensorflow/lite/tools/optimize/model_utils.h" + #include #include "absl/memory/memory.h" @@ -20,6 +21,7 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/model.h" #include "tensorflow/lite/schema/schema_generated.h" +#include "tensorflow/lite/tools/optimize/operator_property.h" namespace tflite { namespace optimize { @@ -121,6 +123,18 @@ bool HasMinMax(const TensorT* tensor) { !tensor->quantization->max.empty(); } +TfLiteStatus SetOperatorCodeVersion(ModelT* model) { + for (int i = 0; i < model->operator_codes.size(); ++i) { + OperatorCodeT* op_code = model->operator_codes[i].get(); + const BuiltinOperator op_buildin_code = op_code->builtin_code; + operator_property::OperatorProperty property; + TF_LITE_ENSURE_STATUS( + operator_property::GetOperatorProperty(op_buildin_code, &property)); + op_code->version = property.version; + } + return kTfLiteOk; +} + } // namespace utils } // namespace optimize } // namespace tflite diff --git a/tensorflow/lite/tools/optimize/model_utils.h b/tensorflow/lite/tools/optimize/model_utils.h index fea87e82678..23380fc3492 100644 --- a/tensorflow/lite/tools/optimize/model_utils.h +++ b/tensorflow/lite/tools/optimize/model_utils.h @@ -53,6 +53,9 @@ bool IsQuantized(const SubGraphT* subgraph, int tensor_index); bool HasMinMax(const TensorT* tensor); +// Set version of OperatorCode. +TfLiteStatus SetOperatorCodeVersion(ModelT* model); + } // namespace utils } // namespace optimize } // namespace tflite diff --git a/tensorflow/lite/tools/optimize/operator_property.cc b/tensorflow/lite/tools/optimize/operator_property.cc index 01a10a57567..1c0eaba4cb0 100644 --- a/tensorflow/lite/tools/optimize/operator_property.cc +++ b/tensorflow/lite/tools/optimize/operator_property.cc @@ -19,7 +19,7 @@ namespace optimize { namespace operator_property { TfLiteStatus GetOperatorProperty(const BuiltinOperator& op, OperatorProperty* property) { - if (op == BuiltinOperator_ADD || op == BuiltinOperator_MUL) { + if (op == BuiltinOperator_ADD) { property->per_axis = false; property->per_axis_index = 0; property->arbitrary_inputs = false; @@ -29,106 +29,7 @@ TfLiteStatus GetOperatorProperty(const BuiltinOperator& op, property->restrict_same_input_output_scale = false; property->restriction_on_output = false; property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_AVERAGE_POOL_2D || - op == BuiltinOperator_MAX_POOL_2D || op == BuiltinOperator_SQUEEZE) { - property->per_axis = false; - property->per_axis_index = 0; - property->arbitrary_inputs = false; - property->input_indexes = {0}; - property->output_indexes = {0}; - property->biases = {}; - property->restrict_same_input_output_scale = true; - property->restriction_on_output = false; - property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_CONCATENATION) { - property->per_axis = false; - property->per_axis_index = 0; - property->arbitrary_inputs = true; - property->input_indexes = {}; - property->output_indexes = {0}; - property->biases = {}; - property->restrict_same_input_output_scale = true; - property->restriction_on_output = false; - property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_CONV_2D) { - property->per_axis = true; - property->per_axis_index = 0; - property->arbitrary_inputs = false; - property->input_indexes = {0, 1}; - property->output_indexes = {0}; - property->biases = {2}; - property->restrict_same_input_output_scale = false; - property->restriction_on_output = false; - property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_DEPTHWISE_CONV_2D) { - property->per_axis = true; - property->per_axis_index = 3; - property->arbitrary_inputs = false; - property->input_indexes = {0, 1}; - property->output_indexes = {0}; - property->biases = {2}; - property->restrict_same_input_output_scale = false; - property->restriction_on_output = false; - property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_FULLY_CONNECTED) { - property->per_axis = false; - property->per_axis_index = 0; - property->arbitrary_inputs = false; - property->input_indexes = {0, 1}; - property->output_indexes = {0}; - property->biases = {2}; - property->restrict_same_input_output_scale = false; - property->restriction_on_output = false; - property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_MEAN || op == BuiltinOperator_PAD || - op == BuiltinOperator_QUANTIZE || op == BuiltinOperator_RESHAPE) { - property->per_axis = false; - property->per_axis_index = 0; - property->arbitrary_inputs = false; - property->input_indexes = {0}; - property->output_indexes = {0}; - property->biases = {}; - property->restrict_same_input_output_scale = false; - property->restriction_on_output = false; - property->restricted_value_on_output = {}; - return kTfLiteOk; - } - if (op == BuiltinOperator_SOFTMAX) { - // Softmax requires output with 1/256 as scale and -128 as zero point. - property->per_axis = false; - property->per_axis_index = 0; - property->arbitrary_inputs = false; - property->input_indexes = {0}; - property->output_indexes = {0}; - property->biases = {}; - property->restrict_same_input_output_scale = false; - property->restriction_on_output = true; - property->restricted_value_on_output = {1 / 256.0, -128}; - return kTfLiteOk; - } - if (op == BuiltinOperator_TANH) { - // Tanh requires output with 1/128 as scale and 0 as zero point. - property->per_axis = false; - property->per_axis_index = 0; - property->arbitrary_inputs = false; - property->input_indexes = {0}; - property->output_indexes = {0}; - property->biases = {}; - property->restrict_same_input_output_scale = false; - property->restriction_on_output = true; - property->restricted_value_on_output = {1 / 128.0, 0}; + property->version = 2; return kTfLiteOk; } if (op == BuiltinOperator_ARG_MAX) { @@ -142,6 +43,191 @@ TfLiteStatus GetOperatorProperty(const BuiltinOperator& op, property->restrict_same_input_output_scale = false; property->restriction_on_output = false; property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_AVERAGE_POOL_2D) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = true; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_CONCATENATION) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = true; + property->input_indexes = {}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = true; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_CONV_2D) { + property->per_axis = true; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0, 1}; + property->output_indexes = {0}; + property->biases = {2}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_DEPTHWISE_CONV_2D) { + property->per_axis = true; + property->per_axis_index = 3; + property->arbitrary_inputs = false; + property->input_indexes = {0, 1}; + property->output_indexes = {0}; + property->biases = {2}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 3; + return kTfLiteOk; + } + if (op == BuiltinOperator_FULLY_CONNECTED) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0, 1}; + property->output_indexes = {0}; + property->biases = {2}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 4; + return kTfLiteOk; + } + if (op == BuiltinOperator_MAX_POOL_2D) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = true; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_MEAN) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_MUL) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0, 1}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_PAD) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_QUANTIZE) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 1; + return kTfLiteOk; + } + if (op == BuiltinOperator_RESHAPE) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 1; + return kTfLiteOk; + } + if (op == BuiltinOperator_SQUEEZE) { + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = true; + property->restriction_on_output = false; + property->restricted_value_on_output = {}; + property->version = 1; + return kTfLiteOk; + } + if (op == BuiltinOperator_SOFTMAX) { + // Softmax requires output with 1/256 as scale and -128 as zero point. + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = true; + property->restricted_value_on_output = {1 / 256.0, -128}; + property->version = 2; + return kTfLiteOk; + } + if (op == BuiltinOperator_TANH) { + // Tanh requires output with 1/128 as scale and 0 as zero point. + property->per_axis = false; + property->per_axis_index = 0; + property->arbitrary_inputs = false; + property->input_indexes = {0}; + property->output_indexes = {0}; + property->biases = {}; + property->restrict_same_input_output_scale = false; + property->restriction_on_output = true; + property->restricted_value_on_output = {1 / 128.0, 0}; + property->version = 2; return kTfLiteOk; } return kTfLiteError; diff --git a/tensorflow/lite/tools/optimize/operator_property.h b/tensorflow/lite/tools/optimize/operator_property.h index dd076b7a0fa..7de9545b765 100644 --- a/tensorflow/lite/tools/optimize/operator_property.h +++ b/tensorflow/lite/tools/optimize/operator_property.h @@ -44,6 +44,9 @@ struct OperatorProperty { bool restrict_same_input_output_scale; bool restriction_on_output; std::pair restricted_value_on_output; + + // Op version. + int version; }; TfLiteStatus GetOperatorProperty(const BuiltinOperator& op, diff --git a/tensorflow/lite/tools/optimize/quantize_model.cc b/tensorflow/lite/tools/optimize/quantize_model.cc index 027d1216899..4e50950d696 100644 --- a/tensorflow/lite/tools/optimize/quantize_model.cc +++ b/tensorflow/lite/tools/optimize/quantize_model.cc @@ -35,6 +35,7 @@ namespace tflite { namespace optimize { namespace { + TfLiteStatus QuantizeBias(ModelT* model, const TensorT* input_tensor, const TensorT* weight_tensor, TensorT* bias_tensor, bool is_per_channel, int channel_dim_index, @@ -538,6 +539,7 @@ TfLiteStatus QuantizeModel(flatbuffers::FlatBufferBuilder* builder, QuantizeWeightsInputOutput(builder, model, error_reporter)); TF_LITE_ENSURE_STATUS(ApplyConstraints(builder, model, error_reporter)); TF_LITE_ENSURE_STATUS(QuantizeBiases(builder, model, error_reporter)); + TF_LITE_ENSURE_STATUS(utils::SetOperatorCodeVersion(model)); SetInputAndOutputTypes(model, input_type, output_type); flatbuffers::Offset output_model_location = diff --git a/tensorflow/lite/tools/optimize/quantize_model_test.cc b/tensorflow/lite/tools/optimize/quantize_model_test.cc index f02e93fd8c8..c6ef2d4792a 100644 --- a/tensorflow/lite/tools/optimize/quantize_model_test.cc +++ b/tensorflow/lite/tools/optimize/quantize_model_test.cc @@ -117,6 +117,10 @@ TEST_F(QuantizeConvModelTest, TensorShapesAndStructureIsUnchanged) { EXPECT_EQ(quant_tensor->name, float_tensor->name()->str()); } } + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_CONV_2D); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } TEST_F(QuantizeConvModelTest, OperatorsAreUnchanged) { @@ -129,7 +133,7 @@ TEST_F(QuantizeConvModelTest, OperatorsAreUnchanged) { const auto float_model_op = readonly_model_->operator_codes()->Get(i); EXPECT_EQ(model_.operator_codes[i]->builtin_code, float_model_op->builtin_code()); - EXPECT_EQ(model_.operator_codes[i]->version, float_model_op->version()); + EXPECT_EQ(model_.operator_codes[i]->version, 2); } ASSERT_EQ(model_.subgraphs.size(), readonly_model_->subgraphs()->size()); @@ -359,6 +363,14 @@ TEST_F(QuantizeConcatModelTest, AddRequantBeforeConcat) { EXPECT_EQ(concat->inputs[0], 3); EXPECT_EQ(concat->inputs[1], 1); EXPECT_EQ(concat->outputs[0], 2); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 2); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, + BuiltinOperator_CONCATENATION); + EXPECT_EQ(model_.operator_codes[0]->version, 2); + EXPECT_EQ(model_.operator_codes[1]->builtin_code, BuiltinOperator_QUANTIZE); + EXPECT_EQ(model_.operator_codes[1]->version, 1); } class QuantizeConvModel1Test : public QuantizeModelTest { @@ -455,6 +467,11 @@ TEST_F(QuantizeConvModel1Test, VerifyConvQuantizationWithUnitScale) { EXPECT_NEAR(dequantized_value, weights_float_buffer[element_idx], eps); } } + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_CONV_2D); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } class QuantizeConvModel2Test : public QuantizeModelTest { @@ -548,6 +565,11 @@ TEST_F(QuantizeConvModel2Test, VerifyConvQuantization) { EXPECT_EQ(zero_point, 0); } } + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_CONV_2D); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } class QuantizeSoftmaxTest : public QuantizeModelTest { @@ -603,6 +625,11 @@ TEST_F(QuantizeSoftmaxTest, VerifySoftmaxQuantization) { ASSERT_EQ(output_quant_params->zero_point.size(), 1); ASSERT_EQ(1.0f / 256.0f, output_quant_params->scale[0]); ASSERT_EQ(-128, output_quant_params->zero_point[0]); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_SOFTMAX); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } class QuantizeAvgPoolTest : public QuantizeModelTest { @@ -658,6 +685,12 @@ TEST_F(QuantizeAvgPoolTest, VerifyAvgPoolQuantization) { EXPECT_EQ(input_quant_params->min[0], output_quant_params->min[0]); EXPECT_EQ(input_quant_params->max[0], output_quant_params->max[0]); EXPECT_EQ(input_quant_params->scale[0], output_quant_params->scale[0]); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, + BuiltinOperator_AVERAGE_POOL_2D); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } class QuantizeMultiInputAddWithReshapeTest : public QuantizeModelTest { @@ -707,6 +740,13 @@ TEST_F(QuantizeMultiInputAddWithReshapeTest, VerifyReshapeQuantization) { ASSERT_EQ(float_output_quant_params->max()->size(), 1); ASSERT_EQ(output_quant_params->min.size(), 1); ASSERT_EQ(output_quant_params->max.size(), 1); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 2); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_ADD); + EXPECT_EQ(model_.operator_codes[0]->version, 2); + EXPECT_EQ(model_.operator_codes[1]->builtin_code, BuiltinOperator_RESHAPE); + EXPECT_EQ(model_.operator_codes[1]->version, 1); } TEST_F(QuantizeMultiInputAddWithReshapeTest, VerifyAddQuantization) { @@ -751,6 +791,13 @@ TEST_F(QuantizeMultiInputAddWithReshapeTest, VerifyAddQuantization) { ASSERT_EQ(float_output_quant_params->max()->size(), 1); ASSERT_EQ(output_quant_params->min.size(), 1); ASSERT_EQ(output_quant_params->max.size(), 1); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 2); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_ADD); + EXPECT_EQ(model_.operator_codes[0]->version, 2); + EXPECT_EQ(model_.operator_codes[1]->builtin_code, BuiltinOperator_RESHAPE); + EXPECT_EQ(model_.operator_codes[1]->version, 1); } class QuantizeConstInputTest : public QuantizeModelTest { @@ -788,6 +835,11 @@ TEST_F(QuantizeConstInputTest, VerifyConstOpInput) { } EXPECT_EQ(subgraph->tensors[op->outputs[0]].get()->type, TensorType_INT8); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_ADD); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } class QuantizeArgMaxTest : public QuantizeModelTest { @@ -825,6 +877,11 @@ TEST_F(QuantizeArgMaxTest, VerifyArgMax) { // The output of ArgMax should still be the same type. ASSERT_EQ(float_graph->tensors()->Get(op->outputs[0])->type(), subgraph->tensors[op->outputs[0]].get()->type); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 1); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, BuiltinOperator_ARG_MAX); + EXPECT_EQ(model_.operator_codes[0]->version, 2); } class QuantizeFCTest : public QuantizeModelTest { @@ -867,6 +924,14 @@ TEST_F(QuantizeFCTest, VerifyFC) { ASSERT_EQ(float_graph->tensors()->Get(op->outputs[0])->type(), TensorType_FLOAT32); EXPECT_EQ(subgraph->tensors[op->outputs[0]].get()->type, TensorType_INT8); + + // check op and versioning. + EXPECT_EQ(model_.operator_codes.size(), 2); + EXPECT_EQ(model_.operator_codes[0]->builtin_code, + BuiltinOperator_FULLY_CONNECTED); + EXPECT_EQ(model_.operator_codes[0]->version, 4); + EXPECT_EQ(model_.operator_codes[1]->builtin_code, BuiltinOperator_RESHAPE); + EXPECT_EQ(model_.operator_codes[1]->version, 1); } } // namespace