| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SOFTMAX_H_ |
| #define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SOFTMAX_H_ |
|
|
| #include <algorithm> |
| #include <limits> |
|
|
| #include "edge-impulse-sdk/third_party/gemmlowp/fixedpoint/fixedpoint.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/common.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/cppmath.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/quantization_util.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/types.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/op_macros.h" |
|
|
| namespace tflite { |
| namespace reference_ops { |
|
|
| inline void Softmax(const SoftmaxParams& params, |
| const RuntimeShape& input_shape, const float* input_data, |
| const RuntimeShape& output_shape, float* output_data) { |
| 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); |
|
|
| for (int i = 0; i < outer_size; ++i) { |
| |
| |
| |
| float max = std::numeric_limits<float>::lowest(); |
| for (int c = 0; c < depth; ++c) { |
| max = std::max(max, input_data[i * depth + c]); |
| } |
|
|
| |
| float sum = 0.f; |
| for (int c = 0; c < depth; ++c) { |
| const float exp_c = std::exp((input_data[i * depth + c] - max) * |
| static_cast<float>(params.beta)); |
| output_data[i * depth + c] = exp_c; |
| sum += exp_c; |
| } |
|
|
| |
| for (int c = 0; c < depth; ++c) { |
| output_data[i * depth + c] = output_data[i * depth + c] / sum; |
| } |
| } |
| } |
|
|
| |
| |
| template <typename InputT, typename OutputT> |
| inline void Softmax(const SoftmaxParams& params, |
| const RuntimeShape& input_shape, const InputT* input_data, |
| const RuntimeShape& output_shape, OutputT* output_data) { |
| const int32_t input_beta_multiplier = params.input_multiplier; |
| const int32_t input_beta_left_shift = params.input_left_shift; |
| const int diff_min = params.diff_min; |
| |
| |
| |
| |
| |
| static const int kScaledDiffIntegerBits = 5; |
| static const int kAccumulationIntegerBits = 12; |
| using FixedPointScaledDiff = |
| gemmlowp::FixedPoint<int32_t, kScaledDiffIntegerBits>; |
| using FixedPointAccum = |
| gemmlowp::FixedPoint<int32_t, kAccumulationIntegerBits>; |
| using FixedPoint0 = gemmlowp::FixedPoint<int32_t, 0>; |
|
|
| 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); |
|
|
| for (int i = 0; i < outer_size; ++i) { |
| InputT max_in_row = std::numeric_limits<InputT>::min(); |
| for (int c = 0; c < depth; ++c) { |
| max_in_row = std::max(max_in_row, input_data[i * depth + c]); |
| } |
|
|
| FixedPointAccum sum_of_exps = FixedPointAccum::Zero(); |
| for (int c = 0; c < depth; ++c) { |
| int32_t input_diff = |
| static_cast<int32_t>(input_data[i * depth + c]) - max_in_row; |
| if (input_diff >= diff_min) { |
| const int32_t input_diff_rescaled = |
| MultiplyByQuantizedMultiplierGreaterThanOne( |
| input_diff, input_beta_multiplier, input_beta_left_shift); |
| const FixedPointScaledDiff scaled_diff_f8 = |
| FixedPointScaledDiff::FromRaw(input_diff_rescaled); |
| sum_of_exps = sum_of_exps + gemmlowp::Rescale<kAccumulationIntegerBits>( |
| exp_on_negative_values(scaled_diff_f8)); |
| } |
| } |
|
|
| int num_bits_over_unit; |
| FixedPoint0 shifted_scale = FixedPoint0::FromRaw(GetReciprocal( |
| sum_of_exps.raw(), kAccumulationIntegerBits, &num_bits_over_unit)); |
|
|
| for (int c = 0; c < depth; ++c) { |
| int32_t input_diff = |
| static_cast<int32_t>(input_data[i * depth + c]) - max_in_row; |
| if (input_diff >= diff_min) { |
| const int32_t input_diff_rescaled = |
| MultiplyByQuantizedMultiplierGreaterThanOne( |
| input_diff, input_beta_multiplier, input_beta_left_shift); |
| const FixedPointScaledDiff scaled_diff_f8 = |
| FixedPointScaledDiff::FromRaw(input_diff_rescaled); |
|
|
| FixedPoint0 exp_in_0 = exp_on_negative_values(scaled_diff_f8); |
| int32_t unsat_output = gemmlowp::RoundingDivideByPOT( |
| (shifted_scale * exp_in_0).raw(), |
| num_bits_over_unit + 31 - (sizeof(OutputT) * 8)); |
|
|
| const int32_t shifted_output = |
| unsat_output + |
| static_cast<int32_t>(std::numeric_limits<OutputT>::min()); |
|
|
| output_data[i * depth + c] = static_cast<OutputT>(std::max( |
| std::min(shifted_output, |
| static_cast<int32_t>(std::numeric_limits<OutputT>::max())), |
| static_cast<int32_t>(std::numeric_limits<OutputT>::min()))); |
| } else { |
| output_data[i * depth + c] = std::numeric_limits<OutputT>::min(); |
| } |
| } |
| } |
| } |
|
|
| |
| inline int16_t SoftMaxCalculateExp(const SoftmaxParams& params, |
| const int16_t* input_data, const int depth, |
| int16_t max_in_row, int i, int c) { |
| int32_t input_diff = input_data[i * depth + c] - max_in_row; |
| |
| |
| int32_t scaled_diff = MultiplyByQuantizedMultiplier( |
| input_diff, params.input_multiplier, params.input_left_shift); |
| |
| int32_t sym_scaled_diff = scaled_diff + 32767; |
| int16_t sat_sym_scaled_diff = |
| std::min(std::max(sym_scaled_diff, static_cast<int32_t>(-32768)), |
| static_cast<int32_t>(32767)); |
| |
| return LUTLookup(sat_sym_scaled_diff, params.exp_lut); |
| } |
| |
| inline void SoftmaxInt16(const SoftmaxParams& params, |
| const RuntimeShape& input_shape, |
| const int16_t* input_data, |
| const RuntimeShape& output_shape, |
| int16_t* output_data) { |
| 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); |
|
|
| for (int i = 0; i < outer_size; ++i) { |
| |
| int16_t max_in_row = std::numeric_limits<int16_t>::min(); |
| for (int c = 0; c < depth; ++c) { |
| max_in_row = std::max(max_in_row, input_data[i * depth + c]); |
| } |
|
|
| |
| |
| |
| |
| int32_t sum_of_exps = 0; |
| int16_t* exp_results_Q015 = output_data + i * depth; |
| for (int c = 0; c < depth; ++c) { |
| exp_results_Q015[c] = |
| SoftMaxCalculateExp(params, input_data, depth, max_in_row, i, c); |
| sum_of_exps += exp_results_Q015[c]; |
| } |
|
|
| |
| uint8_t headroom_plus_one = |
| CountLeadingZeros(static_cast<uint32_t>(sum_of_exps)); |
| int32_t shifted_sum = |
| ((static_cast<int64_t>(sum_of_exps) << (headroom_plus_one - 1)) + |
| (1 << 13)) >> |
| 14; |
| |
| |
| |
| int32_t sym_shifted_sum = shifted_sum + (-((1 << 15) + (1 << 16))); |
| int16_t sat_sym_shifted_sum = static_cast<int16_t>( |
| std::min(std::max(sym_shifted_sum, static_cast<int32_t>(-32768)), |
| static_cast<int32_t>(32767))); |
| |
| int16_t reciprocal_scale_Q015 = |
| LUTLookup(sat_sym_shifted_sum, params.one_over_one_plus_x_lut); |
|
|
| |
| |
| for (int c = 0; c < depth; ++c) { |
| uint8_t right_shift = 31 - headroom_plus_one; |
| int64_t round = 1 << (right_shift - 1); |
| int32_t result = (static_cast<int64_t>(exp_results_Q015[c]) * |
| static_cast<int64_t>(reciprocal_scale_Q015) + |
| round) >> |
| right_shift; |
| output_data[i * depth + c] = static_cast<int16_t>( |
| std::min(std::max(result, static_cast<int32_t>(0)), |
| static_cast<int32_t>(32767))); |
| } |
| } |
| } |
|
|
| } |
| } |
|
|
| #endif |
|
|