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 = [
"//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.
op_params.quantized_activation_min = std::numeric_limits<int8_t>::min();
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);
const int filter_height = filter_shape.Dims(1);
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);
RuntimeShape bias_shape = GetTensorShape(bias);
#if defined(ARM_MATH_DSP) && defined(ARM_MATH_LOOPUNROLL)
if (op_params.depth_multiplier == 1) {
int16_t* buf = nullptr;
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<int32>(bias), GetTensorShape(output),
GetTensorData<int8>(output));
#endif
return kTfLiteOk;
}
@ -276,6 +278,7 @@ TfLiteStatus EvalQuantized(TfLiteContext* context, TfLiteNode* node,
op_params.output_multiplier);
} else
#endif
{
tflite::reference_ops::DepthwiseConv(
op_params, GetTensorShape(input), GetTensorData<uint8_t>(input),
@ -304,7 +307,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
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,
kTfLiteAffineQuantization);
@ -313,6 +317,13 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
filter->quantization.params);
TF_LITE_ENSURE(context, affine_quantization);
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,

View File

@ -103,7 +103,8 @@ TfLiteStatus ValidateConvGoldens(TfLiteTensor* tensors, int tensors_size,
}
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;
}
@ -181,13 +182,14 @@ void TestConvQuantizedPerLayer(
void TestConvQuantizedPerChannel(
const int* input_dims_data, const float* input_data,
int8_t* input_quantized, float input_scale, const int* filter_dims_data,
const float* filter_data, int8_t* filter_data_quantized,
const int* bias_dims_data, const float* bias_data,
int32_t* bias_data_quantized, float* bias_scales, int* bias_zero_points,
const int* output_dims_data, const float* expected_output_data,
int8_t* expected_output_data_quantized, int8_t* output_data,
float output_scale, TfLiteConvParams* conv_params) {
int8_t* input_quantized, float input_scale, int input_zero_point,
const int* filter_dims_data, const float* filter_data,
int8_t* filter_data_quantized, const int* bias_dims_data,
const float* bias_data, int32_t* bias_data_quantized, float* bias_scales,
int* bias_zero_points, const int* output_dims_data,
const float* expected_output_data, int8_t* expected_output_data_quantized,
int8_t* output_data, float output_scale, int output_zero_point,
TfLiteConvParams* conv_params) {
TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data);
TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data);
@ -198,28 +200,30 @@ void TestConvQuantizedPerChannel(
float filter_scales[5];
TfLiteAffineQuantization filter_quant;
TfLiteAffineQuantization bias_quant;
TfLiteTensor input_tensor = CreateQuantizedTensor(
input_data, input_quantized, input_dims, input_scale, 0, "input_tensor");
TfLiteTensor input_tensor =
CreateQuantizedTensor(input_data, input_quantized, input_dims,
input_scale, input_zero_point, "input_tensor");
TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor(
filter_data, filter_data_quantized, filter_dims, filter_scales,
filter_zero_points, &filter_quant, 0 /* quantized dimension */,
"filter_tensor");
TfLiteTensor bias_tensor = CreatePerChannelQuantizedBiasTensor(
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 =
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.
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),
IntArrayFromInts(input_zero_points)};
input_tensor.quantization = {kTfLiteAffineQuantization, &input_quant};
float output_scales[] = {1, output_scale};
int output_zero_points[] = {1, 128};
int output_zero_points[] = {1, output_zero_point};
TfLiteAffineQuantization output_quant = {
FloatArrayFromFloats(output_scales),
IntArrayFromInts(output_zero_points)};
@ -237,11 +241,12 @@ void TestConvQuantizedPerChannel(
tflite::AsymmetricQuantize(expected_output_data,
expected_output_data_quantized, output_dims_count,
output_scale);
output_scale, output_zero_point);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk,
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
@ -306,8 +311,9 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
int8_t output_data[output_dims_count];
const float input_scale = 0.5f;
const float bias_scale = 0.5f;
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 filter_quantized[tflite::testing::kFilterElements];
@ -318,17 +324,49 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
tflite::testing::TestConvQuantizedPerChannel(
tflite::testing::kInputShape, tflite::testing::kInputData,
input_quantized, input_scale, tflite::testing::kFilterShape,
tflite::testing::kFilterData, filter_quantized,
tflite::testing::kBiasShape, tflite::testing::kBiasData, bias_quantized,
scales, zero_points, tflite::testing::kOutputShape,
input_quantized, input_scale, input_zero_point,
tflite::testing::kFilterShape, tflite::testing::kFilterData,
filter_quantized, tflite::testing::kBiasShape, tflite::testing::kBiasData,
bias_quantized, scales, zero_points, tflite::testing::kOutputShape,
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) {
// conv params:
// padding, stride_<width,height>, dilation_<width, height>, activation
// padding, stride_<width,height>, activation, dilation_<width, height>
TfLiteConvParams conv_params = {kTfLitePaddingValid, 1, 1,
kTfLiteActNone, 1, 1};
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};
const float input_scale = 0.5f;
const float bias_scale = 0.5f;
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 filter_quantized[kFilterElements];
@ -362,10 +401,54 @@ TF_LITE_MICRO_TEST(Kernel1x1QuantizedPerChannel) {
float scales[kBiasElements + 1];
tflite::testing::TestConvQuantizedPerChannel(
kInputShape, kInputData, input_quantized, input_scale, kFilterShape,
kFilterData, filter_quantized, kBiasShape, kBiasData, bias_quantized,
scales, zero_points, kOutputShape, kGoldenData, golden_quantized,
output_data, output_scale, &conv_params);
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(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) {
@ -373,7 +456,6 @@ TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
int8_t output_data[output_dims_count];
const float input_scale = 0.5f;
const float bias_scale = 0.5f;
const float output_scale = 1.0f;
int8_t input_quantized[tflite::testing::kInputElements];

View File

@ -163,6 +163,12 @@ void TestDepthwiseConvQuantizedPerLayer(
IntArrayFromInts(filter_zero_points)};
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,
output_zero_point);
ValidateDepthwiseConvGoldens(tensors, tensors_size, golden_quantized,
@ -171,12 +177,14 @@ void TestDepthwiseConvQuantizedPerLayer(
void TestDepthwiseConvQuantizedPerChannel(
const int* input_dims_data, const float* input_data,
int8_t* input_quantized, float input_scale, const int* filter_dims_data,
const float* filter_data, int8_t* filter_data_quantized,
const int* bias_dims_data, const float* bias_data,
int32_t* bias_data_quantized, const int* output_dims_data,
const float* expected_output_data, int8_t* expected_output_data_quantized,
int8_t* output_data, float output_scale, TfLiteFusedActivation activation) {
int8_t* input_quantized, float input_scale, int input_zero_point,
const int* filter_dims_data, const float* filter_data,
int8_t* filter_data_quantized, const int* bias_dims_data,
const float* bias_data, int32_t* bias_data_quantized,
const int* output_dims_data, const float* expected_output_data,
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* filter_dims = IntArrayFromInts(filter_dims_data);
TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data);
@ -189,8 +197,9 @@ void TestDepthwiseConvQuantizedPerChannel(
float bias_scales[kMaxBiasChannels];
TfLiteAffineQuantization filter_quant;
TfLiteAffineQuantization bias_quant;
TfLiteTensor input_tensor = CreateQuantizedTensor(
input_data, input_quantized, input_dims, input_scale, 0, "input_tensor");
TfLiteTensor input_tensor =
CreateQuantizedTensor(input_data, input_quantized, input_dims,
input_scale, input_zero_point, "input_tensor");
TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor(
filter_data, filter_data_quantized, filter_dims, filter_scales,
filter_zero_points, &filter_quant, 3 /* quantized dimension */,
@ -201,17 +210,17 @@ void TestDepthwiseConvQuantizedPerChannel(
"bias_tensor");
TfLiteTensor output_tensor =
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.
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),
IntArrayFromInts(input_zero_points)};
input_tensor.quantization = {kTfLiteAffineQuantization, &input_quant};
float output_scales[] = {1, output_scale};
int output_zero_points[] = {1, 0};
int output_zero_points[] = {1, output_zero_point};
TfLiteAffineQuantization output_quant = {
FloatArrayFromFloats(output_scales),
IntArrayFromInts(output_zero_points)};
@ -228,7 +237,7 @@ void TestDepthwiseConvQuantizedPerChannel(
};
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(
kTfLiteOk, ValidateDepthwiseConvGoldens(
@ -422,6 +431,8 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
const float input_scale = 0.5;
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];
@ -431,10 +442,93 @@ TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
float scales[bias_elements + 1];
tflite::testing::TestDepthwiseConvQuantizedPerChannel(
input_shape, input_values, input_quantized, input_scale, filter_shape,
filter_values, filter_quantized, bias_shape, bias_values, bias_quantized,
output_shape, golden, golden_quantized, output_data, output_scale,
kTfLiteActNone);
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(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) {
@ -460,14 +554,16 @@ TF_LITE_MICRO_TEST(TestQuantizedPerChannelCompareWithFloat) {
int8_t output_data[output_size];
float output_float[output_size];
float input_scale = 0.5;
float output_scale = 1.0;
const float input_scale = 0.5;
const float output_scale = 1.0;
const int input_zero_point = 0;
const int output_zero_point = 0;
tflite::testing::TestDepthwiseConvQuantizedPerChannel(
input_dims, input_data, input_quantized, input_scale, filter_dims,
filter_data, filter_quantized, bias_dims, bias_data, bias_quantized,
output_dims, golden, golden_quantized, output_data, output_scale,
kTfLiteActNone);
input_dims, input_data, input_quantized, input_scale, input_zero_point,
filter_dims, filter_data, filter_quantized, bias_dims, bias_data,
bias_quantized, output_dims, golden, golden_quantized, output_data,
output_scale, output_zero_point, kTfLiteActNone);
tflite::testing::TestDepthwiseConvFloat(
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];
float output_float[output_size];
float input_scale = 0.5;
float output_scale = 1.0;
const float input_scale = 0.5;
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* filter_dims = tflite::testing::IntArrayFromInts(filter_shape);
@ -510,7 +608,8 @@ TF_LITE_MICRO_TEST(FilterDimsNotMatchingAffineQuantization) {
TfLiteAffineQuantization filter_quant;
TfLiteAffineQuantization bias_quant;
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 =
tflite::testing::CreateSymmetricPerChannelQuantizedTensor(
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],
scales, zero_points, &bias_quant, 0, "bias_tensor");
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");
float input_scales[] = {1, input_scale};
int input_zero_points[] = {1, 128};
int input_zero_points[] = {1, input_zero_point};
TfLiteAffineQuantization input_quant = {
tflite::testing::FloatArrayFromFloats(input_scales),
tflite::testing::IntArrayFromInts(input_zero_points)};

View File

@ -35,7 +35,7 @@ constexpr int kInputTensor = 0;
constexpr int kFilterTensor = 1;
constexpr int kBiasTensor = 2;
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.
constexpr int kReshapedFilterDataSize = 1 * 1024;

View File

@ -20,6 +20,7 @@ limitations under the License.
#include <stdint.h>
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/kernels/op_macros.h"
namespace tflite {
@ -130,15 +131,24 @@ void SignedSymmetricPerChannelQuantize(const float* values,
int input_size = ElementCount(*dims);
int channel_count = dims->data[quantized_dimension];
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++) {
float min = 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++) {
int idx = channel * channel_stride + i * stride;
min = fminf(min, values[idx]);

View File

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