Add tests for quantization limits. Ensures that quantization clipping is performed correctly for conv and depthwise_conv.

PiperOrigin-RevId: 284831615
Change-Id: I007f030cb221a7e24ce8ef8fb7682c51c5f32cca
This commit is contained in:
Nat Jeffries 2019-12-10 13:03:34 -08:00 committed by TensorFlower Gardener
parent 8fda428bd1
commit 635a3a0f82
7 changed files with 272 additions and 65 deletions

View File

@ -61,6 +61,7 @@ cc_library(
], ],
deps = [ deps = [
"//tensorflow/lite/c:common", "//tensorflow/lite/c:common",
"//tensorflow/lite/kernels:op_macros",
], ],
) )

View File

@ -153,6 +153,8 @@ TfLiteStatus EvalQuantizedPerChannel(TfLiteContext* context, TfLiteNode* node,
// TODO(b/130439627): Use calculated value for clamping. // TODO(b/130439627): Use calculated value for clamping.
op_params.quantized_activation_min = std::numeric_limits<int8_t>::min(); op_params.quantized_activation_min = std::numeric_limits<int8_t>::min();
op_params.quantized_activation_max = std::numeric_limits<int8_t>::max(); op_params.quantized_activation_max = std::numeric_limits<int8_t>::max();
#if defined(ARM_MATH_DSP) && defined(ARM_MATH_LOOPUNROLL)
RuntimeShape filter_shape = GetTensorShape(filter); RuntimeShape filter_shape = GetTensorShape(filter);
const int filter_height = filter_shape.Dims(1); const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2); const int filter_width = filter_shape.Dims(2);
@ -165,7 +167,6 @@ TfLiteStatus EvalQuantizedPerChannel(TfLiteContext* context, TfLiteNode* node,
const int output_width = output_shape.Dims(2); const int output_width = output_shape.Dims(2);
RuntimeShape bias_shape = GetTensorShape(bias); RuntimeShape bias_shape = GetTensorShape(bias);
#if defined(ARM_MATH_DSP) && defined(ARM_MATH_LOOPUNROLL)
if (op_params.depth_multiplier == 1) { if (op_params.depth_multiplier == 1) {
int16_t* buf = nullptr; int16_t* buf = nullptr;
const int32_t buf_size = arm_depthwise_conv_s8_opt_get_buffer_size( const int32_t buf_size = arm_depthwise_conv_s8_opt_get_buffer_size(
@ -216,6 +217,7 @@ TfLiteStatus EvalQuantizedPerChannel(TfLiteContext* context, TfLiteNode* node,
GetTensorData<int8>(filter), GetTensorShape(bias), GetTensorData<int8>(filter), GetTensorShape(bias),
GetTensorData<int32>(bias), GetTensorShape(output), GetTensorData<int32>(bias), GetTensorShape(output),
GetTensorData<int8>(output)); GetTensorData<int8>(output));
#endif #endif
return kTfLiteOk; return kTfLiteOk;
} }
@ -276,6 +278,7 @@ TfLiteStatus EvalQuantized(TfLiteContext* context, TfLiteNode* node,
op_params.output_multiplier); op_params.output_multiplier);
} else } else
#endif #endif
{ {
tflite::reference_ops::DepthwiseConv( tflite::reference_ops::DepthwiseConv(
op_params, GetTensorShape(input), GetTensorData<uint8_t>(input), op_params, GetTensorShape(input), GetTensorData<uint8_t>(input),
@ -304,7 +307,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
OpData data; OpData data;
if (input->type != kTfLiteFloat32) { // All per-channel quantized tensors need valid zero point and scale arrays.
if (input->type == kTfLiteInt8) {
TF_LITE_ENSURE_EQ(context, filter->quantization.type, TF_LITE_ENSURE_EQ(context, filter->quantization.type,
kTfLiteAffineQuantization); kTfLiteAffineQuantization);
@ -313,6 +317,13 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
filter->quantization.params); filter->quantization.params);
TF_LITE_ENSURE(context, affine_quantization); TF_LITE_ENSURE(context, affine_quantization);
TF_LITE_ENSURE(context, affine_quantization->scale); TF_LITE_ENSURE(context, affine_quantization->scale);
TF_LITE_ENSURE(context, affine_quantization->zero_point);
// Depthwise conv is quantized along dimension 3:
// https://www.tensorflow.org/lite/performance/quantization_spec
TF_LITE_ENSURE_EQ(context, filter->dims->data[3],
affine_quantization->scale->size);
TF_LITE_ENSURE_EQ(context, filter->dims->data[3],
affine_quantization->zero_point->size);
} }
TF_LITE_ENSURE_STATUS(CalculateOpData(context, node, params, width, height, TF_LITE_ENSURE_STATUS(CalculateOpData(context, node, params, width, height,

View File

@ -103,7 +103,8 @@ TfLiteStatus ValidateConvGoldens(TfLiteTensor* tensors, int tensors_size,
} }
for (int i = 0; i < output_length; ++i) { for (int i = 0; i < output_length; ++i) {
TF_LITE_MICRO_EXPECT_EQ(expected_output_data[i], output_data[i]); TF_LITE_MICRO_EXPECT_NEAR(expected_output_data[i], output_data[i],
tolerance);
} }
return kTfLiteOk; return kTfLiteOk;
} }
@ -181,13 +182,14 @@ void TestConvQuantizedPerLayer(
void TestConvQuantizedPerChannel( void TestConvQuantizedPerChannel(
const int* input_dims_data, const float* input_data, const int* input_dims_data, const float* input_data,
int8_t* input_quantized, float input_scale, const int* filter_dims_data, int8_t* input_quantized, float input_scale, int input_zero_point,
const float* filter_data, int8_t* filter_data_quantized, const int* filter_dims_data, const float* filter_data,
const int* bias_dims_data, const float* bias_data, int8_t* filter_data_quantized, const int* bias_dims_data,
int32_t* bias_data_quantized, float* bias_scales, int* bias_zero_points, const float* bias_data, int32_t* bias_data_quantized, float* bias_scales,
const int* output_dims_data, const float* expected_output_data, int* bias_zero_points, const int* output_dims_data,
int8_t* expected_output_data_quantized, int8_t* output_data, const float* expected_output_data, int8_t* expected_output_data_quantized,
float output_scale, TfLiteConvParams* conv_params) { int8_t* output_data, float output_scale, int output_zero_point,
TfLiteConvParams* conv_params) {
TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data); TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data); TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data);
TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data); TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data);
@ -198,28 +200,30 @@ void TestConvQuantizedPerChannel(
float filter_scales[5]; float filter_scales[5];
TfLiteAffineQuantization filter_quant; TfLiteAffineQuantization filter_quant;
TfLiteAffineQuantization bias_quant; TfLiteAffineQuantization bias_quant;
TfLiteTensor input_tensor = CreateQuantizedTensor( TfLiteTensor input_tensor =
input_data, input_quantized, input_dims, input_scale, 0, "input_tensor"); CreateQuantizedTensor(input_data, input_quantized, input_dims,
input_scale, input_zero_point, "input_tensor");
TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor( TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor(
filter_data, filter_data_quantized, filter_dims, filter_scales, filter_data, filter_data_quantized, filter_dims, filter_scales,
filter_zero_points, &filter_quant, 0 /* quantized dimension */, filter_zero_points, &filter_quant, 0 /* quantized dimension */,
"filter_tensor"); "filter_tensor");
TfLiteTensor bias_tensor = CreatePerChannelQuantizedBiasTensor( TfLiteTensor bias_tensor = CreatePerChannelQuantizedBiasTensor(
bias_data, bias_data_quantized, bias_dims, input_scale, &filter_scales[1], bias_data, bias_data_quantized, bias_dims, input_scale, &filter_scales[1],
bias_scales, bias_zero_points, &bias_quant, 0, "bias_tensor"); bias_scales, bias_zero_points, &bias_quant, 0 /* quantized dimension */,
"bias_tensor");
TfLiteTensor output_tensor = TfLiteTensor output_tensor =
CreateQuantizedTensor(output_data, output_dims, output_scale, CreateQuantizedTensor(output_data, output_dims, output_scale,
0 /* quantized dimension */, "output_tensor"); output_zero_point, "output_tensor");
// TODO(njeff): Affine Quantization Params should be set on tensor creation. // TODO(njeff): Affine Quantization Params should be set on tensor creation.
float input_scales[] = {1, input_scale}; float input_scales[] = {1, input_scale};
int input_zero_points[] = {1, 128}; int input_zero_points[] = {1, input_zero_point};
TfLiteAffineQuantization input_quant = {FloatArrayFromFloats(input_scales), TfLiteAffineQuantization input_quant = {FloatArrayFromFloats(input_scales),
IntArrayFromInts(input_zero_points)}; IntArrayFromInts(input_zero_points)};
input_tensor.quantization = {kTfLiteAffineQuantization, &input_quant}; input_tensor.quantization = {kTfLiteAffineQuantization, &input_quant};
float output_scales[] = {1, output_scale}; float output_scales[] = {1, output_scale};
int output_zero_points[] = {1, 128}; int output_zero_points[] = {1, output_zero_point};
TfLiteAffineQuantization output_quant = { TfLiteAffineQuantization output_quant = {
FloatArrayFromFloats(output_scales), FloatArrayFromFloats(output_scales),
IntArrayFromInts(output_zero_points)}; IntArrayFromInts(output_zero_points)};
@ -237,11 +241,12 @@ void TestConvQuantizedPerChannel(
tflite::AsymmetricQuantize(expected_output_data, tflite::AsymmetricQuantize(expected_output_data,
expected_output_data_quantized, output_dims_count, expected_output_data_quantized, output_dims_count,
output_scale); output_scale, output_zero_point);
TF_LITE_MICRO_EXPECT_EQ( TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, kTfLiteOk,
ValidateConvGoldens(tensors, tensors_size, expected_output_data_quantized, ValidateConvGoldens(tensors, tensors_size, expected_output_data_quantized,
output_data, output_dims_count, conv_params)); output_data, output_dims_count, conv_params,
1.0 /* tolerance */));
} }
} // namespace } // namespace
@ -306,8 +311,9 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
int8_t output_data[output_dims_count]; int8_t output_data[output_dims_count];
const float input_scale = 0.5f; const float input_scale = 0.5f;
const float bias_scale = 0.5f;
const float output_scale = 1.0f; const float output_scale = 1.0f;
const int input_zero_point = 0;
const int output_zero_point = 0;
int8_t input_quantized[tflite::testing::kInputElements]; int8_t input_quantized[tflite::testing::kInputElements];
int8_t filter_quantized[tflite::testing::kFilterElements]; int8_t filter_quantized[tflite::testing::kFilterElements];
@ -318,17 +324,49 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
tflite::testing::TestConvQuantizedPerChannel( tflite::testing::TestConvQuantizedPerChannel(
tflite::testing::kInputShape, tflite::testing::kInputData, tflite::testing::kInputShape, tflite::testing::kInputData,
input_quantized, input_scale, tflite::testing::kFilterShape, input_quantized, input_scale, input_zero_point,
tflite::testing::kFilterData, filter_quantized, tflite::testing::kFilterShape, tflite::testing::kFilterData,
tflite::testing::kBiasShape, tflite::testing::kBiasData, bias_quantized, filter_quantized, tflite::testing::kBiasShape, tflite::testing::kBiasData,
scales, zero_points, tflite::testing::kOutputShape, bias_quantized, scales, zero_points, tflite::testing::kOutputShape,
tflite::testing::kGoldenData, golden_quantized, output_data, output_scale, tflite::testing::kGoldenData, golden_quantized, output_data, output_scale,
&tflite::testing::common_conv_params); output_zero_point, &tflite::testing::common_conv_params);
}
TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannelRelu6) {
// conv params:
// padding, stride_<width,height>, dilation_<width, height>, activation
TfLiteConvParams conv_params = {kTfLitePaddingValid, 1, 1, kTfLiteActRelu6};
const int output_dims_count = 12;
int8_t output_data[output_dims_count];
const float bias_values[] = {1, 2, -3};
const float golden_data[] = {6, 2, 0, 6, 2, 0, 6, 4, 0, 6, 4, 0};
const float input_scale = 0.023529f;
const float output_scale = 0.023529f;
const int input_zero_point = -128;
const int output_zero_point = -128;
int8_t input_quantized[tflite::testing::kInputElements];
int8_t filter_quantized[tflite::testing::kFilterElements];
int32_t bias_quantized[tflite::testing::kBiasElements];
int8_t golden_quantized[tflite::testing::kOutputElements];
int zero_points[tflite::testing::kBiasElements + 1];
float scales[tflite::testing::kBiasElements + 1];
tflite::testing::TestConvQuantizedPerChannel(
tflite::testing::kInputShape, tflite::testing::kInputData,
input_quantized, input_scale, input_zero_point,
tflite::testing::kFilterShape, tflite::testing::kFilterData,
filter_quantized, tflite::testing::kBiasShape, bias_values,
bias_quantized, scales, zero_points, tflite::testing::kOutputShape,
golden_data, golden_quantized, output_data, output_scale,
output_zero_point, &tflite::testing::common_conv_params);
} }
TF_LITE_MICRO_TEST(Kernel1x1QuantizedPerChannel) { TF_LITE_MICRO_TEST(Kernel1x1QuantizedPerChannel) {
// conv params: // conv params:
// padding, stride_<width,height>, dilation_<width, height>, activation // padding, stride_<width,height>, activation, dilation_<width, height>
TfLiteConvParams conv_params = {kTfLitePaddingValid, 1, 1, TfLiteConvParams conv_params = {kTfLitePaddingValid, 1, 1,
kTfLiteActNone, 1, 1}; kTfLiteActNone, 1, 1};
const int kInputShape[] = {4, 1, 2, 2, 4}; // [len,N,H,W,C] const int kInputShape[] = {4, 1, 2, 2, 4}; // [len,N,H,W,C]
@ -351,8 +389,9 @@ TF_LITE_MICRO_TEST(Kernel1x1QuantizedPerChannel) {
31, 4, 7, 31, 4, 7}; 31, 4, 7, 31, 4, 7};
const float input_scale = 0.5f; const float input_scale = 0.5f;
const float bias_scale = 0.5f;
const float output_scale = 1.0f; const float output_scale = 1.0f;
const int input_zero_point = 0;
const int output_zero_point = 0;
int8_t input_quantized[kInputElements]; int8_t input_quantized[kInputElements];
int8_t filter_quantized[kFilterElements]; int8_t filter_quantized[kFilterElements];
@ -362,10 +401,54 @@ TF_LITE_MICRO_TEST(Kernel1x1QuantizedPerChannel) {
float scales[kBiasElements + 1]; float scales[kBiasElements + 1];
tflite::testing::TestConvQuantizedPerChannel( tflite::testing::TestConvQuantizedPerChannel(
kInputShape, kInputData, input_quantized, input_scale, kFilterShape, kInputShape, kInputData, input_quantized, input_scale, input_zero_point,
kFilterData, filter_quantized, kBiasShape, kBiasData, bias_quantized, kFilterShape, kFilterData, filter_quantized, kBiasShape, kBiasData,
scales, zero_points, kOutputShape, kGoldenData, golden_quantized, bias_quantized, scales, zero_points, kOutputShape, kGoldenData,
output_data, output_scale, &conv_params); golden_quantized, output_data, output_scale, output_zero_point,
&conv_params);
}
TF_LITE_MICRO_TEST(Kernel1x1QuantizedPerChannelRelu6) {
// conv params:
// padding, stride_<width,height>, dilation_<width, height>, activation
TfLiteConvParams conv_params = {kTfLitePaddingValid, 1, 1, kTfLiteActRelu6};
const int kInputShape[] = {4, 1, 2, 2, 4}; // [len,N,H,W,C]
const int kInputElements =
kInputShape[1] * kInputShape[2] * kInputShape[3] * kInputShape[4];
float kInputData[/* kInputElements */] = {1, 1, 1, 1, 2, 2, 2, 2,
1, 2, 3, 4, 1, 2, 3, 4};
const int kFilterShape[] = {4, 3, 1, 1, 4};
const int kFilterElements =
kFilterShape[1] * kFilterShape[2] * kFilterShape[3] * kFilterShape[4];
float kFilterData[/* kFilterElements */] = {1, 2, 3, 4, -1, 1,
-1, 1, -1, -1, 1, 1};
const int kBiasElements = kFilterShape[1];
const int kBiasShape[] = {1, kBiasElements};
float kBiasData[/* kBiasElements */] = {1, 2, -3};
const int kOutputShape[] = {4, 1, 2, 2, kBiasElements};
const int kOutputElements = 4 * 3;
int8_t output_data[kOutputElements];
const float kGoldenData[/* kOutputElements */] = {6, 2, 0, 6, 2, 0,
6, 4, 1, 6, 4, 1};
const float input_scale = 0.023529f;
const float output_scale = 0.023529f;
const int input_zero_point = -128;
const int output_zero_point = -128;
int8_t input_quantized[kInputElements];
int8_t filter_quantized[kFilterElements];
int32_t bias_quantized[kBiasElements];
int8_t golden_quantized[kOutputElements];
int zero_points[kBiasElements + 1];
float scales[kBiasElements + 1];
tflite::testing::TestConvQuantizedPerChannel(
kInputShape, kInputData, input_quantized, input_scale, input_zero_point,
kFilterShape, kFilterData, filter_quantized, kBiasShape, kBiasData,
bias_quantized, scales, zero_points, kOutputShape, kGoldenData,
golden_quantized, output_data, output_scale, output_zero_point,
&conv_params);
} }
TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) { TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
@ -373,7 +456,6 @@ TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
int8_t output_data[output_dims_count]; int8_t output_data[output_dims_count];
const float input_scale = 0.5f; const float input_scale = 0.5f;
const float bias_scale = 0.5f;
const float output_scale = 1.0f; const float output_scale = 1.0f;
int8_t input_quantized[tflite::testing::kInputElements]; int8_t input_quantized[tflite::testing::kInputElements];

View File

@ -163,6 +163,12 @@ void TestDepthwiseConvQuantizedPerLayer(
IntArrayFromInts(filter_zero_points)}; IntArrayFromInts(filter_zero_points)};
tensors[1].quantization = {kTfLiteAffineQuantization, &filter_quant}; tensors[1].quantization = {kTfLiteAffineQuantization, &filter_quant};
float bias_scales[] = {1, filter_scale * input_scale};
int bias_zero_points[] = {1, 128};
TfLiteAffineQuantization bias_quant = {FloatArrayFromFloats(bias_scales),
IntArrayFromInts(bias_zero_points)};
tensors[2].quantization = {kTfLiteAffineQuantization, &bias_quant};
AsymmetricQuantize(golden, golden_quantized, output_dims_count, output_scale, AsymmetricQuantize(golden, golden_quantized, output_dims_count, output_scale,
output_zero_point); output_zero_point);
ValidateDepthwiseConvGoldens(tensors, tensors_size, golden_quantized, ValidateDepthwiseConvGoldens(tensors, tensors_size, golden_quantized,
@ -171,12 +177,14 @@ void TestDepthwiseConvQuantizedPerLayer(
void TestDepthwiseConvQuantizedPerChannel( void TestDepthwiseConvQuantizedPerChannel(
const int* input_dims_data, const float* input_data, const int* input_dims_data, const float* input_data,
int8_t* input_quantized, float input_scale, const int* filter_dims_data, int8_t* input_quantized, float input_scale, int input_zero_point,
const float* filter_data, int8_t* filter_data_quantized, const int* filter_dims_data, const float* filter_data,
const int* bias_dims_data, const float* bias_data, int8_t* filter_data_quantized, const int* bias_dims_data,
int32_t* bias_data_quantized, const int* output_dims_data, const float* bias_data, int32_t* bias_data_quantized,
const float* expected_output_data, int8_t* expected_output_data_quantized, const int* output_dims_data, const float* expected_output_data,
int8_t* output_data, float output_scale, TfLiteFusedActivation activation) { int8_t* expected_output_data_quantized, int8_t* output_data,
float output_scale, int output_zero_point,
TfLiteFusedActivation activation) {
TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data); TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data); TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data);
TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data); TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data);
@ -189,8 +197,9 @@ void TestDepthwiseConvQuantizedPerChannel(
float bias_scales[kMaxBiasChannels]; float bias_scales[kMaxBiasChannels];
TfLiteAffineQuantization filter_quant; TfLiteAffineQuantization filter_quant;
TfLiteAffineQuantization bias_quant; TfLiteAffineQuantization bias_quant;
TfLiteTensor input_tensor = CreateQuantizedTensor( TfLiteTensor input_tensor =
input_data, input_quantized, input_dims, input_scale, 0, "input_tensor"); CreateQuantizedTensor(input_data, input_quantized, input_dims,
input_scale, input_zero_point, "input_tensor");
TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor( TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor(
filter_data, filter_data_quantized, filter_dims, filter_scales, filter_data, filter_data_quantized, filter_dims, filter_scales,
filter_zero_points, &filter_quant, 3 /* quantized dimension */, filter_zero_points, &filter_quant, 3 /* quantized dimension */,
@ -201,17 +210,17 @@ void TestDepthwiseConvQuantizedPerChannel(
"bias_tensor"); "bias_tensor");
TfLiteTensor output_tensor = TfLiteTensor output_tensor =
CreateQuantizedTensor(output_data, output_dims, output_scale, CreateQuantizedTensor(output_data, output_dims, output_scale,
0 /* zero point */, "output_tensor"); input_zero_point, "output_tensor");
// TODO(njeff): Affine Quantization Params should be set on tensor creation. // TODO(njeff): Affine Quantization Params should be set on tensor creation.
float input_scales[] = {1, input_scale}; float input_scales[] = {1, input_scale};
int input_zero_points[] = {1, 0}; int input_zero_points[] = {1, input_zero_point};
TfLiteAffineQuantization input_quant = {FloatArrayFromFloats(input_scales), TfLiteAffineQuantization input_quant = {FloatArrayFromFloats(input_scales),
IntArrayFromInts(input_zero_points)}; IntArrayFromInts(input_zero_points)};
input_tensor.quantization = {kTfLiteAffineQuantization, &input_quant}; input_tensor.quantization = {kTfLiteAffineQuantization, &input_quant};
float output_scales[] = {1, output_scale}; float output_scales[] = {1, output_scale};
int output_zero_points[] = {1, 0}; int output_zero_points[] = {1, output_zero_point};
TfLiteAffineQuantization output_quant = { TfLiteAffineQuantization output_quant = {
FloatArrayFromFloats(output_scales), FloatArrayFromFloats(output_scales),
IntArrayFromInts(output_zero_points)}; IntArrayFromInts(output_zero_points)};
@ -228,7 +237,7 @@ void TestDepthwiseConvQuantizedPerChannel(
}; };
AsymmetricQuantize(expected_output_data, expected_output_data_quantized, AsymmetricQuantize(expected_output_data, expected_output_data_quantized,
output_dims_count, output_scale, 0); output_dims_count, output_scale, output_zero_point);
TF_LITE_MICRO_EXPECT_EQ( TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, ValidateDepthwiseConvGoldens( kTfLiteOk, ValidateDepthwiseConvGoldens(
@ -422,6 +431,8 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
const float input_scale = 0.5; const float input_scale = 0.5;
const float output_scale = 1.0f; const float output_scale = 1.0f;
const int input_zero_point = 0;
const int output_zero_point = 0;
int8_t input_quantized[input_elements]; int8_t input_quantized[input_elements];
int8_t filter_quantized[filter_elements]; int8_t filter_quantized[filter_elements];
@ -431,10 +442,93 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
float scales[bias_elements + 1]; float scales[bias_elements + 1];
tflite::testing::TestDepthwiseConvQuantizedPerChannel( tflite::testing::TestDepthwiseConvQuantizedPerChannel(
input_shape, input_values, input_quantized, input_scale, filter_shape, input_shape, input_values, input_quantized, input_scale, input_zero_point,
filter_values, filter_quantized, bias_shape, bias_values, bias_quantized, filter_shape, filter_values, filter_quantized, bias_shape, bias_values,
output_shape, golden, golden_quantized, output_data, output_scale, bias_quantized, output_shape, golden, golden_quantized, output_data,
kTfLiteActNone); output_scale, output_zero_point, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannelDepthMultiplier1) {
const int input_elements = 12;
const int input_shape[] = {4, 1, 3, 2, 2};
const float input_values[] = {1, 2, 7, 8, 3, 4, 9, 10, 5, 6, 11, 12};
const int filter_elements = 8;
const int filter_shape[] = {4, 1, 2, 2, 2};
const float filter_values[] = {1, 2, 3, 4, -9, 10, -11, 12};
const int bias_elements = 2;
const int bias_shape[] = {4, 1, 1, 1, 2};
const int output_elements = 4;
const float bias_values[] = {1, 2};
const float golden[] = {
-103,
127,
-128,
127,
};
const int output_shape[] = {4, 1, 2, 1, 2};
const int output_dims_count = 4;
int8_t output_data[output_dims_count];
const float input_scale = 1.0f;
const float output_scale = 1.0f;
const int input_zero_point = 0;
const int output_zero_point = 0;
int8_t input_quantized[input_elements];
int8_t filter_quantized[filter_elements];
int32_t bias_quantized[bias_elements];
int8_t golden_quantized[output_elements];
int zero_points[bias_elements + 1];
float scales[bias_elements + 1];
tflite::testing::TestDepthwiseConvQuantizedPerChannel(
input_shape, input_values, input_quantized, input_scale, input_zero_point,
filter_shape, filter_values, filter_quantized, bias_shape, bias_values,
bias_quantized, output_shape, golden, golden_quantized, output_data,
output_scale, output_zero_point, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(TestQuantizedPerChannelDepthMultiplier1Relu6) {
const int input_elements = 24;
const int input_shape[] = {4, 1, 3, 2, 4};
const float input_values[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
const int filter_elements = 16;
const int filter_shape[] = {4, 1, 2, 2, 4};
const float filter_values[] = {0, 1, 8, -2, -1, 2, -10, 0,
-1, 3, -18, 0, 0, 4, 20, -3};
const int bias_elements = 4;
const int bias_shape[] = {4, 1, 1, 1, 4};
const int output_elements = 8;
const float bias_values[] = {1, 2, 3, 4};
const float golden[] = {
0, 6, 3, 0, 0, 6, 3, 0,
};
const int output_shape[] = {4, 1, 2, 1, 4};
int8_t output_data[output_elements];
float output_float[output_elements];
const float input_scale = 0.023529f;
const float output_scale = 0.023529f;
const int input_zero_point = -128;
const int output_zero_point = -128;
int8_t input_quantized[input_elements];
int8_t filter_quantized[filter_elements];
int32_t bias_quantized[bias_elements];
int8_t golden_quantized[output_elements];
int zero_points[bias_elements + 1];
float scales[bias_elements + 1];
tflite::testing::TestDepthwiseConvFloat(
input_shape, input_values, filter_shape, filter_values, bias_shape,
bias_values, golden, output_shape, kTfLiteActRelu6, output_float);
tflite::testing::TestDepthwiseConvQuantizedPerChannel(
input_shape, input_values, input_quantized, input_scale, input_zero_point,
filter_shape, filter_values, filter_quantized, bias_shape, bias_values,
bias_quantized, output_shape, golden, golden_quantized, output_data,
output_scale, output_zero_point, kTfLiteActRelu6);
} }
TF_LITE_MICRO_TEST(TestQuantizedPerChannelCompareWithFloat) { TF_LITE_MICRO_TEST(TestQuantizedPerChannelCompareWithFloat) {
@ -460,14 +554,16 @@ TF_LITE_MICRO_TEST(TestQuantizedPerChannelCompareWithFloat) {
int8_t output_data[output_size]; int8_t output_data[output_size];
float output_float[output_size]; float output_float[output_size];
float input_scale = 0.5; const float input_scale = 0.5;
float output_scale = 1.0; const float output_scale = 1.0;
const int input_zero_point = 0;
const int output_zero_point = 0;
tflite::testing::TestDepthwiseConvQuantizedPerChannel( tflite::testing::TestDepthwiseConvQuantizedPerChannel(
input_dims, input_data, input_quantized, input_scale, filter_dims, input_dims, input_data, input_quantized, input_scale, input_zero_point,
filter_data, filter_quantized, bias_dims, bias_data, bias_quantized, filter_dims, filter_data, filter_quantized, bias_dims, bias_data,
output_dims, golden, golden_quantized, output_data, output_scale, bias_quantized, output_dims, golden, golden_quantized, output_data,
kTfLiteActNone); output_scale, output_zero_point, kTfLiteActNone);
tflite::testing::TestDepthwiseConvFloat( tflite::testing::TestDepthwiseConvFloat(
input_dims, input_data, filter_dims, filter_data, bias_dims, bias_data, input_dims, input_data, filter_dims, filter_data, bias_dims, bias_data,
@ -497,8 +593,10 @@ TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
int8_t output_data[output_size]; int8_t output_data[output_size];
float output_float[output_size]; float output_float[output_size];
float input_scale = 0.5; const float input_scale = 0.5;
float output_scale = 1.0; const float output_scale = 1.0;
const int input_zero_point = 0;
const int output_zero_point = 0;
TfLiteIntArray* input_dims = tflite::testing::IntArrayFromInts(input_shape); TfLiteIntArray* input_dims = tflite::testing::IntArrayFromInts(input_shape);
TfLiteIntArray* filter_dims = tflite::testing::IntArrayFromInts(filter_shape); TfLiteIntArray* filter_dims = tflite::testing::IntArrayFromInts(filter_shape);
@ -510,7 +608,8 @@ TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
TfLiteAffineQuantization filter_quant; TfLiteAffineQuantization filter_quant;
TfLiteAffineQuantization bias_quant; TfLiteAffineQuantization bias_quant;
TfLiteTensor input_tensor = tflite::testing::CreateQuantizedTensor( TfLiteTensor input_tensor = tflite::testing::CreateQuantizedTensor(
input_data, input_quantized, input_dims, input_scale, 0, "input_tensor"); input_data, input_quantized, input_dims, input_scale, input_zero_point,
"input_tensor");
TfLiteTensor filter_tensor = TfLiteTensor filter_tensor =
tflite::testing::CreateSymmetricPerChannelQuantizedTensor( tflite::testing::CreateSymmetricPerChannelQuantizedTensor(
filter_data, filter_quantized, filter_dims, filter_scales, filter_data, filter_quantized, filter_dims, filter_scales,
@ -521,11 +620,11 @@ TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
bias_data, bias_quantized, bias_dims, input_scale, &filter_scales[1], bias_data, bias_quantized, bias_dims, input_scale, &filter_scales[1],
scales, zero_points, &bias_quant, 0, "bias_tensor"); scales, zero_points, &bias_quant, 0, "bias_tensor");
TfLiteTensor output_tensor = tflite::testing::CreateQuantizedTensor( TfLiteTensor output_tensor = tflite::testing::CreateQuantizedTensor(
output_data, output_dims, output_scale, 0 /* quantized dimension */, output_data, output_dims, output_scale, output_zero_point,
"output_tensor"); "output_tensor");
float input_scales[] = {1, input_scale}; float input_scales[] = {1, input_scale};
int input_zero_points[] = {1, 128}; int input_zero_points[] = {1, input_zero_point};
TfLiteAffineQuantization input_quant = { TfLiteAffineQuantization input_quant = {
tflite::testing::FloatArrayFromFloats(input_scales), tflite::testing::FloatArrayFromFloats(input_scales),
tflite::testing::IntArrayFromInts(input_zero_points)}; tflite::testing::IntArrayFromInts(input_zero_points)};

View File

@ -35,7 +35,7 @@ constexpr int kInputTensor = 0;
constexpr int kFilterTensor = 1; constexpr int kFilterTensor = 1;
constexpr int kBiasTensor = 2; constexpr int kBiasTensor = 2;
constexpr int kOutputTensor = 0; constexpr int kOutputTensor = 0;
constexpr int kMaxChannels = 64; constexpr int kMaxChannels = 256;
// Size of the cached buffer we'll be using to hold reordered weights. // Size of the cached buffer we'll be using to hold reordered weights.
constexpr int kReshapedFilterDataSize = 1 * 1024; constexpr int kReshapedFilterDataSize = 1 * 1024;

View File

@ -20,6 +20,7 @@ limitations under the License.
#include <stdint.h> #include <stdint.h>
#include "tensorflow/lite/c/common.h" #include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/kernels/op_macros.h"
namespace tflite { namespace tflite {
@ -130,15 +131,24 @@ void SignedSymmetricPerChannelQuantize(const float* values,
int input_size = ElementCount(*dims); int input_size = ElementCount(*dims);
int channel_count = dims->data[quantized_dimension]; int channel_count = dims->data[quantized_dimension];
int per_channel_size = input_size / channel_count; int per_channel_size = input_size / channel_count;
int stride;
int channel_stride;
if (quantized_dimension == 0) {
stride = 1;
channel_stride = per_channel_size;
} else if (quantized_dimension == 3) {
stride = channel_count;
channel_stride = 1;
} else {
TF_LITE_FATAL("quantized dimension must be 0 or 3");
}
// Calculate scales for each channel.
for (int channel = 0; channel < channel_count; channel++) { for (int channel = 0; channel < channel_count; channel++) {
float min = 0; float min = 0;
float max = 0; float max = 0;
int stride = 1;
for (int i = 0; i < quantized_dimension; i++) {
stride *= dims->data[i];
}
int channel_stride = per_channel_size / stride;
// Calculate scales for each channel.
for (int i = 0; i < per_channel_size; i++) { for (int i = 0; i < per_channel_size; i++) {
int idx = channel * channel_stride + i * stride; int idx = channel * channel_stride + i * stride;
min = fminf(min, values[idx]); min = fminf(min, values[idx]);

View File

@ -385,6 +385,10 @@ TfLiteTensor CreateSymmetricPerChannelQuantizedTensor(
SignedSymmetricPerChannelQuantize(input, dims, quantized_dimension, quantized, SignedSymmetricPerChannelQuantize(input, dims, quantized_dimension, quantized,
&scales[1]); &scales[1]);
for (int i = 0; i < channel_count; i++) {
zero_points[i + 1] = 0;
}
affine_quant->scale = FloatArrayFromFloats(scales); affine_quant->scale = FloatArrayFromFloats(scales);
affine_quant->zero_point = IntArrayFromInts(zero_points); affine_quant->zero_point = IntArrayFromInts(zero_points);
affine_quant->quantized_dimension = quantized_dimension; affine_quant->quantized_dimension = quantized_dimension;