| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FULLY_CONNECTED_H_ |
| #define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FULLY_CONNECTED_H_ |
|
|
| #include <algorithm> |
|
|
| #include "edge-impulse-sdk/third_party/ruy/ruy/profiler/instrumentation.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" |
|
|
| namespace tflite { |
| namespace reference_ops { |
|
|
| inline void FullyConnected( |
| const FullyConnectedParams& params, const RuntimeShape& input_shape, |
| const float* input_data, const RuntimeShape& weights_shape, |
| const float* weights_data, const RuntimeShape& bias_shape, |
| const float* bias_data, const RuntimeShape& output_shape, |
| float* output_data) { |
| const float output_activation_min = params.float_activation_min; |
| const float output_activation_max = params.float_activation_max; |
| |
| |
| |
| |
| |
| const int output_dims_count = output_shape.DimensionsCount(); |
| const int weights_dims_count = weights_shape.DimensionsCount(); |
| const int batches = FlatSizeSkipDim(output_shape, output_dims_count - 1); |
| const int output_depth = MatchingDim(weights_shape, weights_dims_count - 2, |
| output_shape, output_dims_count - 1); |
| const int accum_depth = weights_shape.Dims(weights_dims_count - 1); |
| for (int b = 0; b < batches; ++b) { |
| for (int out_c = 0; out_c < output_depth; ++out_c) { |
| float total = 0.f; |
| for (int d = 0; d < accum_depth; ++d) { |
| total += input_data[b * accum_depth + d] * |
| weights_data[out_c * accum_depth + d]; |
| } |
| float bias_value = 0.0f; |
| if (bias_data) { |
| bias_value = bias_data[out_c]; |
| } |
| output_data[out_c + output_depth * b] = ActivationFunctionWithMinMax( |
| total + bias_value, output_activation_min, output_activation_max); |
| } |
| } |
| } |
|
|
| inline void FullyConnected( |
| const FullyConnectedParams& params, const RuntimeShape& input_shape, |
| const uint8_t* input_data, const RuntimeShape& filter_shape, |
| const uint8_t* filter_data, const RuntimeShape& bias_shape, |
| const int32_t* bias_data, const RuntimeShape& output_shape, |
| uint8_t* output_data) { |
| const int32_t input_offset = params.input_offset; |
| const int32_t filter_offset = params.weights_offset; |
| const int32_t output_offset = params.output_offset; |
| const int32_t output_multiplier = params.output_multiplier; |
| const int output_shift = params.output_shift; |
| const int32_t output_activation_min = params.quantized_activation_min; |
| const int32_t output_activation_max = params.quantized_activation_max; |
| TFLITE_DCHECK_GE(filter_shape.DimensionsCount(), 2); |
| TFLITE_DCHECK_GE(output_shape.DimensionsCount(), 1); |
|
|
| TFLITE_DCHECK_LE(output_activation_min, output_activation_max); |
| |
| |
| |
| |
| |
| const int output_dim_count = output_shape.DimensionsCount(); |
| const int filter_dim_count = filter_shape.DimensionsCount(); |
| const int batches = FlatSizeSkipDim(output_shape, output_dim_count - 1); |
| const int output_depth = MatchingDim(filter_shape, filter_dim_count - 2, |
| output_shape, output_dim_count - 1); |
| const int accum_depth = filter_shape.Dims(filter_dim_count - 1); |
| for (int b = 0; b < batches; ++b) { |
| for (int out_c = 0; out_c < output_depth; ++out_c) { |
| int32_t acc = 0; |
| for (int d = 0; d < accum_depth; ++d) { |
| int32_t input_val = input_data[b * accum_depth + d]; |
| int32_t filter_val = filter_data[out_c * accum_depth + d]; |
| acc += (filter_val + filter_offset) * (input_val + input_offset); |
| } |
| if (bias_data) { |
| acc += bias_data[out_c]; |
| } |
| acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); |
| acc += output_offset; |
| acc = std::max(acc, output_activation_min); |
| acc = std::min(acc, output_activation_max); |
| output_data[out_c + output_depth * b] = static_cast<uint8_t>(acc); |
| } |
| } |
| } |
|
|
| inline void FullyConnected( |
| const FullyConnectedParams& params, const RuntimeShape& input_shape, |
| const uint8_t* input_data, const RuntimeShape& filter_shape, |
| const uint8_t* filter_data, const RuntimeShape& bias_shape, |
| const int32_t* bias_data, const RuntimeShape& output_shape, |
| int16_t* output_data) { |
| const int32_t input_offset = params.input_offset; |
| const int32_t filter_offset = params.weights_offset; |
| const int32_t output_offset = params.output_offset; |
| const int32_t output_multiplier = params.output_multiplier; |
| const int output_shift = params.output_shift; |
| const int32_t output_activation_min = params.quantized_activation_min; |
| const int32_t output_activation_max = params.quantized_activation_max; |
|
|
| TFLITE_DCHECK_LE(output_activation_min, output_activation_max); |
| TFLITE_DCHECK_EQ(output_offset, 0); |
| |
| |
| |
| |
| |
| const int output_dim_count = output_shape.DimensionsCount(); |
| const int filter_dim_count = filter_shape.DimensionsCount(); |
| const int batches = FlatSizeSkipDim(output_shape, output_dim_count - 1); |
| const int output_depth = MatchingDim(filter_shape, filter_dim_count - 2, |
| output_shape, output_dim_count - 1); |
| const int accum_depth = filter_shape.Dims(filter_dim_count - 1); |
| for (int b = 0; b < batches; ++b) { |
| for (int out_c = 0; out_c < output_depth; ++out_c) { |
| |
| |
| int32_t accum = bias_data[out_c]; |
| |
| for (int d = 0; d < accum_depth; ++d) { |
| int16_t input_val = input_data[b * accum_depth + d] + input_offset; |
| int16_t filter_val = |
| filter_data[out_c * accum_depth + d] + filter_offset; |
| accum += filter_val * input_val; |
| } |
| |
| |
| |
| |
| accum = |
| MultiplyByQuantizedMultiplier(accum, output_multiplier, output_shift); |
| |
| accum = std::max(accum, output_activation_min - output_offset); |
| accum = std::min(accum, output_activation_max - output_offset); |
| accum += output_offset; |
| output_data[out_c + output_depth * b] = accum; |
| } |
| } |
| } |
|
|
| inline void ShuffledFullyConnected( |
| const FullyConnectedParams& params, const RuntimeShape& input_shape, |
| const uint8_t* input_data, const RuntimeShape& weights_shape, |
| const uint8_t* shuffled_weights_data, const RuntimeShape& bias_shape, |
| const int32_t* bias_data, const RuntimeShape& output_shape, |
| int16_t* output_data, uint8_t* shuffled_input_workspace_data) { |
| const int32_t output_multiplier = params.output_multiplier; |
| const int output_shift = params.output_shift; |
| const int32_t output_activation_min = params.quantized_activation_min; |
| const int32_t output_activation_max = params.quantized_activation_max; |
| TFLITE_DCHECK_LE(output_activation_min, output_activation_max); |
|
|
| TFLITE_DCHECK_GE(input_shape.DimensionsCount(), 1); |
| TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); |
| TFLITE_DCHECK_GE(output_shape.DimensionsCount(), 1); |
| |
| |
| |
| |
| |
| const int output_dim_count = output_shape.DimensionsCount(); |
| const int weights_dim_count = weights_shape.DimensionsCount(); |
| const int batches = FlatSizeSkipDim(output_shape, output_dim_count - 1); |
| const int output_depth = MatchingDim(weights_shape, weights_dim_count - 2, |
| output_shape, output_dim_count - 1); |
| const int accum_depth = weights_shape.Dims(weights_dim_count - 1); |
| TFLITE_DCHECK((accum_depth % 16) == 0); |
| TFLITE_DCHECK((output_depth % 4) == 0); |
|
|
| |
| uint8_t* shuffled_input_workspace_ptr = shuffled_input_workspace_data; |
| if (batches == 1) { |
| for (int i = 0; i < accum_depth; i++) { |
| shuffled_input_workspace_data[i] = input_data[i] ^ 0x80; |
| } |
| } else if (batches == 4) { |
| for (int c = 0; c < accum_depth; c += 16) { |
| for (int b = 0; b < 4; b++) { |
| const uint8_t* src_data_ptr = input_data + b * accum_depth + c; |
| for (int j = 0; j < 16; j++) { |
| uint8_t src_val = *src_data_ptr++; |
| |
| |
| |
| uint8_t dst_val = src_val ^ 0x80; |
| *shuffled_input_workspace_ptr++ = dst_val; |
| } |
| } |
| } |
| } else { |
| TFLITE_DCHECK(false); |
| return; |
| } |
|
|
| |
| if (batches == 1) { |
| int16_t* output_ptr = output_data; |
| |
| |
| |
| |
| const int8_t* shuffled_weights_ptr = |
| reinterpret_cast<const int8_t*>(shuffled_weights_data); |
| |
| const int8_t* shuffled_input_data = |
| reinterpret_cast<const int8_t*>(shuffled_input_workspace_data); |
| for (int c = 0; c < output_depth; c += 4) { |
| |
| |
| int32_t accum[4] = {0}; |
| |
| for (int d = 0; d < accum_depth; d += 16) { |
| for (int i = 0; i < 4; i++) { |
| for (int j = 0; j < 16; j++) { |
| int8_t input_val = shuffled_input_data[d + j]; |
| int8_t weights_val = *shuffled_weights_ptr++; |
| accum[i] += weights_val * input_val; |
| } |
| } |
| } |
| for (int i = 0; i < 4; i++) { |
| |
| int32_t acc = accum[i] + bias_data[c + i]; |
| |
| |
| |
| |
| acc = |
| MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); |
| |
| acc = std::max(acc, output_activation_min); |
| acc = std::min(acc, output_activation_max); |
| output_ptr[c + i] = acc; |
| } |
| } |
| } else if (batches == 4) { |
| int16_t* output_ptr = output_data; |
| |
| |
| |
| |
| const int8_t* shuffled_weights_ptr = |
| reinterpret_cast<const int8_t*>(shuffled_weights_data); |
| |
| const int8_t* shuffled_input_data = |
| reinterpret_cast<const int8_t*>(shuffled_input_workspace_data); |
| for (int c = 0; c < output_depth; c += 4) { |
| const int8_t* shuffled_input_ptr = shuffled_input_data; |
| |
| |
| |
| int32_t accum[4][4]; |
| for (int i = 0; i < 4; i++) { |
| for (int b = 0; b < 4; b++) { |
| accum[i][b] = 0; |
| } |
| } |
| for (int d = 0; d < accum_depth; d += 16) { |
| for (int i = 0; i < 4; i++) { |
| for (int b = 0; b < 4; b++) { |
| for (int j = 0; j < 16; j++) { |
| int8_t input_val = shuffled_input_ptr[16 * b + j]; |
| int8_t weights_val = shuffled_weights_ptr[16 * i + j]; |
| accum[i][b] += weights_val * input_val; |
| } |
| } |
| } |
| shuffled_input_ptr += 64; |
| shuffled_weights_ptr += 64; |
| } |
| for (int i = 0; i < 4; i++) { |
| for (int b = 0; b < 4; b++) { |
| |
| int32_t acc = accum[i][b] + bias_data[c + i]; |
| |
| |
| |
| |
| acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, |
| output_shift); |
| |
| acc = std::max(acc, output_activation_min); |
| acc = std::min(acc, output_activation_max); |
| output_ptr[b * output_depth + c + i] = acc; |
| } |
| } |
| } |
| } else { |
| TFLITE_DCHECK(false); |
| return; |
| } |
| } |
|
|
| } |
| } |
|
|
| #endif |
|
|