Add version to op property and set version during quantization.

PiperOrigin-RevId: 244284444
This commit is contained in:
Jian Li 2019-04-18 16:46:31 -07:00 committed by TensorFlower Gardener
parent 00d9ab673c
commit da56f874ef
7 changed files with 276 additions and 102 deletions

View File

@ -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",

View File

@ -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 <memory>
#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

View File

@ -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

View File

@ -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;

View File

@ -44,6 +44,9 @@ struct OperatorProperty {
bool restrict_same_input_output_scale;
bool restriction_on_output;
std::pair<float, float> restricted_value_on_output;
// Op version.
int version;
};
TfLiteStatus GetOperatorProperty(const BuiltinOperator& op,

View File

@ -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<Model> output_model_location =

View File

@ -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