[Pure ACL] Partially Support Reshape Operation (#709)
author박종현/동작제어Lab(SR)/Senior Engineer/삼성전자 <jh1302.park@samsung.com>
Mon, 16 Apr 2018 06:16:00 +0000 (15:16 +0900)
committer김정현/동작제어Lab(SR)/Senior Engineer/삼성전자 <jh0822.kim@samsung.com>
Mon, 16 Apr 2018 06:16:00 +0000 (15:16 +0900)
This commit implements partial support over reshape operation.

Signed-off-by: Jonghyun Park <jh1302.park@samsung.com>
tools/nnapi_bindings/bindings/pure_arm_compute/src/compilation.cc
tools/nnapi_bindings/bindings/pure_arm_compute/src/execution.cc
tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/NodeVisitor.h
tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/Reshape.cc [new file with mode: 0644]
tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/Reshape.h [new file with mode: 0644]
tools/nnapi_bindings/bindings/pure_arm_compute/src/model.cc

index 2242803..5b78e19 100644 (file)
@@ -7,6 +7,7 @@
 #include <arm_compute/runtime/CL/CLSubTensor.h>
 #include <arm_compute/runtime/CL/functions/CLPoolingLayer.h>
 #include <arm_compute/runtime/CL/functions/CLActivationLayer.h>
+#include <arm_compute/runtime/CL/functions/CLReshapeLayer.h>
 
 #include "internal/arm_compute/kernel/View.h"
 #include "internal/nnapi/kernel/Reader.h"
@@ -235,6 +236,7 @@ public:
   void visit(const ::internal::tflite::op::MaxPool2D::implicit::Node &node) override;
   void visit(const ::internal::tflite::op::AvgPool2D::implicit::Node &node) override;
   void visit(const ::internal::tflite::op::Concat::Node &node) override;
+  void visit(const ::internal::tflite::op::Reshape::Node &node) override;
 
 private:
   const ::internal::tflite::operand::Set &_ctx;
@@ -593,6 +595,57 @@ void Planner::visit(const ::internal::tflite::op::Concat::Node &node)
   // NOTE Concat has no actual operation!
 }
 
+void Planner::visit(const ::internal::tflite::op::Reshape::Node &node)
+{
+  const ::internal::tflite::operand::Index output_index{node.param().output_index};
+  const ::internal::tflite::operand::Index input_index{node.param().input_index};
+
+  // NOTE The content of a tensor specified by shape_index should be aligned with
+  //      output tensor shape
+  // TODO Check consistency of ouput shape
+
+  // 'Feature Map' to 'Vector' reshape
+  assert(_ctx.at(input_index).shape().rank() == 4);
+  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);
+
+  _builder.addShapeConstr(output_index, asTensorInfo(out_size));
+  _builder.addShapeConstr(input_index, asTensorInfo(ifm_shape));
+
+  struct Param
+  {
+    int output_index;
+    int input_index;
+  };
+
+  Param param;
+
+  param.output_index = output_index.asInt();
+  param.input_index = input_index.asInt();
+
+  auto stage = [param] (const IAllocationContext &ctx, IExecutionBuilder &builder)
+  {
+    auto output_alloc = ctx.at(::internal::tflite::operand::Index{param.output_index});
+    auto input_alloc = ctx.at(::internal::tflite::operand::Index{param.input_index});
+
+    auto fn = make_layer<::arm_compute::CLReshapeLayer>();
+
+    fn->configure(input_alloc, output_alloc);
+
+    builder.append(std::move(fn));
+  };
+
+  _builder.addStage(stage);
+}
+
 class AllocationContext final : public IAllocationContext
 {
 public:
index 4bb12ef..d704d3b 100644 (file)
@@ -44,6 +44,37 @@ private:
 };
 
 //
+// VectorSink
+//
+class VectorSink final : public Sink
+{
+public:
+  VectorSink(const int32_t vlen, uint8_t *base, const size_t size) : _vlen{vlen}, _base{base}
+  {
+    assert(size >= _vlen * sizeof(float));
+  }
+
+public:
+  void pull(::arm_compute::ITensor &tensor) const override
+  {
+    float *base = reinterpret_cast<float *>(_base);
+
+    for (int32_t n = 0; n < _vlen; ++n)
+    {
+      auto from = reinterpret_cast<float *>(tensor.ptr_to_element(::arm_compute::Coordinates{n}));
+      auto into = base + n;
+
+      *into = *from;
+    }
+  }
+
+private:
+  const int32_t _vlen;
+  uint8_t * const _base;
+};
+
+
+//
 // FeatureSink
 //
 class FeatureSink final : public Sink
@@ -121,10 +152,26 @@ ANeuralNetworksExecution_setOutput(ANeuralNetworksExecution* execution,
   // NOTE The current implemenation assumes that every output is a feature map.
   // TODO Remove this assumption
   const auto operand_index = execution->plan().model().outputs.at(index);
-  const auto &operand_shape = operands.at(operand_index).shape().asFeature();
 
-  execution->sink<FeatureSink>(index,
-                               operand_shape, reinterpret_cast<uint8_t *>(buffer), length);
+  if (operands.at(operand_index).shape().rank() == 2)
+  {
+    assert(operands.at(operand_index).shape().dim(0) == 1);
+
+    const auto len = operands.at(operand_index).shape().dim(1);
+
+    execution->sink<VectorSink>(index, len, reinterpret_cast<uint8_t *>(buffer), length);
+  }
+  else if (operands.at(operand_index).shape().rank() == 4)
+  {
+    const auto &operand_shape = operands.at(operand_index).shape().asFeature();
+
+    execution->sink<FeatureSink>(index,
+                                 operand_shape, reinterpret_cast<uint8_t *>(buffer), length);
+  }
+  else
+  {
+    throw std::runtime_error{"Not supported, yet"};
+  }
 
   return ANEURALNETWORKS_NO_ERROR;
 }
index ba7ee3a..51a4d11 100644 (file)
@@ -5,6 +5,7 @@
 #include "internal/op/MaxPool2D.h"
 #include "internal/op/AvgPool2D.h"
 #include "internal/op/Concat.h"
+#include "internal/op/Reshape.h"
 
 namespace internal
 {
@@ -21,6 +22,7 @@ struct NodeVisitor
   virtual void visit(const MaxPool2D::implicit::Node &) = 0;
   virtual void visit(const AvgPool2D::implicit::Node &) = 0;
   virtual void visit(const Concat::Node &) = 0;
+  virtual void visit(const Reshape::Node &) = 0;
 };
 
 } // namespace op
diff --git a/tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/Reshape.cc b/tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/Reshape.cc
new file mode 100644 (file)
index 0000000..9dcf8b2
--- /dev/null
@@ -0,0 +1,49 @@
+#include "internal/op/Reshape.h"
+#include "internal/op/NodeVisitor.h"
+
+#include <cassert>
+
+namespace internal
+{
+namespace tflite
+{
+namespace op
+{
+namespace Reshape
+{
+
+void Node::accept(NodeVisitor &&v) const { v.visit(*this); }
+
+} // namespace Reshape
+} // namespace op
+} // namespace tflite
+} // namespace internal
+
+namespace internal
+{
+namespace tflite
+{
+namespace op
+{
+namespace Reshape
+{
+
+Param::Param(uint32_t inputCount, const uint32_t* inputs,
+             uint32_t outputCount, const uint32_t* outputs)
+{
+  assert(inputCount == 2 && outputCount == 1);
+
+  output_index = outputs[0];
+
+  // Each input should be interpreted as follows:
+  //
+  //  0 -> A tensor, specifying the tensor to be reshaped.
+  //  1 -> A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32, defining the shape of the output tensor
+  input_index = inputs[0];
+  shape_index = inputs[1];
+}
+
+} // namespace Reshape
+} // namespace op
+} // namespace tflite
+} // namespace internal
diff --git a/tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/Reshape.h b/tools/nnapi_bindings/bindings/pure_arm_compute/src/internal/op/Reshape.h
new file mode 100644 (file)
index 0000000..d591a80
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __INTERNAL_OP_RESHAPE_H__
+#define __INTERNAL_OP_RESHAPE_H__
+
+#include "internal/op/Node.h"
+
+#include <cstdint>
+
+namespace internal
+{
+namespace tflite
+{
+namespace op
+{
+namespace Reshape
+{
+
+struct Param
+{
+  int32_t output_index;
+
+  int32_t input_index;
+  int32_t shape_index;
+
+  Param() = default;
+  Param(uint32_t inputCount, const uint32_t* inputs,
+        uint32_t outputCount, const uint32_t* outputs);
+};
+
+class Node final : public op::Node
+{
+public:
+  Node(const Param &param) : _param(param)
+  {
+    // DO NOTHING
+  }
+
+public:
+  virtual ~Node() = default;
+
+public:
+  const Param &param(void) const { return _param; }
+
+public:
+  void accept(NodeVisitor &&) const override;
+
+private:
+  const Param _param;
+};
+
+} // namespace Reshape
+} // namespace op
+} // namespace tflite
+} // namespace internal
+
+#endif // __INTERNAL_OP_RESHAPE_H__
index 37fb6b8..a394bb0 100644 (file)
@@ -170,6 +170,18 @@ ANeuralNetworksModel_addOperation(ANeuralNetworksModel* model,
 
       break;
     }
+    case ANEURALNETWORKS_RESHAPE:
+    {
+      using internal::tflite::op::Reshape::Param;
+      using internal::tflite::op::Reshape::Node;
+
+      // Add 'operations'
+      auto &operations = model->deref().operations();
+
+      operations.emplace_back<Node>(Param{inputCount, inputs, outputCount, outputs});
+
+      break;
+    }
     default:
       throw std::runtime_error{"Not supported operation"};
   };