Use Requantize after per-channel Conv only if tensor min/max is different from RELU min/max beyond a limit. Also rectify RELU1 bound to (-1, 1)

PiperOrigin-RevId: 317162909
Change-Id: Ice90226436cccf49507bd17877222da755d22644
This commit is contained in:
Sachin Joglekar 2020-06-18 12:58:11 -07:00 committed by TensorFlower Gardener
parent b0418130b4
commit 07d5aa2309
2 changed files with 92 additions and 3 deletions

View File

@ -16,6 +16,7 @@ limitations under the License.
#include <stdint.h>
#include <cmath>
#include <limits>
#include "tensorflow/lite/c/builtin_op_data.h"
@ -197,7 +198,7 @@ TfLiteStatus Conv2dOpBuilder::PopulateSubGraph(const TfLiteIntArray* inputs,
conv_output_min = 0;
conv_output_max = 6;
} else if (activation == kTfLiteActRelu1) {
conv_output_min = 0;
conv_output_min = -1;
conv_output_max = 1;
} else if (activation == kTfLiteActRelu) {
conv_output_min = 0;
@ -351,8 +352,12 @@ TfLiteStatus Conv2dOpBuilder::PopulateSubGraph(const TfLiteIntArray* inputs,
output_max_tensor = AddOutput(sizeof(float), 4, kScalarShape);
}
// Requantize if activation was not None.
if (activation != kTfLiteActNone) {
// Requantize if activation was not None & the TFLite tensor's min/max is
// different (diff > 1e-2) from the RELU bounds.
const float min_bound_diff = std::abs(conv_output_min - output_min);
const float max_bound_diff = std::abs(conv_output_max - output_max);
if (activation != kTfLiteActNone &&
(min_bound_diff > 0.01 || max_bound_diff > 0.01)) {
auto* requantized_min_const = graph_builder_->AddConstNodeWithData(
kScalarShape, reinterpret_cast<char*>(&output_min), sizeof(output_min));
auto* requantized_max_const = graph_builder_->AddConstNodeWithData(

View File

@ -207,6 +207,43 @@ TEST(QuantizedConvolutionOpModel, SimpleConvTestReLU6Activation) {
1e-5)));
}
// Same as above, but the output min/max matches the RELU bounds.
// Therefore, a Requantize node will not get added after Supernode.
TEST(QuantizedConvolutionOpModel,
SimpleConvTestReLU6Activation_NoRequantizeRequired) {
QuantizedConvolutionOpModel m(
BuiltinOperator_CONV_2D, {TensorType_UINT8, {2, 2, 4, 1}, -63.5, 64},
{TensorType_UINT8, {3, 2, 2, 1}, -63.5, 64}, {TensorType_UINT8, {}, 0, 6},
Padding_VALID, /**dilation_factor**/ 1,
/**stride**/ 2, ActivationFunctionType_RELU6);
m.SetInput({
// First batch
1, 1, 1, 1, // row = 1
2, 2, 2, 2, // row = 2
// Second batch
1, 2, 3, 4, // row = 1
1, 2, 3, 4, // row = 2
});
m.SetFilter({
1, 2, 3, 4, // first 2x2 filter
-1, 1, -1, 1, // second 2x2 filter
-1, -1, 1, 1, // third 2x2 filter
});
m.SetBias({1, 2, 3});
m.ApplyDelegateAndInvoke();
EXPECT_THAT(m.GetDequantizedOutput<uint8_t>(),
ElementsAreArray(ArrayFloatNear(
{
6, 2, 5, // first batch, left
6, 2, 5, // first batch, right
6, 4, 3, // second batch, left
6, 4, 3, // second batch, right
},
2e-2)));
}
TEST(QuantizedConvolutionOpModel, SimplePerTensor_Int8) {
QuantizedConvolutionOpModel m(
BuiltinOperator_CONV_2D,
@ -512,6 +549,53 @@ TEST(QuantizedConvolutionOpModel, DepthwiseConvSimplePerTensor_Int8) {
ElementsAreArray(ArrayFloatNear({43, 48, 40, 52, 3, -4, 4, 4}, 0.6f)));
}
TEST(QuantizedConvolutionOpModel, DepthwiseConvSimplePerTensor_Int8_RELU1) {
QuantizedConvolutionOpModel m(
BuiltinOperator_DEPTHWISE_CONV_2D,
{TensorType_INT8, {1, 2, 3, 1}, -63.5, 64, 0.5, -1},
{TensorType_INT8,
// [1 * 2 * 2 * 4] as [input_channel, y, x, output_channel]
{1, 2, 2, 4},
0,
0,
0,
0,
/*per_channel_quantization=*/true,
/*per_channel_quantization_scales=*/{0.1, 2, 3, 0.4},
/*per_channel_quantization_offsets=*/{0, 0, 0, 0},
/*channel_index=*/3},
{TensorType_INT8, {}, -63.5, 64, 0.5, -1}, Padding_VALID,
/**dilation_factor**/ 1,
/**stride**/ 1, ActivationFunctionType_RELU_N1_TO_1);
m.SetInt8Input({
// [1 * 2 * 3 * 1] as [batch, y, x, input_channel]
3, // batch = 0, y = 0, x = 0
1, // batch = 0, y = 0, x = 1
-2, // batch = 0, y = 0, x = 2
4, // batch = 0, y = 1, x = 0
2, // batch = 0, y = 1, x = 1
-4, // batch = 0, y = 1, x = 2
});
m.SetPerChannelQuantizedFilter({
// [1 * 2 * 2 * 4] as [input_channel, y, x, output_channel]
// depth multiplier = 2
1, 2, 3, 4, // y = 0, x = 0
3, 4, 5, 6, // y = 0, x = 1
7, 8, 5, 6, // y = 1, x = 0
3, 4, 1, 2, // y = 1, x = 1
});
m.SetPerChannelQuantizedBias({3, -2, 4, 6});
// Reference output.
m.Invoke();
auto reference_output = m.GetDequantizedOutput<int8_t>();
m.ApplyDelegateAndInvoke();
EXPECT_THAT(m.GetDequantizedOutput<int8_t>(),
ElementsAreArray(ArrayFloatNear(reference_output, 1e-2)));
}
TEST(QuantizedConvolutionOpModel, DepthwiseConvSimplePerAxis_Int8) {
QuantizedConvolutionOpModel m(
BuiltinOperator_DEPTHWISE_CONV_2D,