[nnc backend] Add conv2D operation implementation (#404)
authorVladimir Plazun/AI Tools Lab /SRR/Engineer/삼성전자 <v.plazun@partner.samsung.com>
Mon, 2 Jul 2018 15:11:01 +0000 (19:11 +0400)
committerSergey Vostokov/AI Tools Lab /SRR/Staff Engineer/삼성전자 <s.vostokov@samsung.com>
Mon, 2 Jul 2018 15:11:01 +0000 (00:11 +0900)
[nnc backend] Add conv2D operation implementation

Used by nnc model IR interpreter backend

Signed-off-by: Vladimir Plazun <v.plazun@partner.samsung.com>
contrib/nnc/libs/backend/interpreter/core/include/interpreter/ops/common.h [new file with mode: 0644]
contrib/nnc/libs/backend/interpreter/core/include/interpreter/ops/conv_2D.h [new file with mode: 0644]
contrib/nnc/libs/backend/interpreter/core/src/ops/common.cpp [new file with mode: 0644]
contrib/nnc/libs/backend/interpreter/core/src/ops/conv_2D.cpp [new file with mode: 0644]

diff --git a/contrib/nnc/libs/backend/interpreter/core/include/interpreter/ops/common.h b/contrib/nnc/libs/backend/interpreter/core/include/interpreter/ops/common.h
new file mode 100644 (file)
index 0000000..46eab6e
--- /dev/null
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "nncc/core/ADT/tensor/Index.h"
+#include "nncc/core/ADT/tensor/Shape.h"
+
+namespace nncc
+{
+namespace contrib
+{
+namespace backend
+{
+namespace interpreter
+{
+namespace impl
+{
+
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::Shape;
+
+
+///
+/// Get current input element index using output index, current kernel index, strides and paddings
+///
+/// \param[out] translatedIndex resulting input tensor index
+/// \param[in] sourceIndex current output element
+/// \param[in] kernelIndex current kernel element
+/// \param[in] strides
+/// \param[in] paddings
+void translate(Index &translatedIndex, const Index &sourceIndex, const Index &kernelIndex,
+               const Index &strides, const Index &paddings);
+
+} // namespace impl
+} // namespace interpreter
+} // namespace backend
+} // namespace contrib
+} // namespace nncc
diff --git a/contrib/nnc/libs/backend/interpreter/core/include/interpreter/ops/conv_2D.h b/contrib/nnc/libs/backend/interpreter/core/include/interpreter/ops/conv_2D.h
new file mode 100644 (file)
index 0000000..2635664
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL
+
+#include "interpreter/ops/OperationImpl.h"
+#include "nnc/core/IR/model/operations/conv_2d_op.h"
+
+namespace nncc
+{
+namespace contrib
+{
+namespace backend
+{
+namespace interpreter
+{
+namespace impl
+{
+using nncc::contrib::core::IR::model::ops::Conv2DOp;
+using nncc::contrib::core::IR::model::ops::PaddingType;
+
+class Conv2D : public OperationImpl<float>
+{
+public:
+  explicit Conv2D(const TensorVariant &input, const Conv2DOp &op);
+  std::vector<TensorVariant> operator()() override;
+
+private:
+  const Tensor<float> _input;
+  Tensor<float> _kernel;
+  const Shape _strides;
+  const PaddingType _padding;
+  const Shape &_out_shape;
+  const Conv2DOp &_op;
+};
+
+} // namespace impl
+} // namespace interpreter
+} // namespace backend
+} // namespace contrib
+} // namespace nncc
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL
+
diff --git a/contrib/nnc/libs/backend/interpreter/core/src/ops/common.cpp b/contrib/nnc/libs/backend/interpreter/core/src/ops/common.cpp
new file mode 100644 (file)
index 0000000..0f2c174
--- /dev/null
@@ -0,0 +1,29 @@
+#include "interpreter/ops/common.h"
+
+namespace nncc
+{
+namespace contrib
+{
+namespace backend
+{
+namespace interpreter
+{
+namespace impl
+{
+
+void translate(Index &translatedIndex, const Index &sourceIndex, const Index &kernelIndex,
+               const Shape &strides, const Index &paddings)
+{
+  assert(translatedIndex.rank() == sourceIndex.rank());
+
+  for (uint32_t d = 0; d < outputIndex.rank(); ++d)
+  {
+    translatedIndex.at(d) = sourceIndex.at(d) * strides.dim(d) + kernelIndex.at(d) - paddings.at(d);
+  }
+}
+
+} // namespace impl
+} // namespace interpreter
+} // namespace backend
+} // namespace contrib
+} // namespace nncc
diff --git a/contrib/nnc/libs/backend/interpreter/core/src/ops/conv_2D.cpp b/contrib/nnc/libs/backend/interpreter/core/src/ops/conv_2D.cpp
new file mode 100644 (file)
index 0000000..21bfa02
--- /dev/null
@@ -0,0 +1,96 @@
+#include <cmath>
+
+#include "nnc/core/linalg/ShapeRange.h"
+
+#include "interpreter/ops/conv_2D.h"
+#include "interpreter/ops/common.h"
+
+namespace nncc
+{
+namespace contrib
+{
+namespace backend
+{
+namespace interpreter
+{
+namespace impl
+{
+
+using namespace nncc::core::ADT::tensor;
+
+Index reduce(const Index &idx)
+{
+  Index res = idx;
+  res.resize(idx.rank() - 1);
+  return res;
+}
+
+// Mostly compatible with tensorflow implementation
+// Assuming input is in NHWC format with batch omitted( [in_height, in_width, in_channels] )
+// Kernel is in [filter_height, filter_width, in_channels, out_channels]
+// Refer to https://www.tensorflow.org/api_docs/python/tf/nn/conv2d for info
+std::vector<TensorVariant> Conv2D::operator()()
+{
+  auto res = allocate_tensor(_out_shape);
+  Tensor<float> resAccesor(res);
+  Index pads({(uint32_t)_op.getPadding(0), (uint32_t)_op.getPadding(1), 0u});
+
+  Shape outShape = resAccesor.getShape();
+  outShape.dim(2) = 1;
+  ShapeRange outRange(outShape);
+
+  const Shape& inShape = _input.getShape();
+  ShapeRange inRange(_input.getShape());
+
+  Shape kShape = _kernel.getShape();
+  uint32_t numKernels = kShape.dim(3);
+  kShape.dim(3) = 1;
+  ShapeRange kernelRange(kShape);
+
+  Index inputIdx;
+  inputIdx.resize(inShape.rank());
+
+  for (auto &outIdx : outRange)
+  {
+    for (auto& kernelIdx : kernelRange)
+    {
+      translate(inputIdx, outIdx, kernelIdx, _strides, pads);
+
+      if (inRange.contains(inputIdx))
+      {
+        auto kernelRegion = _kernel.getRegion(kernelIdx);
+        assert( kernelRegion.size() == numKernels );
+
+        auto outRegion = resAccesor.getRegion(outIdx);
+        assert( outRegion.size() == numKernels );
+
+        auto in = _input.at(inputIdx);
+
+        for (uint32_t kernelIndex = 0; kernelIndex < numKernels; ++kernelIndex)
+        {
+          outRegion.base()[kernelIndex] += in * kernelRegion.base()[kernelIndex];
+        }
+      }
+    }
+  }
+
+  return {res};
+}
+
+Conv2D::Conv2D(const TensorVariant &input, const Conv2DOp &op)
+    : _input(input), _kernel(op.getKernel()), _strides(op.getStrides()),
+      _padding(op.getPaddingType()), _out_shape(op.getOutputShape(0)), _op(op)
+{
+
+  assert(_op.getInputShape(0).rank() == 3);
+  assert(input.getShape().rank() == 3);
+  assert(_kernel.getShape().rank() == 4);
+  assert(_strides.dim(2) == 1);
+  assert(_op.getPadding(2) == 0);
+}
+
+} // namespace impl
+} // namespace interpreter
+} // namespace backend
+} // namespace contrib
+} // namespace nncc