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:
parent
8fda428bd1
commit
635a3a0f82
@ -61,6 +61,7 @@ cc_library(
|
||||
],
|
||||
deps = [
|
||||
"//tensorflow/lite/c:common",
|
||||
"//tensorflow/lite/kernels:op_macros",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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];
|
||||
|
@ -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)};
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user