| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_LSTM_CELL_H_ |
| #define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_LSTM_CELL_H_ |
|
|
| #include <algorithm> |
| #include <cmath> |
| #include <cstdint> |
|
|
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/common.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/reference/concatenation.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/reference/fully_connected.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/types.h" |
|
|
| namespace tflite { |
| namespace reference_ops { |
|
|
| inline void LstmCell( |
| const LstmCellParams& params, const RuntimeShape& unextended_input_shape, |
| const float* input_data, const RuntimeShape& unextended_prev_activ_shape, |
| const float* prev_activ_data, const RuntimeShape& weights_shape, |
| const float* weights_data, const RuntimeShape& unextended_bias_shape, |
| const float* bias_data, const RuntimeShape& unextended_prev_state_shape, |
| const float* prev_state_data, |
| const RuntimeShape& unextended_output_state_shape, float* output_state_data, |
| const RuntimeShape& unextended_output_activ_shape, float* output_activ_data, |
| const RuntimeShape& unextended_concat_temp_shape, float* concat_temp_data, |
| const RuntimeShape& unextended_activ_temp_shape, float* activ_temp_data) { |
| TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); |
| const RuntimeShape input_shape = |
| RuntimeShape::ExtendedShape(4, unextended_input_shape); |
| const RuntimeShape prev_activ_shape = |
| RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); |
| const RuntimeShape bias_shape = |
| RuntimeShape::ExtendedShape(4, unextended_bias_shape); |
| const RuntimeShape prev_state_shape = |
| RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); |
| const RuntimeShape output_state_shape = |
| RuntimeShape::ExtendedShape(4, unextended_output_state_shape); |
| const RuntimeShape output_activ_shape = |
| RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); |
| const RuntimeShape concat_temp_shape = |
| RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); |
| const RuntimeShape activ_temp_shape = |
| RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); |
| TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); |
|
|
| const int weights_dim_count = weights_shape.DimensionsCount(); |
| const int batches = |
| MatchingDim(input_shape, 0, prev_activ_shape, 0, prev_state_shape, 0, |
| output_state_shape, 0, output_activ_shape, 0); |
| const int height = |
| MatchingDim(input_shape, 1, prev_activ_shape, 1, prev_state_shape, 1, |
| output_state_shape, 1, output_activ_shape, 1); |
| const int width = |
| MatchingDim(input_shape, 2, prev_activ_shape, 2, prev_state_shape, 2, |
| output_state_shape, 2, output_activ_shape, 2); |
| const int input_depth = input_shape.Dims(3); |
| const int prev_activ_depth = prev_activ_shape.Dims(3); |
| const int total_input_depth = prev_activ_depth + input_depth; |
| TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), |
| total_input_depth); |
| TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); |
| const int intern_activ_depth = |
| MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); |
| TFLITE_DCHECK_EQ(weights_shape.FlatSize(), |
| intern_activ_depth * total_input_depth); |
| TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); |
| const int output_depth = |
| MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, |
| 3, output_activ_shape, 3); |
| TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); |
|
|
| |
| float const* concat_input_arrays_data[2] = {input_data, prev_activ_data}; |
| const RuntimeShape* concat_input_arrays_shapes[2] = {&input_shape, |
| &prev_activ_shape}; |
| tflite::ConcatenationParams concat_params; |
| concat_params.axis = 3; |
| concat_params.inputs_count = 2; |
| Concatenation(concat_params, concat_input_arrays_shapes, |
| concat_input_arrays_data, concat_temp_shape, concat_temp_data); |
|
|
| |
| tflite::FullyConnectedParams fc_params; |
| fc_params.float_activation_min = std::numeric_limits<float>::lowest(); |
| fc_params.float_activation_max = std::numeric_limits<float>::max(); |
| FullyConnected(fc_params, concat_temp_shape, concat_temp_data, weights_shape, |
| weights_data, bias_shape, bias_data, activ_temp_shape, |
| activ_temp_data); |
|
|
| |
| for (int b = 0; b < batches; ++b) { |
| for (int w = 0; w < width; ++w) { |
| for (int h = 0; h < height; ++h) { |
| for (int c = 0; c < output_depth; ++c) { |
| const float input_gate = |
| 1.f / |
| (1.f + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, |
| 0 * output_depth + c)])); |
| const float new_input = std::tanh(activ_temp_data[Offset( |
| activ_temp_shape, b, h, w, 1 * output_depth + c)]); |
| const float forget_gate = |
| 1.f / |
| (1.f + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, |
| 2 * output_depth + c)])); |
| const float output_gate = |
| 1.f / |
| (1.f + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, |
| 3 * output_depth + c)])); |
| const float new_state = |
| input_gate * new_input + |
| forget_gate * |
| prev_state_data[Offset(prev_state_shape, b, h, w, c)]; |
| output_state_data[Offset(output_state_shape, b, h, w, c)] = new_state; |
| output_activ_data[Offset(output_activ_shape, b, h, w, c)] = |
| output_gate * std::tanh(new_state); |
| } |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <int StateIntegerBits> |
| inline void LstmCell(const LstmCellParams& params, |
| const RuntimeShape& unextended_input_shape, |
| const uint8_t* input_data_uint8, |
| const RuntimeShape& unextended_prev_activ_shape, |
| const uint8_t* prev_activ_data_uint8, |
| const RuntimeShape& weights_shape, |
| const uint8_t* weights_data_uint8, |
| const RuntimeShape& unextended_bias_shape, |
| const int32_t* bias_data_int32, |
| const RuntimeShape& unextended_prev_state_shape, |
| const int16_t* prev_state_data_int16, |
| const RuntimeShape& unextended_output_state_shape, |
| int16_t* output_state_data_int16, |
| const RuntimeShape& unextended_output_activ_shape, |
| uint8_t* output_activ_data_uint8, |
| const RuntimeShape& unextended_concat_temp_shape, |
| uint8_t* concat_temp_data_uint8, |
| const RuntimeShape& unextended_activ_temp_shape, |
| int16_t* activ_temp_data_int16, void* gemmlowp_context) { |
| (void)gemmlowp_context; |
| int32_t weights_zero_point = params.weights_zero_point; |
| int32_t accum_multiplier = params.accum_multiplier; |
| int accum_shift = params.accum_shift; |
| TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); |
| TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); |
| const RuntimeShape input_shape = |
| RuntimeShape::ExtendedShape(4, unextended_input_shape); |
| const RuntimeShape prev_activ_shape = |
| RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); |
| const RuntimeShape bias_shape = |
| RuntimeShape::ExtendedShape(4, unextended_bias_shape); |
| const RuntimeShape prev_state_shape = |
| RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); |
| const RuntimeShape output_state_shape = |
| RuntimeShape::ExtendedShape(4, unextended_output_state_shape); |
| const RuntimeShape output_activ_shape = |
| RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); |
| const RuntimeShape concat_temp_shape = |
| RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); |
| const RuntimeShape activ_temp_shape = |
| RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); |
| TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); |
|
|
| |
| const int weights_dim_count = weights_shape.DimensionsCount(); |
| const int outer_size = MatchingFlatSizeSkipDim( |
| input_shape, 3, prev_activ_shape, prev_state_shape, output_state_shape, |
| output_activ_shape); |
| const int input_depth = input_shape.Dims(3); |
| const int prev_activ_depth = prev_activ_shape.Dims(3); |
| const int total_input_depth = prev_activ_depth + input_depth; |
| TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), |
| total_input_depth); |
| const int intern_activ_depth = |
| MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); |
| TFLITE_DCHECK_EQ(weights_shape.FlatSize(), |
| intern_activ_depth * total_input_depth); |
| TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); |
| TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); |
| const int output_depth = |
| MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, |
| 3, output_activ_shape, 3); |
| TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); |
| const int fc_batches = FlatSizeSkipDim(activ_temp_shape, 3); |
| const int fc_output_depth = |
| MatchingDim(weights_shape, weights_dim_count - 2, activ_temp_shape, 3); |
| const int fc_accum_depth = total_input_depth; |
| TFLITE_DCHECK_EQ(fc_output_depth, 4 * output_depth); |
|
|
| |
| uint8_t const* concat_input_arrays_data[2] = {input_data_uint8, |
| prev_activ_data_uint8}; |
| const RuntimeShape* concat_input_arrays_shapes[2] = {&input_shape, |
| &prev_activ_shape}; |
| tflite::ConcatenationParams concat_params; |
| concat_params.axis = 3; |
| concat_params.inputs_count = 2; |
| Concatenation(concat_params, concat_input_arrays_shapes, |
| concat_input_arrays_data, concat_temp_shape, |
| concat_temp_data_uint8); |
|
|
| |
| |
| |
| |
| |
| for (int b = 0; b < fc_batches; ++b) { |
| for (int out_c = 0; out_c < fc_output_depth; ++out_c) { |
| |
| |
| int32_t accum = bias_data_int32[out_c]; |
| |
| for (int d = 0; d < fc_accum_depth; ++d) { |
| int16_t input_val = |
| concat_temp_data_uint8[b * fc_accum_depth + d] - 128; |
| int16_t weights_val = |
| weights_data_uint8[out_c * fc_accum_depth + d] - weights_zero_point; |
| accum += input_val * weights_val; |
| } |
| |
| |
| |
| |
| accum = |
| MultiplyByQuantizedMultiplier(accum, accum_multiplier, accum_shift); |
| |
| accum = std::max(-32768, std::min(32767, accum)); |
| activ_temp_data_int16[out_c + fc_output_depth * b] = accum; |
| } |
| } |
|
|
| |
| |
| for (int b = 0; b < outer_size; ++b) { |
| for (int c = 0; c < output_depth; ++c) { |
| |
| |
| |
| |
| |
| |
| |
| |
| using F0 = gemmlowp::FixedPoint<std::int16_t, 0>; |
| |
| |
| |
| using F3 = gemmlowp::FixedPoint<std::int16_t, 3>; |
| |
| |
| |
| |
| using FS = gemmlowp::FixedPoint<std::int16_t, StateIntegerBits>; |
| |
| F3 input_gate_input = F3::FromRaw( |
| activ_temp_data_int16[b * fc_output_depth + 0 * output_depth + c]); |
| F0 input_gate_output = gemmlowp::logistic(input_gate_input); |
| |
| |
| F3 input_modulation_gate_input = F3::FromRaw( |
| activ_temp_data_int16[b * fc_output_depth + 1 * output_depth + c]); |
| F0 input_modulation_gate_output = |
| gemmlowp::tanh(input_modulation_gate_input); |
| |
| F3 forget_gate_input = F3::FromRaw( |
| activ_temp_data_int16[b * fc_output_depth + 2 * output_depth + c]); |
| F0 forget_gate_output = gemmlowp::logistic(forget_gate_input); |
| |
| F3 output_gate_input = F3::FromRaw( |
| activ_temp_data_int16[b * fc_output_depth + 3 * output_depth + c]); |
| F0 output_gate_output = gemmlowp::logistic(output_gate_input); |
| |
| F0 input_times_input_modulation = |
| input_gate_output * input_modulation_gate_output; |
| FS prev_state = FS::FromRaw(prev_state_data_int16[b * output_depth + c]); |
| FS prev_state_times_forget_state = forget_gate_output * prev_state; |
| |
| FS new_state = gemmlowp::SaturatingAdd( |
| gemmlowp::Rescale<StateIntegerBits>(input_times_input_modulation), |
| prev_state_times_forget_state); |
| |
| |
| |
| |
| |
| |
| |
| |
| F3 new_state_f3 = gemmlowp::Rescale<3>(new_state); |
| F0 output_activ_int16 = output_gate_output * gemmlowp::tanh(new_state_f3); |
| |
| |
| |
| output_state_data_int16[b * output_depth + c] = new_state.raw(); |
| |
| |
| int16_t rescaled_output_activ = |
| gemmlowp::RoundingDivideByPOT(output_activ_int16.raw(), 8); |
| int16_t clamped_output_activ = std::max<int16_t>( |
| -128, std::min<int16_t>(127, rescaled_output_activ)); |
| output_activ_data_uint8[b * output_depth + c] = |
| 128 + clamped_output_activ; |
| } |
| } |
| } |
|
|
| } |
| } |
| #endif |
|
|