| /* Copyright 2021 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 <algorithm> |
| #include <cstdint> |
| |
| #include "tensorflow/lite/c/builtin_op_data.h" |
| #include "tensorflow/lite/c/common.h" |
| #include "tensorflow/lite/kernels/internal/common.h" |
| #include "tensorflow/lite/kernels/internal/quantization_util.h" |
| #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" |
| #include "tensorflow/lite/kernels/internal/types.h" |
| #include "tensorflow/lite/kernels/kernel_util.h" |
| #include "tensorflow/lite/kernels/op_macros.h" |
| #include "tensorflow/lite/micro/kernels/activations.h" |
| #include "tensorflow/lite/micro/kernels/kernel_util.h" |
| #include "tensorflow/lite/micro/micro_utils.h" |
| |
| namespace tflite { |
| |
| const int kActivationsInputTensor = 0; |
| const int kActivationsOutputTensor = 0; |
| |
| void ReluQuantized(const ReluOpData& data, const RuntimeShape& input_shape, |
| const RuntimeShape& output_shape, const int8_t* input_data, |
| int8_t* output_data) { |
| const int flat_size = MatchingFlatSize(input_shape, output_shape); |
| for (int i = 0; i < flat_size; ++i) { |
| const int32_t val = static_cast<int32_t>(input_data[i]); |
| int32_t clamped = |
| data.params.output_offset + |
| MultiplyByQuantizedMultiplier(val - data.params.input_offset, |
| data.params.output_multiplier, |
| data.params.output_shift); |
| clamped = std::max(data.params.quantized_activation_min, clamped); |
| clamped = std::min(data.params.quantized_activation_max, clamped); |
| output_data[i] = static_cast<int8_t>(clamped); |
| } |
| } |
| |
| template <typename T> |
| void CalculateReluOpData(const TfLiteTensor* input, TfLiteTensor* output, |
| ReluOpData* data) { |
| float act_min = 0.0; |
| float act_max = std::numeric_limits<float>::infinity(); |
| double real_multiplier = |
| static_cast<double>(input->params.scale / output->params.scale); |
| |
| const RuntimeShape input_shape = GetTensorShape(input); |
| const RuntimeShape output_shape = GetTensorShape(output); |
| |
| QuantizeMultiplier(real_multiplier, &data->params.output_multiplier, |
| &data->params.output_shift); |
| |
| data->params.quantized_activation_min = std::max( |
| static_cast<int32_t>(std::numeric_limits<T>::min()), |
| output->params.zero_point + |
| static_cast<int32_t>(roundf(act_min / output->params.scale))); |
| data->params.quantized_activation_max = |
| act_max == std::numeric_limits<float>::infinity() |
| ? static_cast<int32_t>(std::numeric_limits<T>::max()) |
| : std::min(static_cast<int32_t>(std::numeric_limits<T>::max()), |
| output->params.zero_point + |
| static_cast<int32_t>( |
| roundf(act_max / output->params.scale))); |
| data->params.input_offset = input->params.zero_point; |
| data->params.output_offset = output->params.zero_point; |
| } |
| |
| void ReluFloat(const RuntimeShape& input_shape, const float* input_data, |
| const RuntimeShape& output_shape, float* output_data) { |
| const int flat_size = MatchingFlatSize(input_shape, output_shape); |
| for (int i = 0; i < flat_size; ++i) { |
| const float val = input_data[i]; |
| const float lower = 0.0f; |
| const float clamped = val < lower ? lower : val; |
| output_data[i] = clamped; |
| } |
| } |
| |
| void Relu6Float(const RuntimeShape& input_shape, const float* input_data, |
| const RuntimeShape& output_shape, float* output_data) { |
| const int flat_size = MatchingFlatSize(input_shape, output_shape); |
| for (int i = 0; i < flat_size; ++i) { |
| const float val = input_data[i]; |
| const float upper = 6.0f; |
| const float lower = 0.0f; |
| const float clamped = val > upper ? upper : val < lower ? lower : val; |
| output_data[i] = clamped; |
| } |
| } |
| |
| void Relu6Quantized(int8_t lower, int8_t upper, const RuntimeShape& input_shape, |
| const int8_t* input_data, const RuntimeShape& output_shape, |
| int8_t* output_data) { |
| const int flat_size = MatchingFlatSize(input_shape, output_shape); |
| for (int i = 0; i < flat_size; ++i) { |
| const int8_t val = input_data[i]; |
| const int8_t clamped = val > upper ? upper : val < lower ? lower : val; |
| output_data[i] = clamped; |
| } |
| } |
| |
| TfLiteStatus ReluPrepare(TfLiteContext* context, TfLiteNode* node) { |
| TFLITE_DCHECK(node->user_data != nullptr); |
| ReluOpData* data = static_cast<ReluOpData*>(node->user_data); |
| |
| MicroContext* micro_context = GetMicroContext(context); |
| TfLiteTensor* input = |
| micro_context->AllocateTempInputTensor(node, kActivationsInputTensor); |
| TF_LITE_ENSURE(context, input != nullptr); |
| TfLiteTensor* output = |
| micro_context->AllocateTempOutputTensor(node, kActivationsOutputTensor); |
| TF_LITE_ENSURE(context, output != nullptr); |
| |
| if (input->type == kTfLiteInt8) { |
| CalculateReluOpData<int8_t>(input, output, data); |
| } |
| |
| micro_context->DeallocateTempTfLiteTensor(input); |
| micro_context->DeallocateTempTfLiteTensor(output); |
| |
| return kTfLiteOk; |
| } |
| |
| TfLiteStatus Relu6Prepare(TfLiteContext* context, TfLiteNode* node) { |
| TFLITE_DCHECK(node->user_data != nullptr); |
| Relu6OpData* data = static_cast<Relu6OpData*>(node->user_data); |
| |
| MicroContext* micro_context = GetMicroContext(context); |
| TfLiteTensor* input = |
| micro_context->AllocateTempInputTensor(node, kActivationsInputTensor); |
| TF_LITE_ENSURE(context, input != nullptr); |
| |
| if (input->type == kTfLiteInt8) { |
| data->six_int8 = FloatToQuantizedType<int8_t>(6.0f, input->params.scale, |
| input->params.zero_point); |
| data->zero_int8 = input->params.zero_point; |
| } |
| |
| micro_context->DeallocateTempTfLiteTensor(input); |
| |
| return kTfLiteOk; |
| } |
| |
| } // namespace tflite |