[CPU] supported ShuffleChannels and added tests (#636)
authorAnton Voronov <anton.voronov@intel.com>
Fri, 5 Jun 2020 11:10:55 +0000 (14:10 +0300)
committerGitHub <noreply@github.com>
Fri, 5 Jun 2020 11:10:55 +0000 (14:10 +0300)
inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp
inference-engine/src/legacy_api/src/ie_cnn_layer_builder_ngraph.cpp
inference-engine/src/mkldnn_plugin/mkldnn_plugin.cpp
inference-engine/src/mkldnn_plugin/nodes/shuffle_channels.cpp
inference-engine/src/transformations/include/transformations/convert_opset3_to_opset2/convert_shuffle_channels3.hpp
inference-engine/src/transformations/src/transformations/convert_opset3_to_opset2/convert_shuffle_channels3.cpp
inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/shuffle_channels.cpp [new file with mode: 0644]
inference-engine/tests/functional/plugin/shared/include/single_layer_tests/shuffle_channels.hpp [new file with mode: 0644]
inference-engine/tests/functional/plugin/shared/src/single_layer_tests/shuffle_channels.cpp [new file with mode: 0644]
inference-engine/tests/ngraph_functions/include/ngraph_functions/builders.hpp
inference-engine/tests/ngraph_functions/src/shuffle_channels.cpp [new file with mode: 0644]

index 349b461..636c6b3 100644 (file)
@@ -601,6 +601,7 @@ std::shared_ptr<CNNNetworkImpl> convertFunctionToICNNNetwork(const std::shared_p
                 std::make_shared<Builder::NodeConverter<::ngraph::op::v1::LogicalNot>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::v1::ReduceLogicalAnd>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::v1::ReduceLogicalOr>>(),
+                std::make_shared<Builder::NodeConverter<::ngraph::op::ShuffleChannels>>(),
         };
         CNNLayerPtr result;
 
index b1fc1e7..6a605a9 100644 (file)
@@ -1381,6 +1381,20 @@ CNNLayer::Ptr NodeConverter<ngraph::op::SquaredDifference>::createLayer(const st
 }
 
 template <>
+CNNLayer::Ptr NodeConverter<ngraph::op::ShuffleChannels>::createLayer(const std::shared_ptr<ngraph::Node>& layer) const {
+    LayerParams params = {layer->get_friendly_name(), "ShuffleChannels", details::convertPrecision(layer->get_output_element_type(0))};
+
+    auto res = std::make_shared<InferenceEngine::ShuffleChannelsLayer>(params);
+    auto castedLayer = ngraph::as_type_ptr<ngraph::op::ShuffleChannels>(layer);
+    if (castedLayer == nullptr) THROW_IE_EXCEPTION << "Cannot get " << params.type << " layer " << params.name;
+
+    res->params["axis"] = std::to_string(castedLayer->get_axis());
+    res->params["group"] = std::to_string(castedLayer->get_group());
+
+    return res;
+}
+
+template <>
 CNNLayer::Ptr NodeConverter<ngraph::op::DetectionOutput>::createLayer(
     const std::shared_ptr<ngraph::Node>& layer) const {
     LayerParams params = {layer->get_friendly_name(), "DetectionOutput",
index c81d633..5fb3095 100644 (file)
@@ -94,8 +94,7 @@ Engine::LoadExeNetworkImpl(const InferenceEngine::ICNNNetwork &network, const st
 
             return std::dynamic_pointer_cast<const ::ngraph::opset2::Gelu>(node) ||
                 std::dynamic_pointer_cast<const ::ngraph::opset2::BatchToSpace>(node) ||
-                std::dynamic_pointer_cast<const ::ngraph::opset2::SpaceToBatch>(node) ||
-                std::dynamic_pointer_cast<const ::ngraph::opset3::ShuffleChannels>(node);
+                std::dynamic_pointer_cast<const ::ngraph::opset2::SpaceToBatch>(node);
         };
         auto nGraphFunc = clonedNetwork->getFunction();
         // Disable shape inference (WA for generic operations)
index 8a818ed..44fcf55 100644 (file)
@@ -7,6 +7,7 @@
 #include <cmath>
 #include <string>
 #include <vector>
+#include <set>
 #include <cassert>
 #include "ie_parallel.hpp"
 
@@ -58,11 +59,9 @@ public:
             if (src_dims.size() != dst_dims.size())
                 THROW_IE_EXCEPTION << layer->name << " Incorrect number of input/output dimensions!";
 
-            if (layer->insData[0].lock()->getTensorDesc().getPrecision() != Precision::FP32)
-                THROW_IE_EXCEPTION << layer->name << " Incorrect input precision. Only F32 is supported!";
-
-            if (layer->outData[0]->getTensorDesc().getPrecision() != Precision::FP32)
-                THROW_IE_EXCEPTION << layer->name << " Incorrect output precision. Only F32 is supported!";
+            const auto precision = layer->insData[0].lock()->getTensorDesc().getPrecision();
+            if (_supported_precisions_sizes.find(precision.size()) == _supported_precisions_sizes.end())
+                THROW_IE_EXCEPTION << layer->name << "has unsupported precision: " << precision.name();
 
             int axis = layer->GetParamAsInt("axis", 1);
             if (axis < 0)
@@ -93,17 +92,62 @@ public:
             ownStrides[2] = own_dims[1];
             work_amount_dst = ownStrides[0] * own_dims[0];
 
-            addConfig(layer, { DataConfigurator(ConfLayout::PLN) }, { DataConfigurator(ConfLayout::PLN) });
+            LayerConfig config;
+            DataConfig inConfig;
+            inConfig.desc = layer->insData[0].lock()->getTensorDesc();
+
+            config.inConfs.push_back(inConfig);
+
+            DataConfig outConfig;
+            outConfig.desc = layer->outData[0]->getTensorDesc();
+            outConfig.desc.setPrecision(inConfig.desc.getPrecision());
+            outConfig.desc.setLayout(inConfig.desc.getLayout());
+            config.outConfs.push_back(outConfig);
+
+            config.dynBatchSupport = false;
+            confs.push_back(config);
         } catch (InferenceEngine::details::InferenceEngineException &ex) {
             errorMsg = ex.what();
         }
     }
 
     StatusCode execute(std::vector<Blob::Ptr>& inputs, std::vector<Blob::Ptr>& outputs, ResponseDesc *resp) noexcept override {
-        const float *src_data = inputs[0]->cbuffer().as<const float *>() +
-            inputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
-        float* dst_data = outputs[0]->cbuffer().as<float *>() +
-            outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
+        switch (inputs[0]->getTensorDesc().getPrecision().size()) {
+            case 1: {
+                process_data<PrecisionTrait<Precision::U8>::value_type>(inputs, outputs);
+                break;
+            }
+            case 2: {
+                process_data<PrecisionTrait<Precision::U16>::value_type>(inputs, outputs);
+                break;
+            }
+            case 4: {
+                process_data<PrecisionTrait<Precision::I32>::value_type>(inputs, outputs);
+                break;
+            }
+            case 8: {
+                process_data<PrecisionTrait<Precision::U64>::value_type>(inputs, outputs);
+                break;
+            }
+            default: {
+                if (resp) {
+                    std::string errorMsg = "ShuffleChannels layer does not support precision '"
+                                           + std::string(inputs[0]->getTensorDesc().getPrecision().name()) + "'";
+                    errorMsg.copy(resp->msg, sizeof(resp->msg) - 1);
+                }
+                return GENERAL_ERROR;
+            }
+        }
+
+        return OK;
+    }
+
+    template<typename T>
+    void process_data(std::vector<Blob::Ptr>& inputs, std::vector<Blob::Ptr>& outputs) noexcept {
+        const T* src_data = inputs[0]->cbuffer().as<const T*>() +
+                                inputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
+        T* dst_data = outputs[0]->cbuffer().as<T*>() +
+                          outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
 
         if (dataLength > 1) {
             //  Vectorized & Parallel
@@ -113,7 +157,7 @@ public:
                 splitter(work_amount_dst, nthr, ithr, start, end);
                 src_idx = initter(start, CNTR_SIZE, counters, own_dims, ownStrides);
                 for (size_t iwork = start, dst_idx = start * dataLength; iwork < end; ++iwork, dst_idx += dataLength) {
-                    memcpy(&dst_data[dst_idx], &src_data[dataLength * src_idx], sizeof(float) * dataLength);
+                    memcpy(&dst_data[dst_idx], &src_data[dataLength * src_idx], sizeof(T) * dataLength);
                     src_idx = updater(src_idx, CNTR_SIZE, counters, own_dims, ownStrides);
                 }
             });
@@ -130,8 +174,6 @@ public:
                 }
             });
         }
-
-        return OK;
     }
 
 private:
@@ -139,8 +181,12 @@ private:
     size_t work_amount_dst;
     size_t own_dims[CNTR_SIZE];
     size_t ownStrides[CNTR_SIZE];
+
+    static const std::set<size_t> _supported_precisions_sizes;
 };
 
+const std::set<size_t> ShuffleChannelsImpl::_supported_precisions_sizes = {1, 2, 4, 8};
+
 REG_FACTORY_FOR(ShuffleChannelsImpl, ShuffleChannels);
 
 }  // namespace Cpu
index 369d405..2ed9809 100644 (file)
@@ -10,6 +10,7 @@
 #include <transformations_visibility.hpp>
 
 #include <ngraph/pass/graph_rewrite.hpp>
+#include "transformations/utils/pass_param.hpp"
 
 namespace ngraph {
 namespace pass {
@@ -19,7 +20,7 @@ class TRANSFORMATIONS_API ConvertShuffleChannels3;
 }  // namespace pass
 }  // namespace ngraph
 
-class ngraph::pass::ConvertShuffleChannels3: public ngraph::pass::GraphRewrite {
+class ngraph::pass::ConvertShuffleChannels3: public ngraph::pass::GraphRewrite, public ngraph::pass::PassParam {
 public:
     ConvertShuffleChannels3() : GraphRewrite() {
         convert_shuffle_channels3();
index ca66b19..b333471 100644 (file)
@@ -17,9 +17,9 @@ void ngraph::pass::ConvertShuffleChannels3::convert_shuffle_channels3() {
     auto input = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1, 1, 1});
     auto shuffle_channels = std::make_shared<::opset3::ShuffleChannels>(input);
 
-    ngraph::graph_rewrite_callback callback = [](pattern::Matcher &m) {
+    ngraph::graph_rewrite_callback callback = [this](pattern::Matcher &m) {
         auto shuffle_channels = std::dynamic_pointer_cast<::opset3::ShuffleChannels>(m.get_match_root());
-        if (!shuffle_channels) {
+        if (!shuffle_channels || transformation_callback(shuffle_channels)) {
             return false;
         }
         if (shuffle_channels->input_value(0).get_partial_shape().rank().is_dynamic()) {
diff --git a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/shuffle_channels.cpp b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/shuffle_channels.cpp
new file mode 100644 (file)
index 0000000..e7117bd
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <vector>
+
+#include "single_layer_tests/shuffle_channels.hpp"
+
+using namespace LayerTestsDefinitions;
+
+namespace {
+
+const std::vector<InferenceEngine::Precision> netPrecisions = {
+        InferenceEngine::Precision::I8,
+        InferenceEngine::Precision::U8,
+        InferenceEngine::Precision::I16,
+        InferenceEngine::Precision::I32,
+        InferenceEngine::Precision::FP32
+};
+
+const std::vector<int> axes = {0, 1, 2, 3};
+const std::vector<int> negativeAxes = {-4, -3, -2, -1};
+const std::vector<int> groups = {1, 2, 3};
+
+const auto shuffleChannelsParams4D = ::testing::Combine(
+        ::testing::ValuesIn(axes),
+        ::testing::ValuesIn(groups)
+);
+
+const auto shuffleChannelsParamsNegativeAxis4D = ::testing::Combine(
+        ::testing::ValuesIn(negativeAxes),
+        ::testing::ValuesIn(groups)
+);
+
+INSTANTIATE_TEST_CASE_P(ShuffleChannels4D, ShuffleChannelsLayerTest,
+        ::testing::Combine(
+                shuffleChannelsParams4D,
+                ::testing::ValuesIn(netPrecisions),
+                ::testing::Values(std::vector<size_t >({6, 6, 6, 6})),
+                ::testing::Values(CommonTestUtils::DEVICE_CPU)),
+        ShuffleChannelsLayerTest::getTestCaseName);
+
+INSTANTIATE_TEST_CASE_P(ShuffleChannelsNegativeAxis4D, ShuffleChannelsLayerTest,
+        ::testing::Combine(
+                shuffleChannelsParamsNegativeAxis4D,
+                ::testing::ValuesIn(netPrecisions),
+                ::testing::Values(std::vector<size_t >({6, 6, 6, 6})),
+                ::testing::Values(CommonTestUtils::DEVICE_CPU)),
+        ShuffleChannelsLayerTest::getTestCaseName);
+
+}  // namespace
diff --git a/inference-engine/tests/functional/plugin/shared/include/single_layer_tests/shuffle_channels.hpp b/inference-engine/tests/functional/plugin/shared/include/single_layer_tests/shuffle_channels.hpp
new file mode 100644 (file)
index 0000000..b27dbac
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <tuple>
+#include <vector>
+#include <string>
+#include <memory>
+
+#include "functional_test_utils/layer_test_utils.hpp"
+
+typedef std::tuple<
+        int, // axis
+        int  // group
+> shuffleChannelsSpecificParams;
+typedef std::tuple<
+        shuffleChannelsSpecificParams,
+        InferenceEngine::Precision,     // Net precision
+        InferenceEngine::SizeVector,    // Input shapes
+        LayerTestsUtils::TargetDevice   // Device name
+> shuffleChannelsLayerTestParamsSet;
+namespace LayerTestsDefinitions {
+
+
+class ShuffleChannelsLayerTest : public testing::WithParamInterface<shuffleChannelsLayerTestParamsSet>,
+                                 public LayerTestsUtils::LayerTestsCommon {
+public:
+    static std::string getTestCaseName(testing::TestParamInfo<shuffleChannelsLayerTestParamsSet> obj);
+
+protected:
+    void SetUp() override;
+};
+
+}  // namespace LayerTestsDefinitions
diff --git a/inference-engine/tests/functional/plugin/shared/src/single_layer_tests/shuffle_channels.cpp b/inference-engine/tests/functional/plugin/shared/src/single_layer_tests/shuffle_channels.cpp
new file mode 100644 (file)
index 0000000..5919bb3
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <tuple>
+#include <vector>
+#include <string>
+#include <memory>
+#include <functional>
+
+#include "single_layer_tests/shuffle_channels.hpp"
+#include "ngraph_functions/builders.hpp"
+
+namespace LayerTestsDefinitions {
+
+std::string ShuffleChannelsLayerTest::getTestCaseName(testing::TestParamInfo<shuffleChannelsLayerTestParamsSet> obj) {
+    shuffleChannelsSpecificParams shuffleChannelsParams;
+    InferenceEngine::Precision netPrecision;
+    InferenceEngine::SizeVector inputShapes;
+    std::string targetDevice;
+    std::tie(shuffleChannelsParams, netPrecision, inputShapes, targetDevice) = obj.param;
+    int axis, group;
+    std::tie(axis, group) = shuffleChannelsParams;
+
+    std::ostringstream result;
+    result << "IS=" << CommonTestUtils::vec2str(inputShapes) << "_";
+    result << "Axis=" << std::to_string(axis) << "_";
+    result << "Group=" << std::to_string(group) << "_";
+    result << "netPRC=" << netPrecision.name() << "_";
+    result << "targetDevice=" << targetDevice;
+    return result.str();
+}
+
+void ShuffleChannelsLayerTest::SetUp() {
+    shuffleChannelsSpecificParams shuffleChannelsParams;
+    std::vector<size_t> inputShape;
+    auto netPrecision   = InferenceEngine::Precision::UNSPECIFIED;
+    std::tie(shuffleChannelsParams, netPrecision, inputShape, targetDevice) = this->GetParam();
+    int axis, group;
+    std::tie(axis, group) = shuffleChannelsParams;
+    auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
+    auto params = ngraph::builder::makeParams(ngPrc, {inputShape});
+    auto paramOuts = ngraph::helpers::convert2OutputVector(
+            ngraph::helpers::castOps2Nodes<ngraph::op::Parameter>(params));
+    auto shuffleChannels = std::dynamic_pointer_cast<ngraph::opset3::ShuffleChannels>(
+            ngraph::builder::makeShuffleChannels(paramOuts[0], axis, group));
+    ngraph::ResultVector results{std::make_shared<ngraph::opset3::Result>(shuffleChannels)};
+    function = std::make_shared<ngraph::Function>(results, params, "shuffleChannels");
+}
+
+TEST_P(ShuffleChannelsLayerTest, CompareWithRefs) {
+    Run();
+}
+}  // namespace LayerTestsDefinitions
index 26eb615..d57247e 100644 (file)
@@ -220,5 +220,9 @@ std::shared_ptr<ngraph::Node> makeSpaceToDepth(const ngraph::Output<Node> &in,
                                                ngraph::opset3::SpaceToDepth::SpaceToDepthMode mode,
                                                size_t blockSize);
 
+std::shared_ptr<Node> makeShuffleChannels(const ngraph::Output<Node> &in,
+                                          int axis,
+                                          int group);
+
 }  // namespace builder
 }  // namespace ngraph
diff --git a/inference-engine/tests/ngraph_functions/src/shuffle_channels.cpp b/inference-engine/tests/ngraph_functions/src/shuffle_channels.cpp
new file mode 100644 (file)
index 0000000..fb150b1
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+//
+
+#include <vector>
+#include <memory>
+
+#include "ngraph_functions/builders.hpp"
+
+namespace ngraph {
+namespace builder {
+
+std::shared_ptr<Node> makeShuffleChannels(const ngraph::Output<Node> &in,
+                                          int axis,
+                                          int group) {
+    return std::make_shared<ngraph::opset3::ShuffleChannels>(in, axis, group);
+}
+
+}  // namespace builder
+}  // namespace ngraph
\ No newline at end of file