From: Павел Ильютченко/AI Tools Lab /SRR/Engineer/삼성전자 Date: Mon, 19 Aug 2019 17:35:00 +0000 (+0300) Subject: [mir2loco] Support DepthwiseConv operation in transformer (#6574) X-Git-Tag: accepted/tizen/unified/20190903.052428~323 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5535d1267cdf88e724ac9f19548ca3181627c96d;p=platform%2Fcore%2Fml%2Fnnfw.git [mir2loco] Support DepthwiseConv operation in transformer (#6574) * Added DepthwiseConv2DOp convertion and test for this Signed-off-by: Pavel Iliutchenko --- diff --git a/compiler/mir2loco/include/mir2loco.h b/compiler/mir2loco/include/mir2loco.h index 0ce9671..bb31dcb 100644 --- a/compiler/mir2loco/include/mir2loco.h +++ b/compiler/mir2loco/include/mir2loco.h @@ -30,6 +30,7 @@ public: void visit(mir::ops::ConcatOp &op) override; void visit(mir::ops::ConstantOp &op) override; void visit(mir::ops::Conv2DOp &op) override; + void visit(mir::ops::DepthwiseConv2DOp &op) override; void visit(mir::ops::InputOp &op) override; void visit(mir::ops::MulOp &op) override; void visit(mir::ops::OutputOp &op) override; diff --git a/compiler/mir2loco/src/mir2loco.cpp b/compiler/mir2loco/src/mir2loco.cpp index 998d668..076b1c3 100644 --- a/compiler/mir2loco/src/mir2loco.cpp +++ b/compiler/mir2loco/src/mir2loco.cpp @@ -20,6 +20,7 @@ #include "mir/ops/ConcatOp.h" #include "mir/ops/ConstantOp.h" #include "mir/ops/Conv2DOp.h" +#include "mir/ops/DepthwiseConv2DOp.h" #include "mir/ops/MulOp.h" #include "mir/ops/PoolOp.h" #include "mir/ops/ReluOp.h" @@ -112,6 +113,22 @@ loco::FeatureDecode *createNHWCFeatureDecode(loco::Graph *graph) return decode_node; } +loco::DepthwiseFilterEncode *createHWCMDepthwiseFilterEncode(loco::Graph *graph) +{ + auto filter_enc = graph->nodes()->create(); + auto enc = stdex::make_unique>(); + // mir using filter convention as TF + // In TensorFlow, dwconv2d filter is a 4-D tensor of following shape: + // [filter_height, filter_width, in_channels, channel_multiplier] + enc->perm()->axis(loco::DepthwiseFilterAxis::Height) = 0; + enc->perm()->axis(loco::DepthwiseFilterAxis::Width) = 1; + enc->perm()->axis(loco::DepthwiseFilterAxis::Depth) = 2; + enc->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 3; + + filter_enc->encoder(std::move(enc)); + return filter_enc; +} + loco::DataType convertDataType(mir::DataType data_type) { switch (data_type) @@ -299,6 +316,40 @@ void Transformer::visit(mir::ops::Conv2DOp &op) _mir2loco_map.emplace(&op, decode_node); } +void Transformer::visit(mir::ops::DepthwiseConv2DOp &op) +{ + auto input = op.getInput(0)->getProducer()->getNode(); + auto kernel = op.getInput(1)->getProducer()->getNode(); + // Get ConstantOp + auto const_node = _mir2loco_map.at(kernel); + + auto filter_enc = createHWCMDepthwiseFilterEncode(_loco_graph.get()); + // Set filter input + filter_enc->input(const_node); + // Setting up DepthwiseConv2D + + // FeatureEncode + auto feature_enc = createNHWCFeatureEncode(_loco_graph.get()); + // Set Input + auto loco_it = _mir2loco_map.find(input); + assert(loco_it != _mir2loco_map.end()); // can't find the input + feature_enc->input(loco_it->second); + // DepthwiseConv2D + auto dw_conv2d_node = _loco_graph->nodes()->create(); + setupStride(op.getStrides(), dw_conv2d_node->stride()); + setupPad(op.getPaddingBefore(), op.getPaddingAfter(), dw_conv2d_node->pad()); + // Set Input + dw_conv2d_node->ifm(feature_enc); + dw_conv2d_node->ker(filter_enc); + // FeatureDecode + auto feature_dec = createNHWCFeatureDecode(_loco_graph.get()); + // Set Input + feature_dec->input(dw_conv2d_node); + // Not set Shape + // Add to map + _mir2loco_map.emplace(&op, feature_dec); +} + void Transformer::visit(mir::ops::InputOp &op) { auto pull_node = _loco_graph->nodes()->create(); diff --git a/compiler/mir2loco/src/mir2loco.test.cpp b/compiler/mir2loco/src/mir2loco.test.cpp index 7df44d8..3e19098 100644 --- a/compiler/mir2loco/src/mir2loco.test.cpp +++ b/compiler/mir2loco/src/mir2loco.test.cpp @@ -20,6 +20,7 @@ #include "mir/ops/ConcatOp.h" #include "mir/ops/ConstantOp.h" #include "mir/ops/Conv2DOp.h" +#include "mir/ops/DepthwiseConv2DOp.h" #include "mir/ops/MulOp.h" #include "mir/ops/PoolOp.h" #include "mir/ops/ReluOp.h" @@ -453,3 +454,64 @@ TEST_F(TestTransformer_mir2loco, Mul_Test) ASSERT_EQ(pull2_node->dim(2), 7); ASSERT_EQ(pull2_node->dim(3), 3); } + +TEST_F(TestTransformer_mir2loco, DepthwiseConv2D_Test) +{ + mir::Graph mir_graph; + + mir::Shape input_shape = mir::Shape({7, 7, 9, 1}); + auto *input = mir_graph.create("input", input_shape); + mir::Shape shape = mir::Shape({2, 3, 1, 1}); + const float data[] = {5.9, 6.7, 5.32, 54.11231, 43.2444, 3.409}; + auto mir_tensor = mir::TensorVariant(mir::DataType::FLOAT32, shape, (const void *)data); + auto *constant = mir_graph.create("constant", mir_tensor); + auto *conv = mir_graph.create( + "dwconv", input->getOutput(0), constant->getOutput(0), mir::Shape{2, 3}, + std::vector{5, 9}, std::vector{7, 4}); + auto *output = mir_graph.create("output", conv->getOutput(0)); + + mir2loco::Transformer transformer; + auto loco_graph = transformer.transform(&mir_graph); + + // Pull + auto inputs = loco_graph->inputs(); + loco::Pull *pull_node = inputs->at(0)->node(); + ASSERT_NE(pull_node, nullptr); + // FeatureEncode + auto pull_uses = loco::succs(pull_node); + ASSERT_EQ(pull_uses.size(), 1); + loco::FeatureEncode *encode_node = dynamic_cast(*pull_uses.begin()); + ASSERT_NE(encode_node, nullptr); + ASSERT_EQ(encode_node->input(), pull_node); + // DepthwiseConv2D + auto encode_uses = loco::succs(encode_node); + ASSERT_EQ(encode_uses.size(), 1); + loco::DepthwiseConv2D *dw_conv_node = dynamic_cast(*encode_uses.begin()); + ASSERT_NE(dw_conv_node, nullptr); + loco::DepthwiseFilterEncode *filter_node = + dynamic_cast(dw_conv_node->ker()); + ASSERT_NE(filter_node, nullptr); + ASSERT_EQ(dw_conv_node->ifm(), encode_node); + // Check params + ASSERT_EQ(dw_conv_node->pad()->left(), 5); + ASSERT_EQ(dw_conv_node->pad()->top(), 9); + ASSERT_EQ(dw_conv_node->pad()->right(), 7); + ASSERT_EQ(dw_conv_node->pad()->bottom(), 4); + ASSERT_EQ(dw_conv_node->stride()->horizontal(), 2); + ASSERT_EQ(dw_conv_node->stride()->vertical(), 3); + // ConstGen + loco::ConstGen *const_node = dynamic_cast(filter_node->input()); + ASSERT_NE(const_node, nullptr); + // FeatureDecode + auto dw_conv_uses = loco::succs(dw_conv_node); + ASSERT_EQ(dw_conv_uses.size(), 1); + loco::FeatureDecode *decode_node = dynamic_cast(*dw_conv_uses.begin()); + ASSERT_NE(decode_node, nullptr); + ASSERT_EQ(decode_node->input(), dw_conv_node); + // Push + auto decode_uses = loco::succs(decode_node); + ASSERT_EQ(decode_uses.size(), 1); + loco::Push *push_node = dynamic_cast(*decode_uses.begin()); + ASSERT_NE(push_node, nullptr); + ASSERT_EQ(push_node->from(), decode_node); +}