From 8fa35a19ff51d28c5d8308619e25f2e27d3cf965 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EC=9C=A4=ED=98=84=EC=8B=9D/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Principal=20Engineer/=EC=82=BC=EC=84=B1?= =?utf8?q?=EC=A0=84=EC=9E=90?= Date: Thu, 26 Jul 2018 07:05:23 +0900 Subject: [PATCH] [Pure ACL] Squeeze operation (#2038) This commit introduces Squeeze operation. 2D, 3D are tested with generated test. Note that 4D test (squeeze.mod.py) does not work. 4D will be worked as a separate PR. Signed-off-by: Hyun Sik Yoon --- runtimes/pure_arm_compute/src/compilation.cc | 48 +++++++++++++++++++ .../pure_arm_compute/src/internal/op/NodeVisitor.h | 2 + .../pure_arm_compute/src/internal/op/Squeeze.cc | 50 ++++++++++++++++++++ .../pure_arm_compute/src/internal/op/Squeeze.h | 54 ++++++++++++++++++++++ runtimes/pure_arm_compute/src/model.cc | 12 +++++ 5 files changed, 166 insertions(+) create mode 100644 runtimes/pure_arm_compute/src/internal/op/Squeeze.cc create mode 100644 runtimes/pure_arm_compute/src/internal/op/Squeeze.h diff --git a/runtimes/pure_arm_compute/src/compilation.cc b/runtimes/pure_arm_compute/src/compilation.cc index e17ddb8..d697757 100644 --- a/runtimes/pure_arm_compute/src/compilation.cc +++ b/runtimes/pure_arm_compute/src/compilation.cc @@ -352,6 +352,7 @@ public: void visit(const ::internal::tflite::op::FullyConnected::Node &node) override; void visit(const ::internal::tflite::op::ResizeBilinear::Node &node) override; void visit(const ::internal::tflite::op::Reshape::Node &node) override; + void visit(const ::internal::tflite::op::Squeeze::Node &node) override; void visit(const ::internal::tflite::op::Softmax::Node &node) override; void visit(const ::internal::tflite::op::StridedSlice::Node &node) override; void visit(const ::internal::tflite::op::ReduceMax::Node &node) override; @@ -2057,6 +2058,53 @@ void Planner::visit(const ::internal::tflite::op::Reshape::Node &node) _builder.addStage(stage); } +void Planner::visit(const ::internal::tflite::op::Squeeze::Node &node) +{ + // node.param().dims_index_optional is ignored since output tensor already has squeezed shape + // by freezer and toco + const ::internal::tflite::operand::Index output_index{node.param().output_index}; + const ::internal::tflite::operand::Index input_index{node.param().input_index}; + + // Currently, 3D-input with dims is tested. Note that param(). dims_index_optional is optional. + // two generated test passed: + // - 3D input : squeeze_float_1 + // - 2D input : squeeze_3D_float_1 + // - 4D input fails (squeeze.mod.py) -> we need general tensor support + assert(_ctx.at(input_index).shape().rank() < 4); + + // TODO Support generic tensor shape + const auto output_shape = _ctx.at(output_index).shape(); + const auto input_shape = _ctx.at(input_index).shape(); + + // Set Shape Constraints + _builder.addShapeConstr(output_index, asTensorInfo(output_shape, _ctx.at(output_index).type())); + _builder.addShapeConstr(input_index, asTensorInfo(input_shape, _ctx.at(input_index).type())); + + // Construct operation parameters + 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 = nnfw::make_unique<::arm_compute::CLReshapeLayer>(); + fn->configure(input_alloc, output_alloc); + + builder.append("Squeeze", std::move(fn)); + }; + + _builder.addStage(stage); +} + void Planner::visit(const ::internal::tflite::op::Softmax::Node &node) { VERBOSE(Softmax) << "Configure SOFTMAX operation" << std::endl; diff --git a/runtimes/pure_arm_compute/src/internal/op/NodeVisitor.h b/runtimes/pure_arm_compute/src/internal/op/NodeVisitor.h index 6fc5e33..2884957 100644 --- a/runtimes/pure_arm_compute/src/internal/op/NodeVisitor.h +++ b/runtimes/pure_arm_compute/src/internal/op/NodeVisitor.h @@ -24,6 +24,7 @@ #include "internal/op/ReLU1.h" #include "internal/op/ReLU6.h" #include "internal/op/Tanh.h" +#include "internal/op/Squeeze.h" namespace internal { @@ -63,6 +64,7 @@ struct NodeVisitor virtual void visit(const ReLU1::Node &) = 0; virtual void visit(const ReLU6::Node &) = 0; virtual void visit(const Tanh::Node &) = 0; + virtual void visit(const Squeeze::Node &) = 0; }; } // namespace op diff --git a/runtimes/pure_arm_compute/src/internal/op/Squeeze.cc b/runtimes/pure_arm_compute/src/internal/op/Squeeze.cc new file mode 100644 index 0000000..8b20736 --- /dev/null +++ b/runtimes/pure_arm_compute/src/internal/op/Squeeze.cc @@ -0,0 +1,50 @@ +#include "internal/op/Squeeze.h" +#include "internal/op/NodeVisitor.h" + +#include + +namespace internal +{ +namespace tflite +{ +namespace op +{ +namespace Squeeze +{ + +void Node::accept(NodeVisitor &&v) const { v.visit(*this); } + +} // namespace Squeeze +} // namespace op +} // namespace tflite +} // namespace internal + +namespace internal +{ +namespace tflite +{ +namespace op +{ +namespace Squeeze +{ +// dims_index is optional input +// if dims_index is not provided, dims_index is set to -1 +Param::Param(uint32_t inputCount, const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs) +{ + assert(inputCount == 1 || inputCount == 2); + assert(outputCount == 1); + + output_index = outputs[0]; + + input_index = inputs[0]; + + // dims_index_optional = -1 by default + if (inputCount == 2) + dims_index_optional = inputs[1]; +} + +} // namespace Squeeze +} // namespace op +} // namespace tflite +} // namespace internal diff --git a/runtimes/pure_arm_compute/src/internal/op/Squeeze.h b/runtimes/pure_arm_compute/src/internal/op/Squeeze.h new file mode 100644 index 0000000..1ce793f --- /dev/null +++ b/runtimes/pure_arm_compute/src/internal/op/Squeeze.h @@ -0,0 +1,54 @@ +#ifndef __INTERNAL_OP_SQUEEZE_H__ +#define __INTERNAL_OP_SQUEEZE_H__ + +#include "internal/op/Node.h" + +#include + +namespace internal +{ +namespace tflite +{ +namespace op +{ +namespace Squeeze +{ + +struct Param +{ + int32_t output_index; + + int32_t input_index; + int32_t dims_index_optional = -1; // optional param. default is -1 + + 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 ¶m) : _param(param) + { + // DO NOTHING + } + +public: + virtual ~Node() = default; + +public: + const Param ¶m(void) const { return _param; } + +public: + void accept(NodeVisitor &&) const override; + +private: + const Param _param; +}; + +} // namespace Squeeze +} // namespace op +} // namespace tflite +} // namespace internal + +#endif // __INTERNAL_OP_SQUEEZE_H__ diff --git a/runtimes/pure_arm_compute/src/model.cc b/runtimes/pure_arm_compute/src/model.cc index ee308a5..dc478a4 100644 --- a/runtimes/pure_arm_compute/src/model.cc +++ b/runtimes/pure_arm_compute/src/model.cc @@ -308,6 +308,18 @@ int ANeuralNetworksModel_addOperation(ANeuralNetworksModel *model, break; } + case ANEURALNETWORKS_SQUEEZE: + { + using internal::tflite::op::Squeeze::Param; + using internal::tflite::op::Squeeze::Node; + + // Add 'operations' + auto &operations = model->deref().operations(); + + operations.emplace_back(Param{inputCount, inputs, outputCount, outputs}); + + break; + } case ANEURALNETWORKS_FULLY_CONNECTED: { using internal::tflite::op::FullyConnected::Param; -- 2.7.4