From 5ab68a6ea335b6949a8f97b7b194921720bf1263 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=95=EC=84=B8=ED=9D=AC/=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: Wed, 14 Nov 2018 10:54:17 +0900 Subject: [PATCH] [enco/frontend] Introduce ConcatenationGraphBuilder (#2262) * [enco/frontend] Introduce ConcatenationGraphBuilder This will introduce ConcatenationGraphBuilder for importing Concat operator from TF lite Signed-off-by: SaeHie Park * remove unused --- contrib/enco/frontend/tflite/src/Frontend.cpp | 144 ++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/contrib/enco/frontend/tflite/src/Frontend.cpp b/contrib/enco/frontend/tflite/src/Frontend.cpp index fa9a327..bb940a8 100644 --- a/contrib/enco/frontend/tflite/src/Frontend.cpp +++ b/contrib/enco/frontend/tflite/src/Frontend.cpp @@ -643,6 +643,149 @@ void AvgPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContex } /** + * @brief GraphBuilder for Concatenation operator + */ +class ConcatenationGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +void ConcatenationGraphBuilder::build(const tflite::Operator *op, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + coco::Module *m = context->m(); + coco::Data *d = context->d(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); + tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 ~ N : any number of input features + // output index 0 : one output feature + assert(opinputs.size() > 0); + assert(opoutputs.size() == 1); + + // Default parameter values are referenced from schema_generated.h + int32_t concat_axis = 0; + tflite::ActivationFunctionType activation = tflite::ActivationFunctionType_NONE; + + if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions()) + { + activation = concatenation_params->fused_activation_function(); + concat_axis = concatenation_params->axis(); + + const int32_t rank = static_cast(tensor_context.shape(opinputs.at(0)).rank()); + if (concat_axis < 0) + { + concat_axis += rank; + } + assert(concat_axis >= 0); + assert(concat_axis < rank); + } + // TODO handle other axis + assert(concat_axis == 3); + assert(activation == tflite::ActivationFunctionType_NONE); + + // Construct a vector of input objects + std::vector input_objects; + + for (auto &input_index : opinputs) + { + const tensor::Shape &input_shape = tensor_context.shape(input_index); + coco::FeatureObject *input_obj = m->entity()->object()->create(); + coco::Bag *input_bag = bags.bag(input_index); + input_obj->bag(input_bag); + input_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(input_shape))); + + input_objects.emplace_back(input_obj); + } + + coco::FeatureObject *last_feature = input_objects.at(0); + + assert(last_feature != nullptr); + assert(last_feature->bag() != nullptr); + + // Update coco IR + // + // Given a sequence of input features %in[0] / %in[1] / ... / %in[N] + // the below code constructs a sequence of eval instructions + // - Load is omitted for simplicity + // + // %tmp = eval(ConcatF(%in[0], %in[1])) + // %tmp = eval(ConcatF(%tmp, %in[2])) + // ... + // %tmp = eval(ConcatF(%tmp, %in[N])) + // %out[0] = copy(%tmp) + // + for (uint32_t n = 1; n < input_objects.size(); ++n) + { + auto const left_feature = last_feature; + auto const left_shape = left_feature->layout()->shape(); + + auto right_feature = input_objects.at(n); + auto right_shape = right_feature->layout()->shape(); + + // Batch is not supported, yet + assert(left_feature->layout()->batch() == 1); + assert(right_feature->layout()->batch() == 1); + + // Height and Width SHOULD BE IDENTICAL for depth concat + assert(left_shape.height() == right_shape.height()); + assert(left_shape.width() == right_shape.width()); + + const uint32_t C = left_shape.depth() + right_shape.depth(); + const uint32_t H = left_shape.height(); + const uint32_t W = left_shape.width(); + + const nncc::core::ADT::feature::Shape out_shape{C, H, W}; + + auto out_bag = m->entity()->bag()->create(num_elements(out_shape)); + auto out_feature = m->entity()->object()->create(); + + out_feature->bag(out_bag); + out_feature->layout(coco::FeatureLayouts::BHWC::create(out_shape)); + + auto left_load = op_builder(m).load(left_feature).pop(); + auto right_load = op_builder(m).load(right_feature).pop(); + + auto concat_f = m->entity()->op()->create(); + + concat_f->axis(coco::ConcatF::Axis::Depth); + concat_f->left(left_load); + concat_f->right(right_load); + + auto eval = instr_builder(m).eval(out_feature, concat_f); + + // Append the constructed Shuffle instruction + blk->instr()->append(eval); + + // Update 'last_feature' + last_feature = out_feature; + } + + // Insert copy instruction from last_feature to output operand + int const ofm_idx = opoutputs.at(0); + auto const ofm_shape = tensor_context.shape(ofm_idx); + + auto ofm_bag = bags.bag(ofm_idx); + auto ofm_obj = m->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Copy instruction from last into ofm + auto copy_ins = instr_builder(m).copy(ofm_obj, last_feature); + + // Append the instruction + blk->instr()->append(copy_ins); +} + +/** * @brief Class to return graph builder for passed tflite::builtinOperator */ class GraphBuilderRegistry @@ -672,6 +815,7 @@ private: // add GraphBuilder for each tflite operation. _builder_map[tflite::BuiltinOperator_CONV_2D] = make_unique(); _builder_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_CONCATENATION] = make_unique(); } private: -- 2.7.4