From e14fd4b1776d7dc14610080dd211e635ef73d063 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB=20=D0=98=D0=BB=D1=8C=D1=8E?= =?utf8?q?=D1=82=D1=87=D0=B5=D0=BD=D0=BA=D0=BE/AI=20Tools=20Lab=20/SRR/Eng?= =?utf8?q?ineer/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Mon, 17 Dec 2018 22:07:17 +0300 Subject: [PATCH] [nnc] Support Elementwise Add and Mul operations with weights array (#2628) * Add support Elementwise Add and Mul operations with weights array * Support constant operation in soft cpu backend * Remove ref type from InputParams and InputOps and add them to Signed-off-by: Pavel Iliutchenko Signed-off-by: Pavel Iliutchenko Signed-off-by: Pavel Iliutchenko * Add refs for all creates Signed-off-by: Pavel Iliutchenko --- contrib/nnc/core/modelIR/Graph.cpp | 8 ++++ contrib/nnc/include/core/modelIR/Graph.h | 6 +++ contrib/nnc/passes/soft_backend/ModelAnalyzer.cpp | 20 ++++++--- contrib/nnc/passes/soft_backend/ModelAnalyzer.h | 6 +-- .../soft_backend/code_snippets/cpp_operations.def | 18 ++++++++ .../passes/tflite_frontend/tflite_op_creator.cpp | 51 ++++++++++++++-------- .../nnc/passes/tflite_frontend/tflite_op_creator.h | 32 +++++++------- 7 files changed, 97 insertions(+), 44 deletions(-) diff --git a/contrib/nnc/core/modelIR/Graph.cpp b/contrib/nnc/core/modelIR/Graph.cpp index fd9eb87..462a712 100644 --- a/contrib/nnc/core/modelIR/Graph.cpp +++ b/contrib/nnc/core/modelIR/Graph.cpp @@ -128,6 +128,14 @@ std::vector Graph::collectInputs() const { return res; } +std::vector Graph::collectConstants() const { + std::vector res; + for (auto& e : _constants) { + res.emplace_back(e); + } + return res; +} + std::vector Graph::collectOutputs() const { std::vector res; for (auto& e : _outputs) { diff --git a/contrib/nnc/include/core/modelIR/Graph.h b/contrib/nnc/include/core/modelIR/Graph.h index edd227d..3456c19 100644 --- a/contrib/nnc/include/core/modelIR/Graph.h +++ b/contrib/nnc/include/core/modelIR/Graph.h @@ -62,6 +62,12 @@ class Graph { std::vector collectInputs() const; /** + * @brief Returns all constants from graph + * @returns vector containing all graph constant nodes + */ + std::vector collectConstants() const; + + /** * @brief Returns all outputs from graph * @returns vector containing all graph outputs nodes */ diff --git a/contrib/nnc/passes/soft_backend/ModelAnalyzer.cpp b/contrib/nnc/passes/soft_backend/ModelAnalyzer.cpp index a2ef5a4..7f278a0 100644 --- a/contrib/nnc/passes/soft_backend/ModelAnalyzer.cpp +++ b/contrib/nnc/passes/soft_backend/ModelAnalyzer.cpp @@ -67,11 +67,16 @@ void ModelAnalyzer::addOpDescr(Operation* op, const string& opName) { size_t nodeTid = INVALID_TENSOR_ID; if (op->getPrevNodes().empty()) { if (auto* p2const = dynamic_cast(op)) { - type = OpDescr::Type::CONSTANT; + type = OpDescr::Type::ORDINARY; + auto* shape = const_cast (&p2const->getOutputShape(0)); - nodeTid = allocateTensor(name, TensorDescription::Type::CONSTANT, shape); + /* + * FIXME allocateTensor get const Shape + */ + nodeTid = allocateTensor(name, TensorDescription::Type::ORDINARY, shape); } else { // process input op + assert(op->getType() == Operation::Type::variable); Shape inputShape = op->getOutputShape(0); nodeTid = allocateTensor(name, TensorDescription::Type::IN, &inputShape); type = OpDescr::Type::IN; @@ -90,10 +95,8 @@ void ModelAnalyzer::addOpDescr(Operation* op, const string& opName) { nodeOutputs.push_back(nodeTid); // process op outputs // consider op as output if it has no consumers - if (op->getNextNodes().empty() && (type != OpDescr::Type::CONSTANT)) { - assert(type == OpDescr::Type::OUT); + if (op->getNextNodes().empty() && (type == OpDescr::Type::OUT)) _outputs.push_back(nodeTid); - } // process op inputs vector nodeInputs; for (const IODescriptor &d: op->getPrevNodes()) { @@ -135,8 +138,13 @@ void ModelAnalyzer::analyze(const mir::Graph* g) { // Set contains pointer to node if it is visited by DFS set visited; + // Collect all inputs and constants + vector init_ops(g->collectInputs()); + vector constant_ops(g->collectConstants()); + init_ops.insert(init_ops.end(), constant_ops.begin(), constant_ops.end()); + // Walk all network inputs - for (Operation* in : g->collectInputs()) { + for (Operation* in : init_ops) { assert(dynamic_cast(in) || dynamic_cast(in)); if (!visited.count(in)) { visited.insert(in); diff --git a/contrib/nnc/passes/soft_backend/ModelAnalyzer.h b/contrib/nnc/passes/soft_backend/ModelAnalyzer.h index e01806e..04a56cb 100644 --- a/contrib/nnc/passes/soft_backend/ModelAnalyzer.h +++ b/contrib/nnc/passes/soft_backend/ModelAnalyzer.h @@ -45,8 +45,7 @@ struct TensorDescription { enum class Type { IN, OUT, - ORDINARY, - CONSTANT + ORDINARY }; size_t _id; Type _type; @@ -62,8 +61,7 @@ struct OpDescr { enum class Type { IN, OUT, - ORDINARY, - CONSTANT + ORDINARY }; Type _type; diff --git a/contrib/nnc/passes/soft_backend/code_snippets/cpp_operations.def b/contrib/nnc/passes/soft_backend/code_snippets/cpp_operations.def index 1547fe1..d837a9b 100644 --- a/contrib/nnc/passes/soft_backend/code_snippets/cpp_operations.def +++ b/contrib/nnc/passes/soft_backend/code_snippets/cpp_operations.def @@ -169,6 +169,20 @@ static bool isAddrAligned(const void *data, int alignment) return (reinterpret_cast(data) % alignment) == 0; } +static inline Tensor deserializeTensor(const char*& buf) +{ + int32_t d_type = deserializeT(buf); + assert(d_type == 1 && "Unknown data type"); + int32_t element_size = deserializeT(buf); + assert(element_size == 4 && "Unsupported element size"); + Shape shape = deserializeShape(buf); + const float* data = reinterpret_cast(buf); + + Tensor tensor(shape, const_cast(data)); + buf += element_size * shape.getNumElems(); + return tensor; +} + static inline Kernel deserializeKernel(const char *&buf) { int32_t dType = deserializeT(buf); @@ -655,3 +669,7 @@ void gather(Tensor &out, const char *params, const Tensor &data, const Tensor &i shapeToRuntimeShape(indices.getShape()), indices.getData(), shapeToRuntimeShape(out.getShape()), out.getData()); } + +void constant(Tensor& out, const char* params) { + out = deserializeTensor(params); +} diff --git a/contrib/nnc/passes/tflite_frontend/tflite_op_creator.cpp b/contrib/nnc/passes/tflite_frontend/tflite_op_creator.cpp index e7bf27b..488a47b 100644 --- a/contrib/nnc/passes/tflite_frontend/tflite_op_creator.cpp +++ b/contrib/nnc/passes/tflite_frontend/tflite_op_creator.cpp @@ -31,6 +31,7 @@ #include "core/modelIR/operations/ReluOp.h" #include "core/modelIR/operations/ReshapeOp.h" #include "core/modelIR/operations/ResizeOp.h" +#include "core/modelIR/operations/ScaleOp.h" #include "core/modelIR/operations/SigmoidOp.h" #include "core/modelIR/operations/SliceOp.h" #include "core/modelIR/operations/SoftmaxOp.h" @@ -38,13 +39,11 @@ #include "core/modelIR/operations/SqueezeOp.h" #include "core/modelIR/operations/TanhOp.h" -#include "core/modelIR/Tensor.h" -#include "core/modelIR/ShapeRange.h" #include "pass/PassException.h" -#include "core/modelIR/Tensor.h" #include "core/modelIR/Shape.h" #include "core/modelIR/ShapeRange.h" +#include "core/modelIR/Tensor.h" using namespace nnc::mir; using namespace ::tflite; @@ -85,7 +84,7 @@ void TFLiteOpCreator::checkConv2D(const Conv2DOptions* opts, checkActivationType(opts->fused_activation_function(), problems_op_set); } -std::vector TFLiteOpCreator::convertConv2D(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::convertConv2D(InputOps& inputs, InputParams& params, const Conv2DOptions* opts) { const auto& input_shape = inputs[0]->getOutputShape(0); const auto& kernel_shape = params[0].getShape(); @@ -108,7 +107,7 @@ void TFLiteOpCreator::checkDepthwiseConv2D(const DepthwiseConv2DOptions* opts, } std::vector -TFLiteOpCreator::convertDepthwiseConv2D(InputOps inputs, InputParams params, +TFLiteOpCreator::convertDepthwiseConv2D(InputOps& inputs, InputParams& params, const DepthwiseConv2DOptions* opts) { const auto& input_shape = inputs[0]->getOutputShape(0); const auto& kernel_shape = params[0].getShape(); @@ -131,8 +130,8 @@ void TFLiteOpCreator::checkConcatenation(const ConcatenationOptions* opts, checkActivationType(opts->fused_activation_function(), problems_op_set); } -std::vector TFLiteOpCreator::convertConcatenation(InputOps inputs, - InputParams params, +std::vector TFLiteOpCreator::convertConcatenation(InputOps& inputs, + InputParams& params, const ConcatenationOptions* opts) { std::vector descriptors; for (auto i : inputs) @@ -146,7 +145,7 @@ void TFLiteOpCreator::checkPool2D(const Pool2DOptions* opts, checkActivationType(opts->fused_activation_function(), problems_op_set); } -std::vector TFLiteOpCreator::convertMaxPool2D(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::convertMaxPool2D(InputOps& inputs, InputParams& params, const Pool2DOptions* opts) { auto& input_shape = inputs[0]->getOutputShape(0); Shape window_shape{opts->filter_height(), opts->filter_width()}; @@ -163,8 +162,8 @@ std::vector TFLiteOpCreator::convertMaxPool2D(InputOps inputs, ops::PoolOp::RoundMode::floor); } -std::vector TFLiteOpCreator::convertAveragePool2D(InputOps inputs, - InputParams params, +std::vector TFLiteOpCreator::convertAveragePool2D(InputOps& inputs, + InputParams& params, const Pool2DOptions* opts) { auto& input_shape = inputs[0]->getOutputShape(0); Shape window_shape{opts->filter_height(), opts->filter_width()}; @@ -181,7 +180,7 @@ std::vector TFLiteOpCreator::convertAveragePool2D(InputOps inpu ops::PoolOp::RoundMode::floor); } -std::vector TFLiteOpCreator::createSoftmax(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::createSoftmax(InputOps& inputs, InputParams& params, const SoftmaxOptions* opts) { // Softmax in TFLite is always 2-D. assert(inputs[0]->getOutputShape(0).rank() == 2); @@ -198,7 +197,7 @@ Shape shapeFromTensor(mir::Tensor&& t) { return temporary_shape; } -std::vector TFLiteOpCreator::createSlice(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::createSlice(InputOps& inputs, InputParams& params, const ::tflite::SliceOptions*) { auto starts = shapeFromTensor(mir::Tensor(params[0])); auto sizes = shapeFromTensor(mir::Tensor(params[1])); @@ -208,7 +207,7 @@ std::vector TFLiteOpCreator::createSlice(InputOps inputs, Input starts, sizes); } -std::vector TFLiteOpCreator::convertReshape(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::convertReshape(InputOps& inputs, InputParams& params, const ReshapeOptions* opts) { // TODO: we should also support "-1" values in new_shape, which means that correct // shape values must be calculated. Better do it in the shape inference module. @@ -245,19 +244,35 @@ TFLiteOpCreator::convertResizeNN(InputOps& inputs, InputParams& params, } std::vector -TFLiteOpCreator::createAdd(InputOps& inputs, InputParams&, const ::tflite::AddOptions* opts) { +TFLiteOpCreator::createAdd(InputOps& inputs, const InputParams& params, + const ::tflite::AddOptions* opts) { std::vector descriptors; + for (auto i : inputs) descriptors.push_back(i->getOutput(0)); + + for (const auto& param : params) { + auto weights_tensor = createOp(ActivationFunctionType_NONE, param); + descriptors.push_back(weights_tensor[0]->getOutput(0)); + } + return createOp(opts->fused_activation_function(), descriptors, ops::ElementwiseOp::OpType::add); } std::vector -TFLiteOpCreator::createMul(InputOps& inputs, InputParams&, const ::tflite::MulOptions* opts) { +TFLiteOpCreator::createMul(InputOps& inputs, const InputParams& params, + const ::tflite::MulOptions* opts) { std::vector descriptors; + for (auto i : inputs) descriptors.push_back(i->getOutput(0)); + + for (const auto& param : params) { + auto weights_tensor = createOp(ActivationFunctionType_NONE, param); + descriptors.push_back(weights_tensor[0]->getOutput(0)); + } + return createOp(opts->fused_activation_function(), descriptors, ops::ElementwiseOp::OpType::mul); } @@ -281,7 +296,7 @@ TFLiteOpCreator::createMax(InputOps& inputs, InputParams&, ops::ElementwiseOp::OpType::max); } -std::vector TFLiteOpCreator::convertReducer(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::convertReducer(InputOps& inputs, InputParams& params, ops::ReduceFOp::FuncType ft, const ::tflite::ReducerOptions* opts) { assert(params.at(0).getShape().rank() <= 1 && "Must be 1-dim or 0-dim tensor"); @@ -356,7 +371,7 @@ mir::Operation* TFLiteOpCreator::addFusedActivation(mir::Operation* input, } std::vector TFLiteOpCreator::createSqueeze( - InputOps inputs, InputParams params, const ::tflite::SqueezeOptions* opts) { + InputOps& inputs, InputParams& params, const ::tflite::SqueezeOptions* opts) { std::vector squeeze_dims{opts->squeeze_dims()->begin(), opts->squeeze_dims()->end()}; @@ -364,7 +379,7 @@ std::vector TFLiteOpCreator::createSqueeze( squeeze_dims); } -std::vector TFLiteOpCreator::createPad(InputOps inputs, InputParams params, +std::vector TFLiteOpCreator::createPad(InputOps& inputs, InputParams& params, const ::tflite::PadOptions *opts) { assert(params.size() == 1); // support pad with one param std::vector> paddings; diff --git a/contrib/nnc/passes/tflite_frontend/tflite_op_creator.h b/contrib/nnc/passes/tflite_frontend/tflite_op_creator.h index 0d802c2..3ae321a 100644 --- a/contrib/nnc/passes/tflite_frontend/tflite_op_creator.h +++ b/contrib/nnc/passes/tflite_frontend/tflite_op_creator.h @@ -42,39 +42,39 @@ using mir::Shape; class TFLiteOpCreator { public: - using InputOps = std::vector&; - using InputParams = std::vector&; + using InputOps = std::vector; + using InputParams = std::vector; explicit TFLiteOpCreator(Graph* g) : _graph(g) {} - std::vector convertConv2D(InputOps, InputParams, const ::tflite::Conv2DOptions*); + std::vector convertConv2D(InputOps&, InputParams&, const ::tflite::Conv2DOptions*); - std::vector convertDepthwiseConv2D(InputOps, InputParams, + std::vector convertDepthwiseConv2D(InputOps&, InputParams&, const ::tflite::DepthwiseConv2DOptions*); - std::vector convertConcatenation(InputOps, InputParams, + std::vector convertConcatenation(InputOps&, InputParams&, const ::tflite::ConcatenationOptions*); - std::vector convertMaxPool2D(InputOps, InputParams, + std::vector convertMaxPool2D(InputOps&, InputParams&, const ::tflite::Pool2DOptions*); - std::vector convertAveragePool2D(InputOps, InputParams, + std::vector convertAveragePool2D(InputOps&, InputParams&, const ::tflite::Pool2DOptions*); - std::vector convertReducer(InputOps, InputParams, ops::ReduceFOp::FuncType, + std::vector convertReducer(InputOps&, InputParams&, ops::ReduceFOp::FuncType, const ::tflite::ReducerOptions*); - std::vector createSoftmax(InputOps, InputParams, const ::tflite::SoftmaxOptions*); + std::vector createSoftmax(InputOps&, InputParams&, const ::tflite::SoftmaxOptions*); - std::vector createSlice(InputOps, InputParams, const ::tflite::SliceOptions*); - - std::vector convertReshape(InputOps, InputParams, + std::vector createSlice(InputOps&, InputParams&, const ::tflite::SliceOptions*); + + std::vector convertReshape(InputOps&, InputParams&, const ::tflite::ReshapeOptions*); - std::vector convertFullyConnected(InputOps, InputParams, + std::vector convertFullyConnected(InputOps&, InputParams&, const ::tflite::FullyConnectedOptions*); - std::vector convertResizeNN(InputOps, InputParams, + std::vector convertResizeNN(InputOps&, InputParams&, const ::tflite::ResizeNearestNeighborOptions*); std::vector createLogistic(InputOps& inputs, InputParams& params); @@ -85,9 +85,9 @@ public: const ::tflite::SqueezeOptions* opts); /** @brief Elementwise Add */ - std::vector createAdd(InputOps&, InputParams&, const ::tflite::AddOptions*); + std::vector createAdd(InputOps&, const InputParams&, const ::tflite::AddOptions*); /** @brief Elementwise product */ - std::vector createMul(InputOps&, InputParams&, const ::tflite::MulOptions*); + std::vector createMul(InputOps&, const InputParams&, const ::tflite::MulOptions*); /** @brief Elementwise maximum */ std::vector createMax(InputOps&, InputParams&, const ::tflite::MaximumMinimumOptions*); /** @brief Elementwise division */ -- 2.7.4