| /******************************************************************************* |
| * Copyright (c) 2020 Cadence Design Systems, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to use this Software with Cadence processor cores only and |
| * not with any other processors and platforms, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| ******************************************************************************/ |
| |
| /* Copyright 2018 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/kernels/internal/reference/softmax.h" |
| |
| #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/kernel_util.h" |
| #include "tensorflow/lite/kernels/op_macros.h" |
| #include "tensorflow/lite/micro/kernels/xtensa_hifimini_staging/xtensa_tf_micro_common.h" |
| |
| namespace tflite { |
| namespace ops { |
| namespace micro { |
| namespace activations { |
| namespace { |
| |
| struct OpData { |
| int32_t input_multiplier; |
| int32_t input_left_shift; |
| int32_t diff_min; |
| int scratch_tensor_index; |
| }; |
| |
| } // namespace |
| |
| TfLiteStatus CalculateSoftmaxOpData(TfLiteContext* context, |
| const TfLiteTensor* input, |
| TfLiteTensor* output, |
| const TfLiteSoftmaxParams* params, |
| OpData* op_data) { |
| if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) { |
| if (input->type == kTfLiteUInt8) { |
| TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0); |
| } else { |
| if (output->type == kTfLiteInt16) { |
| TF_LITE_ENSURE_EQ(context, output->params.zero_point, |
| std::numeric_limits<int16_t>::min()); |
| // NOTE: Current int16 softmax output does not require symmetric scaling |
| // - so no need to verify scale here. |
| } else { |
| TF_LITE_ENSURE_EQ(context, output->params.zero_point, |
| std::numeric_limits<int8_t>::min()); |
| TF_LITE_ENSURE(context, output->params.scale == 1.f / 256); |
| } |
| } |
| |
| static const int kScaledDiffIntegerBits = 5; |
| |
| int input_left_shift; |
| tflite::PreprocessSoftmaxScaling( |
| static_cast<double>(params->beta), |
| static_cast<double>(input->params.scale), kScaledDiffIntegerBits, |
| &op_data->input_multiplier, &input_left_shift); |
| op_data->input_left_shift = input_left_shift; |
| op_data->diff_min = |
| -1.0 * tflite::CalculateInputRadius(kScaledDiffIntegerBits, |
| op_data->input_left_shift); |
| } |
| return kTfLiteOk; |
| } |
| |
| void* SoftmaxInit(TfLiteContext* context, const char* buffer, size_t length) { |
| TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); |
| return context->AllocatePersistentBuffer(context, sizeof(OpData)); |
| } |
| |
| TfLiteStatus SoftmaxPrepare(TfLiteContext* context, TfLiteNode* node) { |
| auto* params = static_cast<TfLiteSoftmaxParams*>(node->builtin_data); |
| |
| TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); |
| TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); |
| const TfLiteTensor* input = GetInput(context, node, 0); |
| TfLiteTensor* output = GetOutput(context, node, 0); |
| TF_LITE_ENSURE(context, NumDimensions(input) >= 1); |
| |
| TFLITE_DCHECK(node->user_data != nullptr); |
| OpData* op_data = static_cast<OpData*>(node->user_data); |
| |
| const RuntimeShape& input_shape = GetTensorShape(input); |
| const RuntimeShape& output_shape = GetTensorShape(output); |
| const int trailing_dim = input_shape.DimensionsCount() - 1; |
| const int depth = |
| MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); |
| int scratch_size = |
| xa_nn_get_softmax_scratch_size(PREC_SYM8S, PREC_SYM8S, depth); |
| |
| const TfLiteStatus scratch_status = context->RequestScratchBufferInArena( |
| context, scratch_size, &(op_data->scratch_tensor_index)); |
| TF_LITE_ENSURE_OK(context, scratch_status); |
| // Allocate an array to precompute exponents over all int8 inputs, applying |
| // the scale and beta before calculating exp. It is mandatory to apply beta |
| // and scale here, since each softmax op may have different beta and scale |
| // values. Beta and scale will remain constant for a given softmax op. |
| |
| TF_LITE_ENSURE_STATUS( |
| CalculateSoftmaxOpData(context, input, output, params, op_data)); |
| |
| return kTfLiteOk; |
| } |
| |
| TfLiteStatus SoftmaxEval(TfLiteContext* context, TfLiteNode* node) { |
| auto* op_data = static_cast<OpData*>(node->user_data); |
| |
| const TfLiteTensor* input = GetInput(context, node, 0); |
| TfLiteTensor* output = GetOutput(context, node, 0); |
| |
| if (input->type == kTfLiteInt8 && output->type == kTfLiteInt16) { |
| const RuntimeShape& input_shape = GetTensorShape(input); |
| const int8_t* input_data = GetTensorData<int8_t>(input); |
| const RuntimeShape& output_shape = GetTensorShape(output); |
| int16* output_data = GetTensorData<int16>(output); |
| const int trailing_dim = input_shape.DimensionsCount() - 1; |
| const int outer_size = |
| MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); |
| const int depth = |
| MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); |
| |
| void* p_scratch = static_cast<void*>( |
| context->GetScratchBuffer(context, op_data->scratch_tensor_index)); |
| TFLITE_DCHECK(p_scratch != nullptr); |
| |
| for (int i = 0; i < outer_size; ++i) { |
| int err = xa_nn_vec_softmax_asym8s_16( |
| &output_data[i * depth], &input_data[i * depth], op_data->diff_min, |
| op_data->input_left_shift, op_data->input_multiplier, depth, |
| p_scratch); |
| CHECK_ERR_HIFI_NNLIB_KER(err, "xa_nn_vec_softmax_asym8s_16 failed"); |
| } |
| return kTfLiteOk; |
| } else { |
| TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.", |
| TfLiteTypeGetName(input->type), input->type); |
| return kTfLiteError; |
| } |
| } |
| } // namespace activations |
| |
| TfLiteRegistration Register_SOFTMAX() { |
| return {/*init=*/activations::SoftmaxInit, |
| /*free=*/nullptr, |
| /*prepare=*/activations::SoftmaxPrepare, |
| /*invoke=*/activations::SoftmaxEval, |
| /*profiling_string=*/nullptr, |
| /*builtin_code=*/0, |
| /*custom_name=*/nullptr, |
| /*version=*/0}; |
| } |
| |
| } // namespace micro |
| } // namespace ops |
| } // namespace tflite |