[neurun] Unify converting functions. (#2806)
author장지섭/동작제어Lab(SR)/Engineer/삼성전자 <jiseob.jang@samsung.com>
Tue, 2 Oct 2018 01:22:28 +0000 (10:22 +0900)
committer오형석/동작제어Lab(SR)/Staff Engineer/삼성전자 <hseok82.oh@samsung.com>
Tue, 2 Oct 2018 01:22:28 +0000 (10:22 +0900)
This commit unifies converting(to ACL) functions.
- Bring Swizzle.h from pure_arm_compute.
- Bring converting functions from pure_arm_compute.
- Modify applying the function.

Signed-off-by: jiseob.jang <jiseob.jang@samsung.com>
runtimes/neurun/src/codegen/Planner.cc
runtimes/neurun/src/internal/Convert.cc
runtimes/neurun/src/internal/Convert.h
runtimes/neurun/src/internal/Swizzle.h [new file with mode: 0644]

index fb2aba1..b3c4ed7 100644 (file)
@@ -38,16 +38,15 @@ void Planner::visit(const graph::operation::Conv2D::Implicit::Node &node)
   const auto ker_index = node.getInputs().at(1);
   const auto bias_index = node.getInputs().at(2);
 
-  const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
-  const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
-  const auto ker_shape = _ctx.at(ker_index).shape().asKernel();
-  const auto bias_size = _ctx.at(bias_index).shape().asVector();
-
   // Set Shape Constraints
-  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(ofm_shape));
-  _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(ifm_shape));
-  _builder.addShapeConstr(ker_index, ::internal::asTensorInfo(ker_shape));
-  _builder.addShapeConstr(bias_index, ::internal::asTensorInfo(bias_size));
+  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(_ctx.at(ofm_index).shape(),
+                                                              _ctx.at(ofm_index).typeInfo()));
+  _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(_ctx.at(ifm_index).shape(),
+                                                              _ctx.at(ifm_index).typeInfo()));
+  _builder.addShapeConstr(ker_index, ::internal::asTensorInfo(_ctx.at(ker_index).shape(),
+                                                              _ctx.at(ker_index).typeInfo()));
+  _builder.addShapeConstr(bias_index, ::internal::asTensorInfo(_ctx.at(bias_index).shape(),
+                                                               _ctx.at(bias_index).typeInfo()));
 
   // backend
   auto backend = node.lower_info()->backend();
@@ -67,12 +66,11 @@ void Planner::visit(const graph::operation::MaxPool2D::Implicit::Node &node)
   const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
   const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
 
-  const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
-  const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
-
   // Set Shape Constraints
-  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(ofm_shape));
-  _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(ifm_shape));
+  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(_ctx.at(ofm_index).shape(),
+                                                              _ctx.at(ofm_index).typeInfo()));
+  _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(_ctx.at(ifm_index).shape(),
+                                                              _ctx.at(ifm_index).typeInfo()));
 
   // backend
   auto backend = node.lower_info()->backend();
@@ -87,12 +85,11 @@ void Planner::visit(const graph::operation::AvgPool2D::Implicit::Node &node)
   const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
   const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
 
-  const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
-  const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
-
   // Set Shape Constraints
-  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(ofm_shape));
-  _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(ifm_shape));
+  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(_ctx.at(ofm_index).shape(),
+                                                              _ctx.at(ofm_index).typeInfo()));
+  _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(_ctx.at(ifm_index).shape(),
+                                                              _ctx.at(ifm_index).typeInfo()));
 
   // backend
   auto backend = node.lower_info()->backend();
@@ -106,24 +103,21 @@ void Planner::visit(const graph::operation::Concat::Node &node)
 {
   const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
 
-  // NOTE This implementation assumes that input and output are a feature
-  // TODO Remove this assumption
-  const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
-
   // NOTE This implementation assumes concat over feature depth
   // TODO Remove this assumption
   assert(_ctx.at(::neurun::graph::operand::Index{node.param().axis_index}).asScalar<int32_t>() ==
          3);
 
   // Set Shape Constraints (for output)
-  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(ofm_shape));
+  _builder.addShapeConstr(ofm_index, ::internal::asTensorInfo(_ctx.at(ofm_index).shape(),
+                                                              _ctx.at(ofm_index).typeInfo()));
 
   // Set Shape Constraints (for input)
   for (const auto &index : node.getInputs())
   {
     const ::neurun::graph::operand::Index ifm_index{index};
-    const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
-    _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(ifm_shape));
+    _builder.addShapeConstr(ifm_index, ::internal::asTensorInfo(_ctx.at(ifm_index).shape(),
+                                                                _ctx.at(ifm_index).typeInfo()));
   }
 
   // backend
@@ -146,26 +140,15 @@ void Planner::visit(const graph::operation::FullyConnected::Node &node)
 
   const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
 
-  assert(_ctx.at(output_index).shape().rank() == 2);
-  const auto output_size = _ctx.at(output_index).shape().dim(1);
-
-  // NOTE We assume that input is a feature map
-  // TODO Remove this restriction!
-  const auto ifm_shape = _ctx.at(input_index).shape().asFeature();
-
-  assert(_ctx.at(weight_index).shape().rank() == 2);
-  const auto num_output = _ctx.at(weight_index).shape().dim(0);
-  const auto input_size = _ctx.at(weight_index).shape().dim(1);
-  assert(ifm_shape.C * ifm_shape.H * ifm_shape.W == input_size);
-
-  const auto bias_size = _ctx.at(bias_index).shape().asVector();
-
   // Set Shape Constraints
-  _builder.addShapeConstr(output_index, ::internal::asTensorInfo(output_size));
-  _builder.addShapeConstr(input_index, ::internal::asTensorInfo(ifm_shape));
-  _builder.addShapeConstr(weight_index,
-                          ::internal::asTensorInfo(num_output /*H*/, input_size /*W*/));
-  _builder.addShapeConstr(bias_index, ::internal::asTensorInfo(bias_size));
+  _builder.addShapeConstr(output_index, ::internal::asTensorInfo(_ctx.at(output_index).shape(),
+                                                                 _ctx.at(output_index).typeInfo()));
+  _builder.addShapeConstr(input_index, ::internal::asTensorInfo(_ctx.at(input_index).shape(),
+                                                                _ctx.at(input_index).typeInfo()));
+  _builder.addShapeConstr(weight_index, ::internal::asTensorInfo(_ctx.at(weight_index).shape(),
+                                                                 _ctx.at(weight_index).typeInfo()));
+  _builder.addShapeConstr(bias_index, ::internal::asTensorInfo(_ctx.at(bias_index).shape(),
+                                                               _ctx.at(bias_index).typeInfo()));
 
   // backend
   auto backend = node.lower_info()->backend();
@@ -194,16 +177,17 @@ void Planner::visit(const graph::operation::Reshape::Node &node)
   assert(_ctx.at(output_index).shape().rank() == 2);
   assert(_ctx.at(output_index).shape().dim(0) == 1);
 
-  const auto ifm_shape = _ctx.at(input_index).shape().asFeature();
-  const auto out_size = _ctx.at(output_index).shape().dim(1);
-
   // NOTE Vector element ordering issue arises when H or W is not 1
-  assert(ifm_shape.H == 1);
-  assert(ifm_shape.W == 1);
-  assert((ifm_shape.C * ifm_shape.H * ifm_shape.W) == out_size);
+  assert(_ctx.at(input_index).shape().dim(1) == 1); // H
+  assert(_ctx.at(input_index).shape().dim(2) == 1); // W
+  // input(4D)'s C * H * W == output(2D)'s W
+  assert((_ctx.at(input_index).shape().dim(3) * _ctx.at(input_index).shape().dim(1) *
+          _ctx.at(input_index).shape().dim(2)) == _ctx.at(output_index).shape().dim(1));
 
-  _builder.addShapeConstr(output_index, ::internal::asTensorInfo(out_size));
-  _builder.addShapeConstr(input_index, ::internal::asTensorInfo(ifm_shape));
+  _builder.addShapeConstr(output_index, ::internal::asTensorInfo(_ctx.at(output_index).shape(),
+                                                                 _ctx.at(output_index).typeInfo()));
+  _builder.addShapeConstr(input_index, ::internal::asTensorInfo(_ctx.at(input_index).shape(),
+                                                                _ctx.at(input_index).typeInfo()));
 
   // backend
   auto backend = node.lower_info()->backend();
@@ -228,10 +212,10 @@ void Planner::visit(const graph::operation::Softmax::Node &node)
   assert(_ctx.at(input_index).shape().dim(0) == _ctx.at(output_index).shape().dim(0));
   assert(_ctx.at(input_index).shape().dim(1) == _ctx.at(output_index).shape().dim(1));
 
-  const uint32_t len = _ctx.at(output_index).shape().dim(1);
-
-  _builder.addShapeConstr(output_index, ::internal::asTensorInfo(len));
-  _builder.addShapeConstr(input_index, ::internal::asTensorInfo(len));
+  _builder.addShapeConstr(output_index, ::internal::asTensorInfo(_ctx.at(output_index).shape(),
+                                                                 _ctx.at(output_index).typeInfo()));
+  _builder.addShapeConstr(input_index, ::internal::asTensorInfo(_ctx.at(input_index).shape(),
+                                                                _ctx.at(input_index).typeInfo()));
 
   // backend
   auto backend = node.lower_info()->backend();
index c0260b0..170fd26 100644 (file)
 
 #include "Convert.h"
 
+#include "internal/Swizzle.h"
+#include "graph/operand/DataType.h"
+
 namespace internal
 {
 
-::arm_compute::TensorShape asTensorShape(int32_t h, int32_t w)
+::arm_compute::TensorShape asTensorShape(const ::neurun::graph::operand::Shape &shape,
+                                         bool apply_dim_correction = true)
 {
-  return ::arm_compute::TensorShape(w, h);
-}
+  const uint32_t rank = shape.rank();
 
-::arm_compute::TensorShape asTensorShape(const nnfw::util::feature::Shape &shape)
-{
-  return ::arm_compute::TensorShape(shape.W, shape.H, shape.C, shape.N);
-}
+  ::arm_compute::TensorShape res{};
 
-::arm_compute::TensorShape asTensorShape(const nnfw::util::kernel::Shape &shape)
-{
-  return ::arm_compute::TensorShape(shape.W, shape.H, shape.C, shape.N);
-}
+  res.set_num_dimensions(rank);
 
-::arm_compute::TensorInfo asTensorInfo(const nnfw::util::feature::Shape &shape)
-{
-  return ::arm_compute::TensorInfo(asTensorShape(shape), 1, ::arm_compute::DataType::F32);
+  for (uint32_t axis = 0; axis < rank; ++axis)
+  {
+    // NOTE In some cases, in incorrect dimensions is required.
+    // For example, intput_size is 1 in LSTM. The input-to-input weights([num_units, input_size]) of
+    // LSTM is used as the weight of the FullyConnected.
+    // The FullyConnected's weight must be greater or equal than 2-dimensions.
+    // However, if the dimension correction is applied to input_to_input_weights with input_size
+    // equal to 1, it will be changed to 1-D.
+    // So input_to_input_weights is not used by the weight of FullyConnected.
+    res.set(ToARMComputeAxis(rank, axis).value(), shape.dim(axis), apply_dim_correction);
+  }
+
+  return res;
 }
 
-::arm_compute::TensorInfo asTensorInfo(const nnfw::util::kernel::Shape &shape)
+::arm_compute::DataType asDataType(const ::neurun::graph::operand::DataType &type)
 {
-  return ::arm_compute::TensorInfo(asTensorShape(shape), 1, ::arm_compute::DataType::F32);
+  switch (type)
+  {
+    case ::neurun::graph::operand::DataType::SCALAR_FLOAT32:
+    case ::neurun::graph::operand::DataType::TENSOR_FLOAT32:
+      return ::arm_compute::DataType::F32;
+    case ::neurun::graph::operand::DataType::SCALAR_INT32:
+    case ::neurun::graph::operand::DataType::TENSOR_INT32:
+      return ::arm_compute::DataType::S32;
+    case ::neurun::graph::operand::DataType::SCALAR_UINT32:
+      return ::arm_compute::DataType::U32;
+    case ::neurun::graph::operand::DataType::TENSOR_QUANT8_ASYMM:
+      return ::arm_compute::DataType::QASYMM8;
+    default:
+      throw std::runtime_error("Not supported, yet");
+      break;
+  }
 }
 
-::arm_compute::TensorInfo asTensorInfo(int32_t size)
+::arm_compute::QuantizationInfo asQuantizationInfo(const float scale, const int32_t offset)
 {
-  return ::arm_compute::TensorInfo(::arm_compute::TensorShape(size), 1,
-                                   ::arm_compute::DataType::F32);
+  return ::arm_compute::QuantizationInfo(scale, offset);
 }
 
-::arm_compute::TensorInfo asTensorInfo(int32_t h, int32_t w)
+::arm_compute::TensorInfo asTensorInfo(const ::neurun::graph::operand::Shape &shape,
+                                       const ::neurun::graph::operand::TypeInfo &typeInfo)
 {
-  return ::arm_compute::TensorInfo(::arm_compute::TensorShape(w, h), 1,
-                                   ::arm_compute::DataType::F32);
+  return ::arm_compute::TensorInfo(asTensorShape(shape), 1, asDataType(typeInfo.type()),
+                                   asQuantizationInfo(typeInfo.scale(), typeInfo.offset()));
 }
 
 } // namespace internal
index f279133..00a0278 100644 (file)
 #include <arm_compute/core/TensorInfo.h>
 #include <arm_compute/core/TensorShape.h>
 
+#include "graph/operand/Shape.h"
+#include "graph/operand/TypeInfo.h"
 #include "util/feature/Shape.h"
 #include "util/kernel/Shape.h"
 
 namespace internal
 {
 
-::arm_compute::TensorShape asTensorShape(int32_t h, int32_t w);
-::arm_compute::TensorShape asTensorShape(const nnfw::util::feature::Shape &shape);
-::arm_compute::TensorShape asTensorShape(const nnfw::util::kernel::Shape &shape);
-
-::arm_compute::TensorInfo asTensorInfo(const nnfw::util::feature::Shape &shape);
-::arm_compute::TensorInfo asTensorInfo(const nnfw::util::kernel::Shape &shape);
-::arm_compute::TensorInfo asTensorInfo(int32_t size);
-::arm_compute::TensorInfo asTensorInfo(int32_t h, int32_t w);
+::arm_compute::TensorInfo asTensorInfo(const ::neurun::graph::operand::Shape &shape,
+                                       const ::neurun::graph::operand::TypeInfo &typeInfo);
 
 } // namespace internal
 
diff --git a/runtimes/neurun/src/internal/Swizzle.h b/runtimes/neurun/src/internal/Swizzle.h
new file mode 100644 (file)
index 0000000..eb982a9
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * 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 __INTERNAL_SWIZZLE_H__
+#define __INTERNAL_SWIZZLE_H__
+
+#include <cassert>
+
+namespace internal
+{
+
+class ARMComputeAxis
+{
+public:
+  ARMComputeAxis() = default;
+
+public:
+  explicit ARMComputeAxis(uint32_t value) : _value{value}
+  {
+    // DO NOTHING
+  }
+
+public:
+  uint32_t value(void) const { return _value; }
+
+private:
+  uint32_t _value;
+};
+
+// Convert T/F Lite / NNAPI axis (based on ...NHWC) to ARMCompute axis (WHCN...)
+inline ARMComputeAxis ToARMComputeAxis(uint32_t rank, uint32_t axis)
+{
+  assert(rank > axis);
+  const ARMComputeAxis reversed{(rank - axis) - 1};
+
+  if (rank < 4)
+  {
+    return reversed;
+  }
+
+  // DEPTH
+  if (0 == reversed.value())
+  {
+    return ARMComputeAxis{2};
+  }
+  // WIDTH
+  if (1 == reversed.value())
+  {
+    return ARMComputeAxis{0};
+  }
+  // HEIGHT
+  if (2 == reversed.value())
+  {
+    return ARMComputeAxis{1};
+  }
+
+  // ELSE
+  return reversed;
+}
+
+template <typename T> inline T ReorderBits(T in, size_t numOfBits)
+{
+  assert(numOfBits > 0);
+  T out = 0;
+  for (int32_t i = numOfBits - 1; i >= 0; --i)
+  {
+    const uint32_t toShift = numOfBits - ToARMComputeAxis(numOfBits, i).value() - 1;
+    out += ((in & 1) << toShift);
+    in >>= 1;
+  }
+  return out;
+}
+
+} // namespace internal
+
+#endif // __INTERNAL_SWIZZLE_H__