From c7ad8fca010d899e62203c7720995737e41cbb0a Mon Sep 17 00:00:00 2001 From: yangruoqi713 Date: Tue, 11 Aug 2020 20:14:35 +0800 Subject: [PATCH] [MS][LITE] fix arm fp32 op bug: conv_depthwise_3x3, batchnorm, scale, etc. --- mindspore/lite/src/populate_parameter.cc | 11 +- .../arm/fp16/convolution_depthwise_fp16.cc | 26 +++- .../arm/fp16/convolution_depthwise_fp16.h | 7 +- .../arm/fp16/deconvolution_depthwise_fp16.cc | 26 +++- .../arm/fp16/deconvolution_depthwise_fp16.h | 10 +- .../src/runtime/kernel/arm/fp32/batchnorm.cc | 57 ++++++++- .../src/runtime/kernel/arm/fp32/batchnorm.h | 4 +- .../kernel/arm/fp32/convolution_depthwise.cc | 36 +++++- .../kernel/arm/fp32/convolution_depthwise.h | 9 +- .../arm/fp32/deconvolution_depthwise.cc | 28 ++++- .../kernel/arm/fp32/deconvolution_depthwise.h | 9 +- .../src/runtime/kernel/arm/fp32/flatten.cc | 8 +- .../kernel/arm/fp32/fused_batchnorm.cc | 118 +++++++++++++++--- .../runtime/kernel/arm/fp32/fused_batchnorm.h | 19 ++- .../lite/src/runtime/kernel/arm/fp32/scale.cc | 51 ++++---- .../lite/src/runtime/kernel/arm/fp32/scale.h | 8 +- .../arm/int8/convolution_depthwise_int8.cc | 29 ++++- .../arm/int8/convolution_depthwise_int8.h | 9 +- .../arm/int8/deconvolution_depthwise_int8.cc | 43 ++++++- .../arm/int8/deconvolution_depthwise_int8.h | 11 +- .../src/runtime/kernel/arm/nnacl/flatten.h | 1 - .../kernel/arm/nnacl/fp32/batchnorm.cc | 17 ++- .../runtime/kernel/arm/nnacl/fp32/batchnorm.h | 3 + .../kernel/arm/nnacl/fp32/conv_depthwise.cc | 28 +++++ .../kernel/arm/nnacl/fused_batchnorm.cc | 35 ------ .../kernel/arm/nnacl/fused_batchnorm.h | 32 ----- .../lite/src/runtime/kernel/arm/nnacl/scale.h | 5 +- .../kernel/arm/fp32/batchnorm_fp32_tests.cc | 45 +++---- .../batchnorm/fusedBatchnorm_input_0.bin | Bin 20736 -> 0 bytes .../batchnorm/fusedBatchnorm_input_1.bin | 1 - .../batchnorm/fusedBatchnorm_input_2.bin | 1 - .../batchnorm/fusedBatchnorm_input_3.bin | 1 - .../batchnorm/fusedBatchnorm_input_4.bin | 1 - .../batchnorm/fusedBatchnorm_out.bin | Bin 20736 -> 0 bytes 34 files changed, 453 insertions(+), 236 deletions(-) delete mode 100644 mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.cc delete mode 100644 mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.h delete mode 100644 mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_0.bin delete mode 100644 mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_1.bin delete mode 100644 mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_2.bin delete mode 100644 mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_3.bin delete mode 100644 mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_4.bin delete mode 100644 mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_out.bin diff --git a/mindspore/lite/src/populate_parameter.cc b/mindspore/lite/src/populate_parameter.cc index 3ce3900ae9..4ecf73e5cf 100644 --- a/mindspore/lite/src/populate_parameter.cc +++ b/mindspore/lite/src/populate_parameter.cc @@ -40,7 +40,6 @@ #include "src/runtime/kernel/arm/nnacl/fp32/reduce.h" #include "src/runtime/kernel/arm/nnacl/fp32/activation.h" #include "src/runtime/kernel/arm/nnacl/fp32/arithmetic.h" -#include "src/runtime/kernel/arm/nnacl/fused_batchnorm.h" #include "src/runtime/kernel/arm/nnacl/fp32/batchnorm.h" #include "src/runtime/kernel/arm/nnacl/power.h" #include "src/runtime/kernel/arm/nnacl/fp32/range.h" @@ -510,15 +509,15 @@ OpParameter *PopulateActivationParameter(const lite::Primitive *primitive) { } OpParameter *PopulateFusedBatchNorm(const lite::Primitive *primitive) { - FusedBatchNormParameter *fuse_batch_norm_param = new (std::nothrow) FusedBatchNormParameter(); - if (fuse_batch_norm_param == nullptr) { + BatchNormParameter *batch_norm_param = new (std::nothrow) BatchNormParameter(); + if (batch_norm_param == nullptr) { MS_LOG(ERROR) << "new FusedBatchNormParameter failed."; return nullptr; } - fuse_batch_norm_param->op_parameter_.type_ = primitive->Type(); + batch_norm_param->op_parameter_.type_ = primitive->Type(); auto param = primitive->Value()->value_as_FusedBatchNorm(); - fuse_batch_norm_param->epsilon_ = param->epsilon(); - return reinterpret_cast(fuse_batch_norm_param); + batch_norm_param->epsilon_ = param->epsilon(); + return reinterpret_cast(batch_norm_param); } OpParameter *PopulateArithmetic(const lite::Primitive *primitive) { diff --git a/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.cc b/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.cc index 522dd49d90..c288550e86 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.cc @@ -28,6 +28,22 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_DepthwiseConv2D; namespace mindspore::kernel { +ConvolutionDepthwiseFp16CPUKernel::~ConvolutionDepthwiseFp16CPUKernel() { + delete sliding_; + if (packed_weight_ != nullptr) { + delete packed_weight_; + packed_weight_ = nullptr; + } + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } +} + int ConvolutionDepthwiseFp16CPUKernel::InitBuffer() { // malloc pack input buffer int C8 = UP_DIV(conv_param_->input_channel_, C8NUM); @@ -113,8 +129,14 @@ int ConvolutionDepthwiseFp16CPUKernel::Init() { } int ConvolutionDepthwiseFp16CPUKernel::ReSize() { - free(packed_input_); - free(packed_output_); + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } ConvolutionBaseCPUKernel::Init(); InitSlidingParam(sliding_, conv_param_, C8NUM); diff --git a/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.h b/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.h index d605ca5ba0..de7f2cce29 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.h +++ b/mindspore/lite/src/runtime/kernel/arm/fp16/convolution_depthwise_fp16.h @@ -29,12 +29,7 @@ class ConvolutionDepthwiseFp16CPUKernel : public ConvolutionBaseCPUKernel { const std::vector &outputs, const Context *ctx, const lite::Primitive *primitive) : ConvolutionBaseCPUKernel(parameter, inputs, outputs, ctx, primitive) {} - ~ConvolutionDepthwiseFp16CPUKernel() override { - delete sliding_; - free(packed_weight_); - free(packed_input_); - free(packed_output_); - } + ~ConvolutionDepthwiseFp16CPUKernel() override; int Init() override; int ReSize() override; diff --git a/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.cc b/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.cc index 0df66a963c..96aa37c63e 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.cc @@ -28,6 +28,22 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_DeDepthwiseConv2D; namespace mindspore::kernel { +DeconvolutionDepthwiseFp16CPUKernel::~DeconvolutionDepthwiseFp16CPUKernel() { + delete sliding_; + if (packed_weight_ != nullptr) { + delete packed_weight_; + packed_weight_ = nullptr; + } + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } +} + int DeconvolutionDepthwiseFp16CPUKernel::InitSlideParam() { conv_param_->input_batch_ = outputs_.front()->shape().at(kNHWC_N); conv_param_->input_h_ = outputs_.front()->shape().at(kNHWC_H); @@ -126,8 +142,14 @@ int DeconvolutionDepthwiseFp16CPUKernel::Init() { } int DeconvolutionDepthwiseFp16CPUKernel::ReSize() { - free(packed_input_); - free(packed_output_); + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } InitSlideParam(); ConvolutionBaseCPUKernel::Init(); diff --git a/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.h b/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.h index 64807fa9d8..be88809971 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.h +++ b/mindspore/lite/src/runtime/kernel/arm/fp16/deconvolution_depthwise_fp16.h @@ -29,14 +29,7 @@ class DeconvolutionDepthwiseFp16CPUKernel : public ConvolutionBaseCPUKernel { const std::vector &outputs, const Context *ctx, const lite::Primitive *primitive) : ConvolutionBaseCPUKernel(parameter, inputs, outputs, ctx, primitive) {} - ~DeconvolutionDepthwiseFp16CPUKernel() override { - delete sliding_; - free(packed_weight_); - if (need_align_) { - free(packed_input_); - free(packed_output_); - } - }; + ~DeconvolutionDepthwiseFp16CPUKernel() override; int Init() override; int ReSize() override; @@ -52,7 +45,6 @@ class DeconvolutionDepthwiseFp16CPUKernel : public ConvolutionBaseCPUKernel { float16_t *packed_weight_; float16_t *packed_input_; float16_t *packed_output_; - bool need_align_ = false; }; } // namespace mindspore::kernel diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.cc b/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.cc index 4a827a78ee..157c4b76c1 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.cc @@ -15,7 +15,6 @@ */ #include "src/runtime/kernel/arm/fp32/batchnorm.h" -#include #include "schema/model_generated.h" #include "src/kernel_registry.h" #include "include/errorcode.h" @@ -28,7 +27,42 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_BatchNorm; namespace mindspore::kernel { +BatchnormCPUKernel::~BatchnormCPUKernel() { + if (mean_addr_ != nullptr) { + free(mean_addr_); + mean_addr_ = nullptr; + } + if (var_addr_ != nullptr) { + free(var_addr_); + var_addr_ = nullptr; + } +} + +int BatchnormCPUKernel::InitConstTensor() { + auto mean = inputs_[1]; + mean_addr_ = reinterpret_cast(malloc(mean->ElementsNum() * sizeof(float))); + if (mean_addr_ == nullptr) { + MS_LOG(ERROR) << "Malloc buffer failed."; + return RET_ERROR; + } + memcpy(mean_addr_, mean->Data(), mean->ElementsNum() * sizeof(float)); + + auto variance = inputs_[2]; + var_addr_ = reinterpret_cast(malloc(variance->ElementsNum() * sizeof(float))); + if (var_addr_ == nullptr) { + MS_LOG(ERROR) << "Malloc buffer failed."; + return RET_ERROR; + } + memcpy(var_addr_, variance->Data(), variance->ElementsNum() * sizeof(float)); + return RET_OK; +} + int BatchnormCPUKernel::Init() { + if (context_->infer_shape_interrupt_ && !context_->running_) { + SetNeedReInit(); + return RET_OK; + } + auto input_shapes = inputs_[0]->shape(); auto n_dim = input_shapes.size(); batchnorm_param_->channel_ = input_shapes[n_dim - 1]; @@ -37,11 +71,24 @@ int BatchnormCPUKernel::Init() { batchnorm_param_->unit_ *= input_shapes[i]; } batchnorm_param_->op_parameter_.thread_num_ = - MSMIN(batchnorm_param_->op_parameter_.thread_num_, batchnorm_param_->unit_); + MSMIN(batchnorm_param_->op_parameter_.thread_num_, batchnorm_param_->channel_); + + auto ret = InitConstTensor(); + if (ret != 0) { + MS_LOG(ERROR) << "Batchnorm fp32 InitConstTensor failed."; + return RET_ERROR; + } return RET_OK; } -int BatchnormCPUKernel::ReSize() { return RET_OK; } +int BatchnormCPUKernel::ReSize() { + auto input_shapes = inputs_[0]->shape(); + batchnorm_param_->unit_ = 1; + for (int i = 0; i < input_shapes.size() - 1; i++) { + batchnorm_param_->unit_ *= input_shapes[i]; + } + return RET_OK; +} int BatchnormCPUKernel::DoExecute(int task_id) { BatchNorm(out_addr_, in_addr_, mean_addr_, var_addr_, task_id, batchnorm_param_); @@ -61,12 +108,10 @@ int BatchNormRun(int task_id, LiteParallelGroupEnv *penv, void *cdata) { int BatchnormCPUKernel::Run() { auto prepare_ret = Prepare(); if (prepare_ret != RET_OK) { - MS_LOG(ERROR) << "Prepare fail!ret: " << prepare_ret; + MS_LOG(ERROR) << "Prepare fail! Ret error code: " << prepare_ret; return prepare_ret; } in_addr_ = reinterpret_cast(inputs_.at(0)->Data()); - mean_addr_ = reinterpret_cast(inputs_.at(1)->Data()); - var_addr_ = reinterpret_cast(inputs_.at(2)->Data()); out_addr_ = reinterpret_cast(outputs_.at(0)->Data()); int ret = LiteBackendParallelLaunch(BatchNormRun, this, batchnorm_param_->op_parameter_.thread_num_); diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.h b/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.h index 4ad0224511..28d9027cf8 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.h +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/batchnorm.h @@ -31,14 +31,14 @@ class BatchnormCPUKernel : public LiteKernel { const std::vector &outputs, const Context *ctx, const lite::Primitive *primitive) : LiteKernel(parameter, inputs, outputs, ctx, primitive) { - opParameter->thread_num_ = ctx->thread_num_; batchnorm_param_ = reinterpret_cast(parameter); } - ~BatchnormCPUKernel() override = default; + ~BatchnormCPUKernel() override; int Init() override; int ReSize() override; int Run() override; + int InitConstTensor(); int DoExecute(int tid); private: diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.cc b/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.cc index 0de33adb56..f9ca15665d 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.cc @@ -29,6 +29,24 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_DepthwiseConv2D; namespace mindspore::kernel { +ConvolutionDepthwiseCPUKernel::~ConvolutionDepthwiseCPUKernel() { + delete sliding_; + if (packed_weight_ != nullptr) { + delete packed_weight_; + packed_weight_ = nullptr; + } + if (need_align_) { + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } + } +} + int ConvolutionDepthwiseCPUKernel::InitWeightBias() { // init weight: o, h, w, i; o == group, i == 1 auto weight_tensor = inputs_[kWeightIndex]; @@ -114,9 +132,16 @@ int ConvolutionDepthwiseCPUKernel::Init() { int ConvolutionDepthwiseCPUKernel::ReSize() { if (need_align_) { - free(packed_input_); - free(packed_output_); + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } } + // conv base init ConvolutionBaseCPUKernel::Init(); @@ -197,10 +222,11 @@ kernel::LiteKernel *CpuConvDwFp32KernelCreator(const std::vector(opParameter); // if (param->kernel_h_ == 3 && param->kernel_w_ == 3 && param->stride_h_ == 1 && param->stride_w_ == 1 && - // param->dilation_h_ == 1 && param->dilation_w_ == 1) { - // kernel = new (std::nothrow) kernel::ConvolutionDepthwise3x3CPUKernel(opParameter, inputs, outputs, ctx); + // param->dilation_h_ == 1 && param->dilation_w_ == 1) { + // kernel = new (std::nothrow) kernel::ConvolutionDepthwise3x3CPUKernel(opParameter, inputs, outputs, ctx, + // primitive); // } else { - // kernel = new (std::nothrow) kernel::ConvolutionDepthwiseCPUKernel(opParameter, inputs, outputs, ctx); + // kernel = new (std::nothrow) kernel::ConvolutionDepthwiseCPUKernel(opParameter, inputs, outputs, ctx, primitive); // } if (kernel == nullptr) { diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.h b/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.h index 08706ac050..22de529bca 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.h +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/convolution_depthwise.h @@ -29,14 +29,7 @@ class ConvolutionDepthwiseCPUKernel : public ConvolutionBaseCPUKernel { const std::vector &outputs, const lite::Context *ctx, const lite::Primitive *primitive) : ConvolutionBaseCPUKernel(parameter, inputs, outputs, ctx, primitive) {} - ~ConvolutionDepthwiseCPUKernel() override { - delete sliding_; - free(packed_weight_); - if (need_align_) { - free(packed_input_); - free(packed_output_); - } - }; + ~ConvolutionDepthwiseCPUKernel() override; int Init() override; int ReSize() override; diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.cc b/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.cc index a60851e178..77cc8ac22e 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.cc @@ -27,6 +27,24 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_DeDepthwiseConv2D; namespace mindspore::kernel { +DeconvolutionDepthwiseCPUKernel::~DeconvolutionDepthwiseCPUKernel() { + delete sliding_; + if (packed_weight_ != nullptr) { + delete packed_weight_; + packed_weight_ = nullptr; + } + if (need_align_) { + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } + } +} + int DeconvolutionDepthwiseCPUKernel::InitSlideParam() { conv_param_->input_batch_ = outputs_.front()->shape().at(kNHWC_N); conv_param_->input_h_ = outputs_.front()->shape().at(kNHWC_H); @@ -126,8 +144,14 @@ int DeconvolutionDepthwiseCPUKernel::Init() { int DeconvolutionDepthwiseCPUKernel::ReSize() { if (need_align_) { - free(packed_input_); - free(packed_output_); + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } } InitSlideParam(); diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.h b/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.h index 0ad3c18d44..06400a3ba1 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.h +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/deconvolution_depthwise.h @@ -29,14 +29,7 @@ class DeconvolutionDepthwiseCPUKernel : public ConvolutionBaseCPUKernel { const std::vector &outputs, const lite::Context *ctx, const lite::Primitive *primitive) : ConvolutionBaseCPUKernel(parameter, inputs, outputs, ctx, primitive) {} - ~DeconvolutionDepthwiseCPUKernel() override { - delete sliding_; - free(packed_weight_); - if (need_align_) { - free(packed_input_); - free(packed_output_); - } - }; + ~DeconvolutionDepthwiseCPUKernel() override; int Init() override; int InitSlideParam(); diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/flatten.cc b/mindspore/lite/src/runtime/kernel/arm/fp32/flatten.cc index 868705746b..30e1ede1ed 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/flatten.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/flatten.cc @@ -32,6 +32,12 @@ int FlattenCPUKernel::Init() { SetNeedReInit(); return RET_OK; } + + ReSize(); + return RET_OK; +} + +int FlattenCPUKernel::ReSize() { auto output_shape = outputs_[0]->shape(); flatten_param_->size = sizeof(float); for (int i = 0; i < output_shape.size(); i++) { @@ -40,8 +46,6 @@ int FlattenCPUKernel::Init() { return RET_OK; } -int FlattenCPUKernel::ReSize() { return RET_OK; } - int FlattenCPUKernel::Run() { auto prepare_ret = Prepare(); if (prepare_ret != RET_OK) { diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/fused_batchnorm.cc b/mindspore/lite/src/runtime/kernel/arm/fp32/fused_batchnorm.cc index 00810f89aa..9f39c30eae 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/fused_batchnorm.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/fused_batchnorm.cc @@ -15,10 +15,10 @@ */ #include "src/runtime/kernel/arm/fp32/fused_batchnorm.h" -#include #include "schema/model_generated.h" #include "src/kernel_registry.h" #include "include/errorcode.h" +#include "src/runtime/runtime_api.h" using mindspore::kernel::KERNEL_ARCH::kCPU; using mindspore::lite::KernelRegistrar; @@ -27,33 +27,121 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_FusedBatchNorm; namespace mindspore::kernel { +FusedBatchnormCPUKernel::~FusedBatchnormCPUKernel() { + if (scale_addr_ != nullptr) { + free(scale_addr_); + scale_addr_ = nullptr; + } + if (offset_addr_ != nullptr) { + free(offset_addr_); + offset_addr_ = nullptr; + } + if (mean_addr_ != nullptr) { + free(mean_addr_); + mean_addr_ = nullptr; + } + if (var_addr_ != nullptr) { + free(var_addr_); + var_addr_ = nullptr; + } +} + +int FusedBatchnormCPUKernel::InitConstTensor() { + auto scale = inputs_[1]; + scale_addr_ = reinterpret_cast(malloc(scale->ElementsNum() * sizeof(float))); + if (scale_addr_ == nullptr) { + MS_LOG(ERROR) << "Malloc buffer failed."; + return RET_ERROR; + } + memcpy(scale_addr_, scale->Data(), scale->ElementsNum() * sizeof(float)); + + auto offset = inputs_[2]; + offset_addr_ = reinterpret_cast(malloc(offset->ElementsNum() * sizeof(float))); + if (offset_addr_ == nullptr) { + MS_LOG(ERROR) << "Malloc buffer failed."; + return RET_ERROR; + } + memcpy(offset_addr_, offset->Data(), offset->ElementsNum() * sizeof(float)); + + auto mean = inputs_[3]; + mean_addr_ = reinterpret_cast(malloc(mean->ElementsNum() * sizeof(float))); + if (mean_addr_ == nullptr) { + MS_LOG(ERROR) << "Malloc buffer failed."; + return RET_ERROR; + } + memcpy(mean_addr_, mean->Data(), mean->ElementsNum() * sizeof(float)); + + auto variance = inputs_[4]; + var_addr_ = reinterpret_cast(malloc(variance->ElementsNum() * sizeof(float))); + if (var_addr_ == nullptr) { + MS_LOG(ERROR) << "Malloc buffer failed."; + return RET_ERROR; + } + memcpy(var_addr_, variance->Data(), variance->ElementsNum() * sizeof(float)); + return RET_OK; +} + int FusedBatchnormCPUKernel::Init() { if (context_->infer_shape_interrupt_ && !context_->running_) { SetNeedReInit(); return RET_OK; } - input_shape_ = reinterpret_cast(malloc(sizeof(int) * inputs_[0]->shape().size())); - memcpy(input_shape_, inputs_[0]->shape().data(), inputs_[0]->shape().size() * sizeof(int)); + auto input_shapes = inputs_[0]->shape(); + auto n_dim = input_shapes.size(); + batchnorm_param_->channel_ = input_shapes[n_dim - 1]; + batchnorm_param_->unit_ = 1; + for (int i = 0; i < n_dim - 1; i++) { + batchnorm_param_->unit_ *= input_shapes[i]; + } + batchnorm_param_->op_parameter_.thread_num_ = + MSMIN(batchnorm_param_->op_parameter_.thread_num_, batchnorm_param_->channel_); + + auto ret = InitConstTensor(); + if (ret != 0) { + MS_LOG(ERROR) << "FusedBatchnorm fp32 InitConstTensor failed."; + return RET_ERROR; + } + return RET_OK; +} + +int FusedBatchnormCPUKernel::ReSize() { + auto input_shapes = inputs_[0]->shape(); + batchnorm_param_->unit_ = 1; + for (int i = 0; i < input_shapes.size() - 1; i++) { + batchnorm_param_->unit_ *= input_shapes[i]; + } + return RET_OK; +} + +int FusedBatchnormCPUKernel::Execute(int task_id) { + FusedBatchNorm(out_addr_, in_addr_, scale_addr_, offset_addr_, mean_addr_, var_addr_, task_id, batchnorm_param_); return RET_OK; } -int FusedBatchnormCPUKernel::ReSize() { return RET_OK; } +int FusedBatchNormRun(int task_id, LiteParallelGroupEnv *penv, void *cdata) { + auto g_kernel = reinterpret_cast(cdata); + auto ret = g_kernel->Execute(task_id); + if (ret != RET_OK) { + MS_LOG(ERROR) << "FusedBatchnormRun error task_id[" << task_id << "] error_code[" << ret << "]"; + return ret; + } + return RET_OK; +} int FusedBatchnormCPUKernel::Run() { auto prepare_ret = Prepare(); if (prepare_ret != RET_OK) { - MS_LOG(ERROR) << "Prepare fail!ret: " << prepare_ret; + MS_LOG(ERROR) << "Prepare fail! Ret error code: " << prepare_ret; return prepare_ret; } - auto input_addr = reinterpret_cast(inputs_.at(0)->Data()); - auto scale_addr = reinterpret_cast(inputs_.at(1)->Data()); - auto offest_addr = reinterpret_cast(inputs_.at(2)->Data()); - auto mean_addr = reinterpret_cast(inputs_.at(3)->Data()); - auto variance_addr = reinterpret_cast(inputs_.at(4)->Data()); - auto output_addr = reinterpret_cast(outputs_.at(0)->Data()); + in_addr_ = reinterpret_cast(inputs_.at(0)->Data()); + out_addr_ = reinterpret_cast(outputs_.at(0)->Data()); - FusedBatchNorm(input_addr, scale_addr, offest_addr, mean_addr, variance_addr, input_shape_, - fused_batchnorm_param_->epsilon_, output_addr); + int ret = LiteBackendParallelLaunch(FusedBatchNormRun, this, batchnorm_param_->op_parameter_.thread_num_); + if (ret != RET_OK) { + MS_LOG(ERROR) << "FusedBatchnormRun error error_code[" << ret << "]"; + return ret; + } return RET_OK; } @@ -63,8 +151,8 @@ kernel::LiteKernel *CpuFusedBatchnormKernelCreator(const std::vector #include "src/lite_kernel.h" -#include "src/runtime/kernel/arm/nnacl/fused_batchnorm.h" +#include "src/runtime/kernel/arm/nnacl/fp32/batchnorm.h" namespace mindspore::kernel { class FusedBatchnormCPUKernel : public LiteKernel { @@ -28,17 +28,26 @@ class FusedBatchnormCPUKernel : public LiteKernel { const std::vector &outputs, const lite::Context *ctx, const lite::Primitive *primitive) : LiteKernel(parameter, inputs, outputs, ctx, primitive) { - fused_batchnorm_param_ = reinterpret_cast(parameter); + batchnorm_param_ = reinterpret_cast(parameter); } - ~FusedBatchnormCPUKernel() override { delete fused_batchnorm_param_; } + ~FusedBatchnormCPUKernel() override; int Init() override; int ReSize() override; int Run() override; + int InitConstTensor(); + int Execute(int task_id); + private: - int *input_shape_{}; - FusedBatchNormParameter *fused_batchnorm_param_; + float *in_addr_; + float *mean_addr_; + float *var_addr_; + float *scale_addr_; + float *offset_addr_; + float *out_addr_; + + BatchNormParameter *batchnorm_param_; }; } // namespace mindspore::kernel diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/scale.cc b/mindspore/lite/src/runtime/kernel/arm/fp32/scale.cc index 69efb8a24e..0bcf08d9d5 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/scale.cc +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/scale.cc @@ -17,7 +17,6 @@ #include "src/runtime/kernel/arm/fp32/scale.h" #include #include -#include "src/runtime/kernel/arm/nnacl/scale.h" #include "schema/model_generated.h" #include "src/kernel_registry.h" #include "include/errorcode.h" @@ -29,23 +28,29 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_Scale; namespace mindspore::kernel { +ScaleCPUKernel::~ScaleCPUKernel() { FreeTmpBuffer(); } + void ScaleCPUKernel::FreeTmpBuffer() { - if (scale_ != nullptr) { - free(scale_); - scale_ = nullptr; + if (scale_param_->const_scale_) { + if (scale_ != nullptr) { + free(scale_); + scale_ = nullptr; + } } - if (offset_ != nullptr) { - free(offset_); - offset_ = nullptr; + if (scale_param_->has_offset_) { + if (offset_ != nullptr) { + free(offset_); + offset_ = nullptr; + } } } int ScaleCPUKernel::InitScaleOffset() { FreeTmpBuffer(); - auto param = reinterpret_cast(opParameter); auto scale_tensor = inputs_.at(1); float *scale_ptr = reinterpret_cast(inputs_.at(1)->Data()); if (scale_ptr != nullptr) { + scale_param_->const_scale_ = true; scale_ = reinterpret_cast(malloc(scale_tensor->ElementsNum() * sizeof(float))); if (scale_ == nullptr) { MS_LOG(ERROR) << "Malloc buffer failed."; @@ -53,6 +58,7 @@ int ScaleCPUKernel::InitScaleOffset() { } memcpy(scale_, scale_ptr, scale_tensor->ElementsNum() * sizeof(float)); } else { + scale_param_->const_scale_ = false; scale_ = nullptr; } @@ -64,40 +70,39 @@ int ScaleCPUKernel::InitScaleOffset() { return RET_ERROR; } memcpy(offset_, offset_tensor->Data(), offset_tensor->ElementsNum() * sizeof(float)); - param->has_offset_ = true; + scale_param_->has_offset_ = true; } else { offset_ = nullptr; - param->has_offset_ = false; + scale_param_->has_offset_ = false; } return RET_OK; } int ScaleCPUKernel::InitParameter() { - auto param = reinterpret_cast(opParameter); auto in_tensor = inputs_.at(0); auto in_shape = in_tensor->shape(); auto scale_tensor = inputs_.at(1); auto scale_shape = scale_tensor->shape(); - if (scale_shape.size() + param->axis_ > in_shape.size()) { + if (scale_shape.size() + scale_param_->axis_ > in_shape.size()) { MS_LOG(ERROR) << "Scale tensor shape is incorrect."; return RET_ERROR; } - param->outer_size_ = 1; - param->axis_size_ = 1; - param->inner_size_ = 1; - for (int i = 0; i < param->axis_; i++) { - param->outer_size_ *= in_shape[i]; + scale_param_->outer_size_ = 1; + scale_param_->axis_size_ = 1; + scale_param_->inner_size_ = 1; + for (int i = 0; i < scale_param_->axis_; i++) { + scale_param_->outer_size_ *= in_shape[i]; } for (int i = 0; i < scale_shape.size(); i++) { - if (in_shape[i + param->axis_] != scale_shape[i]) { + if (in_shape[i + scale_param_->axis_] != scale_shape[i]) { MS_LOG(ERROR) << "Scale tensor shape is incorrect."; return RET_ERROR; } - param->axis_size_ *= in_shape[i + param->axis_]; + scale_param_->axis_size_ *= in_shape[i + scale_param_->axis_]; } - for (int i = param->axis_ + scale_shape.size(); i < in_shape.size(); i++) { - param->inner_size_ *= in_shape[i]; + for (int i = scale_param_->axis_ + scale_shape.size(); i < in_shape.size(); i++) { + scale_param_->inner_size_ *= in_shape[i]; } return RET_OK; } @@ -130,9 +135,7 @@ int ScaleCPUKernel::ReSize() { } int ScaleCPUKernel::Scale(int task_id) { - auto ret = - DoScale(input_ptr_, output_ptr_, scale_, offset_, task_id, reinterpret_cast(opParameter)); - + auto ret = DoScale(input_ptr_, output_ptr_, scale_, offset_, task_id, scale_param_); if (ret != RET_OK) { MS_LOG(ERROR) << "Scale error task_id[" << task_id << "] error_code[" << ret << "]"; return RET_ERROR; diff --git a/mindspore/lite/src/runtime/kernel/arm/fp32/scale.h b/mindspore/lite/src/runtime/kernel/arm/fp32/scale.h index 4c19404d72..38ed517746 100644 --- a/mindspore/lite/src/runtime/kernel/arm/fp32/scale.h +++ b/mindspore/lite/src/runtime/kernel/arm/fp32/scale.h @@ -19,6 +19,7 @@ #include #include "src/lite_kernel.h" +#include "src/runtime/kernel/arm/nnacl/scale.h" namespace mindspore::kernel { @@ -27,10 +28,10 @@ class ScaleCPUKernel : public LiteKernel { ScaleCPUKernel(OpParameter *parameter, const std::vector &inputs, const std::vector &outputs, const lite::Context *ctx, const lite::Primitive *primitive) - : LiteKernel(parameter, inputs, outputs, ctx, primitive) {} - ~ScaleCPUKernel() { - FreeTmpBuffer(); + : LiteKernel(parameter, inputs, outputs, ctx, primitive) { + scale_param_ = reinterpret_cast(opParameter); } + ~ScaleCPUKernel() override; int Init() override; int ReSize() override; @@ -45,6 +46,7 @@ class ScaleCPUKernel : public LiteKernel { float *scale_; float *offset_; float *output_ptr_; + ScaleParameter *scale_param_; }; } // namespace mindspore::kernel diff --git a/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.cc b/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.cc index bf107f65c3..52e4ab6e53 100644 --- a/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.cc +++ b/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.cc @@ -28,6 +28,24 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_DepthwiseConv2D; namespace mindspore::kernel { +ConvolutionDepthwiseInt8CPUKernel::~ConvolutionDepthwiseInt8CPUKernel() { + delete sliding; + if (packed_weight_ != nullptr) { + delete packed_weight_; + packed_weight_ = nullptr; + } + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (need_align_) { + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } + } +} + int ConvolutionDepthwiseInt8CPUKernel::InitWeightBias() { // init weight, int8 -> int16 // o, h, w, i -> o/8, h, w, i, 8; o == group, i == 1 @@ -111,10 +129,17 @@ int ConvolutionDepthwiseInt8CPUKernel::Init() { } int ConvolutionDepthwiseInt8CPUKernel::ReSize() { - free(packed_input_); + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } if (need_align_) { - free(packed_output_); + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } } + // conv base init ConvolutionBaseCPUKernel::Init(); diff --git a/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.h b/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.h index a6e068d90d..e13ba163f0 100644 --- a/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.h +++ b/mindspore/lite/src/runtime/kernel/arm/int8/convolution_depthwise_int8.h @@ -29,14 +29,7 @@ class ConvolutionDepthwiseInt8CPUKernel : public ConvolutionBaseCPUKernel { const std::vector &outputs, const Context *ctx, const lite::Primitive *primitive) : ConvolutionBaseCPUKernel(parameter, inputs, outputs, ctx, primitive) {} - ~ConvolutionDepthwiseInt8CPUKernel() override { - delete sliding; - free(packed_weight_); - free(packed_input_); - if (need_align_) { - free(packed_output_); - } - }; + ~ConvolutionDepthwiseInt8CPUKernel() override; int Init() override; int ReSize() override; diff --git a/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.cc b/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.cc index 542898a089..b2c91baaa2 100644 --- a/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.cc +++ b/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.cc @@ -28,6 +28,28 @@ using mindspore::lite::RET_OK; using mindspore::schema::PrimitiveType_DeDepthwiseConv2D; namespace mindspore::kernel { +DeconvolutionDepthwiseInt8CPUKernel::~DeconvolutionDepthwiseInt8CPUKernel() { + delete sliding; + if (packed_weight_ != nullptr) { + delete packed_weight_; + packed_weight_ = nullptr; + } + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } + if (need_align_) { + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } + } + if (output_buffer_ != nullptr) { + delete output_buffer_; + output_buffer_ = nullptr; + } +} + int DeconvolutionDepthwiseInt8CPUKernel::InitWeightBias() { // init weight: int8 -> int16 // o, h, w, i -> o/8, h, w, i, 8; o == group, i == 1 @@ -101,9 +123,9 @@ int DeconvolutionDepthwiseInt8CPUKernel::InitBuffer() { } // malloc tmp buffer for int32 output - output_buffer = + output_buffer_ = reinterpret_cast(malloc(conv_param_->output_h_ * conv_param_->output_w_ * C4NUM * sizeof(int32_t))); - if (output_buffer == nullptr) { + if (output_buffer_ == nullptr) { MS_LOG(ERROR) << "Malloc buffer failed."; return RET_ERROR; } @@ -144,10 +166,21 @@ int DeconvolutionDepthwiseInt8CPUKernel::Init() { } int DeconvolutionDepthwiseInt8CPUKernel::ReSize() { - free(packed_input_); + if (packed_input_ != nullptr) { + delete packed_input_; + packed_input_ = nullptr; + } if (need_align_) { - free(packed_output_); + if (packed_output_ != nullptr) { + delete packed_output_; + packed_output_ = nullptr; + } + } + if (output_buffer_ != nullptr) { + delete output_buffer_; + output_buffer_ = nullptr; } + InitSlideParam(); // conv base init @@ -162,7 +195,7 @@ int DeconvolutionDepthwiseInt8CPUKernel::ReSize() { } int DeconvolutionDepthwiseInt8CPUKernel::Execute(int task_id) { - DeconvDwInt8(packed_output_, output_buffer, packed_input_, packed_weight_, reinterpret_cast(bias_data_), + DeconvDwInt8(packed_output_, output_buffer_, packed_input_, packed_weight_, reinterpret_cast(bias_data_), conv_param_, sliding, task_id); return RET_OK; } diff --git a/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.h b/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.h index d7b76438f3..3b7ac123e3 100644 --- a/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.h +++ b/mindspore/lite/src/runtime/kernel/arm/int8/deconvolution_depthwise_int8.h @@ -29,14 +29,7 @@ class DeconvolutionDepthwiseInt8CPUKernel : public ConvolutionBaseCPUKernel { const std::vector &outputs, const Context *ctx, const lite::Primitive *primitive) : ConvolutionBaseCPUKernel(parameter, inputs, outputs, ctx, primitive) {} - ~DeconvolutionDepthwiseInt8CPUKernel() override { - delete sliding; - free(packed_weight_); - free(packed_input_); - if (need_align_) { - free(packed_output_); - } - }; + ~DeconvolutionDepthwiseInt8CPUKernel() override; int Init() override; int ReSize() override; @@ -52,7 +45,7 @@ class DeconvolutionDepthwiseInt8CPUKernel : public ConvolutionBaseCPUKernel { int16_t *packed_weight_; int16_t *packed_input_; int8_t *packed_output_; - int32_t *output_buffer; + int32_t *output_buffer_; bool need_align_ = false; }; } // namespace mindspore::kernel diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/flatten.h b/mindspore/lite/src/runtime/kernel/arm/nnacl/flatten.h index b2b2fdfebb..3d29b50f1a 100644 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/flatten.h +++ b/mindspore/lite/src/runtime/kernel/arm/nnacl/flatten.h @@ -24,4 +24,3 @@ typedef struct FlattenParameter { void Flatten(const void *input, void *output, FlattenParameter *flatten_param); #endif // MINDSPORE_LITE_SRC_RUNTIME_KERNEL_ARM_NNACL_FLATTEN_H_ - diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.cc b/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.cc index 269528e0a4..4e08156d3c 100644 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.cc +++ b/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.cc @@ -19,10 +19,21 @@ void BatchNorm(float *output_ptr, const float *input_ptr, const float *mean_ptr, const float *variance_ptr, int task_id, BatchNormParameter *param) { - for (int u = task_id; u < param->unit_; u += param->op_parameter_.thread_num_) { - for (int c = 0; c < param->channel_; c++) { - auto variance_sqrt = sqrt(variance_ptr[c] + param->epsilon_); + for (int c = task_id; c < param->channel_; c += param->op_parameter_.thread_num_) { + auto variance_sqrt = sqrt(variance_ptr[c] + param->epsilon_); + for (int u = 0; u < param->unit_; u++) { output_ptr[u * param->channel_ + c] = (input_ptr[u * param->channel_ + c] - mean_ptr[c]) / variance_sqrt; } } } + +void FusedBatchNorm(float *output_ptr, const float *input_ptr, const float *scale_ptr, const float *offest_ptr, + const float *mean_ptr, const float *variance_ptr, int task_id, BatchNormParameter *param) { + for (int c = task_id; c < param->channel_; c += param->op_parameter_.thread_num_) { + auto variance_sqrt = sqrt(variance_ptr[c] + param->epsilon_); + for (int u = 0; u < param->unit_; u++) { + output_ptr[u * param->channel_ + c] = + (input_ptr[u * param->channel_ + c] - mean_ptr[c]) / variance_sqrt * scale_ptr[c] + offest_ptr[c]; + } + } +} diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.h b/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.h index c6103565e1..b4c187ba9a 100644 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.h +++ b/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.h @@ -29,4 +29,7 @@ typedef struct BatchNormParameter { void BatchNorm(float *output_ptr, const float *input_ptr, const float *mean_ptr, const float *variance_ptr, int task_id, BatchNormParameter *param); +void FusedBatchNorm(float *output_ptr, const float *input_ptr, const float *scale_ptr, const float *offest_ptr, + const float *mean_ptr, const float *variance_ptr, int task_id, BatchNormParameter *param); + #endif // MINDSPORE_LITE_SRC_RUNTIME_KERNEL_ARM_NNACL_FUSED_BATCHNORM_H_ diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/conv_depthwise.cc b/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/conv_depthwise.cc index cd68bc467e..3f543a1d38 100644 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/conv_depthwise.cc +++ b/mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/conv_depthwise.cc @@ -486,6 +486,21 @@ void ConvDw3x3Fp32OutputUnit(float *src_buf, float *dst_output, const float *bia float32x4_t d10 = vaddq_f32(vaddq_f32(vaddq_f32(t10, t11), t12), bias_ptr); float32x4_t d11 = vaddq_f32(vsubq_f32(vsubq_f32(t11, t12), t13), bias_ptr); + float32x4_t zeros = {0, 0, 0, 0}; + float32x4_t bounds = {6, 6, 6, 6}; + if (is_relu) { + d00 = vmaxq_f32(d00, zeros); + d01 = vmaxq_f32(d01, zeros); + d10 = vmaxq_f32(d10, zeros); + d11 = vmaxq_f32(d11, zeros); + } + if (is_relu6) { + d00 = vminq_f32(vmaxq_f32(d00, zeros), bounds); + d01 = vminq_f32(vmaxq_f32(d01, zeros), bounds); + d10 = vminq_f32(vmaxq_f32(d10, zeros), bounds); + d11 = vminq_f32(vmaxq_f32(d11, zeros), bounds); + } + vst1q_f32(dst_output, d00); if (w_in_range) { vst1q_f32(dst_output + channel, d01); @@ -536,6 +551,19 @@ void ConvDw3x3Fp32OutputUnit(float *src_buf, float *dst_output, const float *bia float d10 = t10 + t11 + t12 + bias_ptr[0]; float d11 = t11 - t12 - t13 + bias_ptr[0]; + if (is_relu) { + d00 = MSMAX(d00, 0); + d01 = MSMAX(d01, 0); + d10 = MSMAX(d10, 0); + d11 = MSMAX(d11, 0); + } + if (is_relu6) { + d00 = MSMIN(MSMAX(d00, 0), 6); + d01 = MSMIN(MSMAX(d01, 0), 6); + d10 = MSMIN(MSMAX(d10, 0), 6); + d11 = MSMIN(MSMAX(d11, 0), 6); + } + (dst_output + i)[0] = d00; if (w_in_range) { (dst_output + i + channel)[0] = d01; diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.cc b/mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.cc deleted file mode 100644 index c740c9cdb1..0000000000 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "nnacl/fused_batchnorm.h" -#include - -void FusedBatchNorm(const float *input_ptr, const float *scale_ptr, const float *offest_ptr, const float *mean_ptr, - const float *variance_ptr, int *input_shapes, float epsilon, float *output_ptr) { - int channel = input_shapes[3]; - int units = 1; - for (int i = 0; i < 3; i++) { - units *= input_shapes[i]; - } - for (int c = 0; c < input_shapes[3]; c++) { - auto variance_sqrt = sqrt(variance_ptr[c] + epsilon); - for (int u = 0; u < units; u++) { - output_ptr[u * channel + c] = - (input_ptr[u * channel + c] - mean_ptr[c]) / variance_sqrt * scale_ptr[c] + offest_ptr[c]; - } - } -} - diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.h b/mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.h deleted file mode 100644 index 259b967ac6..0000000000 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_LITE_SRC_RUNTIME_KERNEL_ARM_NNACL_FUSED_BATCHNORM_H_ -#define MINDSPORE_LITE_SRC_RUNTIME_KERNEL_ARM_NNACL_FUSED_BATCHNORM_H_ - -#include "nnacl/op_base.h" - -typedef struct FusedBatchNormParameter { - OpParameter op_parameter_; - float epsilon_; -} FusedBatchNormParameter; - -void FusedBatchNorm(const float *input_ptr, const float *scale_ptr, const float *offest_ptr, const float *mean_ptr, - const float *variance_ptr, int *input_shapes, float epsilon, float *output_ptr); - - -#endif // MINDSPORE_LITE_SRC_RUNTIME_KERNEL_ARM_NNACL_FUSED_BATCHNORM_H_ - diff --git a/mindspore/lite/src/runtime/kernel/arm/nnacl/scale.h b/mindspore/lite/src/runtime/kernel/arm/nnacl/scale.h index 0b1b733f5f..c4dee2a0da 100644 --- a/mindspore/lite/src/runtime/kernel/arm/nnacl/scale.h +++ b/mindspore/lite/src/runtime/kernel/arm/nnacl/scale.h @@ -25,10 +25,9 @@ typedef struct ScaleParameter { int axis_size_; int inner_size_; int axis_; - bool has_offset_; - // todo yangruoqi: axis + bool const_scale_ = false; + bool has_offset_ = false; } ScaleParameter; int DoScale(float *in_data, float *out_data, float *scale, float *offset, int task_id, ScaleParameter *scale_param); #endif // MINDSPORE_LITE_SRC_RUNTIME_KERNEL_ARM_NNACL_SCALE_H_ - diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/fp32/batchnorm_fp32_tests.cc b/mindspore/lite/test/ut/src/runtime/kernel/arm/fp32/batchnorm_fp32_tests.cc index b65e6ecaab..7bd03d842d 100644 --- a/mindspore/lite/test/ut/src/runtime/kernel/arm/fp32/batchnorm_fp32_tests.cc +++ b/mindspore/lite/test/ut/src/runtime/kernel/arm/fp32/batchnorm_fp32_tests.cc @@ -17,33 +17,20 @@ #include "mindspore/core/utils/log_adapter.h" #include "common/common_test.h" #include "mindspore/lite/src/runtime/kernel/arm/nnacl/fp32/batchnorm.h" -#include "mindspore/lite/src/runtime/kernel/arm/nnacl/fused_batchnorm.h" #include "mindspore/lite/src/kernel_registry.h" #include "mindspore/lite/src/lite_kernel.h" -#include "mindspore/lite/src/common/file_utils.h" namespace mindspore { - class TestBatchnormFp32 : public mindspore::Common { public: TestBatchnormFp32() {} }; TEST_F(TestBatchnormFp32, BNTest) { - std::vector in_data = {0.0669681, 0.959215, 0.252686, 0.613594, 0.811776, 0.139469, 0.322848, 0.118354, - 0.082978, 0.399467, 0.961267, 0.0247456, 0.0714259, 0.0791484, 0.0648625, 0.561612, - 0.412069, 0.311492, 0.46109, 0.377125, 0.369283, 0.0332446, 0.696142, 0.715973, - 0.525524, 0.477265, 0.0336351, 0.751577, 0.377548, 0.964603, 0.0196834, 0.174865}; - std::vector in_data1 = {0.855446, 0.821765, 0.281008, 0.0798653, 0.22294, 0.793782, 0.963222, 0.17851, - 0.667549, 0.274381, 0.592842, 0.216552, 0.190274, 0.237873, 0.610063, 0.307559, - 0.830007, 0.760957, 0.583265, 0.763793, 0.456372, 0.391378, 0.547915, 0.862198, - 0.510794, 0.826776, 0.515894, 0.30071, 0.404987, 0.184773}; - std::vector in_data2 = {0.712438, 0.4927, 0.078419, 0.310429, 0.546871, 0.0667141, 0.874321, 0.0265647, - 0.685165, 0.732586, 0.952889, 0.506402, 0.540784, 0.131119, 0.357713, 0.678992, - 0.960839, 0.340706, 0.697678, 0.398146, 0.313321, 0.6485, 0.739153, 0.00190134, - 0.536842, 0.996873, 0.445276, 0.371212, 0.420397, 0.0930115}; - std::vector in_data3(32, 1); - std::vector in_data4(32, 0); + std::vector in_data = {-11.18675, 11.433986, 11.386012, 11.245945, -2.7614849, 14.692399, + -1.1983503, -6.6790967, 6.383416, -13.3213005, -8.693595, 9.476344}; + std::vector in_data1 = {12.352293, 5.122387, 14.249514}; + std::vector in_data2 = {14.632595, 0.70900035, 11.179003}; std::vector inputs_tensor; std::vector outputs_tensor; @@ -51,8 +38,7 @@ TEST_F(TestBatchnormFp32, BNTest) { op_param.op_parameter_.type_ = schema::PrimitiveType_BatchNorm; op_param.epsilon_ = 0.001f; - std::vector in_shape = {1, 2, 4, 4}; - + std::vector shape = {1, 2, 2, 3}; lite::tensor::Tensor input0_tensor; lite::tensor::Tensor input1_tensor; lite::tensor::Tensor input2_tensor; @@ -62,39 +48,40 @@ TEST_F(TestBatchnormFp32, BNTest) { input0_tensor.SetData(in_data.data()); input1_tensor.SetData(in_data1.data()); input2_tensor.SetData(in_data2.data()); - input0_tensor.set_shape(in_shape); + input0_tensor.set_shape(shape); + input1_tensor.set_shape({3}); + input2_tensor.set_shape({3}); - std::vector output(32); - std::vector corr_out(32); - std::vector output_shape = {1, 2, 4, 4}; + std::vector output(12); + std::vector corr_out = {-6.1533737, 7.4904885, -0.8563998, -0.289212, -9.356432, 0.13245535, + -3.5422924, -14.005781, -2.3525476, -6.7113695, -16.396551, -1.4275324}; lite::tensor::Tensor output0_tensor; outputs_tensor.push_back(&output0_tensor); output0_tensor.SetData(output.data()); + output0_tensor.set_shape(shape); kernel::KernelKey desc = {kernel::KERNEL_ARCH::kCPU, kNumberTypeFloat32, schema::PrimitiveType_BatchNorm}; auto creator = lite::KernelRegistry::GetInstance()->GetCreator(desc); ASSERT_NE(creator, nullptr); lite::Context ctx; - ctx.thread_num_ = 7; + ctx.thread_num_ = 1; kernel::LiteKernel *kernel = creator(inputs_tensor, outputs_tensor, reinterpret_cast(&op_param), &ctx, desc, nullptr); ASSERT_NE(kernel, nullptr); auto output_tensor_shape = output0_tensor.shape(); kernel->Run(); - FusedBatchNorm(in_data.data(), in_data3.data(), in_data4.data(), in_data1.data(), in_data2.data(), in_shape.data(), - 0.001f, corr_out.data()); - printf("==================output data=================\n"); - for (int i = 0; i < 1 * 28; i++) { + for (int i = 0; i < output0_tensor.ElementsNum(); i++) { std::cout << output[i] << " ,"; } std::cout << std::endl; - CompareOutputData(output.data(), corr_out.data(), 32, 0.00001); + CompareOutputData(output.data(), corr_out.data(), output0_tensor.ElementsNum(), 0.001); input0_tensor.SetData(nullptr); input1_tensor.SetData(nullptr); input2_tensor.SetData(nullptr); output0_tensor.SetData(nullptr); + MS_LOG(INFO) << "TestBathNormFp32 accuracy passed"; } } // namespace mindspore diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_0.bin b/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_0.bin deleted file mode 100644 index b22edaef0e7683cd56ace393a1dcd5c8b061c979..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20736 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 bCl^pPq7S3RMi`8QWcY8ODNy=$D&zV8UGITs diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_1.bin b/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_1.bin deleted file mode 100644 index 437a6958ad..0000000000 --- a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_1.bin +++ /dev/null @@ -1 +0,0 @@ -ýL[?-"R>‰qƒ>{B¸>´?yx?ó×_>JSD>Gº0? \ No newline at end of file diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_2.bin b/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_2.bin deleted file mode 100644 index 4708330c95..0000000000 --- a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_2.bin +++ /dev/null @@ -1 +0,0 @@ -J[q? §P?¾ŸŒ>gý?õA?>oo? 7G?x¸<¿”"? \ No newline at end of file diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_3.bin b/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_3.bin deleted file mode 100644 index ca38daf512..0000000000 --- a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_3.bin +++ /dev/null @@ -1 +0,0 @@ -WÚU>X™8?*Á ?!—v>›žF>0î ?.<C?Èd? \ No newline at end of file diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_4.bin b/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_4.bin deleted file mode 100644 index dd1fa36149..0000000000 --- a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_input_4.bin +++ /dev/null @@ -1 +0,0 @@ -ÜR?Ü]?žÎ>†c~?um?z1->í??Ø'?—U? \ No newline at end of file diff --git a/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_out.bin b/mindspore/lite/test/ut/src/runtime/kernel/arm/test_data/batchnorm/fusedBatchnorm_out.bin deleted file mode 100644 index 9bc4e213954d4d9a897699b709331dd1cbc54c91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20736 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 wed>52iT=F>SaNUxMSsH}n)jTI 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