From 69a5ff3300662e3aecda60781589d7082d433bc8 Mon Sep 17 00:00:00 2001 From: Hector Li Date: Mon, 10 Dec 2018 11:15:29 -0800 Subject: [PATCH] Upsample opset 9 support for CPU provider (#135) * Upsample opset 9 support for CPU provider * remove template from UpsampleBase * resolve build error on Linux * Fix build warning --- .../providers/cpu/cpu_execution_provider.cc | 8 +-- .../core/providers/cpu/tensor/upsample.cc | 34 +++++++---- .../core/providers/cpu/tensor/upsample.h | 59 ++++++++++++++----- .../core/providers/cuda/tensor/upsample.h | 2 - .../providers/cpu/tensor/upsample_op_test.cc | 31 ++++++++++ 5 files changed, 102 insertions(+), 32 deletions(-) diff --git a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc index 5677053f8b..6a5b94b6b2 100644 --- a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc +++ b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc @@ -182,8 +182,8 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Squ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, Tile); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Transpose); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Unsqueeze); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, float, Upsample); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, int32_t, Upsample); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, float, Upsample); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, int32_t, Upsample); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 8, float, Expand); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 8, Scan); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Scale); @@ -367,8 +367,8 @@ void RegisterOnnxOperatorKernels(std::function fn) { fn(BuildKernel()); fn(BuildKernel()); fn(BuildKernel()); - fn(BuildKernel()); - fn(BuildKernel()); + fn(BuildKernel()); + fn(BuildKernel()); fn(BuildKernel()); fn(BuildKernel()); fn(BuildKernel()); diff --git a/onnxruntime/core/providers/cpu/tensor/upsample.cc b/onnxruntime/core/providers/cpu/tensor/upsample.cc index 904a503f3b..daefce7788 100644 --- a/onnxruntime/core/providers/cpu/tensor/upsample.cc +++ b/onnxruntime/core/providers/cpu/tensor/upsample.cc @@ -8,16 +8,16 @@ using namespace ::onnxruntime::common; using namespace std; namespace onnxruntime { -ONNX_CPU_OPERATOR_TYPED_KERNEL( +ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL( Upsample, - 7, + 7, 9, float, KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), Upsample); -ONNX_CPU_OPERATOR_TYPED_KERNEL( +ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL( Upsample, - 7, + 7, 9, int32_t, KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), Upsample); @@ -191,24 +191,24 @@ void upsampleBilinear( } template -Status Upsample::Compute(OpKernelContext* context) const { +Status Upsample::BaseCompute(OpKernelContext* context, const std::vector& scales) const { const Tensor* X = context->Input(0); ONNXRUNTIME_ENFORCE(X != nullptr); const std::vector& dims = X->Shape().GetDims(); - if (dims.size() != scales_.size()) { + if (dims.size() != scales.size()) { return Status(ONNXRUNTIME, INVALID_ARGUMENT, "Upsample: input tensor's dimension does not match the scales."); } std::vector Y_dims; for (std::size_t i = 0; i < dims.size(); i++) { - Y_dims.push_back(static_cast(scales_[i] * dims[i])); + Y_dims.push_back(static_cast(scales[i] * dims[i])); } Tensor* Y = context->Output(0, Y_dims); switch (mode_) { case UpsampleMode::NN: - return upsampleNearest(X->template Data(), Y->template MutableData(), X->Shape(), Y->Shape(), scales_); + return upsampleNearest(X->template Data(), Y->template MutableData(), X->Shape(), Y->Shape(), scales); case UpsampleMode::LINEAR: { //What's the correct behavior of linear mode is not clear right now, //Only support bilinear with 4D tensor to keep consistent with previous behavior @@ -219,13 +219,27 @@ Status Upsample::Compute(OpKernelContext* context) const { const int64_t input_height = dims[2], input_width = dims[3]; upsampleBilinear(batch_size, num_channels, input_height, input_width, - scales_[2], scales_[3], X->template Data(), Y->template MutableData()); + scales[2], scales[3], X->template Data(), Y->template MutableData()); return Status::OK(); - //return upsampleLiner(X->template Data(), Y->template MutableData(), X->Shape(), Y->Shape(), scales_); } default: return Status(ONNXRUNTIME, FAIL, "Upsample: unexpected mode"); } } + +template +Status Upsample::Compute(OpKernelContext* context) const { + if (OpKernel::Node().InputDefs().size() == 1 || scales_cached_) { + return BaseCompute(context, scales_); + } + + const Tensor* scales = context->Input(1); + ONNXRUNTIME_ENFORCE(scales != nullptr); + int64_t scales_size = scales->Shape().Size(); + std::vector scales_arrary(scales_size); + ParseScalesData(scales, scales_arrary); + return BaseCompute(context, scales_arrary); +} + } // namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/tensor/upsample.h b/onnxruntime/core/providers/cpu/tensor/upsample.h index 3281da6e0e..7c2ff97878 100644 --- a/onnxruntime/core/providers/cpu/tensor/upsample.h +++ b/onnxruntime/core/providers/cpu/tensor/upsample.h @@ -10,33 +10,25 @@ namespace onnxruntime { constexpr const char* UpsampleModeNN = "nearest"; constexpr const char* UpsampleModeLinear = "linear"; - - enum UpsampleMode { - NN = 0, // nearest neighbour - LINEAR = 1, // linear interpolation - }; +enum UpsampleMode { + NN = 0, // nearest neighbour + LINEAR = 1, // linear interpolation +}; class UpsampleBase { protected: UpsampleBase(OpKernelInfo info) { std::string mode; ONNXRUNTIME_ENFORCE(info.GetAttr("mode", &mode).IsOK()); - mode_ = StringToUpsampleMode(mode); - ONNXRUNTIME_ENFORCE(info.GetAttrs("scales", scales_).IsOK()); - for (auto& scale : scales_) { - ONNXRUNTIME_ENFORCE(scale >= 1, "Scale value should be greater than or equal to 1."); - } - - if (UpsampleMode::LINEAR == mode_) { - ONNXRUNTIME_ENFORCE(((scales_[0] == 1) && (scales_[1] == 1)), - "Upsample: linear mode upsample only support bilinear, the first 2 scales should be 1."); + if (info.GetInputCount() == 1) { + ONNXRUNTIME_ENFORCE(info.GetAttrs("scales", scales_).IsOK()); + ScalesValidation(scales_, mode_); } } UpsampleMode mode_; - std::vector scales_; UpsampleMode StringToUpsampleMode(const std::string& mode) { @@ -49,15 +41,50 @@ class UpsampleBase { UpsampleModeNN + "(default) or " + UpsampleModeLinear + "."); } } + + void ScalesValidation(const std::vector& scales, const UpsampleMode mode) const { + for (auto& scale : scales) { + ONNXRUNTIME_ENFORCE(scale >= 1, "Scale value should be greater than or equal to 1."); + } + + if (UpsampleMode::LINEAR == mode) { + ONNXRUNTIME_ENFORCE(scales.size() == 4, "Upsample: linear mode upsample only support bilinear with 4 dimension."); + ONNXRUNTIME_ENFORCE(((scales[0] == 1) && (scales[1] == 1)), + "Upsample: linear mode upsample only support bilinear, the first 2 scales should be 1."); + } + } }; template class Upsample : public UpsampleBase, public OpKernel { public: - Upsample(OpKernelInfo info) : UpsampleBase(info), OpKernel(info) { + Upsample(OpKernelInfo info) : UpsampleBase(info), OpKernel(info), scales_cached_(false) { + if (info.GetInputCount() > 1) { + const Tensor* scale; + bool get_scale = info.TryGetConstantInput(1, &scale); + + if (get_scale) { + ParseScalesData(scale, scales_); + scales_cached_ = true; + } + } } Status Compute(OpKernelContext* context) const override; + + Status BaseCompute(OpKernelContext* context, const std::vector& scales) const; + +private: + void ParseScalesData(const Tensor* scale, std::vector& scales) const { + const float* scale_data = scale->template Data(); + int64_t scales_size = scale->Shape().Size(); + ONNXRUNTIME_ENFORCE(scales_size > 0, "scales size should be greater than 0."); + memcpy(scales.data(), scale_data, scales_size * sizeof(float)); + ScalesValidation(scales, mode_); + } + +private: + bool scales_cached_; }; } // namespace onnxruntime diff --git a/onnxruntime/core/providers/cuda/tensor/upsample.h b/onnxruntime/core/providers/cuda/tensor/upsample.h index d6dfdea2c6..1d6823a29d 100644 --- a/onnxruntime/core/providers/cuda/tensor/upsample.h +++ b/onnxruntime/core/providers/cuda/tensor/upsample.h @@ -10,8 +10,6 @@ namespace onnxruntime { namespace cuda { -struct TVMState; - template class Upsample : public UpsampleBase, public CudaKernel { public: diff --git a/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc b/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc index f3295246d7..f129c5c3f7 100644 --- a/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc +++ b/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc @@ -319,5 +319,36 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest_1D) { test.AddOutput("Y", {10}, Y); test.Run(); } + +TEST(UpsampleOpTest, UpsampleOpNearest2XTest_opset9) { + OpTester test("Upsample", 9); + + std::vector scales{1.0f, 1.0f, 2.0f, 2.0f}; + test.AddAttribute("mode", "nearest"); + + const int64_t N = 1, C = 2, H = 2, W = 2; + std::vector X = {1, 3, + 3, 5, + + 3, 5, + 7, 9}; + + test.AddInput("X", {N, C, H, W}, X); + test.AddInput("scales", {4}, scales); + + std::vector Y = { + 1, 1, 3, 3, + 1, 1, 3, 3, + 3, 3, 5, 5, + 3, 3, 5, 5, + + 3, 3, 5, 5, + 3, 3, 5, 5, + 7, 7, 9, 9, + 7, 7, 9, 9}; + + test.AddOutput("Y", {N, C, (int64_t)(H * scales[2]), (int64_t)(W * scales[3])}, Y); + test.Run(); +} } // namespace test } // namespace onnxruntime