mirror of
https://github.com/vee1e/tflite-micro.git
synced 2026-09-02 10:17:54 +00:00
335 lines
14 KiB
C++
335 lines
14 KiB
C++
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==============================================================================*/
|
|
#include "tensorflow/lite/c/common.h"
|
|
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
|
#include "tensorflow/lite/micro/kernels/kernel_runner.h"
|
|
#include "tensorflow/lite/micro/micro_utils.h"
|
|
#include "tensorflow/lite/micro/test_helpers.h"
|
|
#include "tensorflow/lite/micro/testing/micro_test.h"
|
|
|
|
namespace tflite {
|
|
namespace testing {
|
|
namespace {
|
|
|
|
constexpr int kNumTestShapes = 4;
|
|
constexpr int kMaxTestShapeSize = 5;
|
|
|
|
int test_shape[kNumTestShapes][kMaxTestShapeSize] = {
|
|
{1, 6},
|
|
{2, 2, 3},
|
|
{3, 2, 1, 3},
|
|
{4, 1, 3, 1, 2},
|
|
};
|
|
|
|
template <typename T>
|
|
void ValidateSquaredDifferenceGoldens(TfLiteTensor* tensors, int tensors_size,
|
|
const T* golden, T* output,
|
|
int output_size, float tolerance = 1e-5) {
|
|
int inputs_array_data[] = {2, 0, 1};
|
|
TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_array_data);
|
|
int outputs_array_data[] = {1, 2};
|
|
TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_array_data);
|
|
|
|
const TFLMRegistration registration = tflite::Register_SQUARED_DIFFERENCE();
|
|
micro::KernelRunner runner(registration, tensors, tensors_size, inputs_array,
|
|
outputs_array, /*builtin_data=*/nullptr);
|
|
|
|
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.InitAndPrepare());
|
|
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.Invoke());
|
|
|
|
for (int i = 0; i < output_size; ++i) {
|
|
TF_LITE_MICRO_EXPECT_NEAR(golden[i], output[i], tolerance);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void TestSquaredDifference(int* input1_dims_data, const T* input1_data,
|
|
int* input2_dims_data, const T* input2_data,
|
|
int* output_dims_data, const T* expected_output,
|
|
T* output_data) {
|
|
TfLiteIntArray* input1_dims = IntArrayFromInts(input1_dims_data);
|
|
TfLiteIntArray* input2_dims = IntArrayFromInts(input2_dims_data);
|
|
TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
|
|
|
|
constexpr int inputs_size = 2;
|
|
constexpr int outputs_size = 1;
|
|
constexpr int tensors_size = inputs_size + outputs_size;
|
|
TfLiteTensor tensors[tensors_size] = {
|
|
CreateTensor(input1_data, input1_dims),
|
|
CreateTensor(input2_data, input2_dims),
|
|
CreateTensor(output_data, output_dims),
|
|
};
|
|
|
|
ValidateSquaredDifferenceGoldens(tensors, tensors_size, expected_output,
|
|
output_data, ElementCount(*output_dims));
|
|
}
|
|
|
|
template <typename T>
|
|
void TestSquaredDifferenceQuantized(
|
|
int* input1_dims_data, const float* input1_data, T* input1_quantized,
|
|
float input1_min, float input1_max,
|
|
|
|
int* input2_dims_data, const float* input2_data, T* input2_quantized,
|
|
float input2_min, float input2_max,
|
|
|
|
int* output_dims_data, T* output_data, float output_min, float output_max,
|
|
float* dequantized_output, const float* golden,
|
|
|
|
float tolerance, bool narrow_range = false) {
|
|
QuantizationParams input1_qparams;
|
|
QuantizationParams input2_qparams;
|
|
QuantizationParams output_qparams;
|
|
|
|
input1_qparams = ChooseQuantizationParams<T>(static_cast<double>(input1_min),
|
|
static_cast<double>(input1_max),
|
|
narrow_range);
|
|
input2_qparams = ChooseQuantizationParams<T>(static_cast<double>(input2_min),
|
|
static_cast<double>(input2_max),
|
|
narrow_range);
|
|
output_qparams = ChooseQuantizationParams<T>(static_cast<double>(output_min),
|
|
static_cast<double>(output_max),
|
|
narrow_range);
|
|
|
|
TfLiteIntArray* input1_dims = IntArrayFromInts(input1_dims_data);
|
|
TfLiteIntArray* input2_dims = IntArrayFromInts(input2_dims_data);
|
|
TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
|
|
int output_size = ElementCount(*output_dims);
|
|
|
|
constexpr int inputs_size = 2;
|
|
constexpr int outputs_size = 1;
|
|
constexpr int tensors_size = inputs_size + outputs_size;
|
|
TfLiteTensor tensors[tensors_size] = {
|
|
CreateQuantizedTensor<T>(input1_data, input1_quantized, input1_dims,
|
|
input1_qparams.scale, input1_qparams.zero_point),
|
|
CreateQuantizedTensor<T>(input2_data, input2_quantized, input2_dims,
|
|
input2_qparams.scale, input2_qparams.zero_point),
|
|
CreateQuantizedTensor<T>(output_data, output_dims, output_qparams.scale,
|
|
output_qparams.zero_point),
|
|
};
|
|
|
|
int inputs_array_data[] = {2, 0, 1};
|
|
TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_array_data);
|
|
int outputs_array_data[] = {1, 2};
|
|
TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_array_data);
|
|
|
|
const TFLMRegistration registration = tflite::Register_SQUARED_DIFFERENCE();
|
|
micro::KernelRunner runner(registration, tensors, tensors_size, inputs_array,
|
|
outputs_array, /*builtin_data=*/nullptr);
|
|
|
|
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.InitAndPrepare());
|
|
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.Invoke());
|
|
|
|
Dequantize(output_data, output_size, output_qparams.scale,
|
|
output_qparams.zero_point, dequantized_output);
|
|
|
|
for (int i = 0; i < output_size; ++i) {
|
|
TF_LITE_MICRO_EXPECT_NEAR(golden[i], dequantized_output[i], tolerance);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace testing
|
|
} // namespace tflite
|
|
|
|
TF_LITE_MICRO_TESTS_BEGIN
|
|
|
|
TF_LITE_MICRO_TEST(FloatSquaredDifferenceSameShape) {
|
|
constexpr int data_size = 4;
|
|
int inout_shape[] = {4, 1, 2, 2, 1};
|
|
const float input1_values[] = {-0.2, 0.2, -1.2, 0.8};
|
|
const float input2_values[] = {0.5, 0.2, -1.5, 0.5};
|
|
const float golden_values[] = {0.49, 0.0, 0.09, 0.09};
|
|
float output_data[data_size];
|
|
tflite::testing::TestSquaredDifference(
|
|
inout_shape, input1_values, inout_shape, input2_values, inout_shape,
|
|
golden_values, output_data);
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(FloatSquaredDifferenceVariousShapes) {
|
|
constexpr int data_size = 6;
|
|
const float input1_values[] = {-2.0, 0.2, 0.3, 0.8, 1.1, -2.0};
|
|
const float input2_values[] = {1.0, 0.2, 0.6, 0.4, -1.0, -0.0};
|
|
const float golden_values[] = {9.0, 0.0, 0.09, 0.16, 4.41, 4.0};
|
|
float output_data[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifference(
|
|
tflite::testing::test_shape[i], input1_values,
|
|
tflite::testing::test_shape[i], input2_values,
|
|
tflite::testing::test_shape[i], golden_values, output_data);
|
|
}
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(FloatSquaredDifferenceWithBroadcast) {
|
|
constexpr int data_size = 6;
|
|
|
|
// input 2 is scalar
|
|
int input2_shape[] = {1, 1};
|
|
const float input1_values[] = {-0.2, 0.2, 0.5, 0.8, 0.11, 1.1};
|
|
const float input2_values[] = {0.1};
|
|
const float golden_values[] = {0.09, 0.01, 0.16, 0.49, 0.0001, 1.0};
|
|
float output_data[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifference(
|
|
tflite::testing::test_shape[i], input1_values, input2_shape,
|
|
input2_values, tflite::testing::test_shape[i], golden_values,
|
|
output_data);
|
|
}
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(IntegerSquaredDifferenceSameShape) {
|
|
constexpr int data_size = 4;
|
|
int inout_shape[] = {4, 1, 2, 2, 1};
|
|
const int32_t input1_values[] = {-2, 2, -15, 8};
|
|
const int32_t input2_values[] = {5, -2, -3, 5};
|
|
const int32_t golden_values[] = {49, 16, 144, 9};
|
|
int32_t output_data[data_size];
|
|
tflite::testing::TestSquaredDifference(
|
|
inout_shape, input1_values, inout_shape, input2_values, inout_shape,
|
|
golden_values, output_data);
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(IntegerSquaredDifferenceVariousShapes) {
|
|
constexpr int data_size = 6;
|
|
const int32_t input1_values[] = {-20, 2, 3, 8, 11, -20};
|
|
const int32_t input2_values[] = {1, 2, 6, 5, -5, -20};
|
|
const int32_t golden_values[] = {441, 0, 9, 9, 256, 0};
|
|
int32_t output_data[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifference(
|
|
tflite::testing::test_shape[i], input1_values,
|
|
tflite::testing::test_shape[i], input2_values,
|
|
tflite::testing::test_shape[i], golden_values, output_data);
|
|
}
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(IntegerSquaredDifferenceWithBroadcast) {
|
|
constexpr int data_size = 6;
|
|
|
|
// input 2 is a scalar
|
|
int input2_shape[] = {1, 1};
|
|
const int32_t input1_values[] = {-20, 10, 7, 3, 1, 13};
|
|
const int32_t input2_values[] = {3};
|
|
const int32_t golden_values[] = {529, 49, 16, 0, 4, 100};
|
|
int32_t output_data[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifference(
|
|
tflite::testing::test_shape[i], input1_values, input2_shape,
|
|
input2_values, tflite::testing::test_shape[i], golden_values,
|
|
output_data);
|
|
}
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(QuantizedSquaredDifferenceSameShape) {
|
|
constexpr int data_size = 4;
|
|
int inout_shape[] = {4, 1, 2, 2, 1};
|
|
const float input1_values[] = {-0.2, 0.2, -1.2, 0.8};
|
|
const float input2_values[] = {0.5, 0.2, -1.5, 0.5};
|
|
const float golden_values[] = {0.49, 0.0, 0.09, 0.09};
|
|
float output_dequantized[data_size];
|
|
// Int8 case
|
|
int8_t input1_int8[data_size];
|
|
int8_t input2_int8[data_size];
|
|
int8_t output_int8[data_size];
|
|
tflite::testing::TestSquaredDifferenceQuantized(
|
|
inout_shape, input1_values, input1_int8, -1.2f, 0.8f, inout_shape,
|
|
input2_values, input2_int8, -1.5f, 0.5f, inout_shape, output_int8, 0.0f,
|
|
0.5f, output_dequantized, golden_values, 2.0f / 255.0f);
|
|
|
|
// Int16 case
|
|
int16_t input1_int16[data_size];
|
|
int16_t input2_int16[data_size];
|
|
int16_t output_int16[data_size];
|
|
// Symmetrical quantization: (rmin == -rmax), requires narrow range (qmin =
|
|
// -qmax).
|
|
// TODO(b/269352046): understand the tolerance level
|
|
// http://b/269352046#comment7
|
|
tflite::testing::TestSquaredDifferenceQuantized(
|
|
inout_shape, input1_values, input1_int16, -1.2f, 1.2f, inout_shape,
|
|
input2_values, input2_int16, -1.5f, 1.5f, inout_shape, output_int16,
|
|
-0.5f, 0.5f, output_dequantized, golden_values, 6.0f / 32768.0f,
|
|
/*narrow_range=*/true);
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(QuantizedSquaredDifferenceVariousShapes) {
|
|
constexpr int data_size = 6;
|
|
const float input1_values[] = {-2.0, 0.2, 0.3, 0.8, 1.1, -2.0};
|
|
const float input2_values[] = {1.0, 0.2, 0.6, 0.4, -1.0, -0.0};
|
|
const float golden_values[] = {9.0, 0.0, 0.09, 0.16, 4.41, 4.0};
|
|
// Int8 case
|
|
int8_t input1_int8[data_size];
|
|
int8_t input2_int8[data_size];
|
|
int8_t output_int8[data_size];
|
|
float output_dequantized[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifferenceQuantized(
|
|
tflite::testing::test_shape[i], input1_values, input1_int8, -2.0f, 1.7f,
|
|
tflite::testing::test_shape[i], input2_values, input2_int8, -1.0f, 1.0f,
|
|
tflite::testing::test_shape[i], output_int8, 0.0f, 9.0f,
|
|
output_dequantized, golden_values, 18.0f / 255.0f);
|
|
}
|
|
|
|
// Int16 case
|
|
int16_t input1_int16[data_size];
|
|
int16_t input2_int16[data_size];
|
|
int16_t output_int16[data_size];
|
|
// Symmetrical quantization: (rmin == -rmax), requires narrow range (qmin =
|
|
// -qmax).
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifferenceQuantized(
|
|
tflite::testing::test_shape[i], input1_values, input1_int16, -2.0f,
|
|
2.0f, tflite::testing::test_shape[i], input2_values, input2_int16,
|
|
-1.0f, 1.0f, tflite::testing::test_shape[i], output_int16, -9.0f, 9.0f,
|
|
output_dequantized, golden_values, 18.0f / 32768.0f,
|
|
/*narrow_range=*/true);
|
|
}
|
|
}
|
|
|
|
TF_LITE_MICRO_TEST(FloatSquaredDifferenceWithBroadcast) {
|
|
constexpr int data_size = 6;
|
|
|
|
// input 2 is a scalar
|
|
int input2_shape[] = {1, 1};
|
|
const float input1_values[] = {-0.2, 0.2, 0.5, 0.8, 0.11, 1.1};
|
|
const float input2_values[] = {0.1};
|
|
const float golden_values[] = {0.09, 0.01, 0.16, 0.49, 0.0001, 1.0};
|
|
|
|
// Int8 case
|
|
int8_t input1_int8[data_size];
|
|
int8_t input2_int8[data_size];
|
|
int8_t output_int8[data_size];
|
|
float output_dequantized[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifferenceQuantized(
|
|
tflite::testing::test_shape[i], input1_values, input1_int8, -0.2f, 1.1f,
|
|
input2_shape, input2_values, input2_int8, 0.0f, 1.0f,
|
|
tflite::testing::test_shape[i], output_int8, 0.0f, 1.0f,
|
|
output_dequantized, golden_values, 2.0f / 255.0f);
|
|
}
|
|
|
|
// Int16 case
|
|
int16_t input1_int16[data_size];
|
|
int16_t input2_int16[data_size];
|
|
int16_t output_int16[data_size];
|
|
for (int i = 0; i < tflite::testing::kNumTestShapes; ++i) {
|
|
tflite::testing::TestSquaredDifferenceQuantized(
|
|
tflite::testing::test_shape[i], input1_values, input1_int16, -1.1f,
|
|
1.1f, input2_shape, input2_values, input2_int16, -1.0f, 1.0f,
|
|
tflite::testing::test_shape[i], output_int16, -1.0f, 1.0f,
|
|
output_dequantized, golden_values, 2.0f / 32768.0f,
|
|
/*narrow_range=*/true);
|
|
}
|
|
}
|
|
|
|
TF_LITE_MICRO_TESTS_END
|