[neurun] Introduce ConstantInitializer (#3426)
author이한종/동작제어Lab(SR)/Engineer/삼성전자 <hanjoung.lee@samsung.com>
Wed, 7 Nov 2018 07:02:25 +0000 (16:02 +0900)
committer오형석/동작제어Lab(SR)/Staff Engineer/삼성전자 <hseok82.oh@samsung.com>
Wed, 7 Nov 2018 07:02:25 +0000 (16:02 +0900)
* [neurun] Introduce ConstantInitializer

This commit introduces `ConstantIntializer` class that fills operand
objects with the constant values which were specified by
`ANeuralNetworksModel_setOperandValue`. Plus, this class replaces the
InitializerGenerator.

NOTE due to FullyConnected operation's implicit reshaping, it is
implemented with workarounds.

Signed-off-by: Hanjoung Lee <hanjoung.lee@samsung.com>
* Add license

Signed-off-by: Hanjoung Lee <hanjoung.lee@samsung.com>
runtimes/neurun/src/codegen/ConstantInitializer.cc [new file with mode: 0644]
runtimes/neurun/src/codegen/ConstantInitializer.h [new file with mode: 0644]
runtimes/neurun/src/codegen/PlanBuilder.cc
runtimes/neurun/src/frontend/wrapper/compilation.cc

diff --git a/runtimes/neurun/src/codegen/ConstantInitializer.cc b/runtimes/neurun/src/codegen/ConstantInitializer.cc
new file mode 100644 (file)
index 0000000..e653120
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+#include "ConstantInitializer.h"
+
+#include <arm_compute/core/Coordinates.h>
+
+#include "backend/interface/operand/IObject.h"
+#include "backend/interface/IConfig.h"
+#include "backend/acl_cl/kernel/View.h"
+#include "backend/BackendManager.h"
+#include "internal/nnapi/kernel/Reader.h"
+#include "internal/nnapi/kernel/View.h"
+#include "util/kernel/IndexIterator.h"
+#include "graph/operation/FullyConnected.h"
+#include "logging.h"
+
+namespace neurun
+{
+namespace codegen
+{
+
+ConstantInitializer::ConstantInitializer(const graph::Graph &graph, Plan &plan)
+    : _graph{graph}, _plan{plan}
+{
+}
+
+void ConstantInitializer::operator()()
+{
+  // Fill operand data
+  _plan.operands().iterate([&](int ind, neurun::backend::operand::IObject &obj) {
+    neurun::graph::operand::Index index(ind);
+    const auto &model_obj = _graph.operands().at(index);
+
+    // For only CONSTANTS
+    if (model_obj.getUsage() != neurun::graph::operand::OperandUsage::CONSTANT)
+      return;
+
+    // Only float32 is supported
+    auto type = model_obj.typeInfo().type();
+    if (type != ::neurun::graph::operand::DataType::TENSOR_FLOAT32)
+      throw std::runtime_error{"Unsupported data type. Only TENSOR_FLOAT32 is supported."};
+
+    VERBOSE(FillOperandData) << "Fill data for operand " << ind << std::endl;
+
+    auto layout =
+        model_obj.lower_info()->def_backends().getOnlyElement()->config()->getOperandLayout();
+    const auto shape = model_obj.shape();
+    auto base = model_obj.data().base();
+    auto size = model_obj.data().size();
+
+    obj.access([&](::arm_compute::ITensor &tensor) {
+      switch (shape.rank())
+      {
+        case 1:
+        {
+          auto vec_size = shape.asVector();
+          for (int32_t n = 0; n < vec_size; ++n)
+          {
+            const ::arm_compute::Coordinates coordinate{n};
+
+            float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+            const float *from = reinterpret_cast<const float *>(base) + n;
+            const auto value = *from;
+
+            *into = value;
+          }
+          break;
+        }
+        case 2:
+        {
+          // NOTE This is a WORKAROUND which supports FullyConnected weight only
+          //      For FullyConnected, we must know the IFM shape to deduce 2D weight shape from 4D
+          //      IFM.
+          //      This is because of NHWC/NCHW layout, the order of mapping will be different.
+          // TODO Support general case - explicitly insert Reshape op for IFM as 2D
+
+          // Find corresponding FullyConnected IFM
+          auto operation_index = _graph.operands().at(index).getUses().list().front();
+          auto operation = &_graph.operations().at(operation_index);
+          auto fc_operation =
+              dynamic_cast<const neurun::graph::operation::FullyConnected::Node *>(operation);
+
+          if (fc_operation == nullptr)
+            break;
+
+          auto ifm_index =
+              fc_operation->getInputs().at(neurun::graph::operation::FullyConnected::Input::INPUT);
+          const auto &ifm = _graph.operands().at(ifm_index);
+          const auto ifm_shape = ifm.shape().asFeature();
+          const auto num_output = shape.dim(0);
+
+          const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H,
+                                                      ifm_shape.W};
+          const ::internal::nnapi::kernel::Reader<float> from{ker_shape, base, size};
+
+          if (layout == neurun::graph::operand::Layout::NHWC)
+          {
+            ::nnfw::util::kernel::iterate(ker_shape)
+                << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+                     const auto value = from.at(nth, ch, row, col);
+
+                     uint32_t offset = 0;
+
+                     // NNAPI uses NHWC ordering
+                     offset += nth * ifm_shape.H * ifm_shape.W * ifm_shape.C;
+                     offset += row * ifm_shape.W * ifm_shape.C;
+                     offset += col * ifm_shape.C;
+                     offset += ch;
+
+                     const ::arm_compute::Coordinates coordinate{offset};
+
+                     auto into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+                     *into = value;
+                   };
+          }
+          else
+          {
+            assert(layout == neurun::graph::operand::Layout::NCHW);
+
+            ::nnfw::util::kernel::iterate(ker_shape)
+                << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+                     const auto value = from.at(nth, ch, row, col);
+
+                     uint32_t offset = 0;
+
+                     // 'NCHW' ordering
+                     offset += nth * ifm_shape.C * ifm_shape.H * ifm_shape.W;
+                     offset += ch * ifm_shape.H * ifm_shape.W;
+                     offset += row * ifm_shape.W;
+                     offset += col;
+
+                     const ::arm_compute::Coordinates coordinate{offset};
+
+                     auto into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+                     *into = value;
+                   };
+          }
+
+          break;
+        }
+        case 4:
+        {
+          auto ker_shape = shape.asKernel();
+          auto from = ::internal::nnapi::kernel::Reader<float>{ker_shape, base, size};
+
+          if (layout == neurun::graph::operand::Layout::NHWC)
+          {
+            auto into = ::internal::nnapi::kernel::View<float>{&tensor};
+
+            ::nnfw::util::kernel::iterate(ker_shape)
+                << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+                     const auto value = from.at(nth, ch, row, col);
+                     into.at(nth, row, col, ch) = value;
+                   };
+          }
+          else
+          {
+            assert(layout == neurun::graph::operand::Layout::NCHW);
+
+            auto into = ::internal::arm_compute::kernel::View<float>{&tensor};
+
+            ::nnfw::util::kernel::iterate(ker_shape)
+                << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+                     const auto value = from.at(nth, ch, row, col);
+                     into.at(nth, ch, row, col) = value;
+                   };
+          }
+          break;
+        }
+        default:
+          throw std::runtime_error{"Not yet supported"};
+      }
+    });
+  });
+}
+
+} // namespace codegen
+} // namespace neurun
diff --git a/runtimes/neurun/src/codegen/ConstantInitializer.h b/runtimes/neurun/src/codegen/ConstantInitializer.h
new file mode 100644 (file)
index 0000000..6e4de87
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 __NEURUN_CODEGEN_CONSTANT_INITIALIZER_H__
+#define __NEURUN_CODEGEN_CONSTANT_INITIALIZER_H__
+
+#include "graph/Graph.h"
+#include "codegen/Plan.h"
+
+namespace neurun
+{
+namespace codegen
+{
+
+class ConstantInitializer
+{
+public:
+  ConstantInitializer(const graph::Graph &graph, Plan &plan);
+
+  void operator()();
+
+private:
+  const graph::Graph &_graph;
+  Plan &_plan;
+};
+
+} // namespace codegen
+} // namespace neurun
+
+#endif // __NEURUN_CODEGEN_CONSTANT_INITIALIZER_H__
index 575cf78..76187c7 100644 (file)
@@ -70,15 +70,6 @@ void PlanBuilder::finalize(const backend::TensorBuilderSet &tensor_builders)
   {
     tensor_builder->allocate();
   }
-
-  // Fill weight/bias
-  for (auto it = _initializer_ctx.begin(); it != _initializer_ctx.end(); ++it)
-  {
-    const ::neurun::graph::operand::Index operand_index{it->first};
-    auto object = _plan.operands().at(operand_index);
-
-    object->access(it->second);
-  }
 }
 
 } // namepsace codegen
index 5f448d0..13f4fda 100644 (file)
@@ -23,6 +23,7 @@
 #include "codegen/Planner.h"
 #include "codegen/PlanBuilder.h"
 #include "middleend/SubTensorAnalyzer.h"
+#include "codegen/ConstantInitializer.h"
 
 #include "linear/Linear.h"
 #include "util/EnvVar.h"
@@ -72,5 +73,7 @@ int ANeuralNetworksCompilation::finish()
   // TODO Add optimization passes
   plan_builder.finalize(tensor_builders);
 
+  neurun::codegen::ConstantInitializer{graph, plan}();
+
   return ANEURALNETWORKS_NO_ERROR;
 }