[GNA] Added support permute layer (#723)

* [GNA] Added GNA natively supported permute layer cases.
This commit is contained in:
Andrey Dmitriev 2020-06-09 16:43:01 +03:00 committed by GitHub
parent 18004bdb5e
commit 0bf1f53356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 524 additions and 24 deletions

View File

@ -311,6 +311,65 @@ void GNAPluginNS::backend::AMIntelDNN::InitPiecewiseLinearComponentPrivate(intel
}
}
void GNAPluginNS::backend::AMIntelDNN::InitInterleaveComponentPrivate(intel_dnn_component_t &comp,
uint32_t num_rows_in,
uint32_t num_columns_in,
uint32_t num_bytes_per_input,
uint32_t num_bytes_per_output,
float output_scale_factor,
void *&ptr_inputs,
void *&ptr_outputs,
bool postInitMem) {
comp.num_rows_in = num_rows_in;
comp.num_columns_in = num_columns_in;
comp.num_rows_out = num_columns_in;
comp.num_columns_out = num_rows_in;
comp.num_bytes_per_input = num_bytes_per_input;
comp.num_bytes_per_output = num_bytes_per_output;
comp.operation = kDnnInterleaveOp;
comp.macro_operation = kDnnMacroOpNone;
comp.orientation_in = kDnnNonInterleavedOrientation;
comp.orientation_out = kDnnInterleavedOrientation;
comp.output_scale_factor = output_scale_factor;
comp.input_scale_factor = output_scale_factor;
if (!postInitMem) {
comp.ptr_inputs = ptr_inputs;
comp.ptr_outputs = ptr_outputs;
} else {
ptr_inputs = &comp.ptr_inputs;
ptr_outputs = &comp.ptr_outputs;
}
}
void GNAPluginNS::backend::AMIntelDNN::InitDeinterleaveComponentPrivate(intel_dnn_component_t &comp,
uint32_t num_rows_in,
uint32_t num_columns_in,
uint32_t num_bytes_per_input,
uint32_t num_bytes_per_output,
float output_scale_factor,
void *&ptr_inputs,
void *&ptr_outputs,
bool postInitMem) {
comp.num_rows_in = num_rows_in;
comp.num_columns_in = num_columns_in;
comp.num_rows_out = num_columns_in;
comp.num_columns_out = num_rows_in;
comp.num_bytes_per_input = num_bytes_per_input;
comp.num_bytes_per_output = num_bytes_per_output;
comp.operation = kDnnDeinterleaveOp;
comp.macro_operation = kDnnMacroOpNone;
comp.orientation_in = kDnnInterleavedOrientation;
comp.orientation_out = kDnnNonInterleavedOrientation;
comp.output_scale_factor = output_scale_factor;
comp.input_scale_factor = output_scale_factor;
if (!postInitMem) {
comp.ptr_inputs = ptr_inputs;
comp.ptr_outputs = ptr_outputs;
} else {
ptr_inputs = &comp.ptr_inputs;
ptr_outputs = &comp.ptr_outputs;
}
}
void GNAPluginNS::backend::AMIntelDNN::Propagate() {
for (uint32_t i = 0; i < component.size(); i++) {

View File

@ -193,6 +193,46 @@ public:
true);
}
template<class A, class B>
static void InitDeinterleaveComponent(intel_dnn_component_t &cmp,
uint32_t num_rows_in,
uint32_t num_columns_in,
uint32_t num_bytes_per_input,
uint32_t num_bytes_per_output,
float output_scale_factor,
A *&ptr_inputs,
B *&ptr_outputs) {
InitDeinterleaveComponentPrivate(cmp,
num_rows_in,
num_columns_in,
num_bytes_per_input,
num_bytes_per_output,
output_scale_factor,
(void *&) ptr_inputs,
(void *&) ptr_outputs,
true);
}
template<class A, class B>
static void InitInterleaveComponent(intel_dnn_component_t &cmp,
uint32_t num_rows_in,
uint32_t num_columns_in,
uint32_t num_bytes_per_input,
uint32_t num_bytes_per_output,
float output_scale_factor,
A *&ptr_inputs,
B *&ptr_outputs) {
InitInterleaveComponentPrivate(cmp,
num_rows_in,
num_columns_in,
num_bytes_per_input,
num_bytes_per_output,
output_scale_factor,
(void *&) ptr_inputs,
(void *&) ptr_outputs,
true);
}
template<class A, class B>
static void InitCopyComponent(intel_dnn_component_t &cmp,
@ -343,6 +383,26 @@ private:
intel_pwl_segment_t *ptr_segments,
bool postInitMem);
static void InitInterleaveComponentPrivate(intel_dnn_component_t &cmp,
uint32_t num_rows_in,
uint32_t num_columns_in,
uint32_t num_bytes_per_input,
uint32_t num_bytes_per_output,
float output_scale_factor,
void *&ptr_inputs,
void *&ptr_outputs,
bool postInitMem);
static void InitDeinterleaveComponentPrivate(intel_dnn_component_t &cmp,
uint32_t num_rows_in,
uint32_t num_columns_in,
uint32_t num_bytes_per_input,
uint32_t num_bytes_per_output,
float output_scale_factor,
void *&ptr_inputs,
void *&ptr_outputs,
bool postInitMem);
static void InitConvolutional1DComponentPrivate(intel_dnn_component_t &comp,
uint32_t num_rows_in,
uint32_t num_columns_in,

View File

@ -1412,30 +1412,76 @@ case name:\
}
void GNAGraphCompiler::PermutePrimitive(InferenceEngine::CNNLayerPtr layer) {
if (LayerInfo(layer).isTrivialPermute()) {
return;
}
auto layerOrder = layer->GetParamAsInts("order");
auto quantized = InferenceEngine::getInjectedData<QuantizedLayerParams>(layer);
auto inputs = layer->insData.begin()->lock();
auto inputsOrder = inputs->getTensorDesc().getDims();
auto outputs = layer->outData.front();
string dimMessage;
if (layerOrder == vector<int>({0, 3, 2, 1})) {
return; // supported case
// squeeze order vector
SizeVector squeezedInputOrder;
for (auto input_shape : inputsOrder) {
if (input_shape != 1) squeezedInputOrder.push_back(input_shape);
}
SizeVector squeezedOutputOrder;
for (auto output_shape : layerOrder) {
if (output_shape != 0) squeezedOutputOrder.push_back(output_shape);
}
if (layerOrder == vector<int>({1, 0, 2})) {
IE_ASSERT(!layer->insData.empty());
auto inputs = layer->insData.begin()->lock();
auto inputs_size = inputs->getTensorDesc().getDims().size();
if (inputs_size != layerOrder.size()) {
THROW_IE_EXCEPTION << "[GNA plugin] Invalid input tensor size for permute layer " <<
layer->GetParamAsString("order");
}
auto permuteDim0 = FROM_IR_DIM(inputs, inputs_size);
auto permuteDim1 = FROM_IR_DIM(inputs, inputs_size - 1);
if (permuteDim0 == 1 || permuteDim1 == 1) {
return; // supported case
}
dimMessage = " (with first dim = " + to_string(permuteDim0) + ", second dim = " + to_string(permuteDim1) + ")";
void* ptr_inputs = nullptr;
void* ptr_outputs = nullptr;
if (squeezedInputOrder.size() > 2) {
THROW_GNA_LAYER_EXCEPTION(layer) << "unsupported permute (requested transpose is not 2D)";
}
THROW_IE_EXCEPTION << "[GNA plugin] Unsupported permute order: was " << layer->GetParamAsString("order") <<
dimMessage << ", but only support 1,0,2 (with first or second dim = 1) and 0,3,2,1";
if (std::min(squeezedInputOrder[0], squeezedInputOrder[1]) > 8) {
THROW_GNA_LAYER_EXCEPTION(layer) << "unsupported permute (minor dimension="
<< std::min(squeezedInputOrder[0], squeezedInputOrder[1]) << " > 8)";
}
// now this can be run on GNA
if (squeezedInputOrder[0] < squeezedInputOrder[1]) { // interleave case
if (ALIGN(squeezedInputOrder[1], 8) != squeezedInputOrder[1]) {
THROW_GNA_LAYER_EXCEPTION(layer) << "unsupported permute (row size not a multiple of 8)";
} else {
auto& currentComponent = dnnComponents.addComponent(layer->name, "interleave");
dnn->InitInterleaveComponent(currentComponent,
squeezedInputOrder[0],
squeezedInputOrder[1],
inputs->getPrecision().size(),
outputs->getPrecision().size(),
(quantized == nullptr) ? 1.0f : quantized->_dst_quant.scale,
ptr_inputs,
ptr_outputs);
}
} else { // deinterleave case
if (ALIGN(squeezedInputOrder[0], 8) != squeezedInputOrder[0]) {
THROW_GNA_LAYER_EXCEPTION(layer) << "[GNA plugin] unsupported permute (column size not a multiple of 8)";
} else {
auto& currentComponent = dnnComponents.addComponent(layer->name, "deinterleave");
dnn->InitDeinterleaveComponent(currentComponent,
squeezedInputOrder[0],
squeezedInputOrder[1],
inputs->getPrecision().size(),
outputs->getPrecision().size(),
quantized == nullptr ? 1 : quantized->_dst_quant.scale,
ptr_inputs,
ptr_outputs);
}
}
size_t num_data_bytes_out = ALIGN(InferenceEngine::details::product(
begin(outputs->getDims()), end(outputs->getDims())), 8)
* outputs->getPrecision().size();
size_t num_data_bytes_in = squeezedInputOrder[0] * squeezedInputOrder[1] * inputs->getPrecision().size();
connectInput(layer, ptr_inputs, num_data_bytes_in);
connectOutput(layer, ptr_outputs, num_data_bytes_out);
}
void SKIP(GNAGraphCompiler*, CNNLayerPtr) {}
@ -1795,9 +1841,14 @@ GNAPluginNS::ConnectionDetails GNAGraphCompiler::connectInput(CNNLayerPtr layer,
return connectInput(prevLayer, ptr, num_data_bytes_in, offset, 0);
}
// permute layer resulted in trivial permute
if (LayerInfo(prevLayer).isPermute()) {
gnalog() << "Skipping permute layer: " << prevLayer->name << "\n";
return {connectInput(prevLayer, ptr, num_data_bytes_in, offset, 0).input, true, prevLayer};
if (!LayerInfo(prevLayer).isTrivialPermute()) {
// we should have GNA primitive for it
THROW_GNA_EXCEPTION << "missed gna primitive for permute: " << prevLayer->name;
}
gnalog() << "Skipping trivial permute layer: " << prevLayer->name << "\n";
return connectInput(prevLayer, ptr, num_data_bytes_in, offset, 0);
}

View File

@ -11,6 +11,7 @@
#include "details/caseless.hpp"
#include "ie_algorithm.hpp"
#include "gna-api.h"
#include "gna_permute.hpp"
namespace GNAPluginNS {
@ -194,6 +195,42 @@ class LayerInfo {
bool isPermute() const noexcept {
return isOfType("permute");
}
// @brief this not only mathematically trivial, has some WA for kaldi case
bool isTrivialPermute() {
if (!isPermute()) return false;
auto layerOrder = layer->GetParamAsInts("order");
if (layerOrder == std::vector<int>({ 0, 3, 2, 1 })) {
return true; // supported case
}
auto inputs = layer->insData.begin()->lock();
auto inputsOrder = inputs->getTensorDesc().getDims();
// cases when all permutations happened either between 1 and X shape where no other dims in between
auto permuteSequence = genPermutations(layerOrder.begin(), layerOrder.end());
auto inputsOrderTransformed = inputsOrder;
for (auto && permute : permuteSequence) {
// check dims of permuted
if (inputsOrderTransformed[permute.first] == 1 &&
inputsOrderTransformed[permute.second] == 1) {
return true;
}
if (inputsOrderTransformed[permute.first] != 1 &&
inputsOrderTransformed[permute.second] != 1) {
return false;
}
// check dims in between
for (int j = permute.first + 1; j != permute.second; j++) {
if (inputsOrderTransformed[j] != 1) {
return false;
}
}
// apply permutation
std::swap(inputsOrderTransformed[permute.first], inputsOrderTransformed[permute.second]);
}
return true;
}
bool isPooling() const noexcept {
return isOfType("pooling");
}

View File

@ -0,0 +1,105 @@
// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <vector>
#include <list>
#include <utility>
#include "gna_plugin_log.hpp"
namespace GNAPluginNS {
template <class T>
class PermuteSequence {
public:
using cnt_type = std::vector<std::pair<T, T>>;
private:
std::vector<T> orderVec;
cnt_type permutes;
public:
explicit PermuteSequence(std::vector<T> && orderVecIn) : orderVec(std::move(orderVecIn)) {
std::vector<bool> counter(orderVec.size());
for (auto && x : this->orderVec) {
if (x < 0) {
THROW_GNA_EXCEPTION << "invalid order: element " << x << " should be >= 0";
}
if (x >= counter.size()) {
THROW_GNA_EXCEPTION << "invalid order: element " << x << " should be < "<< counter.size();
}
if (counter[x]) {
THROW_GNA_EXCEPTION << "invalid order: element " << x << " present more than once";
}
counter[x] = true;
}
// generating permutation graph
std::fill(counter.begin(), counter.end(), false);
// length of current cycle
std::list<cnt_type> permuteCycles;
int seqId = 0;
bool newSeq = false;
for (int i = 0; i != orderVec.size();) {
// we have this permutation on the list already
if (counter[i]) {
newSeq = false;
i++;
continue;
}
counter[i] = true;
// looks we found a permutation
if (orderVec[i] != i) {
if (!newSeq) {
newSeq = true;
permuteCycles.push_back({});
}
permuteCycles.back().push_back({i, orderVec[i]});
counter[i] = true;
i = orderVec[i];
continue;
}
// this dims not permuted
i++;
}
for (auto && cycle : permuteCycles) {
for (int i = 0; i + 1 < cycle.size(); i++) {
permutes.push_back(cycle[i]);
}
}
}
const cnt_type & cnt() const noexcept {
return permutes;
}
};
/**
* @brief generates permutations sequence in order to reach given order
* @tparam Iterator
* @return
*/
template <class Iterator>
inline typename PermuteSequence<typename std::iterator_traits<Iterator>::value_type>::cnt_type genPermutations(
Iterator beg, Iterator en) {
static_assert(
std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<Iterator>::iterator_category>::value,
"The genPermutations() function only accepts random access iterators or raw pointers to an array.\n");
using value_type = typename std::iterator_traits<Iterator>::value_type;
std::vector<value_type> v;
for (; beg != en; beg++) {
v.push_back(*beg);
}
auto permute = PermuteSequence<value_type> (std::move(v));
return permute.cnt();
}
template <class T>
inline typename PermuteSequence<T>::cnt_type genPermutations(const std::initializer_list<T> & lst) {
return genPermutations(lst.begin(), lst.end());
}
} // namespace GNAPluginNS

View File

@ -0,0 +1,28 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#include <vector>
#include "subgraph_tests/reshape_permute_reshape.hpp"
#include "common_test_utils/test_constants.hpp"
using namespace LayerTestsDefinitions;
namespace {
std::vector<std::vector<std::vector<size_t>>> inputs{
{{1, 4 , 160}, {0, 2, 1}},
{{8, 16}, {1, 0}},
{{1, 1, 4, 16}, {3, 1, 2, 0}},
{{1, 8, 200}, {0, 2, 1}},
{{1, 8, 16}, {2, 1, 0}},
};
std::vector<InferenceEngine::Precision> netPrecisions = {InferenceEngine::Precision::FP32,
InferenceEngine::Precision::FP16,
};
INSTANTIATE_TEST_CASE_P(reshape_permute_reshape, ReshapePermuteReshape,
::testing::Combine(
::testing::ValuesIn(inputs),
::testing::ValuesIn(netPrecisions),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ReshapePermuteReshape::getTestCaseName);
} // namespace

View File

@ -0,0 +1,30 @@
// 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"
#include "ngraph_functions/utils/ngraph_helpers.hpp"
#include "ngraph_functions/builders.hpp"
namespace LayerTestsDefinitions {
typedef std::tuple<
std::vector<std::vector<size_t>>, //input shapes and permute shapes
InferenceEngine::Precision, //Network precision
std::string //Device name
> ReshapePermuteReshapeTuple;
class ReshapePermuteReshape : public testing::WithParamInterface<ReshapePermuteReshapeTuple>,
public LayerTestsUtils::LayerTestsCommon {
public:
static std::string getTestCaseName(const testing::TestParamInfo<ReshapePermuteReshapeTuple> &obj);
protected:
void SetUp() override;
};
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,56 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#include <tuple>
#include <string>
#include <vector>
#include <memory>
#include <debug.h>
#include "common_test_utils/common_utils.hpp"
#include "functional_test_utils/precision_utils.hpp"
#include "functional_test_utils/skip_tests_config.hpp"
#include "subgraph_tests/reshape_permute_reshape.hpp"
namespace LayerTestsDefinitions {
std::string ReshapePermuteReshape::getTestCaseName(const testing::TestParamInfo<ReshapePermuteReshapeTuple> &obj) {
std::vector<std::vector<size_t >> input;
InferenceEngine::Precision netPrecision;
std::string targetName;
std::tie(input, netPrecision, targetName) = obj.param;
std::ostringstream results;
results << "IS=" << CommonTestUtils::vec2str(input[0]) << "_";
results << "netPRC=" << netPrecision.name() << "_";
results << "targetDevice=" << targetName << "_";
return results.str();
}
void ReshapePermuteReshape::SetUp() {
std::vector<std::vector<size_t >> inputs;
InferenceEngine::Precision netPrecision;
std::tie(inputs, netPrecision, targetDevice) = this->GetParam();
const std::size_t input_dim = InferenceEngine::details::product(inputs[0]);
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
std::vector<size_t> shape_input{1, input_dim};
auto input = ngraph::builder::makeParams(ngPrc, {shape_input});
auto reshape1_pattern = std::make_shared<ngraph::op::Constant>(ngraph::element::i64,
ngraph::Shape{inputs[0].size()},
inputs[0]);
auto reshape1 = std::make_shared<ngraph::op::v1::Reshape>(input[0], reshape1_pattern, false);
auto permute_params = std::make_shared<ngraph::opset1::Constant>(ngraph::element::i64,
ngraph::Shape{inputs[1].size()},
inputs[1]);
auto permute = std::make_shared<ngraph::opset1::Transpose>(reshape1, permute_params);
auto reshape2_pattern = std::make_shared<ngraph::op::Constant>(ngraph::element::i64,
ngraph::Shape{2},
std::vector<size_t>{1, input_dim});
auto reshape2 = std::make_shared<ngraph::op::v1::Reshape>(permute, reshape2_pattern, false);
function = std::make_shared<ngraph::Function>(reshape2, input, "reshape_permute_reshape");
}
TEST_P(ReshapePermuteReshape, CompareWithRefs){
Run();
if (targetDevice == std::string{CommonTestUtils::DEVICE_GPU}) {
PluginCache::get().reset();
} };
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,74 @@
// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include <gtest/gtest.h>
#include <layers/gna_permute.hpp>
using namespace GNAPluginNS;
class PermuteSequenceTest : public ::testing::Test {
};
TEST_F(PermuteSequenceTest, testImpossiblePermute_neg_value) {
ASSERT_ANY_THROW(genPermutations({-1}));
}
TEST_F(PermuteSequenceTest, testImpossiblePermute_big_value) {
ASSERT_ANY_THROW(genPermutations({1}));
}
TEST_F(PermuteSequenceTest, testImpossiblePermute_big_value_2) {
ASSERT_ANY_THROW(genPermutations({0, 2}));
}
TEST_F(PermuteSequenceTest, testImpossiblePermute_same_value) {
ASSERT_ANY_THROW(genPermutations({0, 1, 0}));
}
TEST_F(PermuteSequenceTest, testIdentity1d) {
ASSERT_EQ(0, genPermutations({0}).size());
}
TEST_F(PermuteSequenceTest, testIdentity2d) {
ASSERT_EQ(0, genPermutations({0, 1}).size());
}
TEST_F(PermuteSequenceTest, testIdentity3d) {
ASSERT_EQ(0, genPermutations({0, 1, 2}).size());
}
TEST_F(PermuteSequenceTest, testIdentity4d) {
ASSERT_EQ(0, genPermutations({0, 1, 2, 3}).size());
}
TEST_F(PermuteSequenceTest, test2d) {
ASSERT_EQ(1, genPermutations({1, 0}).size());
ASSERT_EQ(std::make_pair(0, 1), genPermutations({1, 0}).front());
}
TEST_F(PermuteSequenceTest, test3d_1_Permutation) {
ASSERT_EQ(1, genPermutations({1, 0, 2}).size());
ASSERT_EQ(std::make_pair(0, 1), genPermutations({1, 0, 2})[0]);
}
TEST_F(PermuteSequenceTest, test3d_1_Permutation_2) {
ASSERT_EQ(1, genPermutations({2, 1, 0}).size());
ASSERT_EQ(std::make_pair(0, 2), genPermutations({2, 1, 0})[0]);
}
TEST_F(PermuteSequenceTest, test3d_2_Permutations) {
ASSERT_EQ(2, genPermutations({2, 0, 1}).size());
ASSERT_EQ(std::make_pair(0, 2), genPermutations({2, 0, 1})[0]);
ASSERT_EQ(std::make_pair(2, 1), genPermutations({2, 0, 1})[1]);
}
TEST_F(PermuteSequenceTest, test3d_4_Permutation) {
auto permutation = {2, 1, 4, 5, 6, 3, 0, 7};
ASSERT_EQ(4, genPermutations(permutation).size());
ASSERT_EQ(std::make_pair(0, 2), genPermutations(permutation)[0]);
ASSERT_EQ(std::make_pair(2, 4), genPermutations(permutation)[1]);
ASSERT_EQ(std::make_pair(4, 6), genPermutations(permutation)[2]);
ASSERT_EQ(std::make_pair(3, 5), genPermutations(permutation)[3]);
}

View File

@ -64,9 +64,9 @@ const Permute3dimTestParam gna_permute3d_test_params[] = {
{{{1, 0, 2}, {8, 1, 1}}, true},
{{{1, 0, 2}, {4, 2, 1}}, false},
{{{1, 0, 2}, {2, 4, 1}}, false},
{{{1, 2, 0}, {1, 2, 4}}, false},
{{{0, 1, 2}, {1, 2, 4}}, false},
{{{0, 2, 1}, {2, 1, 4}}, false},
{{{1, 2, 0}, {1, 2, 4}}, true},
{{{0, 1, 2}, {1, 2, 4}}, true},
{{{0, 2, 1}, {2, 1, 4}}, true},
{{{2, 0, 1}, {1, 2, 4}}, false},
{{{2, 1, 0}, {2, 1, 4}}, false}
};