[GNA] Transpose compressing (#18608)

This commit is contained in:
Mikhail Ryzhov 2023-07-21 16:36:30 +02:00 committed by GitHub
parent 40e9ad106e
commit 85ca561546
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 290 additions and 1 deletions

View File

@ -753,6 +753,9 @@ bool Limitations::is_transpose_supported(const ov::Shape& shape) {
max_input_dim <= kTransposeMaxSize) {
return true;
}
} else if (graph_utils::is_one_dim_shape(squeezed_shape)) {
// it means that transpose input has only one dimension > 1
return true;
}
return false;
}

View File

@ -62,6 +62,7 @@
#include "transformations/split_eltwise.hpp"
#include "transformations/substitute_softsign.hpp"
#include "transformations/swap_input_matmul_gna.hpp"
#include "transformations/transpose_compress.hpp"
#include "transformations/transpose_sinking/ts_concat.hpp"
#include "transformations/transpose_sinking/ts_fuse.hpp"
#include "transformations/transpose_sinking/ts_general.hpp"
@ -139,6 +140,7 @@ void TransformationsPipeline::apply(const std::shared_ptr<ov::Model>& model,
manager.register_pass<ov::intel_gna::pass::ReplaceGnaNHWCLayers>();
manager.register_pass<ov::intel_gna::pass::InsertConvolutionTransposeHW>();
manager.register_pass<ov::pass::TransposeSinkingGeneral>();
manager.register_pass<ov::intel_gna::pass::TransposeCompress>();
manager.register_pass<ov::intel_gna::pass::TSConcatForward>();
manager.register_pass<ov::intel_gna::pass::TSSplitBackward>();
manager.register_pass<ov::intel_gna::pass::GatherSinkingGeneral>();

View File

@ -190,7 +190,8 @@ HandleTransposeBeforeMatMul::HandleTransposeBeforeMatMul() {
}
if (prev_node) {
if (Limitations::is_transpose_supported(prev_node->get_output_shape(0))) {
if (graph_utils::is_shape_2d(prev_node->get_output_shape(0)) &&
Limitations::is_transpose_supported(prev_node->get_output_shape(0))) {
InsertTranspose(prev_node, matmul_node->get_friendly_name(), true);
}
}

View File

@ -0,0 +1,94 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/transpose_compress.hpp"
#include "common/graph_utils.hpp"
#include "openvino/cc/ngraph/itt.hpp"
#include "openvino/opsets/opset11.hpp"
#include "openvino/pass/pattern/op/wrap_type.hpp"
using namespace ov::opset11;
using namespace ov::pass;
using namespace ov::intel_gna::pass;
namespace {
inline std::vector<size_t> fix_indexes(const std::vector<size_t>& ids) {
std::vector<size_t> ids_fixed(ids.size());
std::iota(ids_fixed.begin(), ids_fixed.end(), 0);
stable_sort(ids_fixed.begin(), ids_fixed.end(), [&ids](size_t a, size_t b) {
return ids[a] < ids[b];
});
return ids_fixed;
}
} // namespace
TransposeCompress::TransposeCompress() {
MATCHER_SCOPE(TransposeCompress);
auto transpose_const = pattern::wrap_type<Constant>();
auto transpose =
pattern::wrap_type<Transpose>({pattern::any_input(), transpose_const}, [](const ov::Output<ov::Node>& node) {
return !limitations::Limitations::is_transpose_supported(node.get_node_shared_ptr());
});
ov::matcher_pass_callback callback = [=](pattern::Matcher& m) {
const auto& pattern_map = m.get_pattern_value_map();
const auto transpose_order = as_type_ptr<Constant>(pattern_map.at(transpose_const).get_node_shared_ptr());
const auto transpose_node = pattern_map.at(transpose).get_node_shared_ptr();
const ov::Shape& shape = transpose_node->get_input_shape(0);
const ov::Shape& shape_out = transpose_node->get_output_shape(0);
ov::AxisVector axis = transpose_order->get_axis_vector_val();
ov::AxisVector axis_compressed = {};
ov::Shape shape_compressed_out = {};
for (const size_t& axis : axis) {
if (axis_compressed.empty() || (axis - axis_compressed.back()) != 1) {
axis_compressed.push_back(axis);
shape_compressed_out.push_back(shape[axis]);
} else {
shape_compressed_out.back() *= shape[axis];
}
}
// check that compressing is required
if (axis.size() == axis_compressed.size()) {
return false;
}
// correct fused indexes, e.g. (2, 0, 3) -> (1, 0, 2)
ov::AxisVector axis_fused_fixed = fix_indexes(axis_compressed);
size_t fused_sz = axis_fused_fixed.size();
// Restore input shape
ov::Shape shape_fused_in(fused_sz);
for (size_t i = 0; i < fused_sz; ++i) {
shape_fused_in[i] = shape_compressed_out[axis_fused_fixed[i]];
}
if (!limitations::Limitations::is_transpose_supported(shape_fused_in)) {
return false;
}
// Reshape in
auto reshape_in_const =
std::make_shared<Constant>(ov::element::i32, ov::Shape{shape_fused_in.size()}, shape_fused_in);
auto reshape_in = std::make_shared<Reshape>(transpose_node->input_value(0), reshape_in_const, false);
// Transpose
auto transpose_const =
std::make_shared<Constant>(ov::element::i8, ov::Shape{axis_fused_fixed.size()}, axis_fused_fixed);
auto transpose = std::make_shared<Transpose>(reshape_in, transpose_const);
// Reshape out
auto reshape_out_const = std::make_shared<Constant>(ov::element::i32, ov::Shape{shape_out.size()}, shape_out);
auto reshape_out = std::make_shared<Reshape>(transpose, reshape_out_const, false);
//
ov::replace_output_update_name(transpose_node->output(0), reshape_out->output(0));
ov::copy_runtime_info({transpose_node, transpose_order},
{transpose, transpose_const, reshape_in, reshape_in_const});
return true;
};
auto m = std::make_shared<pattern::Matcher>(transpose, matcher_name);
this->register_matcher(m, callback);
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "openvino/pass/graph_rewrite.hpp"
namespace ov {
namespace intel_gna {
namespace pass {
/**
* @brief Reduce the rank of Transpose shape by merging consecutive dimensions
* if those dimensions are not changed by transposition order.
* For example, shape[2,3,4] -> Transpose[2, 0, 1] -> shape[4,2,3] will be replaced with
* shape[6,4] -> Transpose[1, 0] -> shape[4,6]
* If the new Transpose layer is not supported by GNA (see full conditions in the is_transpose_supported())
* then Transpose will not be changed.
*
* [A, B, C, D] [A, B, C, D]
* | |
* Transpose(0, 3, 1, 2) Reshape
* | |
* [A, D, B, C] [A, B*C, D]
* | |
* | Transpose(0, 2, 1)
* | -> |
* | <- [A, D, B*C]
* | |
* | Reshape
* | |
* | [A, D, B, C]
* | |
* Any Layer Any Layer
*/
class TransposeCompress : public ov::pass::MatcherPass {
public:
OPENVINO_RTTI("TransposeCompress", "0");
TransposeCompress();
};
} // namespace pass
} // namespace intel_gna
} // namespace ov

View File

@ -0,0 +1,144 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/transpose_compress.hpp"
#include <gtest/gtest.h>
#include "common_test_utils/ngraph_test_utils.hpp"
#include "openvino/core/model.hpp"
#include "openvino/opsets/opset12.hpp"
using namespace ov::opset12;
namespace transpose_compress_test {
struct TestData {
ov::Shape shape_src;
ov::AxisVector tr_order_src;
ov::Shape shape_ref;
ov::AxisVector tr_order_ref;
};
typedef std::tuple<TestData> // Transpose order
test_params;
class TransposeCompressTest : public CommonTestUtils::TestsCommon, public ::testing::WithParamInterface<test_params> {
public:
static std::string getTestCaseName(const testing::TestParamInfo<test_params>& obj) {
TestData test_shapes;
std::tie(test_shapes) = obj.param;
std::ostringstream result;
result << "InputShape=" << test_shapes.shape_src << "_";
result << "TransposeOrder=" << test_shapes.tr_order_src << "_";
return result.str();
}
virtual void set_test_model() {
auto param = std::make_shared<Parameter>(m_type, m_test_shapes.shape_src);
auto transpose_const = std::make_shared<Constant>(ov::element::i32,
ov::Shape{m_test_shapes.tr_order_src.size()},
m_test_shapes.tr_order_src);
auto transpose = std::make_shared<Transpose>(param, transpose_const);
m_shape_out = transpose->get_output_shape(0);
auto result = std::make_shared<Result>(transpose);
m_model_test =
std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param}, "transpose_compress");
}
virtual void set_ref_model() {
auto param = std::make_shared<Parameter>(m_type, m_test_shapes.shape_src);
auto shape_in_const = std::make_shared<Constant>(ov::element::i32,
ov::Shape{m_test_shapes.shape_ref.size()},
m_test_shapes.shape_ref);
auto shape_in = std::make_shared<Reshape>(param, shape_in_const, false);
auto transpose_const = std::make_shared<Constant>(ov::element::i8,
ov::Shape{m_test_shapes.tr_order_ref.size()},
m_test_shapes.tr_order_ref);
auto transpose = std::make_shared<Transpose>(shape_in, transpose_const);
auto shape_out_const = std::make_shared<Constant>(ov::element::i32, ov::Shape{m_shape_out.size()}, m_shape_out);
auto shape_out = std::make_shared<Reshape>(transpose, shape_out_const, false);
auto result = std::make_shared<Result>(shape_out);
m_model_ref =
std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param}, "transpose_compress");
}
void SetUp() override {
std::tie(m_test_shapes) = this->GetParam();
set_test_model();
set_ref_model();
};
void Validate() {
ov::pass::Manager m;
m.register_pass<ov::pass::InitNodeInfo>();
m.register_pass<ov::intel_gna::pass::TransposeCompress>();
m.run_passes(m_model_test);
check_rt_info(m_model_test);
const FunctionsComparator func_comparator =
FunctionsComparator::with_default().enable(FunctionsComparator::ATTRIBUTES);
const FunctionsComparator::Result result = func_comparator(m_model_test, m_model_ref);
ASSERT_TRUE(result.valid) << result.message;
}
void Run() {
SetUp();
Validate();
}
public:
TestData m_test_shapes;
ov::Shape m_shape_out;
ov::element::Type m_type = ov::element::f32;
std::shared_ptr<ov::Model> m_model_test;
std::shared_ptr<ov::Model> m_model_ref;
};
class TransposeCompressNegTest : public TransposeCompressTest {
void set_ref_model() override {
m_model_ref = m_model_test->clone();
}
};
TEST_P(TransposeCompressTest, CompareWithRefs) {
Run();
}
TEST_P(TransposeCompressNegTest, CompareWithRefs) {
Run();
}
const std::vector<TestData> test_shapes = {{{1, 2, 3}, {1, 2, 0}, {1, 6}, {1, 0}},
{{1, 2, 4}, {1, 2, 0}, {1, 8}, {1, 0}},
{{2, 2, 4}, {1, 2, 0}, {2, 8}, {1, 0}},
{{2, 2, 4, 4}, {2, 3, 0, 1}, {4, 16}, {1, 0}}};
const std::vector<TestData> test_neg_shapes = {{{1, 2, 3, 4}, {1, 0, 2, 3}, {}, {}},
{{1, 2, 3, 4}, {0, 2, 1, 3}, {}, {}},
{{1, 2, 3, 4}, {2, 3, 0, 1}, {}, {}},
{{10, 20}, {1, 0}, {}, {}}};
INSTANTIATE_TEST_SUITE_P(smoke_transpose_compress_test,
TransposeCompressTest,
::testing::Combine(::testing::ValuesIn(test_shapes)),
TransposeCompressTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_transpose_compress_test,
TransposeCompressNegTest,
::testing::Combine(::testing::ValuesIn(test_neg_shapes)),
TransposeCompressNegTest::getTestCaseName);
} // namespace transpose_compress_test