From 9dffeb340e38e7c4c5f5620769fe24de5ec08caa Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Thu, 13 Dec 2018 20:50:03 -0800 Subject: [PATCH 1/4] Initial commit Maxunpool operator --- .../providers/cpu/cpu_execution_provider.cc | 6 +- onnxruntime/core/providers/cpu/nn/Unpool.cc | 195 +++++++++ onnxruntime/core/providers/cpu/nn/unpool.h | 69 ++++ .../test/providers/cpu/nn/unpool_op_test.cc | 382 ++++++++++++++++++ 4 files changed, 650 insertions(+), 2 deletions(-) create mode 100644 onnxruntime/core/providers/cpu/nn/Unpool.cc create mode 100644 onnxruntime/core/providers/cpu/nn/unpool.h create mode 100644 onnxruntime/test/providers/cpu/nn/unpool_op_test.cc diff --git a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc index 1e6318949a73e..8f27b0f93ed22 100644 --- a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc +++ b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc @@ -196,6 +196,7 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Eye class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, float, IsNaN); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, MLFloat16, IsNaN); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Erf); +class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, MaxUnpool); void RegisterOnnxOperatorKernels(std::function fn) { fn(BuildKernel()); @@ -384,6 +385,7 @@ void RegisterOnnxOperatorKernels(std::function fn) { fn(BuildKernel()); fn(BuildKernel()); fn(BuildKernel()); + fn(BuildKernel()); } // Forward declarations of ml op kernels @@ -485,7 +487,7 @@ static void RegisterCPUKernels(std::function create_fn std::shared_ptr CPUExecutionProvider::GetKernelRegistry() const { static std::shared_ptr - kernel_registry = std::make_shared(RegisterCPUKernels); + kernel_registry = std::make_shared(RegisterCPUKernels); return kernel_registry; } @@ -493,7 +495,7 @@ std::vector> CPUExecutionProvider::GetCapability(const onnxruntime::GraphViewer& graph, const std::vector& kernel_registries) const { std::vector> - result = IExecutionProvider::GetCapability(graph, kernel_registries); + result = IExecutionProvider::GetCapability(graph, kernel_registries); for (auto& rule : fuse_rules_) { rule(graph, result); diff --git a/onnxruntime/core/providers/cpu/nn/Unpool.cc b/onnxruntime/core/providers/cpu/nn/Unpool.cc new file mode 100644 index 0000000000000..f6b0c47644c9c --- /dev/null +++ b/onnxruntime/core/providers/cpu/nn/Unpool.cc @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/providers/cpu/nn/unpool.h" +#include "core/providers/cpu/tensor/utils.h" +#include + +using namespace ::onnxruntime::common; + +namespace onnxruntime { + +ONNX_CPU_OPERATOR_KERNEL( + MaxUnpool, + 9, + KernelDefBuilder() + .TypeConstraint("T", DataTypeImpl::GetTensorType()) + .TypeConstraint("I", DataTypeImpl::GetTensorType()) + .TypeConstraint("Y", DataTypeImpl::GetTensorType()), + MaxUnpool); + +Status MaxUnpool::Compute(OpKernelContext* context) const { + // Get pooled values tensor + const Tensor* X = context->Input(0); + const TensorShape& X_shape = X->Shape(); + const float* X_data = X->template Data(); + + ONNXRUNTIME_RETURN_IF_NOT(X_shape.NumDimensions() >= 3, "Input dimension cannot be less than 3."); + + // Supported sizes check + size_t pooling_dims = X_shape.NumDimensions() - 2; + if (pooling_dims > 3) { + return Status(ONNXRUNTIME, INVALID_ARGUMENT, "Unsupported pooling size."); + } + + // Get pooled index tensor + const Tensor* I = context->Input(1); + const TensorShape& I_shape = I->Shape(); + const int64_t* I_data = I->template Data(); + + ONNXRUNTIME_RETURN_IF_NOT(I_shape == X_shape, "Index tensor shape should be same as that of the input data tensor to unpool."); + + // Calculate output tensor shape from attributes + std::vector inferredOutputShape(X_shape.NumDimensions()); + + // Copy batch and channel dims + inferredOutputShape[0] = X_shape[0]; + inferredOutputShape[1] = X_shape[1]; + + // For feature dims calculate reversing the formula used for Maxpool + for (auto dim = 0; dim < kernel_shape_.size(); ++dim) { + inferredOutputShape[dim + 2] = (X_shape[dim + 2] - 1) * strides_[dim] - (pads_[dim + 2] + pads_[kernel_shape_.size() + dim + 4]) + kernel_shape_[dim]; + } + + // If outputshape is provided use that to infer additional padding. + std::vector inferredPads; + std::vector givenOutputShape; + bool padsInferred = false; + + if (num_inputs_ == 3) { + auto& tensor_shape = *context->Input(2); + ONNXRUNTIME_ENFORCE(tensor_shape.Shape().GetDims().size() == 1, "Shape must be 1 dimensional as it's tensor data is a shape"); + + // Turn the shape tensor data into an actual shape + const int64_t* p_shape = tensor_shape.template Data(); + std::vector shape{p_shape, p_shape + tensor_shape.Shape().Size()}; + givenOutputShape = shape; + + inferredPads.resize(inferredOutputShape.size() * 2, 0); + + // calculate if output shape has any padding over the inferred shape for feature dims. + for (auto dim = 2; dim < shape.size(); dim++) { + ONNXRUNTIME_ENFORCE(inferredOutputShape[dim] <= shape[dim], "Incorrect output shape"); + + int64_t inferredPad = shape[dim] - inferredOutputShape[dim]; + ONNXRUNTIME_ENFORCE(inferredPad <= kernel_shape_[dim - 2], "Incorrect output shape"); + + if (inferredPad > 0) { + padsInferred = true; + if (inferredPad == kernel_shape_[dim - 2]) { + inferredPads[dim] = 1; + inferredPads[dim + inferredOutputShape.size()] = inferredPad - 1; + } else { + inferredPads[dim + inferredOutputShape.size()] = inferredPad; + } + } + } + } + + // unpool + int64_t totalPooledElem = 1; + int64_t totalOutputElem = 1; + + for (auto dim = 0; dim < X_shape.NumDimensions(); dim++) { + totalPooledElem *= X_shape[dim]; + totalOutputElem *= inferredOutputShape[dim]; + } + + // if there are no pads inferred from outputshape simply create the new unpooled tensor + if (!padsInferred) { + TensorShape shape(inferredOutputShape); + + Tensor* Y = context->Output(0, shape); + auto Y_data = Y->template MutableData(); + auto out = gsl::make_span(Y_data, Y->Shape().Size()); + std::fill_n(out.data(), out.size(), 0.f); + + for (auto curElem = 0; curElem < totalPooledElem; ++curElem) { + out[I_data[curElem]] = X_data[curElem]; + } + } else { + // If the output shape has pads over the inferred dims , first + // create the tensor with the inferred dims and add the padding. + + // Generate tensor with inferred dims. + TensorShape shape(inferredOutputShape); + + AllocatorPtr alloc; + ONNXRUNTIME_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc)); + auto element_type = DataTypeImpl::GetType(); + + void* buffer = alloc->Alloc(sizeof(float) * shape.Size()); + std::unique_ptr p_tensor = std::make_unique(element_type, + shape, + buffer, + alloc->Info(), + alloc); + + float* p = p_tensor->template MutableData(); + + auto out = gsl::make_span(p, p_tensor->Shape().Size()); + std::fill_n(out.data(), out.size(), 0.f); + + for (auto curElem = 0; curElem < totalPooledElem; ++curElem) { + out[I_data[curElem]] = X_data[curElem]; + } + + std::vector output_dims(inferredOutputShape); + size_t dimension_count = output_dims.size(); + + std::vector input_starts; + std::vector input_extents; + + // Calculate output dimensions + for (size_t i = 0; i < dimension_count; i++) { + input_starts.push_back(slices_[i]); + input_extents.push_back(output_dims[i] + slices_[i] + slices_[i + dimension_count]); + output_dims[i] += inferredPads[i] + inferredPads[i + dimension_count] + slices_[i] + slices_[i + dimension_count]; + } + + // setup output object + TensorShape output_shape(givenOutputShape); + Tensor* Y = context->Output(0, output_shape); + auto Y_data = Y->template MutableData(); + + auto outData = gsl::make_span(Y_data, Y->Shape().Size()); + + std::fill_n(outData.data(), outData.size(), 0.f); + + // add padding + TensorPitches output_pitches(*Y); + size_t alignSkip = 0; // Amount to skip to align to where the next input tensor data needs to be written + + // Initial skip, sum up the begin padding on each axis + for (size_t i = 0; i < dimension_count; i++) + alignSkip += inferredPads[i] * output_pitches[i]; + + size_t inner_axis = dimension_count - 1; + + TensorAxisCounters input_counters(*p_tensor); + SliceIterator input(*p_tensor, input_starts, input_extents); + + while (input_counters) { + Y_data += alignSkip; + { + Y_data = input.CopyInnermostAxis(Y_data); + int64_t prePad = inferredPads[inner_axis]; + int64_t postPad = inferredPads[inner_axis + dimension_count]; + Y_data += postPad; + alignSkip = prePad; + } + // Calculate the size of the next block of padding (skipping over the innermost axis since that's already done) + while (input_counters.Increment()) { + ptrdiff_t inner_pitch = output_pitches[input_counters.Axis()]; + int64_t prePad = inferredPads[input_counters.Axis()]; + int64_t postPad = inferredPads[input_counters.Axis() + dimension_count]; + Y_data += inner_pitch * postPad; + alignSkip += inner_pitch * prePad; + } + } + } + + return Status::OK(); +} + +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/nn/unpool.h b/onnxruntime/core/providers/cpu/nn/unpool.h new file mode 100644 index 0000000000000..41fc241c90fbf --- /dev/null +++ b/onnxruntime/core/providers/cpu/nn/unpool.h @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include "core/common/common.h" +#include "core/framework/op_kernel.h" +#include "core/providers/cpu/nn/autopad_type.h" +#include "core/mlas/inc/mlas.h" + +namespace onnxruntime { + +class MaxUnpool : public OpKernel { + public: + MaxUnpool(const OpKernelInfo& info) : OpKernel(info) { + ONNXRUNTIME_ENFORCE(info.GetAttrs("kernel_shape", kernel_shape_).IsOK(), + "No kernel shape is set."); + + num_inputs_ = OpKernel::Node().InputDefs().size(); + + if (num_inputs_ == 3 && !pads_.empty()) { + // ignore pads attribute value + } + + // setup defaults. + if (!info.GetAttrs("pads", pads_).IsOK() || pads_.empty()) { + pads_.resize(kernel_shape_.size() * 2, 0); + } + + if (!info.GetAttrs("strides", strides_).IsOK() || strides_.empty()) { + strides_.resize(kernel_shape_.size(), 1); + } + + for (size_t dim = 0; dim < kernel_shape_.size(); ++dim) { + ONNXRUNTIME_ENFORCE(kernel_shape_[dim] > 0); + ONNXRUNTIME_ENFORCE(pads_[dim] < kernel_shape_[dim] && pads_[dim + kernel_shape_.size()] < kernel_shape_[dim], + "Pad should be smaller than kernel."); + } + + ONNXRUNTIME_ENFORCE(strides_.size() == kernel_shape_.size()); + + // Add 4 pad values (0) for batch and channel dimensions + pads_.insert(pads_.begin(), {0, 0}); + pads_.insert(pads_.begin() + 2 + kernel_shape_.size(), {0, 0}); + + // Separate out any negative pads_ into the slices_ array + slices_.resize(pads_.size(), 0); + for (size_t index = 0; index < pads_.size(); index++) { + if (pads_[index] < 0) { + slices_[index] = pads_[index]; + pads_[index] = 0; + } + } + } + + ~MaxUnpool() override{}; + + Status Compute(OpKernelContext* context) const override; + + private: + std::vector kernel_shape_; + std::vector pads_; + std::vector strides_; + std::vector slices_; // All of the negative padding values are separated out into slices_ + int64_t num_inputs_; +}; + +} // namespace onnxruntime diff --git a/onnxruntime/test/providers/cpu/nn/unpool_op_test.cc b/onnxruntime/test/providers/cpu/nn/unpool_op_test.cc new file mode 100644 index 0000000000000..fd9b20ba9c3e9 --- /dev/null +++ b/onnxruntime/test/providers/cpu/nn/unpool_op_test.cc @@ -0,0 +1,382 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "gtest/gtest.h" +#include "test/providers/provider_test_utils.h" + +using namespace std; +namespace onnxruntime { +namespace test { + +TEST(UnpoolTest, MaxUnPool1D) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2}); + test.AddAttribute("kernel_shape", vector{2}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 4}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 4}; + + std::vector expected_dims = {1, 1, 8}; + std::vector expected_vals = {0, 1, 0, 2, 3, 0, 4, 0}; + + std::vector inputDims = {3}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + test.AddInput("output_shape", inputDims, expected_dims); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool2D) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2}); + test.AddAttribute("kernel_shape", std::vector{2, 2}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 2, 2}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 2, 2}; + + std::vector expected_dims = {1, 1, 4, 4}; + std::vector expected_vals = {0, 1, 0, 2, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + std::vector inputDims = {4}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + test.AddInput("output_shape", inputDims, expected_dims); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool3D) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2, 2}); + test.AddAttribute("kernel_shape", vector{2, 2, 2}); + + std::vector t_vals = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector t_dims = {1, 1, 2, 2, 2}; + + std::vector i_vals = {1, 3, 24, 30, 32, 38, 60, 62}; + std::vector i_dims = {1, 1, 2, 2, 2}; + + std::vector expected_dims = {1, 1, 4, 4, 4}; + std::vector expectedDims_Size = {5}; + + std::vector expected_vals = + { + //slice 1 + 0, 1, 0, 2, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + // slice 2 + 0, 0, 0, 0, + 0, 0, 0, 0, + 3, 0, 0, 0, + 0, 0, 4, 0, + + //slice 3 + 5, 0, 0, 0, + 0, 0, 6, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + // slice 4 + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 7, 0, 8, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + test.AddInput("output_shape", expectedDims_Size, expected_dims); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool1D_Without_OutputShape) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2}); + test.AddAttribute("kernel_shape", vector{2}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 4}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 4}; + + std::vector expected_dims = {1, 1, 8}; + std::vector expected_vals = {0, 1, 0, 2, 3, 0, 4, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool2D_Without_OutputShape) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2}); + test.AddAttribute("kernel_shape", vector{2, 2}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 2, 2}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 2, 2}; + + std::vector expected_dims = {1, 1, 4, 4}; + std::vector expected_vals = {0, 1, 0, 2, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool3D_Without_OutputShape) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2, 2}); + test.AddAttribute("kernel_shape", vector{2, 2, 2}); + + std::vector t_vals = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector t_dims = {1, 1, 2, 2, 2}; + + std::vector i_vals = {1, 3, 24, 30, 32, 38, 60, 62}; + std::vector i_dims = {1, 1, 2, 2, 2}; + + std::vector expected_dims = {1, 1, 4, 4, 4}; + + std::vector expected_vals = + { + //slice 1 + 0, 1, 0, 2, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + // slice 2 + 0, 0, 0, 0, + 0, 0, 0, 0, + 3, 0, 0, 0, + 0, 0, 4, 0, + + //slice 3 + 5, 0, 0, 0, + 0, 0, 6, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + // slice 4 + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 7, 0, 8, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool1D_Padding) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2}); + test.AddAttribute("kernel_shape", vector{2}); + test.AddAttribute("pads", vector{1, 0}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 4}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 4}; + + std::vector expected_dims = {1, 1, 7}; + std::vector expected_vals = {0, 1, 0, 2, 3, 0, 4}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + + test.AddOutput("YP", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool2D_Padding) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2}); + test.AddAttribute("kernel_shape", vector{2, 2}); + test.AddAttribute("pads", vector{1, 1, 0, 0}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 2, 2}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 2, 2}; + + std::vector expected_dims = {1, 1, 3, 3}; + std::vector expected_vals = {0, 1, 0, 2, 3, 0, 4, 0, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool3D_Padding) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2, 2}); + test.AddAttribute("kernel_shape", vector{2, 2, 2}); + test.AddAttribute("pads", vector{0, 1, 1, 0, 0, 0}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 1, 2, 2}; + + std::vector i_vals = {1, 4, 8, 12}; + std::vector i_dims = {1, 1, 1, 2, 2}; + + std::vector expected_dims = {1, 1, 2, 3, 3}; + + std::vector expected_vals = { + 0, 1, 0, + 0, 2, 0, + 0, 0, 3, + 0, 0, 0, + 4, 0, 0, + 0, 0, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool1D_WithPaddedOutput) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2}); + test.AddAttribute("kernel_shape", vector{2}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 4}; + + std::vector i_vals = {1, 3, 4, 6}; + std::vector i_dims = {1, 1, 4}; + + std::vector expected_dims = {1, 1, 10}; + std::vector expected_vals = {0, 0, 1, 0, 2, 3, 0, 4, 0, 0}; + + std::vector inputDims = {3}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + test.AddInput("output_shape", inputDims, expected_dims); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool2D_WithPaddedOutput) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2}); + test.AddAttribute("kernel_shape", std::vector{2, 2}); + + std::vector t_vals = {1, 2, 3, 4}; + std::vector t_dims = {1, 1, 2, 2}; + + std::vector i_vals = {1, 3, 8, 10}; + std::vector i_dims = {1, 1, 2, 2}; + + std::vector expected_dims = {1, 1, 5, 5}; + std::vector expected_vals = { + 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, + 3, 0, 4, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0}; + + std::vector inputDims = {4}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + test.AddInput("output_shape", inputDims, expected_dims); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +TEST(UnpoolTest, MaxUnPool3D_WithPaddedOutput) { + OpTester test("MaxUnpool", 9); + + test.AddAttribute("strides", std::vector{2, 2, 2}); + test.AddAttribute("kernel_shape", vector{2, 2, 2}); + + std::vector t_vals = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector t_dims = {1, 1, 2, 2, 2}; + + std::vector i_vals = {1, 3, 24, 30, 32, 38, 60, 62}; + std::vector i_dims = {1, 1, 2, 2, 2}; + + std::vector expected_dims = {1, 1, 4, 4, 5}; + std::vector expectedDims_Size = {5}; + + std::vector expected_vals = + { + //slice 1 + 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + // slice 2 + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, + 0, 0, 4, 0, 0, + + //slice 3 + 5, 0, 0, 0, 0, + 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + // slice 4 + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 7, 0, 8, 0, 0}; + + test.AddInput("xT", t_dims, t_vals); + test.AddInput("xI", i_dims, i_vals); + test.AddInput("output_shape", expectedDims_Size, expected_dims); + + test.AddOutput("Y", expected_dims, expected_vals); + test.Run(); +} + +} // namespace test +} // namespace onnxruntime From a04fadb2d7b918c689196f407b1b81d5c6f5c3b8 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Fri, 14 Dec 2018 11:48:28 -0800 Subject: [PATCH 2/4] fix gpu build failure --- onnxruntime/core/providers/cpu/nn/Unpool.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/onnxruntime/core/providers/cpu/nn/Unpool.cc b/onnxruntime/core/providers/cpu/nn/Unpool.cc index f6b0c47644c9c..626d1ef0931d4 100644 --- a/onnxruntime/core/providers/cpu/nn/Unpool.cc +++ b/onnxruntime/core/providers/cpu/nn/Unpool.cc @@ -1,6 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// disable warning because std::copy is used by Sliceiterator +// std::copy_n is not an option for raw pointer destinations as used by gsl::copy. +#ifdef _MSC_VER +#pragma warning(disable : 4996) +#endif #include "core/providers/cpu/nn/unpool.h" #include "core/providers/cpu/tensor/utils.h" #include From e9d86e7182c152b328c7457f08030b973b2e2740 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Wed, 19 Dec 2018 14:58:06 -0800 Subject: [PATCH 3/4] remove op test from excluded list --- onnxruntime/test/onnx/main.cc | 2 -- onnxruntime/test/python/onnx_backend_test_series.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/onnxruntime/test/onnx/main.cc b/onnxruntime/test/onnx/main.cc index df1ed84854504..94f89ad390948 100644 --- a/onnxruntime/test/onnx/main.cc +++ b/onnxruntime/test/onnx/main.cc @@ -309,8 +309,6 @@ int real_main(int argc, char* argv[]) { {"operator_rnn_single_layer", "disable reason"}, {"prelu_broadcast", "disable reason"}, {"prelu_example", "disable reason"}, - {"maxunpool_export_with_output_shape", "opset 9 not supported yet"}, - {"maxunpool_export_without_output_shape", "opset 9 not supported yet"}, {"upsample_nearest", "opset 9 not supported yet"}, {"onehot_with_axis", "opset 9 not supported yet"}, {"onehot_without_axis", "opset 9 not supported yet"}, // also has bug in current test re: output type. Spandan to fix. diff --git a/onnxruntime/test/python/onnx_backend_test_series.py b/onnxruntime/test/python/onnx_backend_test_series.py index 80f7f6c81f34d..f8753bc1cb363 100644 --- a/onnxruntime/test/python/onnx_backend_test_series.py +++ b/onnxruntime/test/python/onnx_backend_test_series.py @@ -35,8 +35,6 @@ '|test_eyelike_with_dtype_cpu.*' '|test_eyelike_without_dtype_cpu.*' '|test_gru_seq_length_cpu.*' -'|test_maxunpool_export_with_output_shape_cpu.*' -'|test_maxunpool_export_without_output_shape_cpu.*' '|test_onehot_with_axis_cpu.*' '|test_onehot_without_axis_cpu.*' '|test_scan_sum_cpu.*' From 67e7b2c2dc67875644254dcdfacf805896f064d8 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Wed, 19 Dec 2018 19:58:10 -0800 Subject: [PATCH 4/4] Change to ORT --- onnxruntime/core/providers/cpu/nn/Unpool.cc | 12 ++++++------ onnxruntime/core/providers/cpu/nn/unpool.h | 13 ++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/Unpool.cc b/onnxruntime/core/providers/cpu/nn/Unpool.cc index 626d1ef0931d4..391326dbb2d61 100644 --- a/onnxruntime/core/providers/cpu/nn/Unpool.cc +++ b/onnxruntime/core/providers/cpu/nn/Unpool.cc @@ -29,7 +29,7 @@ Status MaxUnpool::Compute(OpKernelContext* context) const { const TensorShape& X_shape = X->Shape(); const float* X_data = X->template Data(); - ONNXRUNTIME_RETURN_IF_NOT(X_shape.NumDimensions() >= 3, "Input dimension cannot be less than 3."); + ORT_RETURN_IF_NOT(X_shape.NumDimensions() >= 3, "Input dimension cannot be less than 3."); // Supported sizes check size_t pooling_dims = X_shape.NumDimensions() - 2; @@ -42,7 +42,7 @@ Status MaxUnpool::Compute(OpKernelContext* context) const { const TensorShape& I_shape = I->Shape(); const int64_t* I_data = I->template Data(); - ONNXRUNTIME_RETURN_IF_NOT(I_shape == X_shape, "Index tensor shape should be same as that of the input data tensor to unpool."); + ORT_RETURN_IF_NOT(I_shape == X_shape, "Index tensor shape should be same as that of the input data tensor to unpool."); // Calculate output tensor shape from attributes std::vector inferredOutputShape(X_shape.NumDimensions()); @@ -63,7 +63,7 @@ Status MaxUnpool::Compute(OpKernelContext* context) const { if (num_inputs_ == 3) { auto& tensor_shape = *context->Input(2); - ONNXRUNTIME_ENFORCE(tensor_shape.Shape().GetDims().size() == 1, "Shape must be 1 dimensional as it's tensor data is a shape"); + ORT_RETURN_IF_NOT(tensor_shape.Shape().GetDims().size() == 1, "Shape must be 1 dimensional as it's tensor data is a shape"); // Turn the shape tensor data into an actual shape const int64_t* p_shape = tensor_shape.template Data(); @@ -74,10 +74,10 @@ Status MaxUnpool::Compute(OpKernelContext* context) const { // calculate if output shape has any padding over the inferred shape for feature dims. for (auto dim = 2; dim < shape.size(); dim++) { - ONNXRUNTIME_ENFORCE(inferredOutputShape[dim] <= shape[dim], "Incorrect output shape"); + ORT_RETURN_IF_NOT(inferredOutputShape[dim] <= shape[dim], "Incorrect output shape"); int64_t inferredPad = shape[dim] - inferredOutputShape[dim]; - ONNXRUNTIME_ENFORCE(inferredPad <= kernel_shape_[dim - 2], "Incorrect output shape"); + ORT_RETURN_IF_NOT(inferredPad <= kernel_shape_[dim - 2], "Incorrect output shape"); if (inferredPad > 0) { padsInferred = true; @@ -120,7 +120,7 @@ Status MaxUnpool::Compute(OpKernelContext* context) const { TensorShape shape(inferredOutputShape); AllocatorPtr alloc; - ONNXRUNTIME_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc)); + ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc)); auto element_type = DataTypeImpl::GetType(); void* buffer = alloc->Alloc(sizeof(float) * shape.Size()); diff --git a/onnxruntime/core/providers/cpu/nn/unpool.h b/onnxruntime/core/providers/cpu/nn/unpool.h index 41fc241c90fbf..012c1da30d140 100644 --- a/onnxruntime/core/providers/cpu/nn/unpool.h +++ b/onnxruntime/core/providers/cpu/nn/unpool.h @@ -7,15 +7,14 @@ #include "core/common/common.h" #include "core/framework/op_kernel.h" #include "core/providers/cpu/nn/autopad_type.h" -#include "core/mlas/inc/mlas.h" namespace onnxruntime { class MaxUnpool : public OpKernel { public: MaxUnpool(const OpKernelInfo& info) : OpKernel(info) { - ONNXRUNTIME_ENFORCE(info.GetAttrs("kernel_shape", kernel_shape_).IsOK(), - "No kernel shape is set."); + ORT_ENFORCE(info.GetAttrs("kernel_shape", kernel_shape_).IsOK(), + "No kernel shape is set."); num_inputs_ = OpKernel::Node().InputDefs().size(); @@ -33,12 +32,12 @@ class MaxUnpool : public OpKernel { } for (size_t dim = 0; dim < kernel_shape_.size(); ++dim) { - ONNXRUNTIME_ENFORCE(kernel_shape_[dim] > 0); - ONNXRUNTIME_ENFORCE(pads_[dim] < kernel_shape_[dim] && pads_[dim + kernel_shape_.size()] < kernel_shape_[dim], - "Pad should be smaller than kernel."); + ORT_ENFORCE(kernel_shape_[dim] > 0); + ORT_ENFORCE(pads_[dim] < kernel_shape_[dim] && pads_[dim + kernel_shape_.size()] < kernel_shape_[dim], + "Pad should be smaller than kernel."); } - ONNXRUNTIME_ENFORCE(strides_.size() == kernel_shape_.size()); + ORT_ENFORCE(strides_.size() == kernel_shape_.size()); // Add 4 pad values (0) for batch and channel dimensions pads_.insert(pads_.begin(), {0, 0});