Address review comments.
This commit is contained in:
parent
3aa4d9952c
commit
0a775a0c66
@ -36,6 +36,43 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "conv",
|
||||
srcs = [
|
||||
"conv_common.cc",
|
||||
] + select({
|
||||
"//conditions:default": [
|
||||
"conv.cc",
|
||||
],
|
||||
":xtensa_hifimini": [
|
||||
"xtensa/conv.cc",
|
||||
],
|
||||
}),
|
||||
hdrs = ["conv.h"],
|
||||
copts = micro_copts(),
|
||||
visibility = [
|
||||
# Kernel variants need to be visible to the examples and benchmarks.
|
||||
":micro",
|
||||
],
|
||||
deps = [
|
||||
":fixedpoint_utils",
|
||||
":kernel_util",
|
||||
":xtensa",
|
||||
"//tensorflow/lite/c:common",
|
||||
"//tensorflow/lite/kernels/internal:common",
|
||||
"//tensorflow/lite/kernels/internal:quantization_util",
|
||||
"//tensorflow/lite/kernels/internal:reference_base",
|
||||
"//tensorflow/lite/kernels/internal:tensor",
|
||||
"//tensorflow/lite/kernels:kernel_util",
|
||||
"//tensorflow/lite/kernels:padding",
|
||||
] + select({
|
||||
"//conditions:default": [],
|
||||
":xtensa_hifimini": [
|
||||
#"//third_party/xtensa/cstub64s:hifi_mini",
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "conv_test_common",
|
||||
srcs = [
|
||||
@ -136,43 +173,6 @@ cc_library(
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "conv",
|
||||
srcs = [
|
||||
"conv_common.cc",
|
||||
] + select({
|
||||
"//conditions:default": [
|
||||
"conv.cc",
|
||||
],
|
||||
":xtensa_hifimini": [
|
||||
"xtensa/conv.cc",
|
||||
],
|
||||
}),
|
||||
hdrs = ["conv.h"],
|
||||
copts = micro_copts(),
|
||||
visibility = [
|
||||
# Kernel variants need to be visible to the examples and benchmarks.
|
||||
":micro",
|
||||
],
|
||||
deps = [
|
||||
":fixedpoint_utils",
|
||||
":kernel_util",
|
||||
":xtensa",
|
||||
"//tensorflow/lite/c:common",
|
||||
"//tensorflow/lite/kernels/internal:common",
|
||||
"//tensorflow/lite/kernels/internal:quantization_util",
|
||||
"//tensorflow/lite/kernels/internal:reference_base",
|
||||
"//tensorflow/lite/kernels/internal:tensor",
|
||||
"//tensorflow/lite/kernels:kernel_util",
|
||||
"//tensorflow/lite/kernels:padding",
|
||||
] + select({
|
||||
"//conditions:default": [],
|
||||
":xtensa_hifimini": [
|
||||
#"//third_party/xtensa/cstub64s:hifi_mini",
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "kernel_runner",
|
||||
srcs = [
|
||||
@ -930,6 +930,7 @@ cc_test(
|
||||
":kernel_runner",
|
||||
"//tensorflow/lite/c:common",
|
||||
"//tensorflow/lite/micro:micro_utils",
|
||||
"//tensorflow/lite/micro:op_resolvers",
|
||||
"//tensorflow/lite/micro:test_helpers",
|
||||
"//tensorflow/lite/micro/testing:micro_test",
|
||||
],
|
||||
|
@ -31,9 +31,16 @@ limitations under the License.
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
struct OpData {
|
||||
OpDataConv reference_op_data;
|
||||
|
||||
// Index to buffer for optimizations if applicable.
|
||||
int buffer_idx;
|
||||
};
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataConv));
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
@ -41,8 +48,9 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
int32_t buf_size = 0;
|
||||
const auto params = static_cast<const TfLiteConvParams*>(node->builtin_data);
|
||||
OpDataConv* data = static_cast<OpDataConv*>(node->user_data);
|
||||
const auto& params =
|
||||
*(static_cast<const TfLiteConvParams*>(node->builtin_data));
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
|
||||
const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
|
||||
@ -78,34 +86,31 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
// non-int8 cases. Protect this section with a if (input->type == kTfLiteInt8)
|
||||
// when the issue is fixed.
|
||||
const int num_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
data->per_channel_output_multiplier =
|
||||
data->reference_op_data.per_channel_output_multiplier =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
data->per_channel_output_shift =
|
||||
data->reference_op_data.per_channel_output_shift =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateOpDataConv(
|
||||
context, node, params, input_dims.w, input_dims.h, filter_dims.w,
|
||||
filter_dims.h, output_dims.w, output_dims.h, input->type, data));
|
||||
|
||||
data->input_zero_point = input->params.zero_point;
|
||||
data->filter_zero_point = filter->params.zero_point;
|
||||
data->output_zero_point = output->params.zero_point;
|
||||
filter_dims.h, output_dims.w, output_dims.h, input->type,
|
||||
&data->reference_op_data));
|
||||
|
||||
if (input->type == kTfLiteInt8) {
|
||||
// Initialize cmsis_nn convolution parameters
|
||||
cmsis_nn_conv_params conv_params;
|
||||
conv_params.input_offset = -input->params.zero_point;
|
||||
conv_params.output_offset = output->params.zero_point;
|
||||
conv_params.stride.h = params->stride_height;
|
||||
conv_params.stride.w = params->stride_width;
|
||||
conv_params.dilation.h = params->dilation_height_factor;
|
||||
conv_params.dilation.w = params->dilation_width_factor;
|
||||
conv_params.padding.h = data->padding.height;
|
||||
conv_params.padding.w = data->padding.width;
|
||||
conv_params.activation.min = data->output_activation_min;
|
||||
conv_params.activation.max = data->output_activation_max;
|
||||
conv_params.stride.h = params.stride_height;
|
||||
conv_params.stride.w = params.stride_width;
|
||||
conv_params.dilation.h = params.dilation_height_factor;
|
||||
conv_params.dilation.w = params.dilation_width_factor;
|
||||
conv_params.padding.h = data->reference_op_data.padding.height;
|
||||
conv_params.padding.w = data->reference_op_data.padding.width;
|
||||
conv_params.activation.min = data->reference_op_data.output_activation_min;
|
||||
conv_params.activation.max = data->reference_op_data.output_activation_max;
|
||||
|
||||
buf_size = arm_convolve_wrapper_s8_get_buffer_size(
|
||||
&conv_params, &input_dims, &filter_dims, &output_dims);
|
||||
@ -121,32 +126,33 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
TfLiteStatus EvalQuantizedPerChannel(
|
||||
TfLiteContext* context, TfLiteNode* node, TfLiteConvParams* params,
|
||||
const OpDataConv& data, const TfLiteEvalTensor* input,
|
||||
TfLiteContext* context, TfLiteNode* node, const TfLiteConvParams& params,
|
||||
const OpData& data, const TfLiteEvalTensor* input,
|
||||
const TfLiteEvalTensor* filter, const TfLiteEvalTensor* bias,
|
||||
TfLiteEvalTensor* output, TfLiteEvalTensor* im2col) {
|
||||
cmsis_nn_conv_params conv_params;
|
||||
conv_params.dilation.h = params->dilation_height_factor;
|
||||
conv_params.dilation.w = params->dilation_width_factor;
|
||||
conv_params.dilation.h = params.dilation_height_factor;
|
||||
conv_params.dilation.w = params.dilation_width_factor;
|
||||
// TODO(#43557) Remove checks for dilation and call to reference
|
||||
// implementation when dilation is supported in the optimized implementation
|
||||
// by CMSIS-NN.
|
||||
if (conv_params.dilation.h == 1 && conv_params.dilation.w == 1) {
|
||||
// Initialize cmsis_nn convolution parameters
|
||||
conv_params.input_offset = -data.input_zero_point;
|
||||
conv_params.output_offset = data.output_zero_point;
|
||||
conv_params.stride.h = params->stride_height;
|
||||
conv_params.stride.w = params->stride_width;
|
||||
conv_params.padding.h = data.padding.height;
|
||||
conv_params.padding.w = data.padding.width;
|
||||
conv_params.activation.min = data.output_activation_min;
|
||||
conv_params.activation.max = data.output_activation_max;
|
||||
conv_params.input_offset = -data.reference_op_data.input_zero_point;
|
||||
conv_params.output_offset = data.reference_op_data.output_zero_point;
|
||||
conv_params.stride.h = params.stride_height;
|
||||
conv_params.stride.w = params.stride_width;
|
||||
conv_params.padding.h = data.reference_op_data.padding.height;
|
||||
conv_params.padding.w = data.reference_op_data.padding.width;
|
||||
conv_params.activation.min = data.reference_op_data.output_activation_min;
|
||||
conv_params.activation.max = data.reference_op_data.output_activation_max;
|
||||
|
||||
// Initialize cmsis_nn per channel quantization parameters
|
||||
cmsis_nn_per_channel_quant_params quant_params;
|
||||
quant_params.multiplier =
|
||||
const_cast<int32_t*>(data.per_channel_output_multiplier);
|
||||
quant_params.shift = const_cast<int32_t*>(data.per_channel_output_shift);
|
||||
quant_params.multiplier = const_cast<int32_t*>(
|
||||
data.reference_op_data.per_channel_output_multiplier);
|
||||
quant_params.shift =
|
||||
const_cast<int32_t*>(data.reference_op_data.per_channel_output_shift);
|
||||
|
||||
RuntimeShape filter_shape = tflite::micro::GetTensorShape(filter);
|
||||
RuntimeShape input_shape = tflite::micro::GetTensorShape(input);
|
||||
@ -218,8 +224,10 @@ TfLiteStatus EvalQuantizedPerChannel(
|
||||
ARM_MATH_SUCCESS);
|
||||
} else {
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, data), data.per_channel_output_multiplier,
|
||||
data.per_channel_output_shift, tflite::micro::GetTensorShape(input),
|
||||
ConvParamsQuantized(params, data.reference_op_data),
|
||||
data.reference_op_data.per_channel_output_multiplier,
|
||||
data.reference_op_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),
|
||||
@ -232,7 +240,8 @@ TfLiteStatus EvalQuantizedPerChannel(
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params = reinterpret_cast<TfLiteConvParams*>(node->builtin_data);
|
||||
const auto& params =
|
||||
*(reinterpret_cast<TfLiteConvParams*>(node->builtin_data));
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kConvInputTensor);
|
||||
@ -246,7 +255,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetEvalOutput(context, node, kConvOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpDataConv& data = *(static_cast<const OpDataConv*>(node->user_data));
|
||||
const OpData& data = *(static_cast<const OpData*>(node->user_data));
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input->type, output->type);
|
||||
TF_LITE_ENSURE_MSG(context, input->type == filter->type,
|
||||
@ -255,7 +264,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32: {
|
||||
tflite::reference_ops::Conv(
|
||||
ConvParamsFloat(params, data), tflite::micro::GetTensorShape(input),
|
||||
ConvParamsFloat(params, data.reference_op_data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
@ -271,7 +281,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
bias, output, nullptr);
|
||||
break;
|
||||
case kTfLiteUInt8: {
|
||||
reference_ops::Conv(ConvParamsQuantized(params, data),
|
||||
reference_ops::Conv(ConvParamsQuantized(params, data.reference_op_data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<uint8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
|
@ -34,69 +34,7 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataConv));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
OpDataConv* data = static_cast<OpDataConv*>(node->user_data);
|
||||
const auto params = static_cast<const TfLiteConvParams*>(node->builtin_data);
|
||||
|
||||
TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
|
||||
int input_width = input->dims->data[2];
|
||||
int input_height = input->dims->data[1];
|
||||
int filter_width = filter->dims->data[2];
|
||||
int filter_height = filter->dims->data[1];
|
||||
int output_width = output->dims->data[2];
|
||||
int output_height = output->dims->data[1];
|
||||
|
||||
// Dynamically allocate per-channel quantization parameters.
|
||||
const int num_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
data->per_channel_output_multiplier =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
data->per_channel_output_shift =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
|
||||
// All per-channel quantized tensors need valid zero point and scale arrays.
|
||||
if (input->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_EQ(context, filter->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
|
||||
const auto* affine_quantization =
|
||||
static_cast<TfLiteAffineQuantization*>(filter->quantization.params);
|
||||
TF_LITE_ENSURE(context, affine_quantization);
|
||||
TF_LITE_ENSURE(context, affine_quantization->scale);
|
||||
TF_LITE_ENSURE(context, affine_quantization->zero_point);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
affine_quantization->scale->size == 1 ||
|
||||
affine_quantization->scale->size ==
|
||||
filter->dims->data[kConvQuantizedDimension]);
|
||||
TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size,
|
||||
affine_quantization->zero_point->size);
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateOpDataConv(
|
||||
context, node, params, input_width, input_height, filter_width,
|
||||
filter_height, output_width, output_height, input->type, data));
|
||||
|
||||
data->input_zero_point = input->params.zero_point;
|
||||
data->filter_zero_point = filter->params.zero_point;
|
||||
data->output_zero_point = output->params.zero_point;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params = reinterpret_cast<TfLiteConvParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kConvInputTensor);
|
||||
const TfLiteEvalTensor* filter =
|
||||
@ -108,6 +46,9 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
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));
|
||||
|
||||
@ -169,7 +110,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteRegistration Register_CONV_2D() {
|
||||
return {/*init=*/Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*prepare=*/ConvPrepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
|
@ -45,9 +45,6 @@ struct OpDataConv {
|
||||
// uint8_t these would be 0 and 255.
|
||||
int32_t output_activation_min;
|
||||
int32_t output_activation_max;
|
||||
|
||||
// Index to buffer for optimizations if applicable.
|
||||
int buffer_idx;
|
||||
};
|
||||
|
||||
extern const int kConvInputTensor;
|
||||
@ -58,20 +55,23 @@ extern const int kConvQuantizedDimension;
|
||||
|
||||
// Returns a ConvParams struct with all the parameters needed for a
|
||||
// float computation.
|
||||
ConvParams ConvParamsFloat(TfLiteConvParams* params, const OpDataConv& data);
|
||||
ConvParams ConvParamsFloat(const TfLiteConvParams& params,
|
||||
const OpDataConv& data);
|
||||
|
||||
// Returns a ConvParams struct with all the parameters needed for a
|
||||
// quantized computation.
|
||||
ConvParams ConvParamsQuantized(TfLiteConvParams* params,
|
||||
ConvParams ConvParamsQuantized(const TfLiteConvParams& params,
|
||||
const OpDataConv& data);
|
||||
|
||||
TfLiteStatus CalculateOpDataConv(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteConvParams* params, int width,
|
||||
const TfLiteConvParams& params, int width,
|
||||
int height, int filter_width,
|
||||
int filter_height, int out_width,
|
||||
int out_height, const TfLiteType data_type,
|
||||
OpDataConv* data);
|
||||
|
||||
TfLiteStatus ConvPrepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_CONV_H_
|
||||
|
@ -38,23 +38,24 @@ const int kConvQuantizedDimension = 0;
|
||||
|
||||
// Returns a ConvParams struct with all the parameters needed for a
|
||||
// float computation.
|
||||
ConvParams ConvParamsFloat(TfLiteConvParams* params, const OpDataConv& data) {
|
||||
ConvParams ConvParamsFloat(const TfLiteConvParams& params,
|
||||
const OpDataConv& data) {
|
||||
ConvParams op_params;
|
||||
CalculateActivationRange(params->activation, &op_params.float_activation_min,
|
||||
CalculateActivationRange(params.activation, &op_params.float_activation_min,
|
||||
&op_params.float_activation_max);
|
||||
op_params.padding_type = tflite::micro::RuntimePaddingType(params->padding);
|
||||
op_params.padding_type = tflite::micro::RuntimePaddingType(params.padding);
|
||||
op_params.padding_values.width = data.padding.width;
|
||||
op_params.padding_values.height = data.padding.height;
|
||||
op_params.stride_width = params->stride_width;
|
||||
op_params.stride_height = params->stride_height;
|
||||
op_params.dilation_width_factor = params->dilation_width_factor;
|
||||
op_params.dilation_height_factor = params->dilation_height_factor;
|
||||
op_params.stride_width = params.stride_width;
|
||||
op_params.stride_height = params.stride_height;
|
||||
op_params.dilation_width_factor = params.dilation_width_factor;
|
||||
op_params.dilation_height_factor = params.dilation_height_factor;
|
||||
return op_params;
|
||||
}
|
||||
|
||||
// Returns a ConvParams struct with all the parameters needed for a
|
||||
// quantized computation.
|
||||
ConvParams ConvParamsQuantized(TfLiteConvParams* params,
|
||||
ConvParams ConvParamsQuantized(const TfLiteConvParams& params,
|
||||
const OpDataConv& data) {
|
||||
ConvParams op_params;
|
||||
op_params.input_offset = -data.input_zero_point;
|
||||
@ -62,20 +63,20 @@ ConvParams ConvParamsQuantized(TfLiteConvParams* params,
|
||||
op_params.output_offset = data.output_zero_point;
|
||||
op_params.output_multiplier = data.output_multiplier;
|
||||
op_params.output_shift = -data.output_shift;
|
||||
op_params.padding_type = tflite::micro::RuntimePaddingType(params->padding);
|
||||
op_params.padding_type = tflite::micro::RuntimePaddingType(params.padding);
|
||||
op_params.padding_values.height = data.padding.height;
|
||||
op_params.padding_values.width = data.padding.width;
|
||||
op_params.stride_height = params->stride_height;
|
||||
op_params.stride_width = params->stride_width;
|
||||
op_params.dilation_height_factor = params->dilation_height_factor;
|
||||
op_params.dilation_width_factor = params->dilation_width_factor;
|
||||
op_params.stride_height = params.stride_height;
|
||||
op_params.stride_width = params.stride_width;
|
||||
op_params.dilation_height_factor = params.dilation_height_factor;
|
||||
op_params.dilation_width_factor = params.dilation_width_factor;
|
||||
op_params.quantized_activation_min = data.output_activation_min;
|
||||
op_params.quantized_activation_max = data.output_activation_max;
|
||||
return op_params;
|
||||
}
|
||||
|
||||
TfLiteStatus CalculateOpDataConv(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteConvParams* params, int width,
|
||||
const TfLiteConvParams& params, int width,
|
||||
int height, int filter_width,
|
||||
int filter_height, int out_width,
|
||||
int out_height, const TfLiteType data_type,
|
||||
@ -86,47 +87,49 @@ TfLiteStatus CalculateOpDataConv(TfLiteContext* context, TfLiteNode* node,
|
||||
TF_LITE_ENSURE_EQ(context, node->outputs->size, 1);
|
||||
|
||||
// Matching GetWindowedOutputSize in TensorFlow.
|
||||
auto padding = params->padding;
|
||||
auto padding = params.padding;
|
||||
data->padding = ComputePaddingHeightWidth(
|
||||
params->stride_height, params->stride_width,
|
||||
params->dilation_height_factor, params->dilation_width_factor, height,
|
||||
width, filter_height, filter_width, padding, &out_height, &out_width);
|
||||
params.stride_height, params.stride_width, params.dilation_height_factor,
|
||||
params.dilation_width_factor, height, width, filter_height, filter_width,
|
||||
padding, &out_height, &out_width);
|
||||
|
||||
const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
const TfLiteTensor* bias =
|
||||
GetOptionalInputTensor(context, node, kConvBiasTensor);
|
||||
TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
// Note that quantized inference requires that all tensors have their
|
||||
// parameters set. This is usually done during quantized training.
|
||||
if (data_type != kTfLiteFloat32) {
|
||||
const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
const TfLiteTensor* bias =
|
||||
GetOptionalInputTensor(context, node, kConvBiasTensor);
|
||||
TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
int output_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
|
||||
TF_LITE_ENSURE_STATUS(tflite::PopulateConvolutionQuantizationParams(
|
||||
context, input, filter, bias, output, params->activation,
|
||||
context, input, filter, bias, output, params.activation,
|
||||
&data->output_multiplier, &data->output_shift,
|
||||
&data->output_activation_min, &data->output_activation_max,
|
||||
data->per_channel_output_multiplier,
|
||||
reinterpret_cast<int*>(data->per_channel_output_shift),
|
||||
output_channels));
|
||||
}
|
||||
|
||||
data->input_zero_point = input->params.zero_point;
|
||||
data->filter_zero_point = filter->params.zero_point;
|
||||
data->output_zero_point = output->params.zero_point;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* InitConv(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataConv));
|
||||
}
|
||||
|
||||
TfLiteStatus PrepareConv(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteStatus ConvPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
OpDataConv* data = static_cast<OpDataConv*>(node->user_data);
|
||||
const auto params = static_cast<const TfLiteConvParams*>(node->builtin_data);
|
||||
const auto& params =
|
||||
*(static_cast<const TfLiteConvParams*>(node->builtin_data));
|
||||
|
||||
TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
@ -135,12 +138,12 @@ TfLiteStatus PrepareConv(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
|
||||
int input_width = input->dims->data[2];
|
||||
int input_height = input->dims->data[1];
|
||||
int filter_width = filter->dims->data[2];
|
||||
int filter_height = filter->dims->data[1];
|
||||
int output_width = output->dims->data[2];
|
||||
int output_height = output->dims->data[1];
|
||||
const int input_width = input->dims->data[2];
|
||||
const int input_height = input->dims->data[1];
|
||||
const int filter_width = filter->dims->data[2];
|
||||
const int filter_height = filter->dims->data[1];
|
||||
const int output_width = output->dims->data[2];
|
||||
const int output_height = output->dims->data[1];
|
||||
|
||||
// Dynamically allocate per-channel quantization parameters.
|
||||
const int num_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
@ -158,9 +161,9 @@ TfLiteStatus PrepareConv(TfLiteContext* context, TfLiteNode* node) {
|
||||
|
||||
const auto* affine_quantization =
|
||||
static_cast<TfLiteAffineQuantization*>(filter->quantization.params);
|
||||
TF_LITE_ENSURE(context, affine_quantization);
|
||||
TF_LITE_ENSURE(context, affine_quantization->scale);
|
||||
TF_LITE_ENSURE(context, affine_quantization->zero_point);
|
||||
TFLITE_DCHECK(affine_quantization != nullptr);
|
||||
TFLITE_DCHECK(affine_quantization->scale != nullptr);
|
||||
TFLITE_DCHECK(affine_quantization->zero_point != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
affine_quantization->scale->size == 1 ||
|
||||
@ -174,11 +177,6 @@ TfLiteStatus PrepareConv(TfLiteContext* context, TfLiteNode* node) {
|
||||
context, node, params, input_width, input_height, filter_width,
|
||||
filter_height, output_width, output_height, input->type, data));
|
||||
|
||||
data->input_zero_point = input->params.zero_point;
|
||||
data->filter_zero_point = filter->params.zero_point;
|
||||
data->output_zero_point = output->params.zero_point;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
@ -37,5 +37,17 @@ const RuntimeShape GetTensorShape(const TfLiteEvalTensor* tensor) {
|
||||
return RuntimeShape(dims_size, dims_data);
|
||||
}
|
||||
|
||||
PaddingType RuntimePaddingType(TfLitePadding padding) {
|
||||
switch (padding) {
|
||||
case TfLitePadding::kTfLitePaddingSame:
|
||||
return PaddingType::kSame;
|
||||
case TfLitePadding::kTfLitePaddingValid:
|
||||
return PaddingType::kValid;
|
||||
case TfLitePadding::kTfLitePaddingUnknown:
|
||||
default:
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
|
@ -70,17 +70,7 @@ const RuntimeShape GetTensorShape(const TfLiteEvalTensor* tensor);
|
||||
bool HaveSameShapes(const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2);
|
||||
|
||||
inline PaddingType RuntimePaddingType(TfLitePadding padding) {
|
||||
switch (padding) {
|
||||
case TfLitePadding::kTfLitePaddingSame:
|
||||
return PaddingType::kSame;
|
||||
case TfLitePadding::kTfLitePaddingValid:
|
||||
return PaddingType::kValid;
|
||||
case TfLitePadding::kTfLitePaddingUnknown:
|
||||
default:
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
}
|
||||
PaddingType RuntimePaddingType(TfLitePadding padding);
|
||||
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
|
@ -239,66 +239,12 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataConv));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
auto* params = reinterpret_cast<TfLiteConvParams*>(node->builtin_data);
|
||||
|
||||
TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
|
||||
const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
|
||||
const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
|
||||
|
||||
auto* op_data = reinterpret_cast<OpDataConv*>(node->user_data);
|
||||
|
||||
int input_width = input->dims->data[2];
|
||||
int input_height = input->dims->data[1];
|
||||
int filter_width = filter->dims->data[2];
|
||||
int filter_height = filter->dims->data[1];
|
||||
int output_width = output->dims->data[2];
|
||||
int output_height = output->dims->data[1];
|
||||
|
||||
// Per channel quantization is only needed for int8_t inference. For other
|
||||
// quantized types, only a single scale and zero point is needed.
|
||||
const int num_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
// Dynamically allocate per-channel quantization parameters.
|
||||
op_data->per_channel_output_multiplier =
|
||||
reinterpret_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
op_data->per_channel_output_shift =
|
||||
reinterpret_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
op_data->input_zero_point = input->params.zero_point;
|
||||
op_data->output_zero_point = output->params.zero_point;
|
||||
// All per-channel quantized tensors need valid zero point and scale arrays.
|
||||
if (input->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_EQ(context, filter->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
|
||||
const auto* affine_quantization =
|
||||
reinterpret_cast<TfLiteAffineQuantization*>(
|
||||
filter->quantization.params);
|
||||
TF_LITE_ENSURE(context, affine_quantization);
|
||||
TF_LITE_ENSURE(context, affine_quantization->scale);
|
||||
TF_LITE_ENSURE(context, affine_quantization->zero_point);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
affine_quantization->scale->size == 1 ||
|
||||
affine_quantization->scale->size ==
|
||||
filter->dims->data[kConvQuantizedDimension]);
|
||||
TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size,
|
||||
affine_quantization->zero_point->size);
|
||||
}
|
||||
|
||||
return CalculateOpDataConv(context, node, params, input_width, input_height,
|
||||
filter_width, filter_height, output_width,
|
||||
output_height, input->type, op_data);
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
auto* params = reinterpret_cast<TfLiteConvParams*>(node->builtin_data);
|
||||
auto* op_data = reinterpret_cast<OpDataConv*>(node->user_data);
|
||||
const auto& params =
|
||||
*(reinterpret_cast<TfLiteConvParams*>(node->builtin_data));
|
||||
const auto& op_data = *(reinterpret_cast<OpDataConv*>(node->user_data));
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kConvOutputTensor);
|
||||
@ -318,10 +264,10 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
input_dims[3] == 32 && filter_dims[0] == 32 && filter_dims[1] == 1 &&
|
||||
filter_dims[2] == 1 && filter_dims[3] == 32) {
|
||||
Conv1x32Input32x32Filter(
|
||||
-op_data->input_zero_point, op_data->output_zero_point,
|
||||
op_data->output_activation_min, op_data->output_activation_max,
|
||||
op_data->per_channel_output_multiplier,
|
||||
op_data->per_channel_output_shift, tflite::micro::GetTensorShape(input),
|
||||
-op_data.input_zero_point, op_data.output_zero_point,
|
||||
op_data.output_activation_min, op_data.output_activation_max,
|
||||
op_data.per_channel_output_multiplier, op_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),
|
||||
@ -336,9 +282,9 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
switch (input->type) {
|
||||
case kTfLiteInt8: {
|
||||
#if defined(HIFIMINI)
|
||||
ConvPerChannel(ConvParamsQuantized(params, *op_data),
|
||||
op_data->per_channel_output_multiplier,
|
||||
op_data->per_channel_output_shift,
|
||||
ConvPerChannel(ConvParamsQuantized(params, op_data),
|
||||
op_data.per_channel_output_multiplier,
|
||||
op_data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
@ -350,7 +296,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
#else
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, op_data),
|
||||
data->per_channel_output_multiplier, data->per_channel_output_shift,
|
||||
op_data.per_channel_output_multiplier,
|
||||
op_data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
@ -374,7 +321,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteRegistration Register_CONV_2D() {
|
||||
return {/*init=*/Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*prepare=*/ConvPrepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user