Optimized Conv2D integration - Fork the kernel and call optimized helpers where appropriate Change-Id: I502afed093f7bfb9cfb8b3f7b57ec8110a3d15ca
diff --git a/tensorflow/lite/micro/kernels/BUILD b/tensorflow/lite/micro/kernels/BUILD index 94a6d5b..41e996b 100644 --- a/tensorflow/lite/micro/kernels/BUILD +++ b/tensorflow/lite/micro/kernels/BUILD
@@ -67,6 +67,9 @@ hdrs = [ "conv_test.h", ], + visibility = [ + "//visibility:public", + ], deps = [ ":kernel_runner", ":micro_ops",
diff --git a/tensorflow/lite/micro/kernels/kelvin/conv.cc b/tensorflow/lite/micro/kernels/kelvin/conv.cc new file mode 100644 index 0000000..0d2f068 --- /dev/null +++ b/tensorflow/lite/micro/kernels/kelvin/conv.cc
@@ -0,0 +1,172 @@ +// Copyright 2023 Google LLC +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "tensorflow/lite/kernels/internal/reference/conv.h" + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/portable_tensor_utils.h" +#include "tensorflow/lite/kernels/internal/reference/integer_ops/conv.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/conv.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_log.h" +#include "tflm/opt/opt.h" + +namespace tflite { +namespace { + +void* Init(TfLiteContext* context, const char* buffer, size_t length) { + TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); + return context->AllocatePersistentBuffer(context, sizeof(OpDataConv)); +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteEvalTensor* input = + tflite::micro::GetEvalInput(context, node, kConvInputTensor); + const TfLiteEvalTensor* filter = + tflite::micro::GetEvalInput(context, node, kConvWeightsTensor); + const TfLiteEvalTensor* bias = + (NumInputs(node) == 3) + ? tflite::micro::GetEvalInput(context, node, kConvBiasTensor) + : nullptr; + TfLiteEvalTensor* output = + tflite::micro::GetEvalOutput(context, node, kConvOutputTensor); + + TFLITE_DCHECK(node->builtin_data != nullptr); + const auto& params = + *(reinterpret_cast<TfLiteConvParams*>(node->builtin_data)); + TFLITE_DCHECK(node->user_data != nullptr); + const auto& data = *(static_cast<const OpDataConv*>(node->user_data)); + + TF_LITE_ENSURE_EQ(context, input->type, output->type); + TF_LITE_ENSURE_MSG( + context, + input->type == filter->type || + (input->type == kTfLiteInt16 && filter->type == kTfLiteInt8) || + (input->type == kTfLiteInt8 && filter->type == kTfLiteInt4), + "Hybrid models are not supported on TFLite Micro."); + + switch (input->type) { // Already know in/out types are same. + case kTfLiteFloat32: { + tflite::reference_ops::Conv( + ConvParamsFloat(params, data), tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData<float>(input), + tflite::micro::GetTensorShape(filter), + tflite::micro::GetTensorData<float>(filter), + tflite::micro::GetTensorShape(bias), + tflite::micro::GetOptionalTensorData<float>(bias), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData<float>(output), + tflite::micro::GetTensorShape(nullptr), nullptr); + break; + } + case kTfLiteInt16: { + const auto params_q = ConvParamsQuantized(params, data); + bool opt = + !(params_q.padding_values.width > 0 || + params_q.padding_values.height > 0 || + params_q.dilation_width_factor > 1 || + params_q.dilation_height_factor > 1); + switch (bias->type) { + case kTfLiteInt32: { + const auto fn = opt ? kelvin::opt::conv_per_channel_b32 : reference_integer_ops::ConvPerChannel<int32_t>; + fn( + params_q, + data.per_channel_output_multiplier, data.per_channel_output_shift, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData<int16_t>(input), + tflite::micro::GetTensorShape(filter), + tflite::micro::GetTensorData<int8_t>(filter), + tflite::micro::GetTensorShape(bias), + tflite::micro::GetOptionalTensorData<std::int32_t>(bias), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData<int16_t>(output)); + break; + } + case kTfLiteInt64: { + const auto fn = opt ? kelvin::opt::conv_per_channel_b64 : reference_integer_ops::ConvPerChannel<int64_t>; + fn( + params_q, + data.per_channel_output_multiplier, data.per_channel_output_shift, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData<int16_t>(input), + tflite::micro::GetTensorShape(filter), + tflite::micro::GetTensorData<int8_t>(filter), + tflite::micro::GetTensorShape(bias), + tflite::micro::GetOptionalTensorData<std::int64_t>(bias), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData<int16_t>(output)); + break; + } + default: + MicroPrintf("Bias type %s (%d) not supported.", + TfLiteTypeGetName(bias->type), bias->type); + return kTfLiteError; + } + break; + } + case kTfLiteInt8: { + switch (filter->type) { + case kTfLiteInt4: { + int8_t* unpacked_filter_data = reinterpret_cast<int8_t*>( + context->GetScratchBuffer(context, data.filter_buffer_index)); + tflite::tensor_utils::UnpackDenseInt4IntoInt8( + tflite::micro::GetTensorData<int8_t>(filter), + tflite::micro::GetTensorShape(filter).FlatSize(), + unpacked_filter_data); + reference_integer_ops::ConvPerChannel( + ConvParamsQuantized(params, data), + data.per_channel_output_multiplier, data.per_channel_output_shift, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData<int8_t>(input), + tflite::micro::GetTensorShape(filter), unpacked_filter_data, + tflite::micro::GetTensorShape(bias), + tflite::micro::GetOptionalTensorData<int32_t>(bias), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData<int8_t>(output)); + break; + } + case kTfLiteInt8: { + const auto params_q = ConvParamsQuantized(params, data); + bool opt = !(params_q.padding_values.width > 0 || + params_q.padding_values.height > 0 || + params_q.dilation_width_factor > 1 || + params_q.dilation_height_factor > 1); + auto fn = kelvin::opt::conv_per_channel_b8; + if (!opt) fn = reference_integer_ops::ConvPerChannel; + fn( + params_q, data.per_channel_output_multiplier, + data.per_channel_output_shift, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData<int8_t>(input), + tflite::micro::GetTensorShape(filter), + tflite::micro::GetTensorData<int8_t>(filter), + tflite::micro::GetTensorShape(bias), + tflite::micro::GetOptionalTensorData<int32_t>(bias), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData<int8_t>(output)); + break; + } + default: + MicroPrintf("Weight type %s (%d) not supported.", + TfLiteTypeGetName(filter->type), filter->type); + return kTfLiteError; + } + break; + } + default: + MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type), + input->type); + return kTfLiteError; + } + return kTfLiteOk; +} + +} // namespace + +TFLMRegistration Register_CONV_2D() { + return tflite::micro::RegisterOp(Init, ConvPrepare, Eval); +} + +} // namespace tflite
diff --git a/tensorflow/lite/micro/kernels/testdata/BUILD b/tensorflow/lite/micro/kernels/testdata/BUILD index a20337a..16df712 100644 --- a/tensorflow/lite/micro/kernels/testdata/BUILD +++ b/tensorflow/lite/micro/kernels/testdata/BUILD
@@ -15,6 +15,7 @@ name = "conv_test_data", srcs = ["conv_test_data.cc"], hdrs = ["conv_test_data.h"], + visibility = ["//visibility:public"], deps = ["//tensorflow/lite/c:common"], )