[GNA] Detect unsupported concat layers (#7599)

* [GNA] Detect unsupported concat layers

* [GNA] Add support for 3D transposes around convolutions and replace exception with user warning
This commit is contained in:
Szymon Irzabek authored and GitHub committed 2021-12-10 11:17:29 +03:00
1 parent afea9e0e87
commit 1c6c7bac2d
13 files changed
+769 -23

No files matched your search

@@ -0,0 +1,495 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
//
#include <vector>
#include <tuple>
#include <string>
#include <ie_core.hpp>
#include "common_test_utils/common_utils.hpp"
#include "functional_test_utils/plugin_cache.hpp"
#include "shared_test_classes/base/layer_test_utils.hpp"
#include "functional_test_utils/blob_utils.hpp"
#include "ngraph_functions/utils/ngraph_helpers.hpp"
#include "ngraph_functions/builders.hpp"
#include "ngraph_functions/pass/convert_prc.hpp"
/* ============= Concat Layer Restrictions Tests ============= */
using ConcatRestrictionsParamsTuple = typename std::tuple<
InferenceEngine::SizeVector, // Input shapes
unsigned int, // Concatenation axis
InferenceEngine::Precision, // Network Precision
std::map<std::string, std::string>, // Configuration
std::string>; // Device name
namespace ConcatTestsDefinitions {
struct ReLUConcatAxis {
static const char* getName() { return "ReLUConcatAxis"; }
static std::shared_ptr<ngraph::Function> createTopology(const InferenceEngine::SizeVector& inputShape, const unsigned int& axis,
const InferenceEngine::Precision& netPrecision) {
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
ngraph::OutputVector concatInputs;
ngraph::ParameterVector params = ngraph::builder::makeParams(ngPrc, {inputShape});
auto relu = ngraph::builder::makeActivation(params[0], ngPrc, ngraph::helpers::ActivationTypes::Relu);
concatInputs.push_back(relu);
size_t totalSize = ngraph::shape_size(inputShape);
auto constValues = CommonTestUtils::generate_float_numbers(totalSize, -0.1f, 0.1f);
auto constNode = ngraph::builder::makeConstant(ngPrc, {inputShape}, constValues);
concatInputs.push_back(constNode);
auto concat = ngraph::builder::makeConcat(concatInputs, axis);
ngraph::ResultVector results{std::make_shared<ngraph::opset8::Result>(concat)};
return std::make_shared<ngraph::Function>(results, params, getName());
}
static const char* getMatch() { return "type: Concat, and concatenation axis("; }
};
struct MatmulConcatAxis {
static const char* getName() { return "MatmulConcatAxis"; }
static std::shared_ptr<ngraph::Function> createTopology(const InferenceEngine::SizeVector& inputShape, const unsigned int& axis,
const InferenceEngine::Precision& netPrecision) {
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
ngraph::OutputVector concatInputs;
ngraph::ParameterVector params = ngraph::builder::makeParams(ngPrc, {inputShape});
ngraph::Shape mulConstShape;
switch (inputShape.size()) {
default:
case 2:
mulConstShape = {inputShape[1], inputShape[1]};
break;
case 3:
mulConstShape = {inputShape[0], inputShape[2], inputShape[2]};
break;
case 4:
mulConstShape = {inputShape[0], inputShape[1], inputShape[3], inputShape[3]};
break;
}
size_t mulConstSize = ngraph::shape_size(mulConstShape);
std::vector<float> weights1(mulConstSize);
std::vector<float> weights2(mulConstSize);
std::iota(weights1.begin(), weights1.end(), 0.0f);
std::iota(weights2.begin(), weights2.end(), 0.0f);
auto constMul1 = ngraph::builder::makeConstant<float>(ngPrc, mulConstShape, weights1);
auto constMul2 = ngraph::builder::makeConstant<float>(ngPrc, mulConstShape, weights2);
auto matmul1 = std::make_shared<ngraph::opset8::MatMul>(params[0], constMul1, false, true);
concatInputs.push_back(matmul1);
auto matmul2 = std::make_shared<ngraph::opset8::MatMul>(params[0], constMul2, false, true);
concatInputs.push_back(matmul2);
auto concat = ngraph::builder::makeConcat(concatInputs, axis);
ngraph::ResultVector results{std::make_shared<ngraph::opset8::Result>(concat)};
return std::make_shared<ngraph::Function>(results, params, getName());
}
static const char* getMatch() { return "type: Concat, and concatenation axis("; }
};
struct ConvNCHWConcatAxis {
static const char* getName() { return "ConvNCHWConcatAxis"; }
static std::shared_ptr<ngraph::Function> createTopology(const InferenceEngine::SizeVector& inputShape, const unsigned int& axis,
const InferenceEngine::Precision& netPrecision) {
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
ngraph::OutputVector concatInputs;
ngraph::ParameterVector params = ngraph::builder::makeParams(ngPrc, {inputShape});
size_t numOutChannels = 8;
size_t kernelSize = 1;
std::vector<float> filterWeights = CommonTestUtils::generate_float_numbers(numOutChannels * inputShape[1] * kernelSize,
-0.2f, 0.2f);
auto conv = ngraph::builder::makeConvolution(params[0], ngPrc, {1, kernelSize}, {1, 1}, {0, 0}, {0, 0}, {1, 1},
ngraph::op::PadType::VALID, numOutChannels, true, filterWeights);
concatInputs.push_back(conv);
size_t totalSize = ngraph::shape_size(inputShape);
auto constValues = CommonTestUtils::generate_float_numbers(totalSize, -0.0001f, 0.0001f);
auto constNode = ngraph::builder::makeConstant(ngPrc, {inputShape}, constValues);
concatInputs.push_back(constNode);
auto concat = ngraph::builder::makeConcat(concatInputs, axis);
ngraph::ResultVector results{std::make_shared<ngraph::opset8::Result>(concat)};
return std::make_shared<ngraph::Function>(results, params, getName());
}
static const char* getMatch() { return "for input dimensions"; }
};
struct ConvNHWCConcatAxis {
static const char* getName() { return "ConvNHWCConcatAxis"; }
static std::shared_ptr<ngraph::Function> createTopology(const InferenceEngine::SizeVector& inputShape, const unsigned int& axis,
const InferenceEngine::Precision& netPrecision) {
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
ngraph::OutputVector concatInputs;
ngraph::ParameterVector params = ngraph::builder::makeParams(ngPrc, {inputShape});
auto transposeInOrder = ngraph::op::Constant::create(ngraph::element::i64, ngraph::Shape{4}, {0, 3, 1, 2});
auto transposeIn = std::make_shared<ngraph::opset8::Transpose>(params[0], transposeInOrder);
size_t numOutChannels = 8;
size_t kernelSize = 1;
std::vector<float> filterWeights = CommonTestUtils::generate_float_numbers(numOutChannels * inputShape[3] * kernelSize,
-0.2f, 0.2f);
auto conv = ngraph::builder::makeConvolution(transposeIn, ngPrc, {1, kernelSize}, {1, 1}, {0, 0}, {0, 0}, {1, 1},
ngraph::op::PadType::VALID, numOutChannels, true, filterWeights);
auto transposeOutOrder = ngraph::op::Constant::create(ngraph::element::i64, ngraph::Shape{4}, {0, 2, 3, 1});
auto transposeOut = std::make_shared<ngraph::opset8::Transpose>(conv, transposeOutOrder);
concatInputs.push_back(transposeOut);
size_t totalSize = ngraph::shape_size(inputShape);
auto constValues = CommonTestUtils::generate_float_numbers(totalSize, -0.0001f, 0.0001f);
auto constNode = ngraph::builder::makeConstant(ngPrc, {inputShape}, constValues);
concatInputs.push_back(constNode);
auto concat = ngraph::builder::makeConcat(concatInputs, axis);
ngraph::ResultVector results{std::make_shared<ngraph::opset8::Result>(concat)};
return std::make_shared<ngraph::Function>(results, params, getName());
}
static const char* getMatch() { return "type: Concat, and concatenation axis("; }
};
struct ConvConcatNHWCAxis {
static const char* getName() { return "ConvConcatNHWCAxis"; }
static std::shared_ptr<ngraph::Function> createTopology(const InferenceEngine::SizeVector& inputShape, const unsigned int& axis,
const InferenceEngine::Precision& netPrecision) {
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
ngraph::OutputVector concatInputs;
ngraph::ParameterVector params = ngraph::builder::makeParams(ngPrc, {inputShape});
auto transposeInOrder = ngraph::op::Constant::create(ngraph::element::i64, ngraph::Shape{4}, {0, 3, 1, 2});
auto transposeIn1 = std::make_shared<ngraph::opset8::Transpose>(params[0], transposeInOrder);
auto transposeIn2 = std::make_shared<ngraph::opset8::Transpose>(params[0], transposeInOrder);
size_t numOutChannels = 8;
size_t kernelSize = 1;
std::vector<float> filterWeights1 = CommonTestUtils::generate_float_numbers(numOutChannels * inputShape[3] * kernelSize,
-0.1f, 2.2f);
std::vector<float> filterWeights2 = CommonTestUtils::generate_float_numbers(numOutChannels * inputShape[3] * kernelSize,
-1.2f, 0.5f);
auto conv1 = ngraph::builder::makeConvolution(transposeIn1, ngPrc, {1, kernelSize}, {1, 1}, {0, 0}, {0, 0}, {1, 1},
ngraph::op::PadType::VALID, numOutChannels, true, filterWeights1);
auto conv2 = ngraph::builder::makeConvolution(transposeIn2, ngPrc, {1, kernelSize}, {1, 1}, {0, 0}, {0, 0}, {1, 1},
ngraph::op::PadType::VALID, numOutChannels, true, filterWeights2);
concatInputs.push_back(conv1);
concatInputs.push_back(conv2);
auto concat = ngraph::builder::makeConcat(concatInputs, axis);
auto transposeOutOrder = ngraph::op::Constant::create(ngraph::element::i64, ngraph::Shape{4}, {0, 2, 3, 1});
auto transposeOut = std::make_shared<ngraph::opset8::Transpose>(concat, transposeOutOrder);
ngraph::ResultVector results{std::make_shared<ngraph::opset8::Result>(transposeOut)};
return std::make_shared<ngraph::Function>(results, params, getName());
}
static const char* getMatch() { return "type: Concat, and concatenation axis("; }
};
template<typename T>
class ConcatRestrictions : public testing::WithParamInterface<ConcatRestrictionsParamsTuple>,
public LayerTestsUtils::LayerTestsCommon {
public:
static std::string getTestCaseName(testing::TestParamInfo<ConcatRestrictionsParamsTuple> obj) {
InferenceEngine::SizeVector inputShape;
unsigned int concatAxis;
InferenceEngine::Precision netPrecision;
std::map<std::string, std::string> configuration;
std::string targetDevice;
std::tie(inputShape, concatAxis, netPrecision, configuration, targetDevice) = obj.param;
std::ostringstream result;
result << T::getName() << "_";
result << "inputShape=" << CommonTestUtils::vec2str(inputShape) << "_";
result << "concatAxis=" << concatAxis << "_";
result << "netPRC=" << netPrecision.name() << "_";
result << "targetDevice=" << targetDevice << "_";
for (auto const& configItem : configuration) {
result << "configItem=" << configItem.first << "_" << configItem.second << "_";
}
return result.str();
}
static const char* getMatch() { return T::getMatch(); }
protected:
void SetUp() override {
InferenceEngine::SizeVector inputShape;
unsigned int concatAxis;
InferenceEngine::Precision netPrecision;
std::tie(inputShape, concatAxis, netPrecision, configuration, targetDevice) = this->GetParam();
function = T::createTopology(inputShape, concatAxis, netPrecision);
}
};
using ReLUConcatRestrictionsNeg = ConcatRestrictions<ReLUConcatAxis>;
using ReLUConcatRestrictionsPos = ConcatRestrictions<ReLUConcatAxis>;
using MatMulConcatRestrictionsNeg = ConcatRestrictions<MatmulConcatAxis>;
using MatMulConcatRestrictionsPos = ConcatRestrictions<MatmulConcatAxis>;
using ConvNCHWConcatRestrictionsNeg = ConcatRestrictions<ConvNCHWConcatAxis>;
using ConvNCHWConcatRestrictionsPos = ConcatRestrictions<ConvNCHWConcatAxis>;
using ConvNHWCConcatRestrictionsNeg = ConcatRestrictions<ConvNHWCConcatAxis>;
using ConvNHWCConcatRestrictionsPos = ConcatRestrictions<ConvNHWCConcatAxis>;
using ConvConcatNHWCRestrictionsNeg = ConcatRestrictions<ConvConcatNHWCAxis>;
using ConvConcatNHWCRestrictionsPos = ConcatRestrictions<ConvConcatNHWCAxis>;
TEST_P(ReLUConcatRestrictionsNeg, CompareWithRefImpl) {
std::stringstream what;
std::streambuf* sbuf = std::cout.rdbuf();
std::cout.rdbuf(what.rdbuf());
LoadNetwork();
EXPECT_TRUE(what.str().find(getMatch()) != std::string::npos);
std::cout.rdbuf(sbuf);
};
// TODO: this test is left for future when GNA plugin handles const tranposition required for concats with interleaved layers
//TEST_P(ReLUConcatRestrictionsPos, CompareWithRefImpl) {
// Run();
//};
TEST_P(MatMulConcatRestrictionsNeg, CompareWithRefImpl) {
std::stringstream what;
std::streambuf* sbuf = std::cout.rdbuf();
std::cout.rdbuf(what.rdbuf());
LoadNetwork();
EXPECT_TRUE(what.str().find(getMatch()) != std::string::npos);
std::cout.rdbuf(sbuf);
};
TEST_P(MatMulConcatRestrictionsPos, CompareWithRefImpl) {
Run();
};
TEST_P(ConvNCHWConcatRestrictionsNeg, CompareWithRefImpl) {
std::string what;
try {
LoadNetwork();
}
catch (const std::exception& e) {
what.assign(e.what());
}
EXPECT_TRUE(what.find(getMatch()) != std::string::npos);
};
TEST_P(ConvNCHWConcatRestrictionsPos, CompareWithRefImpl) {
Run();
};
TEST_P(ConvNHWCConcatRestrictionsNeg, CompareWithRefImpl) {
std::stringstream what;
std::streambuf* sbuf = std::cout.rdbuf();
std::cout.rdbuf(what.rdbuf());
LoadNetwork();
EXPECT_TRUE(what.str().find(getMatch()) != std::string::npos);
std::cout.rdbuf(sbuf);
};
TEST_P(ConvNHWCConcatRestrictionsPos, CompareWithRefImpl) {
Run();
};
TEST_P(ConvConcatNHWCRestrictionsNeg, CompareWithRefImpl) {
std::stringstream what;
std::streambuf* sbuf = std::cout.rdbuf();
std::cout.rdbuf(what.rdbuf());
LoadNetwork();
EXPECT_TRUE(what.str().find(getMatch()) != std::string::npos);
std::cout.rdbuf(sbuf);
};
TEST_P(ConvConcatNHWCRestrictionsPos, CompareWithRefImpl) {
Run();
};
const std::vector<InferenceEngine::Precision> netPrecisions = {InferenceEngine::Precision::FP32};
const std::vector<std::map<std::string, std::string>> configs = {
{
{"GNA_DEVICE_MODE", "GNA_SW_FP32"},
{"LOG_LEVEL", "LOG_WARNING"}
}
};
// Negative 4D MatMul cases
const std::vector<std::vector<size_t>> inputShapesMatMul4D_neg = {{1, 2, 4, 8}};
const std::vector<unsigned int> concatAxisMatMul4D_neg = {2, 3};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_matmul_4d, MatMulConcatRestrictionsNeg,
::testing::Combine(
::testing::ValuesIn(inputShapesMatMul4D_neg),
::testing::ValuesIn(concatAxisMatMul4D_neg),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
MatMulConcatRestrictionsNeg::getTestCaseName);
// Positive 4D MatMul cases - TODO: this test fails with 4D Gemm computation errors
//const std::vector<std::vector<size_t>> inputShapesMatMul4D_pos = {{1, 2, 4, 8}};
//const std::vector<unsigned int> concatAxisMatMul4D_pos = {0, 1};
//
//INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_matmul_4d, MatMulConcatRestrictionsPos,
// ::testing::Combine(
// ::testing::ValuesIn(inputShapesMatMul4D_pos),
// ::testing::ValuesIn(concatAxisMatMul4D_pos),
// ::testing::ValuesIn(netPrecisions),
// ::testing::ValuesIn(configs),
// ::testing::Values(CommonTestUtils::DEVICE_GNA)),
// MatMulConcatRestrictionsPos::getTestCaseName);
// Negative 3D MatMul cases
const std::vector<std::vector<size_t>> inputShapesMatMul3D_neg = {{2, 4, 8}};
const std::vector<unsigned int> concatAxisMatMul3D_neg = {0, 2};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_matmul_3d, MatMulConcatRestrictionsNeg,
::testing::Combine(
::testing::ValuesIn(inputShapesMatMul3D_neg),
::testing::ValuesIn(concatAxisMatMul3D_neg),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
MatMulConcatRestrictionsNeg::getTestCaseName);
// Positive 3D MatMul cases - TODO: this test fails with 3D Gemm computation errors
//const std::vector<std::vector<size_t>> inputShapesMatMul3D_pos = {{2, 4, 8}};
//const std::vector<unsigned int> concatAxisMatMul3D_pos = {1};
//
//INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_matmul_3d, MatMulConcatRestrictionsPos,
// ::testing::Combine(
// ::testing::ValuesIn(inputShapesMatMul3D_pos),
// ::testing::ValuesIn(concatAxisMatMul3D_pos),
// ::testing::ValuesIn(netPrecisions),
// ::testing::ValuesIn(configs),
// ::testing::Values(CommonTestUtils::DEVICE_GNA)),
// MatMulConcatRestrictionsPos::getTestCaseName);
// Negative 2D MatMul cases
const std::vector<std::vector<size_t>> inputShapesMatMul2D_neg = {{8, 64}};
const std::vector<unsigned int> concatAxisMatMul2D_neg = {0};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_matmul_2d, MatMulConcatRestrictionsNeg,
::testing::Combine(
::testing::ValuesIn(inputShapesMatMul2D_neg),
::testing::ValuesIn(concatAxisMatMul2D_neg),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
MatMulConcatRestrictionsNeg::getTestCaseName);
// Positive 2D MatMul cases
const std::vector<std::vector<size_t>> inputShapesMatMul2D_pos = {{8, 64}};
const std::vector<unsigned int> concatAxisMatMul2D_pos = {1};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_matmul_2d, MatMulConcatRestrictionsPos,
::testing::Combine(
::testing::ValuesIn(inputShapesMatMul2D_pos),
::testing::ValuesIn(concatAxisMatMul2D_pos),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
MatMulConcatRestrictionsPos::getTestCaseName);
// Negative ReLU cases
const std::vector<std::vector<size_t>> inputShapesReLU_neg = {{64, 128}};
const std::vector<unsigned int> concatAxisReLU_neg = {0};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_relu, ReLUConcatRestrictionsNeg,
::testing::Combine(
::testing::ValuesIn(inputShapesReLU_neg),
::testing::ValuesIn(concatAxisReLU_neg),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ReLUConcatRestrictionsNeg::getTestCaseName);
// Positive ReLU cases
const std::vector<std::vector<size_t>> inputShapesReLU_pos = {{64, 128}};
const std::vector<unsigned int> concatAxisReLU_pos = {1};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions_relu, ReLUConcatRestrictionsPos,
::testing::Combine(
::testing::ValuesIn(inputShapesReLU_pos),
::testing::ValuesIn(concatAxisReLU_pos),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ReLUConcatRestrictionsPos::getTestCaseName);
// Negative cases NCHW
const std::vector<std::vector<size_t>> inputShapesConvNCHW_neg = {{1, 8, 16, 32}};
const std::vector<unsigned int> concatAxisConvNCHW_neg = {3}; // Axis 1 should be negative as well,
// but is handled by the plugin in this case
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions, ConvNCHWConcatRestrictionsNeg,
::testing::Combine(
::testing::ValuesIn(inputShapesConvNCHW_neg),
::testing::ValuesIn(concatAxisConvNCHW_neg),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvNCHWConcatRestrictionsNeg::getTestCaseName);
// Positive cases NCHW
const std::vector<std::vector<size_t>> inputShapesConvNCHW_pos = {{1, 8, 1, 64}};
const std::vector<unsigned int> concatAxisConvNCHW_pos = {2, 3}; // TODO: incorrect output buffer calculation
// when 0 axis is used
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions, ConvNCHWConcatRestrictionsPos,
::testing::Combine(
::testing::ValuesIn(inputShapesConvNCHW_pos),
::testing::ValuesIn(concatAxisConvNCHW_pos),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvNCHWConcatRestrictionsPos::getTestCaseName);
// Negative cases NHWC
const std::vector<std::vector<size_t>> inputShapesNHWC_neg = {{1, 2, 16, 8}};
const std::vector<unsigned int> concatAxisNHWC_neg = {2, 3};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions, ConvNHWCConcatRestrictionsNeg,
::testing::Combine(
::testing::ValuesIn(inputShapesNHWC_neg),
::testing::ValuesIn(concatAxisNHWC_neg),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvNHWCConcatRestrictionsNeg::getTestCaseName);
// Positive cases NHWC
const std::vector<std::vector<size_t>> inputShapesNHWC_pos = {{1, 1, 16, 8}};
const std::vector<unsigned int> concatAxisNHWC_pos = {1, 2};
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions, ConvNHWCConcatRestrictionsPos,
::testing::Combine(
::testing::ValuesIn(inputShapesNHWC_pos),
::testing::ValuesIn(concatAxisNHWC_pos),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvNHWCConcatRestrictionsPos::getTestCaseName);
// Negative cases NHWC with concat inside transposes - TODO: this test fails, because the transposes are not removed
//const std::vector<std::vector<size_t>> inputShapesConcatNHWC_neg = {{1, 1, 16, 8}};
//const std::vector<unsigned int> concatAxisConcatNHWC_neg = {1};
//
//INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions, ConvConcatNHWCRestrictionsNeg,
// ::testing::Combine(
// ::testing::ValuesIn(inputShapesConcatNHWC_neg),
// ::testing::ValuesIn(concatAxisConcatNHWC_neg),
// ::testing::ValuesIn(netPrecisions),
// ::testing::ValuesIn(configs),
// ::testing::Values(CommonTestUtils::DEVICE_GNA)),
// ConvConcatNHWCRestrictionsNeg::getTestCaseName);
// Positive cases NHWC with concat inside transposes
const std::vector<std::vector<size_t>> inputShapesConcatNHWC_pos = {{1, 1, 16, 8}};
const std::vector<unsigned int> concatAxisConcatNHWC_pos = {2, 3}; // TODO: 0 fails with unsupported permute,
// because the transposes are not removed
INSTANTIATE_TEST_SUITE_P(smoke_concat_restrictions, ConvConcatNHWCRestrictionsPos,
::testing::Combine(
::testing::ValuesIn(inputShapesConcatNHWC_pos),
::testing::ValuesIn(concatAxisConcatNHWC_pos),
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(configs),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvConcatNHWCRestrictionsPos::getTestCaseName);
} // namespace ConcatTestsDefinitions
@@ -26,7 +26,8 @@ const std::map<std::string, std::string> supportedConfigKeysWithDefaults = {
{GNA_CONFIG_KEY(PWL_MAX_ERROR_PERCENT), "1.000000"},
{CONFIG_KEY(PERF_COUNT), CONFIG_VALUE(NO)},
{GNA_CONFIG_KEY(LIB_N_THREADS), "1"},
{CONFIG_KEY(SINGLE_THREAD), CONFIG_VALUE(YES)}
{CONFIG_KEY(SINGLE_THREAD), CONFIG_VALUE(YES)},
{CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_NONE}
};
class GNAPluginConfigTest : public ::testing::Test {
@@ -216,3 +217,14 @@ TEST_F(GNAPluginConfigTest, GnaConfigGnaCompileTargetTest) {
ExpectThrow(GNA_CONFIG_KEY(COMPILE_TARGET), "GNA_TARGET_1_5");
ExpectThrow(GNA_CONFIG_KEY(COMPILE_TARGET), "GNA_TARGET");
}
TEST_F(GNAPluginConfigTest, GnaConfigLogLevel) {
SetAndCompare(CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_WARNING);
EXPECT_EQ(config.gnaFlags.log_level, PluginConfigParams::LOG_WARNING);
SetAndCompare(CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_NONE);
EXPECT_EQ(config.gnaFlags.log_level, PluginConfigParams::LOG_NONE);
ExpectThrow(CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_ERROR);
ExpectThrow(CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_INFO);
ExpectThrow(CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_DEBUG);
ExpectThrow(CONFIG_KEY(LOG_LEVEL), PluginConfigParams::LOG_TRACE);
}
+1
View File
@@ -685,6 +685,7 @@ int main(int argc, char* argv[]) {
gnaPluginConfig[GNAConfigParams::KEY_GNA_EXEC_TARGET] = FLAGS_exec_target;
gnaPluginConfig[GNAConfigParams::KEY_GNA_COMPILE_TARGET] = FLAGS_compile_target;
gnaPluginConfig[PluginConfigParams::KEY_LOG_LEVEL] = FLAGS_log;
gnaPluginConfig[GNAConfigParams::KEY_GNA_LIB_N_THREADS] =
std::to_string((FLAGS_cw_r > 0 || FLAGS_cw_l > 0) ? 1 : FLAGS_nthreads);
gnaPluginConfig[GNA_CONFIG_KEY(COMPACT_MODE)] = CONFIG_VALUE(NO);
@@ -43,6 +43,11 @@ static const char compile_target_message[] = "Optional. Specify GNA compile targ
"or the latest fully supported generation by the software. "
"See the GNA Plugin's GNA_COMPILE_TARGET config option description.";
/// @brief message for enabling GNA log
static const char enable_log_message[] = "Optional. Enable GNA logging, which may give additional info "
"about potential issues found in network. "
"By default logging is disabled.";
/// @brief message for performance counters
static const char performance_counter_message[] = "Optional. Enables per-layer performance report.";
@@ -140,6 +145,9 @@ DEFINE_string(exec_target, "", execution_target_message);
/// \brief GNA compile target <br>
DEFINE_string(compile_target, "", compile_target_message);
/// \brief GNA log level (default LOG_NONE) <br>
DEFINE_string(log, "LOG_NONE", enable_log_message);
/// \brief Enable per-layer performance report
DEFINE_bool(pc, false, performance_counter_message);
+189 -10
View File
@@ -10,6 +10,7 @@
#include <legacy/graph_tools.hpp>
#include <layers/gna_layer_type.hpp>
#include <layers/gna_layer_info.hpp>
#include "gna_graph_tools.hpp"
namespace GNAPluginNS {
namespace GNALimitations {
@@ -167,8 +168,182 @@ void Validator::ThrowIfNotEmpty(const std::string prefix, const std::string erro
} // namespace Cnn2D
bool AreLayersSupported(InferenceEngine::CNNNetwork& network, std::string& errMessage) {
IE_SUPPRESS_DEPRECATED_START
IE_SUPPRESS_DEPRECATED_START
static bool ValidateConcatAxis(const InferenceEngine::CNNLayerPtr layer, std::string& errMessage) {
LayerInfo info(layer);
auto concat_layer = info.as<InferenceEngine::ConcatLayer*>();
IE_ASSERT(concat_layer);
auto dims_size = concat_layer->insData[0].lock()->getDims().size();
auto in_dims = concat_layer->insData[0].lock()->getDims();
auto concat_axis = concat_layer->_axis;
if (dims_size >= 2) {
InferenceEngine::CNNLayerPtr prev_layer, pre_prev_layer;
// Skip all convolutions in this check, they will be handled during concat primitive creation
auto isFusableWithConv = [](InferenceEngine::CNNLayerPtr ptr) {
return (LayerInfo(ptr).isFusableWithConv() || LayerInfo(ptr).isNonFunctional() ||
(LayerInfo(ptr).isPermute() && ((ptr->input()->getLayout() == InferenceEngine::Layout::NCHW &&
ptr->GetParamAsInts("order") == GetPermuteOrder(InferenceEngine::Layout::NCHW, InferenceEngine::Layout::NHWC)) ||
(ptr->input()->getLayout() == InferenceEngine::Layout::CHW &&
ptr->GetParamAsInts("order") == std::vector<int32_t>{0, 2, 1} /* NCW to NWC */))));
};
for (auto input_idx = 0; input_idx != concat_layer->insData.size(); input_idx++) {
prev_layer = InferenceEngine::CNNNetPrevLayerSkipCertain(layer, input_idx, isFusableWithConv);
if (prev_layer && LayerInfo(prev_layer).isConvolution())
return true;
}
// Look for trivial cases which will be flattened later
// for explanation of what is meant by trivial case,
// look to FlattenTrivialConcatPass comments
// TODO: detection of trivial cases could be moved to one common place
// when all transformations are migrated to ngraph
bool is_not_trivial_concat = false;
// Concatentaion of consts and input parameters only is supported, even if first dimentsion of input parameter > 1
bool concat_all_const_or_inputs = false;
// If concat axis > 0, detect any dimension > 1 before the concat axis
if (concat_axis > 0) {
for (auto axis = 0; axis < concat_axis; axis++) {
if (in_dims[axis] > 1) {
is_not_trivial_concat = true;
break;
}
}
// If concat axis == 0, detect any preceding functional layer's input
// with 0'th dimension > 1, but take into account that some layers need to be skipped
} else {
concat_all_const_or_inputs = true;
for (auto input_idx = 0; input_idx != concat_layer->insData.size(); input_idx++) {
if (concat_layer->insData[input_idx].lock()->getDims()[0] != 1) {
// First we're checking concat input layers
prev_layer = InferenceEngine::CNNNetPrevLayerSkipCertain(concat_layer, input_idx,
[](InferenceEngine::CNNLayerPtr ptr) {
return LayerInfo(ptr).isNonFunctional() || LayerInfo(ptr).isFakeQuantize();
});
IE_ASSERT(prev_layer);
if (LayerInfo(prev_layer).isInput() && prev_layer->outData[0]->getDims()[0] == 1 ||
LayerInfo(prev_layer).isConst()) {
continue;
} else if ((LayerInfo(prev_layer).isInput() && prev_layer->outData[0]->getDims()[0] != 1)) {
is_not_trivial_concat = true;
break;
}
// If it's not clear still if concat is supported,
// we're moving one more layer back to see the dimensions
pre_prev_layer = InferenceEngine::CNNNetPrevLayerSkipCertain(prev_layer, 0,
[](InferenceEngine::CNNLayerPtr ptr) {
return LayerInfo(ptr).isNonFunctional() || LayerInfo(ptr).isFakeQuantize() ||
LayerInfo(ptr).isSplit();
});
IE_ASSERT(pre_prev_layer);
if (LayerInfo(pre_prev_layer).isConst()) {
continue;
}
concat_all_const_or_inputs = false;
if (LayerInfo(pre_prev_layer).isInput() && pre_prev_layer->outData[0]->getDims()[0] == 1)
continue;
if (pre_prev_layer->outData[0]->getDims()[0] != 1) {
is_not_trivial_concat = true;
break;
}
}
}
}
// This is a trivial concat or it isn't a 'not trivial one' :-)
// it can be flattened and we're allowing it
if (!is_not_trivial_concat || concat_all_const_or_inputs)
return true;
// For interleaved inputs start checking from axis 1
// and allow concatenation on axis 0 only when all other dimesions = 1
std::rotate(in_dims.begin(), in_dims.begin() + 1, in_dims.end());
concat_axis == 0 ? concat_axis = dims_size - 1 : concat_axis--;
// Looking for any axis with dimension > 1 before concatentaion axis;
// in general such concatenation is unsupported
auto end_dim = in_dims.begin() + concat_axis;
auto unsupported_concat_axis = std::find_if(in_dims.begin(), end_dim, [](const size_t& in_dim) {
return (in_dim > 1);
});
if (unsupported_concat_axis != end_dim) {
auto dims = concat_layer->insData[0].lock()->getDims();
std::ostringstream in_dims_oss;
std::copy(dims.begin(), dims.end(), std::ostream_iterator<size_t>(in_dims_oss, ","));
errMessage = "[ WARNING ] Topology with layer: " + layer->name + ", type: " + layer->type +
", and concatenation axis(" + std::to_string(concat_layer->_axis) +
") for input dimensions(" + in_dims_oss.str() + ") not supported\n";
return false;
}
}
return true;
}
bool ValidateConvConcatAxis(const InferenceEngine::ConcatLayer* concat_layer) {
IE_ASSERT(concat_layer);
auto dims_size = concat_layer->insData[0].lock()->getDims().size();
if (dims_size >= 2) {
InferenceEngine::CNNLayerPtr prev_layer;
// Skipping here all layers which would disappear or otherwise fuse with convolution in the final GNA graph
auto isFusableWithConv = [](InferenceEngine::CNNLayerPtr ptr) {
return (LayerInfo(ptr).isFusableWithConv() || LayerInfo(ptr).isNonFunctional());
};
auto in_dims = concat_layer->insData[0].lock()->getDims();
auto concat_axis = concat_layer->_axis;
auto concat_layout = concat_layer->input()->getLayout();
for (auto input_idx = 0; input_idx != concat_layer->insData.size(); input_idx++) {
// Supported cases for concatenation of a convolution
prev_layer = InferenceEngine::CNNNetPrevLayerSkipCertain(concat_layer, input_idx, isFusableWithConv);
if (prev_layer && LayerInfo(prev_layer).isConvolution()) {
// Allow concatenation along N axis for non-interleaved primitives
// (currently only convolution)
if (concat_layer->_axis == 0)
break;
// Convert dims to NHWC layout to allow later verification
auto new_order = GetPermuteOrder(concat_layout, InferenceEngine::Layout::NHWC);
InferenceEngine::SizeVector new_dims;
for (size_t i = 0; i < dims_size; ++i) {
new_dims.push_back(in_dims[new_order[i]]);
}
concat_axis = GetPermuteOrder(InferenceEngine::Layout::NHWC, concat_layout)[concat_axis];
// Looking for any axis with dimension > 1 before concatentaion axis;
// in general such concatenation is unsupported
auto end_dim = new_dims.begin() + concat_axis;
auto unsupportedconcat_axis = std::find_if(new_dims.begin(), end_dim, [](const size_t& inDim) {
return (inDim > 1);
});
if (unsupportedconcat_axis != end_dim) {
return false;
}
break;
}
}
}
return true;
}
bool AreLayersSupported(InferenceEngine::CNNNetwork& network, std::string& errMessage, bool userWarning) {
InferenceEngine::InputsDataMap inputs = network.getInputsInfo();
std::unordered_set<InferenceEngine::CNNLayer *> allLayers;
InferenceEngine::CNNLayerPtr startLayer;
@@ -197,32 +372,36 @@ bool AreLayersSupported(InferenceEngine::CNNNetwork& network, std::string& errMe
startLayer = secondLayers.begin()->second;
}
auto batch_size = network.getBatchSize();
bool check_result = true;
InferenceEngine::details::UnorderedDFS(allLayers,
startLayer,
[&](const InferenceEngine::CNNLayerPtr layer) {
LayerInfo info(layer);
if (GNAPluginNS::LayerTypeFromStr(layer->type) == GNAPluginNS::LayerType::NO_TYPE) {
errMessage = "The plugin does not support layer: " + layer->name + ":" + layer->type + "\n";
check_result = false;
}
if (batch_size != 1 && GNAPluginNS::LayerInfo::isBatchSizeConstrained(layer->type)) {
if (batch_size != 1 && info.isBatchSizeConstrained()) {
errMessage = "topology with layer: " + layer->name + ", type: " + layer->type +
", and batch size(" + std::to_string(batch_size) + ") != 1 not supported";
check_result = false;
}
if (GNAPluginNS::LayerInfo(layer).isFullyConnected()) {
size_t output_batch_size = GNAPluginNS::LayerInfo(layer).getOutputBatchSize();
if (info.isFullyConnected()) {
size_t output_batch_size = info.getOutputBatchSize();
if (output_batch_size > 8) {
errMessage = "topology with layer: " + layer->name + ", type: " + layer->type +
", and batch size(" + std::to_string(output_batch_size) + ") not supported";
check_result = false;
errMessage = "topology with layer: " + layer->name + ", type: " + layer->type +
", and batch size(" + std::to_string(output_batch_size) + ") not supported";
check_result = false;
}
} else if (info.isConcat()) {
if (!ValidateConcatAxis(layer, errMessage) && userWarning) {
std::cout << errMessage;
}
}
}, false);
IE_SUPPRESS_DEPRECATED_END
return check_result;
}
IE_SUPPRESS_DEPRECATED_END
} // namespace GNALimitations
} // namespace GNAPluginNS
@@ -8,6 +8,7 @@
#include <cstdint>
#include <cpp/ie_cnn_network.h>
#include <ie_algorithm.hpp>
#include <legacy/ie_layers.h>
namespace GNAPluginNS {
namespace GNALimitations {
@@ -138,12 +139,21 @@ public:
};
} // namespace Cnn2D
bool AreLayersSupported(InferenceEngine::CNNNetwork& network, std::string& errMessage);
bool AreLayersSupported(InferenceEngine::CNNNetwork& network, std::string& errMessage, bool userWarning);
inline size_t GetMinBatchToFitInBuffer(InferenceEngine::DataPtr input) {
auto total_size = InferenceEngine::details::product(std::begin(input->getDims()), std::end(input->getDims()));
return total_size / bufferMaxSize + 1;
}
/**
* @brief Validates if concat layer axis is supported by GNA
* @param layer concat layer
* @return true if concat layer axis is valid
*/
IE_SUPPRESS_DEPRECATED_START
bool ValidateConvConcatAxis(const InferenceEngine::ConcatLayer* concatLayer);
IE_SUPPRESS_DEPRECATED_END
} // namespace GNALimitations
} // namespace GNAPluginNS
@@ -5,6 +5,7 @@
#pragma once
#include <cstdint>
#include "gna/gna_config.hpp"
namespace GNAPluginNS {
struct GNAFlags {
@@ -18,5 +19,6 @@ struct GNAFlags {
bool sw_fp32 = false;
bool performance_counting = false;
bool input_low_precision = false;
std::string log_level = InferenceEngine::PluginConfigParams::LOG_NONE;
};
} // namespace GNAPluginNS
@@ -952,6 +952,16 @@ void GNAGraphCompiler::ConcatPrimitive(InferenceEngine::CNNLayerPtr layer) {
}
}
// Concat axis validation
if (!GNALimitations::ValidateConvConcatAxis(concatLayer) && gnaFlags->log_level == PluginConfigParams::LOG_WARNING) {
std::ostringstream in_dims_oss;
auto in_dims = concatLayer->insData[0].lock()->getDims();
std::copy(in_dims.begin(), in_dims.end(), std::ostream_iterator<size_t>(in_dims_oss, ","));
std::cout << "[ WARNING ] Topology with layer: " + layer->name + ", type: " + layer->type +
", and concatenation axis(" + std::to_string(concatLayer->_axis) +
") for input dimensions(" + in_dims_oss.str() + ") not supported\n";
}
auto& concatLayerInfo = concat_connection.find(concatLayer->name)->second;
for (auto &&outLayer : getInputTo(concatLayer->outData.front())) {
auto concatCandidate = outLayer.second;
+1 -1
View File
@@ -394,4 +394,4 @@ inline std::vector<TranspositionInfo> FindTranspositionInfoFromNextLayers(Infere
return findTranspositionInfoRecursive(layer);
}
} // namespace GNAPluginNS
} // namespace GNAPluginNS
+1 -1
View File
@@ -761,7 +761,7 @@ void GNAPlugin::LoadNetwork(CNNNetwork & _network) {
// Check the input network
std::string error;
if (!GNAPluginNS::GNALimitations::AreLayersSupported(network, error)) {
if (!GNAPluginNS::GNALimitations::AreLayersSupported(network, error, gnaFlags->log_level == PluginConfigParams::LOG_WARNING)) {
THROW_GNA_EXCEPTION << error.c_str();
}
+10 -2
View File
@@ -226,8 +226,15 @@ void Config::UpdateFromMap(const std::map<std::string, std::string>& config) {
} else if (value == PluginConfigParams::NO) {
gnaFlags.gna_openmp_multithreading = true;
} else {
log << "EXCLUSIVE_ASYNC_REQUESTS should be YES/NO, but not" << value;
THROW_GNA_EXCEPTION << "EXCLUSIVE_ASYNC_REQUESTS should be YES/NO, but not" << value;
log << "SINGLE_THREAD should be YES/NO, but not" << value;
THROW_GNA_EXCEPTION << "SINGLE_THREAD should be YES/NO, but not" << value;
}
} else if (key == CONFIG_KEY(LOG_LEVEL)) {
if (value == PluginConfigParams::LOG_WARNING || value == PluginConfigParams::LOG_NONE) {
gnaFlags.log_level = value;
} else {
log << "Currently only LOG_LEVEL = LOG_WARNING or LOG_NONE is supported, not " << value;
THROW_GNA_EXCEPTION << "Currently only LOG_LEVEL = LOG_WARNING and LOG_NONE are supported, not " << value;
}
} else {
IE_THROW(NotFound)
@@ -298,6 +305,7 @@ void Config::AdjustKeyMapValues() {
keyConfigMap[GNA_CONFIG_KEY(LIB_N_THREADS)] = std::to_string(gnaFlags.gna_lib_async_threads_num);
keyConfigMap[CONFIG_KEY(SINGLE_THREAD)] =
gnaFlags.gna_openmp_multithreading ? PluginConfigParams::NO: PluginConfigParams::YES;
keyConfigMap[CONFIG_KEY(LOG_LEVEL)] = gnaFlags.log_level;
}
std::string Config::GetParameter(const std::string& name) const {
@@ -88,9 +88,9 @@ class LayerInfo {
}
return false;
}
static bool isBatchSizeConstrained(const std::string name) {
bool isBatchSizeConstrained() {
static InferenceEngine::details::caseless_set<std::string> layersWithConstrains = {"memory", "convolution"};
return layersWithConstrains.find(name) != layersWithConstrains.end();
return layersWithConstrains.find(layer->name) != layersWithConstrains.end();
}
size_t getOutputBatchSize() const {
if (!layer) {
@@ -339,13 +339,15 @@ class LayerInfo {
bool isCopy() const noexcept {
return isOfType(CopyLayerName) || isOfType(DelayedCopyLayerName);
}
bool isCopyDelayed() const noexcept {
return isOfType(DelayedCopyLayerName);
}
bool isWeightableIdentity() const noexcept {
return isConcatAlignFilter() || isSyntheticScaleShift() || isCropAffined();
}
bool isFusableWithConv() const noexcept {
return isActivation() || isMaxPooling();
}
bool isSynthetic() const noexcept {
return isConcatAlignFilter() || isSyntheticScaleShift() || isConvolutionFilter() || isAffineFilter();
@@ -654,6 +654,8 @@ void RemovePermutationsNHWCToNCHWPass::run() {
THROW_GNA_EXCEPTION << data->getName() <<
" unexpected dimensions size in Permute - Conv - Permute pattern";
}
// HWC layout enum is used here as the only available in CNNNetwork for 3D vectors,
// but the real layout is NCW and it's the one used in order vector later
return dims_size == 4 ? Layout::NHWC : Layout::HWC;
};
@@ -661,6 +663,15 @@ void RemovePermutationsNHWCToNCHWPass::run() {
auto layout = getTransposedLayout(data);
if (data->getLayout() == layout) return;
auto current_layer = getCreatorLayer(data).lock();
if (LayerInfo(current_layer).isConcat()) {
auto concat_layer = dynamic_cast<InferenceEngine::ConcatLayer*> (current_layer.get());
auto dims_size = data->getDims().size();
concat_layer->_axis = (dims_size == 4 ? GetPermuteOrder(Layout::NHWC, Layout::NCHW) :
std::vector<int32_t>{0, 2, 1})[concat_layer->_axis];
}
// NWC->NCW layouts are used here for order vector, see comments a few lines above
auto dims = data->getDims();
auto order = dims.size() == 4 ? GetPermuteOrder(Layout::NCHW, Layout::NHWC) :
std::vector<int32_t>{0, 2, 1};
@@ -971,11 +982,13 @@ void InsertCopyLayerPass::run() {
void FlattenTrivialConcatPass::run() {
OV_ITT_SCOPED_TASK(itt::domains::GNA_LT, "FlattenTrivialConcatPass");
// change all trivial concatenations (concatenation where output buffer is a buffer made by appending input buffers)
// by reshaping its inputs to 1 x total_input_size and its output to 1 x total_cocat_size and chaning the axis to 1
// for example if 4D concat have unaligned inputs then ConcatAlignFilters need to be used if sizes before
// axis are all ones then concat can be changed to 2D for example, lets say all unputs have same shape equal to:
// by reshaping its inputs to 1 x total_input_size and its output to 1 x total_concat_size and changing the axis to 1
// for example if 4D concat have unaligned inputs then ConcatAlignFilters need to be used; if sizes before concat
// axis are all ones then concat can be changed to 2D for example, let's say all inputs have the same shape equal to:
// 1, 1, 5, 3 then for axis 0, 1, 2 the change will be made and inputs will be reshaped to 1, 15,
// but for shape 2, 1, 5, 3 only axis 0 is valid and inputs will reshape to 1, 30
// but for shape 2, 1, 5, 3 only axis 0 is valid and in such case inputs will be reshaped to 1, 30
// TODO: detection of trivial cases could be moved to one common place when all transformations are migrated to ngraph.
// See as well code for detection of unsupported concat
auto quantized = InferenceEngine::getInjectedData<QuantizedLayerParams>(pLayers->front());
auto getLayerByIndex = [](int idx, ConcatLayer* concatLayer) {
@@ -993,21 +1006,26 @@ void FlattenTrivialConcatPass::run() {
if (!concatLayer) continue;
if (concatLayer->insData.size() < 1) continue;
// Skip obvious supported cases
auto dims_size = concatLayer->insData[0].lock()->getDims().size();
if (dims_size < 2 || concatLayer->_axis == dims_size - 1) continue;
// Skip cases which cannot be flattened (these might be unsupported at all)
auto axis = concatLayer->_axis;
bool skip_layer = false;
for (unsigned int i = 0; i < axis; i++) {
if (concatLayer->insData[0].lock()->getDims()[i] != 1) skip_layer = true;
}
if (skip_layer) continue;
// Calculate total input sizes
std::vector<size_t> total_sizes;
for (auto& input : concatLayer->insData) {
auto input_dims = input.lock()->getDims();
total_sizes.push_back(std::accumulate(input_dims.begin(), input_dims.end(), size_t(1), std::multiplies<size_t>()));
}
// Reshape concat inputs
for (size_t input_idx = 0; input_idx != concatLayer->insData.size(); input_idx++) {
auto concatInput = getLayerByIndex(input_idx, concatLayer);
@@ -1020,6 +1038,7 @@ void FlattenTrivialConcatPass::run() {
gnalog() << "\tInserted " << reshapeName << " between " << getCreatorLayer(concatInput).lock()->name << " and " << l->name << std::endl;
}
// Reshape concat outputs back to the original size
for (auto output_idx = 0; output_idx != concatLayer->outData.size(); output_idx++) {
auto output = concatLayer->outData[output_idx];
auto output_tensor_copy = TensorDesc(output->getTensorDesc());