Compare commits
12 Commits
dependabot
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
43de73cbd6 | |
|
|
1d226d4edc | |
|
|
0908908227 | |
|
|
4e6ee7f15e | |
|
|
f5c3fc3330 | |
|
|
8eaa65e026 | |
|
|
91b3977169 | |
|
|
00f4c99074 | |
|
|
5393b8a77a | |
|
|
deb5e63ff2 | |
|
|
395d6b7e09 | |
|
|
6e2286086f |
|
|
@ -33,6 +33,7 @@ function(build_docs)
|
|||
set(DOXYGEN_MAPPING_SCRIPT "${SCRIPTS_DIR}/create_mapping.py")
|
||||
set(DOCS_MAPPING_SCRIPT "${SCRIPTS_DIR}/create_doc_mapping.py")
|
||||
set(BREATHE_APIDOC_SCRIPT "${SCRIPTS_DIR}/apidoc.py")
|
||||
set(OV_INSTALLATION_SCRIPT "${SCRIPTS_DIR}/install_appropriate_openvino_version.py")
|
||||
|
||||
# Doxygen/Sphinx setup
|
||||
set(DOXYGEN_XML_OUTPUT "${DOCS_BUILD_DIR}/xml")
|
||||
|
|
@ -62,7 +63,9 @@ function(build_docs)
|
|||
|
||||
if(${ENABLE_PYTHON_API})
|
||||
list(APPEND commands COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --green "STARTED preprocessing OpenVINO Python API")
|
||||
list(APPEND commands COMMAND ${Python3_EXECUTABLE} -m pip install openvino)
|
||||
list(APPEND commands COMMAND ${Python3_EXECUTABLE} ${OV_INSTALLATION_SCRIPT}
|
||||
--ov_dir=${SPHINX_SETUP_DIR}
|
||||
--python=${Python3_EXECUTABLE})
|
||||
list(APPEND commands COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --green "FINISHED preprocessing OpenVINO Python API")
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b78713020891a6655e3860338b7db430840560d8f2860162eca3d00b84533f24
|
||||
size 36362
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:353c5b7f35a8f9f6c2c3074485a5a4e51f8c6ee776214444666da4037cc44832
|
||||
size 45347
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:696672a8c38260147f2fa68d266866ab4390ef919fa092cea1149b0eb529c68e
|
||||
size 58557
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d378718b968f6a40555b31bf54af83fac978c3bfc07e234f9b1370229222cccd
|
||||
size 31092
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6b3511899521a49c1bfdc569e16b8ad1e12157fe107d2b86d25ee04ff67a85b2
|
||||
size 43066
|
||||
|
|
@ -37,7 +37,7 @@ compiled_model_3 = core.compile_model(
|
|||
# ! [ov:intel_cpu:multi_threading:part0]
|
||||
|
||||
# ! [ov:intel_cpu:multi_threading:part1]
|
||||
# Disable CPU threads pinning for inference when the system supports it
|
||||
# Disable CPU thread pinning for inference when the system supports it
|
||||
compiled_model_4 = core.compile_model(
|
||||
model=model,
|
||||
device_name=device_name,
|
||||
|
|
|
|||
|
|
@ -171,8 +171,8 @@ if (m->match(node->output(0))) {
|
|||
bool openvino_api_examples(std::shared_ptr<ov::Node> node) {
|
||||
{
|
||||
// ! [ov:ports_example]
|
||||
// Let's suppose that node is opset8::Convolution operation
|
||||
// as we know opset8::Convolution has two input ports (data, weights) and one output port
|
||||
// Let's suppose that node is of ov::op::v0::Convolution type.
|
||||
// As we know ov::op::v0::Convolution has two input ports (data, weights) and one output port.
|
||||
ov::Input<ov::Node> data = node->input(0);
|
||||
ov::Input<ov::Node> weights = node->input(1);
|
||||
ov::Output<ov::Node> output = node->output(0);
|
||||
|
|
@ -181,7 +181,7 @@ ov::Output<ov::Node> output = node->output(0);
|
|||
auto pshape = data.get_partial_shape();
|
||||
auto el_type = data.get_element_type();
|
||||
|
||||
// Getting parent for input port
|
||||
// Getting parent for input port i.e. Output mapped by the input
|
||||
ov::Output<ov::Node> parent_output;
|
||||
parent_output = data.get_source_output();
|
||||
|
||||
|
|
@ -216,15 +216,15 @@ return true;
|
|||
|
||||
// ! [ov:replace_node]
|
||||
bool ov_replace_node(std::shared_ptr<ov::Node> node) {
|
||||
// Step 1. Verify that node has opset8::Negative type
|
||||
auto neg = std::dynamic_pointer_cast<ov::opset8::Negative>(node);
|
||||
// Step 1. Verify that node is of type ov::op::v0::Negative
|
||||
auto neg = std::dynamic_pointer_cast<ov::op::v0::Negative>(node);
|
||||
if (!neg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2. Create opset8::Multiply operation where the first input is negative operation input and second as Constant with -1 value
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(neg->input_value(0),
|
||||
ov::opset8::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
// Step 2. Create ov::op::v1::Multiply operation with the first input being the output going into Negative and second as Constant with -1 value
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(neg->input_value(0),
|
||||
ov::op::v0::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
|
||||
mul->set_friendly_name(neg->get_friendly_name());
|
||||
ov::copy_runtime_info(neg, mul);
|
||||
|
|
@ -233,18 +233,18 @@ bool ov_replace_node(std::shared_ptr<ov::Node> node) {
|
|||
ov::replace_node(neg, mul);
|
||||
return true;
|
||||
|
||||
// Step 4. Negative operation will be removed automatically because all consumers was moved to Multiply operation
|
||||
// Step 4. Negative operation will be removed automatically because all consumers were moved to Multiply operation
|
||||
}
|
||||
// ! [ov:replace_node]
|
||||
|
||||
bool ov_manual_replace_node(std::shared_ptr<ov::Node> node) {
|
||||
auto neg = std::dynamic_pointer_cast<ov::opset8::Negative>(node);
|
||||
auto neg = std::dynamic_pointer_cast<ov::op::v0::Negative>(node);
|
||||
if (!neg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(neg->input_value(0),
|
||||
ov::opset8::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(neg->input_value(0),
|
||||
ov::op::v0::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
|
||||
mul->set_friendly_name(neg->get_friendly_name());
|
||||
ov::copy_runtime_info(neg, mul);
|
||||
|
|
@ -262,8 +262,8 @@ void insert_example(std::shared_ptr<ov::Node> node) {
|
|||
// Get all consumers for node
|
||||
auto consumers = node->output(0).get_target_inputs();
|
||||
|
||||
// Step 2. Create new node. Let it be opset8::Relu.
|
||||
auto new_node = std::make_shared<ov::opset8::Relu>(node);
|
||||
// Step 2. Create new node ov::op::v0::Relu.
|
||||
auto new_node = std::make_shared<ov::op::v0::Relu>(node);
|
||||
|
||||
// Step 3. Reconnect all consumers to new_node
|
||||
for (auto input : consumers) {
|
||||
|
|
@ -277,7 +277,7 @@ void insert_example_with_copy(std::shared_ptr<ov::Node> node) {
|
|||
// Make a node copy
|
||||
auto node_copy = node->clone_with_new_inputs(node->input_values());
|
||||
// Create new node
|
||||
auto new_node = std::make_shared<ov::opset8::Relu>(node_copy);
|
||||
auto new_node = std::make_shared<ov::op::v0::Relu>(node_copy);
|
||||
ov::replace_node(node, new_node);
|
||||
}
|
||||
// ! [ov:insert_node_with_copy]
|
||||
|
|
@ -290,12 +290,12 @@ bool success = ov::replace_output_update_name(node->output(0), node->input_value
|
|||
}
|
||||
|
||||
void replace_friendly_name() {
|
||||
auto div = std::make_shared<ov::opset8::Divide>();
|
||||
auto div = std::make_shared<ov::op::v1::Divide>();
|
||||
// ! [ov:replace_friendly_name]
|
||||
// Replace Div operation with Power and Multiply sub-graph and set original friendly name to Multiply operation
|
||||
auto pow = std::make_shared<ov::opset8::Power>(div->input(1).get_source_output(),
|
||||
auto pow = std::make_shared<ov::op::v1::Power>(div->input(1).get_source_output(),
|
||||
ov::op::v0::Constant::create(div->get_input_element_type(1), ov::Shape{1}, {-1}));
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(div->input(0).get_source_output(), pow);
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(div->input(0).get_source_output(), pow);
|
||||
mul->set_friendly_name(div->get_friendly_name());
|
||||
ov::replace_node(div, mul);
|
||||
// ! [ov:replace_friendly_name]
|
||||
|
|
@ -304,10 +304,10 @@ ov::replace_node(div, mul);
|
|||
void constant_subgraph() {
|
||||
// ! [ov:constant_subgraph]
|
||||
// After ConstantFolding pass Power will be replaced with Constant
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::Shape{1});
|
||||
auto pow = std::make_shared<ov::opset8::Power>(ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {2}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {3}));
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(input /* not constant input */, pow);
|
||||
auto input = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1});
|
||||
auto pow = std::make_shared<ov::op::v1::Power>(ov::op::v0::Constant::create(ov::element::f32, ov::Shape{1}, {2}),
|
||||
ov::op::v0::Constant::create(ov::element::f32, ov::Shape{1}, {3}));
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(input /* not constant input */, pow);
|
||||
// ! [ov:constant_subgraph]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// ! [ov::imports]
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common_test_utils/matcher.hpp"
|
||||
#include "openvino/op/abs.hpp"
|
||||
#include "openvino/op/add.hpp"
|
||||
#include "openvino/op/matmul.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/relu.hpp"
|
||||
#include "openvino/op/sigmoid.hpp"
|
||||
#include "openvino/pass/pattern/op/optional.hpp"
|
||||
#include "openvino/pass/pattern/op/or.hpp"
|
||||
#include "openvino/pass/pattern/op/wrap_type.hpp"
|
||||
#include "transformations/utils/utils.hpp"
|
||||
|
||||
using namespace ov;
|
||||
using namespace ov::pass;
|
||||
using namespace std;
|
||||
// ! [ov::imports]
|
||||
|
||||
// ! [ov:create_simple_model_and_pattern]
|
||||
TEST(pattern, simple_model_and_pattern) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = std::make_shared<ov::op::v0::MatMul>(pattern::any_input(), pattern::any_input(), false, false);
|
||||
auto pattern_abs = std::make_shared<ov::op::v0::Abs>(pattern_mul->output(0));
|
||||
auto pattern_relu = std::make_shared<ov::op::v0::Relu>(pattern_abs->output(0));
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:create_simple_model_and_pattern]
|
||||
|
||||
|
||||
// ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
TEST(pattern, simple_model_and_pattern_wrap_type) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
|
||||
// ! [ov:wrap_type_list]
|
||||
TEST(pattern, wrap_type_list) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
auto model_sig = std::make_shared<ov::op::v0::Sigmoid>(model_abs->output(0));
|
||||
auto model_result1 = std::make_shared<ov::op::v0::Result>(model_sig->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu, ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_sig));
|
||||
}
|
||||
// ! [ov:wrap_type_list]
|
||||
|
||||
void patterns_misc() {
|
||||
// ! [ov:any_input]
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
// ! [ov:any_input]
|
||||
|
||||
// ! [ov:wrap_type_predicate]
|
||||
ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern::any_input()}, pattern::consumers_count(2));
|
||||
// ! [ov:wrap_type_predicate]
|
||||
|
||||
|
||||
// ! [ov:any_input_predicate]
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input([](const Output<Node>& value){
|
||||
return value.get_shape().size() == 4;}),
|
||||
pattern::any_input([](const Output<Node>& value){
|
||||
return value.get_shape().size() == 4;})});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
// ! [ov:any_input_predicate]
|
||||
|
||||
|
||||
// ! [ov:optional_predicate]
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu, pattern::consumers_count(2));
|
||||
// ! [ov:optional_predicate]
|
||||
}
|
||||
|
||||
|
||||
// ! [ov::pattern_or]
|
||||
TEST(pattern, pattern_or) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a red branch
|
||||
auto red_pattern_add = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto red_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({red_pattern_add->output(0)});
|
||||
auto red_pattern_sigmoid = ov::pass::pattern::wrap_type<ov::op::v0::Sigmoid>({red_pattern_relu->output(0)});
|
||||
|
||||
// Create a blue branch
|
||||
auto blue_pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto blue_pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({blue_pattern_mul->output(0)});
|
||||
auto blue_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({blue_pattern_abs->output(0)});
|
||||
|
||||
// Create Or node
|
||||
auto pattern_or = std::make_shared<ov::pass::pattern::op::Or>(OutputVector{red_pattern_sigmoid->output(0), blue_pattern_relu->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_or, model_relu));
|
||||
}
|
||||
// ! [ov::pattern_or]
|
||||
|
||||
|
||||
// ! [ov:pattern_optional_middle]
|
||||
TEST(pattern, pattern_optional_middle) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern with an Optional node in the middle
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_sig_opt->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_optional_middle]
|
||||
|
||||
|
||||
// ! [ov:pattern_optional_top]
|
||||
TEST(pattern, pattern_optional_top) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern::any_input());
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern_sig_opt, pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_optional_top]
|
||||
|
||||
|
||||
// ! [ov:pattern_optional_root]
|
||||
TEST(pattern, pattern_optional_root) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu);
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_optional_root]
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
# Copyright (C) 2018-2024 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# ! [ov:imports]
|
||||
import pytest
|
||||
from openvino import PartialShape
|
||||
from openvino.runtime import opset13 as ops
|
||||
from openvino.runtime.passes import Matcher, WrapType, Or, AnyInput, Optional
|
||||
# ! [ov:imports]
|
||||
from openvino.runtime.passes import (
|
||||
consumers_count,
|
||||
has_static_dim,
|
||||
has_static_dims,
|
||||
has_static_shape,
|
||||
has_static_rank,
|
||||
type_matches,
|
||||
type_matches_any,
|
||||
)
|
||||
|
||||
# ! [ov:create_simple_model_and_pattern]
|
||||
def simple_model_and_pattern():
|
||||
# Create a sample model
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = ops.matmul(AnyInput(), AnyInput(), False, False)
|
||||
pattern_abs = ops.abs(pattern_mul)
|
||||
pattern_relu = ops.relu(pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:create_simple_model_and_pattern]
|
||||
|
||||
# ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
def simple_model_and_pattern_wrap_type():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
# ! [ov:wrap_type_list]
|
||||
def wrap_type_list():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
model_sig = ops.sigmoid(model_abs) # Note that we've added a Sigmoid node after Abs
|
||||
model_result1 = ops.result(model_sig)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType(["opset13.Relu", "opset13.Sigmoid"], pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
assert matcher.match(model_sig)
|
||||
# ! [ov:wrap_type_list]
|
||||
|
||||
def any_input():
|
||||
# ! [ov:any_input]
|
||||
# Create a pattern with a MatMul node taking any inputs.
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
# ! [ov:any_input]
|
||||
|
||||
def wrap_type_predicate():
|
||||
# ! [ov:wrap_type_predicate]
|
||||
WrapType("opset13.Relu", AnyInput(), consumers_count(2))
|
||||
# ! [ov:wrap_type_predicate]
|
||||
|
||||
# ! [ov:any_input_predicate]
|
||||
# Create a pattern with an MatMul node taking any input that has a rank 4.
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(lambda output: len(output.get_shape()) == 4), AnyInput(lambda output: len(output.get_shape()) == 4)])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
# ! [ov:any_input_predicate]
|
||||
|
||||
# ! [ov::pattern_or]
|
||||
def pattern_or():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a red branch
|
||||
red_pattern_add = WrapType("opset13.Add", [AnyInput(), AnyInput()])
|
||||
red_pattern_relu = WrapType("opset13.Relu", red_pattern_add)
|
||||
red_pattern_sigmoid = WrapType(["opset13.Sigmoid"], red_pattern_relu)
|
||||
|
||||
# Create a blue branch
|
||||
blue_pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
blue_pattern_abs = WrapType("opset13.Abs", blue_pattern_mul)
|
||||
blue_pattern_relu = WrapType(["opset13.Relu"], blue_pattern_abs)
|
||||
|
||||
#Create Or node
|
||||
pattern_or = Or([red_pattern_sigmoid, blue_pattern_relu])
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_or, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov::pattern_or]
|
||||
|
||||
# ! [ov:pattern_optional_middle]
|
||||
def pattern_optional_middle():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern with an Optional node in the middle
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_abs)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_sig_opt)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_optional_middle]
|
||||
|
||||
# ! [ov:pattern_optional_top]
|
||||
def pattern_optional_top():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern an optional top node
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], AnyInput())
|
||||
pattern_mul = WrapType("opset13.MatMul", [pattern_sig_opt, AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid going into MatMul
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_optional_top]
|
||||
|
||||
# ! [ov:pattern_optional_root]
|
||||
def pattern_optional_root():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu)
|
||||
|
||||
matcher = Matcher(pattern_sig_opt, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid as root
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_optional_root]
|
||||
|
||||
# ! [ov:optional_predicate]
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu, consumers_count(1))
|
||||
# ! [ov:optional_predicate]
|
||||
|
|
@ -16,25 +16,26 @@ Overview of Transformations API
|
|||
transformation-api/model-pass
|
||||
transformation-api/matcher-pass
|
||||
transformation-api/graph-rewrite-pass
|
||||
transformation-api/patterns-python-api
|
||||
|
||||
OpenVINO Transformation mechanism allows to develop transformation passes to modify ``ov::Model``. You can use this mechanism to apply additional optimizations to the original Model or transform unsupported subgraphs and operations to new operations which are supported by the plugin.
|
||||
This guide contains all necessary information that you need to start implementing OpenVINO™ transformations.
|
||||
OpenVINO Transformation mechanism allows to develop transformation passes to modify ``ov::Model``. You can use this mechanism to apply additional optimizations to the original Model or transform unsupported subgraphs and operations to new operations supported by the plugin.
|
||||
This guide contains all the necessary information to start implementing OpenVINO™ transformations.
|
||||
|
||||
Working with Model
|
||||
##################
|
||||
|
||||
Before the moving to transformation part it is needed to say several words about functions which allow to modify ``ov::Model``.
|
||||
This chapter extends the :doc:`model representation guide <../../openvino-workflow/running-inference/integrate-openvino-with-your-application/model-representation>` and shows an API that allows us to manipulate with ``ov::Model``.
|
||||
Before moving to the transformation part, it is important to say a few words about the functions which allow modifying ``ov::Model``.
|
||||
This section extends the :doc:`model representation guide <../../openvino-workflow/running-inference/integrate-openvino-with-your-application/model-representation>` and introduces an API for ``ov::Model`` manipulation.
|
||||
|
||||
Working with node input and output ports
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
First of all let's talk about ``ov::Node`` input/output ports. Each OpenVINO™ operation has input and output ports except cases when operation has ``Parameter`` or ``Constant`` type.
|
||||
Each OpenVINO operation has ``ov::Node`` input and output ports, except for ``Parameter`` and ``Constant`` types.
|
||||
The terms ``node`` and ``operation`` are used interchangeably in OpenVINO, but this article will maintain consistency in their use.
|
||||
|
||||
Every port belongs to its node, so using a port we can access parent node, get shape and type for particular input/output, get all consumers in case of output port, and get producer node in case of input port.
|
||||
With output port we can set inputs for newly created operations.
|
||||
Every port is associated with a node, allowing access to the node it belongs to, including its shape, type, all consumers for output ports and the producer node for input ports.
|
||||
|
||||
Lets look at the code example.
|
||||
Take a look at the code example:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
|
|
@ -47,7 +48,7 @@ OpenVINO™ provides two ways for node replacement: via OpenVINO™ helper funct
|
|||
|
||||
Let's start with OpenVINO™ helper functions. The most popular function is ``ov::replace_node(old_node, new_node)``.
|
||||
|
||||
We will review real replacement case where Negative operation is replaced with Multiply.
|
||||
Let's review a replacement case where a Negative operation is replaced with Multiply.
|
||||
|
||||
.. image:: ../../assets/images/ov_replace_node.png
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ We will review real replacement case where Negative operation is replaced with M
|
|||
:language: cpp
|
||||
:fragment: [ov:replace_node]
|
||||
|
||||
``ov::replace_node`` has a constraint that number of output ports for both of ops must be the same; otherwise, it raises an exception.
|
||||
``ov::replace_node`` has a constraint that number of output ports for both nodes must be the same. Otherwise, the attempt to replace the nodes will result in an exception.
|
||||
|
||||
The alternative way to do the same replacement is the following:
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ The alternative way to do the same replacement is the following:
|
|||
:language: cpp
|
||||
:fragment: [ov:manual_replace]
|
||||
|
||||
Another transformation example is insertion.
|
||||
Another transformation example is insertion. Let's insert an additional Relu node.
|
||||
|
||||
.. image:: ../../assets/images/ov_insert_node.png
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ Another transformation example is insertion.
|
|||
:language: cpp
|
||||
:fragment: [ov:insert_node]
|
||||
|
||||
The alternative way to the insert operation is to make a node copy and use ``ov::replace_node()``:
|
||||
The alternative way of inserting a node is to make a copy of the node and use ``ov::replace_node()``:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
|
|
@ -80,15 +81,15 @@ The alternative way to the insert operation is to make a node copy and use ``ov:
|
|||
Node elimination
|
||||
++++++++++++++++
|
||||
|
||||
Another type of node replacement is its elimination.
|
||||
Another type of node replacement is elimination of a node.
|
||||
|
||||
To eliminate operation, OpenVINO™ has special method that considers all limitations related to OpenVINO™ Runtime.
|
||||
To eliminate a node, OpenVINO provides a method that considers all limitations of the OpenVINO Runtime.
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:eliminate_node]
|
||||
|
||||
``ov::replace_output_update_name()`` in case of successful replacement it automatically preserves friendly name and runtime info.
|
||||
If the replacement is successful, ``ov::replace_output_update_name()`` automatically preserves the friendly name and runtime info.
|
||||
|
||||
.. _transformations_types:
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ OpenVINO™ Runtime has three main transformation types:
|
|||
|
||||
* :doc:`Model pass <transformation-api/model-pass>` - straightforward way to work with ``ov::Model`` directly
|
||||
* :doc:`Matcher pass <transformation-api/matcher-pass>` - pattern-based transformation approach
|
||||
* :doc:`Graph rewrite pass <transformation-api/graph-rewrite-pass>` - container for matcher passes needed for efficient execution
|
||||
* :doc:`Graph rewrite pass <transformation-api/graph-rewrite-pass>` - container for matcher passes used for efficient execution
|
||||
|
||||
.. image:: ../../assets/images/transformations_structure.png
|
||||
|
||||
|
|
@ -116,36 +117,36 @@ Transformation library has two internal macros to support conditional compilatio
|
|||
Transformation writing essentials
|
||||
#################################
|
||||
|
||||
When developing a transformation, you need to follow these transformation rules:
|
||||
To develop a transformation, follow these transformation rules:
|
||||
|
||||
1. Friendly Names
|
||||
+++++++++++++++++
|
||||
|
||||
Each ``ov::Node`` has an unique name and a friendly name. In transformations we care only about friendly name because it represents the name from the model.
|
||||
To avoid losing friendly name when replacing node with other node or subgraph, set the original friendly name to the latest node in replacing subgraph. See the example below.
|
||||
Each ``ov::Node`` has a unique name and a friendly name. In transformations, only the friendly name matters because it represents the name from the model's perspective.
|
||||
To prevent losing the friendly name when replacing a node with another node or a subgraph, the original friendly name is set to the last node in the replacing subgraph. See the example below.
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:replace_friendly_name]
|
||||
|
||||
In more advanced cases, when replaced operation has several outputs and we add additional consumers to its outputs, we make a decision how to set friendly name by arrangement.
|
||||
In more complicated cases, when a replaced operation has several outputs and additional consumers are added to its outputs, the decision on how to set the friendly name is determined by an agreement.
|
||||
|
||||
2. Runtime Info
|
||||
+++++++++++++++
|
||||
|
||||
Runtime info is a map ``std::map<std::string, ov::Any>`` located inside ``ov::Node`` class. It represents additional attributes in ``ov::Node``.
|
||||
These attributes can be set by users or by plugins and when executing transformation that changes ``ov::Model`` we need to preserve these attributes as they will not be automatically propagated.
|
||||
Runtime info is a map ``std::map<std::string, ov::Any>`` located inside the ``ov::Node`` class. It represents additional attributes of the ``ov::Node``.
|
||||
These attributes, which can be set by users or plugins, need to be preserved when executing a transformation that changes ``ov::Model``, as they are not automatically propagated.
|
||||
In most cases, transformations have the following types: 1:1 (replace node with another node), 1:N (replace node with a sub-graph), N:1 (fuse sub-graph into a single node), N:M (any other transformation).
|
||||
Currently, there is no mechanism that automatically detects transformation types, so we need to propagate this runtime information manually. See the examples below.
|
||||
Currently, there is no mechanism that automatically detects transformation types, so this runtime information needs to be propagated manually. See the example below:
|
||||
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:copy_runtime_info]
|
||||
|
||||
When transformation has multiple fusions or decompositions, ``ov::copy_runtime_info`` must be called multiple times for each case.
|
||||
When a transformation has multiple fusions or decompositions, ``ov::copy_runtime_info`` must be called multiple times for each case.
|
||||
|
||||
.. note:: ``copy_runtime_info`` removes ``rt_info`` from destination nodes. If you want to keep it, you need to specify them in source nodes like this: ``copy_runtime_info({a, b, c}, {a, b})``
|
||||
.. note:: ``copy_runtime_info`` removes ``rt_info`` from destination nodes. If you want to keep it, specify them in source nodes as following: ``copy_runtime_info({a, b, c}, {a, b})``
|
||||
|
||||
3. Constant Folding
|
||||
+++++++++++++++++++
|
||||
|
|
@ -172,21 +173,21 @@ Common mistakes in transformations
|
|||
|
||||
In transformation development process:
|
||||
|
||||
* Do not use deprecated OpenVINO™ API. Deprecated methods has the ``OPENVINO_DEPRECATED`` macros in its definition.
|
||||
* Do not pass ``shared_ptr<Node>`` as an input for other node if type of node is unknown or it has multiple outputs. Use explicit output port.
|
||||
* If you replace node with another node that produces different shape, remember that new shape will not be propagated until the first ``validate_nodes_and_infer_types`` call for ``ov::Model``. If you are using ``ov::pass::Manager``, it will automatically call this method after each transformation execution.
|
||||
* Do not use deprecated OpenVINO™ API. Deprecated methods are marked with ``OPENVINO_DEPRECATED`` macro in their definition.
|
||||
* Do not pass ``shared_ptr<Node>`` as input for another node if the type of the node is unknown or if it has multiple outputs. Instead, use explicit output ports.
|
||||
* If you replace a node with another node that produces different shape, note that the new shape will not be propagated until the first ``validate_nodes_and_infer_types`` call for ``ov::Model``. If you are using ``ov::pass::Manager``, it will automatically call this method after each transformation execution.
|
||||
* Do not forget to call the ``ov::pass::ConstantFolding`` pass if your transformation creates constant subgraphs.
|
||||
* Use latest OpSet if you are not developing downgrade transformation pass.
|
||||
* When developing a callback for ``ov::pass::MatcherPass``, do not change nodes that come after the root node in topological order.
|
||||
* When developing a callback for ``ov::pass::MatcherPass``, do not change nodes that come after the root node in the topological order.
|
||||
|
||||
.. _using_pass_manager:
|
||||
|
||||
Using pass manager
|
||||
##################
|
||||
|
||||
``ov::pass::Manager`` is a container class that can store the list of transformations and execute them. The main idea of this class is to have high-level representation for grouped list of transformations.
|
||||
It can register and apply any `transformation pass <#transformations_types>`__ on model.
|
||||
In addition, ``ov::pass::Manager`` has extended debug capabilities (find more information in the `how to debug transformations <#how_to_debug_transformations>`__ section).
|
||||
``ov::pass::Manager`` is a container class that can store a list of transformations and execute them. The main idea of this class is to have a high-level representation for grouped list of transformations.
|
||||
It can register and apply any `transformation pass <#transformations_types>`__ on a model.
|
||||
In addition, ``ov::pass::Manager`` has extended debug capabilities (find more information in the `how to debug transformations <#how-to-debug-transformations>`__ section).
|
||||
|
||||
The example below shows basic usage of ``ov::pass::Manager``
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
|
||||
Transformation Patterns with OpenVINO API
|
||||
==================================================
|
||||
|
||||
.. meta::
|
||||
:description: Learn how to apply additional model optimizations or transform
|
||||
unsupported subgraphs and operations using OpenVINO™ Transformations API.
|
||||
|
||||
Pattern matching is an essential component of OpenVINO™ transformations. Before performing any transformation on a subgraph of a graph, it is necessary to find that subgraph in the graph.
|
||||
Patterns serve as a searching utility to identify nodes intended for transformations. This article covers the basics of pattern
|
||||
creation using OpenVINO™ API and helpful utilities to facilitate working with them. While this guide focuses on creating patterns, if you want to learn more about ``MatcherPass``, refer to the :doc:`OpenVINO Matcher Pass article <./matcher-pass>`. Note that some examples may be intentionally simplified for ease of understanding.
|
||||
|
||||
Before proceeding further, it is necessary to add some imports. These imports include the operations to be used and additional utilities described in this guide.
|
||||
Add the following lines to your file:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:imports]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:imports]
|
||||
|
||||
Pattern Creation
|
||||
+++++++++++++++++++++
|
||||
|
||||
A pattern is a simplified model comprised of nodes aimed to be matched. It lacks some features of a model and cannot function as one.
|
||||
|
||||
Consider a straightforward pattern consisting of three nodes to be found in a given model.
|
||||
|
||||
.. image:: ./../../../assets/images/simple_pattern_example.png
|
||||
|
||||
Let's create the model and the pattern:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:create_simple_model_and_pattern]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:create_simple_model_and_pattern]
|
||||
|
||||
.. note:: This example uses testing utilities that directly compare given sequences of nodes. In reality, the process of finding a pattern within a model is more complicated. However, to focus only on patterns and their functionality, these details are intentionally omitted.
|
||||
|
||||
Although the code is functional, in OpenVINO, patterns are typically not created using the same nodes as those used for creating the model. Instead, wrappers are preferred, providing additional functionality.
|
||||
For the given case, ``WrapType`` is used and the code looks as following:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
1. WrapType
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
``WrapType`` is a wrapper used to store one or many types to match them. As demonstrated earlier, it is possible to specify a single type in ``WrapType`` and use it for matching.
|
||||
However, you can also list all possible types for a given node, for example:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:wrap_type_list]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:wrap_type_list]
|
||||
|
||||
Note that ``pattern_sig`` is created with the list ``["opset13.Relu", "opset13.Sigmoid"]``, meaning it can be either a ``Relu`` or a ``Sigmoid``.
|
||||
This feature enables matching the same pattern against different nodes. Essentially, ``WrapType`` can represent "one of listed" types. ``WrapType`` supports specifying more than two types.
|
||||
|
||||
To add additional checks for your node, create a predicate by providing a function or a lambda. This function will be executed during matching, performing the additional validation specified in the logic of the function. For example, you might want to check the consumers count of a given node:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:wrap_type_predicate]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:wrap_type_predicate]
|
||||
|
||||
2. AnyInput
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
``AnyInput`` is used when there is no need to specify a particular input for a given node.
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:any_input]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:any_input]
|
||||
|
||||
You can also create ``AnyInput()`` with a predicate, if you want additional checks for you input. It will look similar to ``WrapType`` with a lambda or a function. For example, to ensure that the input has a rank of 4:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:any_input_predicate]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:any_input_predicate]
|
||||
|
||||
3. Or
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
``Or`` functions similar to ``WrapType``, however, while ``WrapType`` can only match one of the types provided in the list, ``Or`` is used to match different branches of nodes.
|
||||
Suppose the goal is to match the model against two different sequences of nodes. The ``Or`` type
|
||||
facilitates this by creating two different branches (``Or`` supports more than two branches), looking as follows:
|
||||
|
||||
.. image:: ./../../../assets/images/or_branches.png
|
||||
|
||||
The red branch will not match, but it will work perfectly for the blue one.
|
||||
Here is how it looks in code:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_or]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_or]
|
||||
|
||||
Note that matching will succeed for the first matching branch and the remaining ones will not be checked.
|
||||
|
||||
4. Optional
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
``Optional`` is a bit tricky. It allows specifying whether a node might be present or absent in the model. Under the hood,
|
||||
the pattern will create two branches using ``Or``: one with the optional node present and another one without it. Here is what it would look like with the ``Optional``
|
||||
unfolding into two branches:
|
||||
|
||||
.. image:: ./../../../assets/images/optional.png
|
||||
|
||||
The code for our model looks as follows:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_optional_middle]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_optional_middle]
|
||||
|
||||
The ``Optional`` does not necessarily have to be in the middle of the pattern. It can be a top node and a root node.
|
||||
|
||||
|
||||
Top node:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_optional_top]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_optional_top]
|
||||
|
||||
Root node:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_optional_root]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_optional_root]
|
||||
|
||||
``Optional`` also supports adding a predicate the same way ``WrapType`` and ``AnyInput`` do:
|
||||
|
||||
.. doxygensnippet:: docs/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:optional_predicate]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:optional_predicate]
|
||||
|
|
@ -7,7 +7,7 @@ CPU Device
|
|||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
cpu-device/performance-hint-and-threads-scheduling
|
||||
cpu-device/performance-hint-and-thread-scheduling
|
||||
|
||||
.. meta::
|
||||
:description: The CPU plugin in the Intel® Distribution of OpenVINO™ toolkit
|
||||
|
|
@ -234,6 +234,8 @@ This can be achieved by specifying ``MULTI:CPU,GPU.0`` as a target device in cas
|
|||
|
||||
For more details, see the :doc:`Multi-device execution <multi-device>` article.
|
||||
|
||||
.. _multi_stream_execution:
|
||||
|
||||
Multi-stream Execution
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
|
|
@ -242,7 +244,7 @@ property is set for CPU plugin, then multiple streams are created for the model.
|
|||
host thread, which means that incoming infer requests can be processed simultaneously. Each stream is pinned to its own group of
|
||||
physical cores with respect to NUMA nodes physical memory usage to minimize overhead on data transfer between NUMA nodes.
|
||||
|
||||
For more details, see the :doc:`optimization guide <../optimize-inference>` and :doc:`threads scheduling introduction <cpu-device/performance-hint-and-threads-scheduling>`.
|
||||
For more details, see the :doc:`optimization guide <../optimize-inference>` and :doc:`thread scheduling introduction <cpu-device/performance-hint-and-thread-scheduling>`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -390,7 +392,7 @@ Multi-Threading Optimization
|
|||
|
||||
CPU inference will infer an input or multiple inputs in parallel on multiple logical processors.
|
||||
|
||||
For more details, see the :doc:`threads scheduling introduction <cpu-device/performance-hint-and-threads-scheduling>`.
|
||||
For more details, see the :doc:`thread scheduling introduction <cpu-device/performance-hint-and-thread-scheduling>`.
|
||||
|
||||
|
||||
Denormals Optimization
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
|
||||
Performance Hints and Threads Scheduling
|
||||
Performance Hints and Thread Scheduling
|
||||
========================================
|
||||
|
||||
.. meta::
|
||||
:description: The Threads Scheduling of CPU plugin in OpenVINO™ Runtime
|
||||
:description: Thread Scheduling of the CPU plugin in OpenVINO™ Runtime
|
||||
detects CPU architecture and sets low-level properties based
|
||||
on performance hints automatically.
|
||||
|
||||
While all supported devices in OpenVINO offer low-level performance settings, it is advisable
|
||||
not to use these settings widely unless targeting specific platforms and models. The recommended
|
||||
approach is to configure performance in OpenVINO Runtime using the high-level performance hints
|
||||
property ``ov::hint::performance_mode``. Performance hints ensure optimal portability and
|
||||
scalability of applications across various platforms and models.
|
||||
|
||||
To simplify the configuration of hardware devices, OpenVINO offers two performance hints: the
|
||||
latency hint ``ov::hint::PerformanceMode::LATENCY`` and the throughput hint
|
||||
``ov::hint::PerformanceMode::THROUGHPUT``.
|
||||
To simplify the configuration of hardware devices, it is recommended to use the
|
||||
:doc:` ov::hint::PerformanceMode::LATENCY and ov::hint::PerformanceMode::THROUGHPUT <../../../../optimize-inference/high-level-performance-hints>`
|
||||
high-level performance hints. Both performance hints ensure optimal portability
|
||||
and scalability of applications across various platforms and models.
|
||||
|
||||
- ``ov::inference_num_threads`` limits the number of logical processors used for CPU inference.
|
||||
If the number set by the user is greater than the number of logical processors on the platform,
|
||||
the multi-threading scheduler only uses the platform number for CPU inference.
|
||||
- ``ov::num_streams`` limits the number of infer requests that can be run in parallel.
|
||||
If the number set by the user is greater than the number of inference threads, multi-threading
|
||||
scheduler only uses the number of inference threads to ensure that there is at least one thread per stream.
|
||||
- ``ov::hint::scheduling_core_type`` specifies the type of CPU cores for CPU inference when the user runs
|
||||
inference on a hybird platform that includes both Performance-cores (P-cores) and Efficient-cores (E-cores).
|
||||
If the user platform only has one type of CPU core, this property has no effect, and CPU inference always uses this unique core type.
|
||||
scheduler only uses the number of inference threads to ensure that there is at least
|
||||
one thread per stream.
|
||||
- ``ov::hint::scheduling_core_type`` specifies the type of CPU cores for CPU inference when
|
||||
the user runs inference on a hybird platform that includes both Performance-cores (P-cores)
|
||||
and Efficient-cores (E-cores). If the user platform only has one type of CPU core, this
|
||||
property has no effect, and CPU inference always uses this unique core type.
|
||||
- ``ov::hint::enable_hyper_threading`` limits the use of one or two logical processors per CPU
|
||||
core when the platform has CPU hyperthreading enabled.
|
||||
If there is only one logical processor per CPU core, such as Efficient-cores, this property has no effect, and CPU inference uses all logical processors.
|
||||
If there is only one logical processor per CPU core, such as Efficient-cores, this
|
||||
property has no effect, and CPU inference uses all logical processors.
|
||||
- ``ov::hint::enable_cpu_pinning`` enables CPU pinning during CPU inference.
|
||||
If the user enables this property but the inference scenario does not support it, this property will be disabled during model compilation.
|
||||
If the user enables this property but the inference scenario does not support it, this
|
||||
property will be disabled during model compilation.
|
||||
|
||||
For additional details on the above configurations, refer to `Multi-stream Execution <https://docs.openvino.ai/2024/openvino-workflow/running-inference/inference-devices-and-modes/cpu-device.html#multi-stream-execution>`__.
|
||||
For additional details on the above configurations, refer to
|
||||
:ref:`Multi-stream Execution <multi_stream_execution>`.
|
||||
|
||||
Latency Hint
|
||||
###################################
|
||||
#####################
|
||||
|
||||
In this scenario, the default setting of ``ov::hint::scheduling_core_type`` is determined by
|
||||
the model precision and the ratio of P-cores and E-cores.
|
||||
|
||||
.. note::
|
||||
|
||||
P-cores is short for Performance-cores and E-cores stands for Efficient-cores. These types of cores are available starting with the 12th Gen Intel® Core™ processors.
|
||||
P-cores is short for Performance-cores and E-cores stands for Efficient-cores. These
|
||||
types of cores are available starting with the 12th Gen Intel® Core™ processors.
|
||||
|
||||
.. _Core Type Table of Latency Hint:
|
||||
+----------------------------+---------------------+---------------------+
|
||||
|
|
@ -57,7 +57,8 @@ the model precision and the ratio of P-cores and E-cores.
|
|||
|
||||
.. note::
|
||||
|
||||
Both P-cores and E-cores may be used for any configuration starting with 14th Gen Intel® Core™ processors on Windows.
|
||||
Both P-cores and E-cores may be used for any configuration starting with 14th Gen Intel®
|
||||
Core™ processors on Windows.
|
||||
|
||||
Then the default settings for low-level performance properties on Windows and Linux are as follows:
|
||||
|
||||
|
|
@ -77,13 +78,20 @@ Then the default settings for low-level performance properties on Windows and Li
|
|||
|
||||
.. note::
|
||||
|
||||
- ``ov::hint::scheduling_core_type`` may be adjusted for a particular inferred model on a specific platform based on internal heuristics to guarantee optimal performance.
|
||||
- Both P-cores and E-cores are used for the Latency Hint on Intel® Core™ Ultra Processors on Windows, except in the case of large language models.
|
||||
- In case hyper-threading is enabled, two logical processors share the hardware resources of one CPU core. OpenVINO does not expect to use both logical processors in one stream for a single infer request. So ``ov::hint::enable_hyper_threading`` is set to ``No`` in this scenario.
|
||||
- ``ov::hint::enable_cpu_pinning`` is disabled by default on Windows and macOS, and enabled on Linux. Such default settings are aligned with typical workloads running in the corresponding environments to guarantee better out-of-the-box (OOB) performance.
|
||||
- ``ov::hint::scheduling_core_type`` may be adjusted for a particular inferred model on a
|
||||
specific platform based on internal heuristics to guarantee optimal performance.
|
||||
- Both P-cores and E-cores are used for the Latency Hint on Intel® Core™ Ultra Processors
|
||||
on Windows, except in the case of large language models.
|
||||
- In case hyper-threading is enabled, two logical processors share the hardware resources
|
||||
of one CPU core. OpenVINO does not expect to use both logical processors in one stream
|
||||
for a single infer request. So ``ov::hint::enable_hyper_threading`` is set to
|
||||
``No`` in this scenario.
|
||||
- ``ov::hint::enable_cpu_pinning`` is disabled by default on Windows and macOS, and
|
||||
enabled on Linux. Such default settings are aligned with typical workloads running
|
||||
in the corresponding environments to guarantee better out-of-the-box (OOB) performance.
|
||||
|
||||
Throughput Hint
|
||||
######################################
|
||||
#####################
|
||||
|
||||
In this scenario, thread scheduling first evaluates the memory pressure of the model being
|
||||
inferred on the current platform, and determines the number of threads per stream, as shown below.
|
||||
|
|
@ -99,7 +107,8 @@ inferred on the current platform, and determines the number of threads per strea
|
|||
+-----------------+-----------------------+
|
||||
|
||||
Then the value of ``ov::num_streams`` is calculated by dividing ``ov::inference_num_threads``
|
||||
by the number of threads per stream. The default settings for low-level performance properties on Windows and Linux are as follows:
|
||||
by the number of threads per stream. The default settings for low-level performance
|
||||
properties on Windows and Linux are as follows:
|
||||
|
||||
+--------------------------------------+-------------------------------+-------------------------------+
|
||||
| Property | Windows | Linux |
|
||||
|
|
@ -117,13 +126,15 @@ by the number of threads per stream. The default settings for low-level performa
|
|||
|
||||
.. note::
|
||||
|
||||
- By default, different core types are not mixed within a single stream in this scenario. The cores from different NUMA nodes are not mixed within a single stream.
|
||||
By default, different core types are not mixed within a single stream in this scenario.
|
||||
The cores from different NUMA nodes are not mixed within a single stream.
|
||||
|
||||
Multi-Threading Optimization
|
||||
##############################################
|
||||
############################
|
||||
|
||||
The following properties can be used to limit the available CPU resources for model inference.
|
||||
If the platform or operating system supports this behavior, the OpenVINO Runtime will perform multi-threading scheduling based on the limited available CPU.
|
||||
If the platform or operating system supports this behavior, the OpenVINO Runtime will
|
||||
perform multi-threading scheduling based on the limited available CPU.
|
||||
|
||||
- ``ov::inference_num_threads``
|
||||
- ``ov::hint::scheduling_core_type``
|
||||
|
|
@ -148,11 +159,13 @@ If the platform or operating system supports this behavior, the OpenVINO Runtime
|
|||
|
||||
.. note::
|
||||
|
||||
``ov::hint::scheduling_core_type`` and ``ov::hint::enable_hyper_threading`` only support Intel® x86-64 CPU on Linux and Windows in the current release.
|
||||
``ov::hint::scheduling_core_type`` and ``ov::hint::enable_hyper_threading`` only support
|
||||
Intel® x86-64 CPU on Linux and Windows in the current release.
|
||||
|
||||
In some use cases, OpenVINO Runtime will enable CPU thread pinning by default for better performance.
|
||||
Users can also turn this feature on or off using the property ``ov::hint::enable_cpu_pinning``.
|
||||
Disabling thread pinning may be beneficial in complex applications where several workloads are executed in parallel.
|
||||
Disabling thread pinning may be beneficial in complex applications where several workloads
|
||||
are executed in parallel.
|
||||
|
||||
.. tab-set::
|
||||
|
||||
|
|
@ -172,4 +185,4 @@ Disabling thread pinning may be beneficial in complex applications where several
|
|||
|
||||
|
||||
For details on multi-stream execution check the
|
||||
:doc:`optimization guide <../../optimize-inference/optimizing-throughput/advanced_throughput_options>`.
|
||||
:doc:`optimization guide <../../optimize-inference/optimizing-throughput/advanced_throughput_options>`.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{{ fullname | escape | underline}}
|
||||
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
.. autoclass:: {{ objname }}
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
||||
|
||||
{% block methods %}
|
||||
.. automethod:: __init__
|
||||
|
||||
{% if methods %}
|
||||
.. rubric:: Methods
|
||||
|
||||
.. autosummary::
|
||||
{% for met in methods %}
|
||||
~{{ name }}.{{ met }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block attributes %}
|
||||
{% if attributes %}
|
||||
.. rubric:: Attributes
|
||||
|
||||
.. autosummary::
|
||||
{% for att in attributes %}
|
||||
~{{ name }}.{{ att }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
{{ fullname | escape | underline}}
|
||||
|
||||
.. automodule:: {{ fullname }}
|
||||
|
||||
{% block attributes %}
|
||||
{% if attributes %}
|
||||
.. rubric:: Module Attributes
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
{% for attr in attributes %}
|
||||
{{ attr }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block functions %}
|
||||
{% if functions %}
|
||||
.. rubric:: Functions
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
{% for func in functions %}
|
||||
{% if not func.startswith('_') %}
|
||||
{{ func }}
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block classes %}
|
||||
{% if classes %}
|
||||
.. rubric:: Classes
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
:template: custom-class-template.rst
|
||||
{% for cl in classes %}
|
||||
{{ cl }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block exceptions %}
|
||||
{% if exceptions %}
|
||||
.. rubric:: Exceptions
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
{% for item in exceptions %}
|
||||
{{ item }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block modules %}
|
||||
{% if modules %}
|
||||
.. rubric:: Modules
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
:template: custom-module-template.rst
|
||||
:recursive:
|
||||
{% for mod in modules %}
|
||||
{{ mod }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import re
|
||||
import argparse
|
||||
import subprocess
|
||||
import requests
|
||||
import pkg_resources
|
||||
from packaging import version
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def determine_openvino_version(file_path):
|
||||
pattern = r"version_name\s*=\s*['\"]([^'\"]+)['\"]"
|
||||
|
||||
with open(file_path, 'r') as file:
|
||||
content = file.read()
|
||||
|
||||
match = re.search(pattern, content)
|
||||
|
||||
if match:
|
||||
return match.group(1)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_latest_version(major_version):
|
||||
url = f"https://pypi.org/pypi/openvino/json"
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
versions = data['releases'].keys()
|
||||
|
||||
# Filter versions by the major version prefix
|
||||
matching_versions = [v for v in versions if v.startswith(major_version)]
|
||||
|
||||
# Sort the matching versions and return the latest one
|
||||
if matching_versions:
|
||||
matching_versions.sort(key=version.parse)
|
||||
return matching_versions[-1]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--ov_dir', type=Path, help='OpenVINO docs directory')
|
||||
parser.add_argument('--python', type=Path, help='Python executable')
|
||||
args = parser.parse_args()
|
||||
ov_dir = args.ov_dir
|
||||
python_executable = args.python
|
||||
version_name = determine_openvino_version(ov_dir.joinpath("conf.py"))
|
||||
|
||||
if version_name is None:
|
||||
ov_version = "openvino"
|
||||
elif version_name == "nightly":
|
||||
ov_version = "openvino-nightly"
|
||||
else:
|
||||
latest_version = get_latest_version(version_name)
|
||||
if latest_version:
|
||||
ov_version = f"openvino=={latest_version}"
|
||||
else:
|
||||
ov_version = f"openvino=={version_name}"
|
||||
subprocess.check_call([f'{python_executable}', '-m', 'pip', 'install', '-U', ov_version, '--no-cache-dir'])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -12,11 +12,13 @@ class ReaderWorker : public Napi::AsyncWorker {
|
|||
public:
|
||||
/**
|
||||
* @brief Constructs ReaderWorker class that is responisible for reading the model asynchronously.
|
||||
* @note In the Execute() method, the Core object might be used concurrently to call read_model().
|
||||
* @param info contains passed arguments. Can be empty.
|
||||
*/
|
||||
ReaderWorker(const Napi::Env& env, ReadModelArgs* args)
|
||||
ReaderWorker(const Napi::Env& env, ov::Core& core, ReadModelArgs* args)
|
||||
: Napi::AsyncWorker{env, "ReaderWorker"},
|
||||
_deferred{env},
|
||||
_core{core},
|
||||
_args{args},
|
||||
_model{} {
|
||||
OPENVINO_ASSERT(_args, "Invalid pointer to ReadModelArgs.");
|
||||
|
|
@ -35,6 +37,7 @@ protected:
|
|||
///@}
|
||||
private:
|
||||
Napi::Promise::Deferred _deferred;
|
||||
ov::Core& _core;
|
||||
ReadModelArgs* _args;
|
||||
std::shared_ptr<ov::Model> _model;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@
|
|||
#include "node/include/model_wrap.hpp"
|
||||
|
||||
void ReaderWorker::Execute() {
|
||||
ov::Core core;
|
||||
|
||||
if (_args->model_str.empty())
|
||||
_model = core.read_model(_args->model_path, _args->bin_path);
|
||||
_model = _core.read_model(_args->model_path, _args->bin_path);
|
||||
else
|
||||
_model = core.read_model(_args->model_str, _args->weight_tensor);
|
||||
_model = _core.read_model(_args->model_str, _args->weight_tensor);
|
||||
}
|
||||
|
||||
void ReaderWorker::OnOK() {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ Napi::Value CoreWrap::read_model_sync(const Napi::CallbackInfo& info) {
|
|||
Napi::Value CoreWrap::read_model_async(const Napi::CallbackInfo& info) {
|
||||
try {
|
||||
ReadModelArgs* args = new ReadModelArgs(info);
|
||||
ReaderWorker* _readerWorker = new ReaderWorker(info.Env(), args);
|
||||
ReaderWorker* _readerWorker = new ReaderWorker(info.Env(), _core, args);
|
||||
_readerWorker->Queue();
|
||||
|
||||
return _readerWorker->GetPromise();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,173 @@ from openvino.runtime.utils.types import get_element_type
|
|||
from tests.test_transformations.utils.utils import expect_exception
|
||||
|
||||
|
||||
def test_simple_model_and_pattern():
|
||||
# Create a sample model
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = ops.matmul(AnyInput(), AnyInput(), False, False)
|
||||
pattern_abs = ops.abs(pattern_mul)
|
||||
pattern_relu = ops.relu(pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_simple_model_and_pattern_wrap_type():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_wrap_type_list():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
model_sig = ops.sigmoid(model_abs) # Note that we've added a Sigmoid node after Abs
|
||||
model_result1 = ops.result(model_sig) # noqa
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType(["opset13.Relu", "opset13.Sigmoid"], pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
assert matcher.match(model_sig)
|
||||
|
||||
|
||||
def test_pattern_or():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a red branch
|
||||
red_pattern_add = WrapType("opset13.Add", [AnyInput(), AnyInput()])
|
||||
red_pattern_relu = WrapType("opset13.Relu", red_pattern_add)
|
||||
red_pattern_sigmoid = WrapType(["opset13.Sigmoid"], red_pattern_relu)
|
||||
|
||||
# Create a blue branch
|
||||
blue_pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
blue_pattern_abs = WrapType("opset13.Abs", blue_pattern_mul)
|
||||
blue_pattern_relu = WrapType(["opset13.Relu"], blue_pattern_abs)
|
||||
|
||||
# Create Or node
|
||||
pattern_or = Or([red_pattern_sigmoid, blue_pattern_relu])
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_or, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_pattern_optional_middle():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern with an Optional node in the middle
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_abs)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_sig_opt)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_pattern_optional_top():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern an optional top node
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], AnyInput())
|
||||
pattern_mul = WrapType("opset13.MatMul", [pattern_sig_opt, AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid going into MatMul
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_pattern_optional_root():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern with an optional root node
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu)
|
||||
|
||||
matcher = Matcher(pattern_sig_opt, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid as root
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_wrap_type_pattern_type():
|
||||
last_opset_number = 15
|
||||
for i in range(1, last_opset_number + 1):
|
||||
|
|
|
|||
|
|
@ -691,6 +691,15 @@ size_t NetworkHelper::calculateLevels(
|
|||
float& dequantizationSub,
|
||||
float& updatedOutputLowValue,
|
||||
float& updatedOutputHighValue) {
|
||||
if (combinedIntervalHigh == combinedIntervalLow) {
|
||||
// degenerate case: quantization to a point
|
||||
dequantizationMul = 1.f;
|
||||
dequantizationSub = 0.f;
|
||||
updatedOutputLowValue = combinedIntervalLow;
|
||||
updatedOutputHighValue = combinedIntervalHigh;
|
||||
return 1ull;
|
||||
}
|
||||
|
||||
const float maxOutputInterval = combinedIntervalHigh - combinedIntervalLow;
|
||||
// FQ -> SUB_quantization -> MUL_quantization -[INT8]-> SUB_dequantization -> MUL_dequantization ->
|
||||
const float quantizationMul = (dataPrecisionMax - dataPrecisionMin) / maxOutputInterval;
|
||||
|
|
@ -882,38 +891,27 @@ std::tuple<std::shared_ptr<Node>, std::shared_ptr<Node>> NetworkHelper::decompos
|
|||
std::vector<float> shifts(outputSize, 0.f);
|
||||
std::vector<float> scales(outputSize);
|
||||
|
||||
// compute dequantizations (in double for INT32)
|
||||
if (precision == element::i32 || precision == element::u32) {
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
if (outputHighValues[i] != outputLowValues[i]) {
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
if (outputHighValues[i] != outputLowValues[i]) {
|
||||
// compute dequantizations (in double for INT32)
|
||||
if (precision == element::i32 || precision == element::u32) {
|
||||
shifts[i] = static_cast<float>(
|
||||
(static_cast<double>(min) * outputHighValues[i] - static_cast<double>(max) * outputLowValues[i]) /
|
||||
(static_cast<double>(outputHighValues[i]) - outputLowValues[i]));
|
||||
scales[i] = static_cast<float>(
|
||||
(static_cast<double>(outputHighValues[i]) - outputLowValues[i]) / (static_cast<double>(max) - min));
|
||||
if (shifts[i] == -0.f) {
|
||||
shifts[i] = 0.f;
|
||||
}
|
||||
} else {
|
||||
scales[i] = outputHighValues[i];
|
||||
minValues[i] = 1.f;
|
||||
maxValues[i] = 1.f;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
if (outputHighValues[i] != outputLowValues[i]) {
|
||||
shifts[i] = (min * outputHighValues[i] - max * outputLowValues[i]) /
|
||||
(outputHighValues[i] - outputLowValues[i]);
|
||||
scales[i] = (outputHighValues[i] - outputLowValues[i]) / (max - min);
|
||||
if (shifts[i] == -0.f) {
|
||||
shifts[i] = 0.f;
|
||||
}
|
||||
} else {
|
||||
scales[i] = outputHighValues[i];
|
||||
minValues[i] = 1.f;
|
||||
maxValues[i] = 1.f;
|
||||
}
|
||||
if (shifts[i] == -0.f) {
|
||||
shifts[i] = 0.f;
|
||||
}
|
||||
} else {
|
||||
scales[i] = 1.f;
|
||||
minValues[i] = outputHighValues[i];
|
||||
maxValues[i] = outputHighValues[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -981,15 +979,12 @@ std::tuple<std::shared_ptr<Node>, std::shared_ptr<Node>> NetworkHelper::decompos
|
|||
std::shared_ptr<ov::Node> convert2;
|
||||
if (updatePrecision) {
|
||||
std::shared_ptr<Node> convert;
|
||||
std::shared_ptr<ov::opset1::Constant> newFqConstant = ov::as_type_ptr<ov::opset1::Constant>(newFQ);
|
||||
|
||||
if (ov::is_type<ov::opset1::Constant>(newFQ)) {
|
||||
convert = foldConvert(newFQ->output(0), precision);
|
||||
} else if (ov::is_type<ov::opset1::FakeQuantize>(newFQ)) {
|
||||
if (ov::is_type<ov::opset1::FakeQuantize>(newFQ)) {
|
||||
newFQ = setOutDataPrecision(ov::as_type_ptr<ov::opset1::FakeQuantize>(newFQ), precision);
|
||||
convert = newFQ;
|
||||
} else {
|
||||
THROW_IE_LPT_EXCEPTION(*newFQ) << "unexpected operation type";
|
||||
convert = foldConvert(newFQ->output(0), precision);
|
||||
}
|
||||
|
||||
convert2 = std::make_shared<ov::opset1::Convert>(convert, element::f32);
|
||||
|
|
|
|||
|
|
@ -44,11 +44,10 @@ std::shared_ptr<Node> Load::clone_with_new_inputs(const OutputVector& new_args)
|
|||
LoadReshape::LoadReshape(const Output<ov::Node>& x, const size_t count, const size_t offset, std::vector<size_t> order)
|
||||
: Load(x, count, offset), m_order(std::move(order)) {
|
||||
const auto& in_shape = x.get_partial_shape();
|
||||
OPENVINO_ASSERT(in_shape.is_static(), "LoadReshape supports only static input shapes");
|
||||
const auto in_shape_size = in_shape.size();
|
||||
OPENVINO_ASSERT(m_order.size() == in_shape_size, "LoadReshape got new_order of invalid size");
|
||||
OPENVINO_ASSERT(*std::max_element(m_order.begin(), m_order.end()) == in_shape_size - 1 &&
|
||||
*std::min_element(m_order.begin(), m_order.end()) == 0, "LoadReshape detected invalid values in new_order");
|
||||
*std::min_element(m_order.begin(), m_order.end()) == 0, "LoadReshape detected invalid values in new_order");
|
||||
const std::set<size_t> unique_dims(order.begin(), order.end());
|
||||
OPENVINO_ASSERT(unique_dims.size() == order.size(), "LoadReshape order must not contain repeated elements");
|
||||
constructor_validate_and_infer_types();
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ auto is_supported_op(const std::shared_ptr<const Node> &n) -> bool {
|
|||
};
|
||||
auto is_supported_transpose = [](const std::shared_ptr<const Node>& n) -> bool {
|
||||
const auto& transpose = as_type_ptr<const opset1::Transpose>(n);
|
||||
const auto& out_shape = n->get_output_partial_shape(0);
|
||||
if (transpose && out_shape.is_static()) {
|
||||
if (transpose) {
|
||||
const auto parent = transpose->get_input_node_shared_ptr(0);
|
||||
const auto child = transpose->get_output_target_inputs(0).begin()->get_node()->shared_from_this();
|
||||
auto is_brgemm_case = ov::is_type<opset1::MatMul>(parent) || ov::is_type<opset1::MatMul>(child);
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ void ov::snippets::pass::ExplicitTransposeMatMulInputs::extract(const ov::Input<
|
|||
OPENVINO_ASSERT(consumers.size() == 1,
|
||||
"ExplicitTransposeMatMulInputs expects Parameter with one consumer in cases when there isn't existing Transpose on input");
|
||||
// Extract Transpose from MatMul
|
||||
OPENVINO_ASSERT(input.get_partial_shape().is_static(), "ExplicitTransposeMatMulInputs supports only static shapes");
|
||||
const auto rank = input.get_shape().size();
|
||||
OPENVINO_ASSERT(input.get_partial_shape().rank().is_static(), "ExplicitTransposeMatMulInputs supports only static ranks of shapes");
|
||||
const auto rank = input.get_partial_shape().size();
|
||||
std::vector<size_t> transpose_order(rank, 0);
|
||||
std::iota(transpose_order.begin(), transpose_order.end(), 0);
|
||||
std::swap(transpose_order[rank - 1], transpose_order[rank - 2]);
|
||||
|
|
@ -72,9 +72,7 @@ void ov::snippets::pass::ExplicitTransposeMatMulInputs::extract(const ov::Input<
|
|||
ov::snippets::pass::ExplicitTransposeMatMulInputs::ExplicitTransposeMatMulInputs() {
|
||||
MATCHER_SCOPE(ExplicitTransposeMatMulInputs);
|
||||
|
||||
auto m_matmul0 = std::make_shared<ov::op::v0::MatMul>(
|
||||
ov::pass::pattern::any_input(ov::pass::pattern::has_static_shape()),
|
||||
ov::pass::pattern::any_input(ov::pass::pattern::has_static_shape()));
|
||||
auto m_matmul0 = std::make_shared<ov::op::v0::MatMul>(ov::pass::pattern::any_input(), ov::pass::pattern::any_input());
|
||||
|
||||
register_matcher(std::make_shared<ov::pass::pattern::Matcher>(m_matmul0, matcher_name),
|
||||
[=](ov::pass::pattern::Matcher &m) {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ FuseTransposeBrgemm::FuseTransposeBrgemm() {
|
|||
const auto& transpose_out = m.get_match_value();
|
||||
const auto& const_order = ov::as_type_ptr<ov::op::v0::Constant>(transpose_out.get_node_shared_ptr()->get_input_node_shared_ptr(1));
|
||||
const auto& original_port = ov::snippets::lowered::PortDescriptorUtils::get_port_descriptor_ptr(brgemm_out);
|
||||
original_port->set_shape(transpose_out.get_shape());
|
||||
original_port->set_shape(utils::pshape_to_vdims(transpose_out.get_partial_shape()));
|
||||
original_port->set_layout(const_order->cast_vector<size_t>());
|
||||
for (const auto& in : transpose_out.get_target_inputs())
|
||||
in.replace_source_output(brgemm->output(0));
|
||||
|
|
@ -75,7 +75,7 @@ FuseTransposeBrgemm::FuseTransposeBrgemm() {
|
|||
const auto& const_order = ov::as_type_ptr<ov::op::v0::Constant>(transpose->get_input_node_shared_ptr(1));
|
||||
brgemm->set_argument(i, transpose->input_value(0));
|
||||
const auto& original_port = ov::snippets::lowered::PortDescriptorUtils::get_port_descriptor_ptr(in);
|
||||
original_port->set_shape(transpose->get_input_shape(0));
|
||||
original_port->set_shape(utils::pshape_to_vdims(transpose->get_input_partial_shape(0)));
|
||||
original_port->set_layout(const_order->cast_vector<size_t>());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ TransposeDecomposition::TransposeDecomposition() {
|
|||
const auto transpose = ov::as_type_ptr<ov::opset1::Transpose>(pattern_to_output.at(match_transpose).get_node_shared_ptr());
|
||||
|
||||
const auto order = ov::as_type_ptr<ov::op::v0::Constant>(pattern_to_output.at(match_order).get_node_shared_ptr());
|
||||
if (transformation_callback(transpose) || transpose->is_dynamic())
|
||||
if (transformation_callback(transpose))
|
||||
return false;
|
||||
|
||||
auto order_value = order->cast_vector<int>();
|
||||
|
|
@ -65,10 +65,14 @@ TransposeDecomposition::TransposeDecomposition() {
|
|||
auto load = std::make_shared<snippets::op::LoadReshape>(data_input, subtensor[0], 0, layout);
|
||||
auto store = std::make_shared<snippets::op::Store>(load, subtensor[0]);
|
||||
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(load->input(0), std::make_shared<PortDescriptor>(load->get_input_shape(0), subtensor, layout));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(load->output(0), std::make_shared<PortDescriptor>(load->get_output_shape(0), subtensor));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(store->input(0), std::make_shared<PortDescriptor>(store->get_input_shape(0), subtensor));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(store->output(0), std::make_shared<PortDescriptor>(store->get_output_shape(0), subtensor));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(load->input(0),
|
||||
std::make_shared<PortDescriptor>(utils::pshape_to_vdims(load->get_input_partial_shape(0)), subtensor, layout));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(load->output(0),
|
||||
std::make_shared<PortDescriptor>(utils::pshape_to_vdims(load->get_output_partial_shape(0)), subtensor));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(store->input(0),
|
||||
std::make_shared<PortDescriptor>(utils::pshape_to_vdims(store->get_input_partial_shape(0)), subtensor));
|
||||
PortDescriptorUtils::set_port_descriptor_ptr(store->output(0),
|
||||
std::make_shared<PortDescriptor>(utils::pshape_to_vdims(store->get_output_partial_shape(0)), subtensor));
|
||||
|
||||
for (auto& input : transpose->output(0).get_target_inputs()) {
|
||||
input.replace_source_output(store->output(0));
|
||||
|
|
|
|||
|
|
@ -555,7 +555,7 @@ ov::pass::RoPEFusionQwen::RoPEFusionQwen(int split_output_id) {
|
|||
{{"special_zero", true}});
|
||||
auto slice_Slice_543 = GenSlice(view_Reshape_424, 0, head_size, 1, 3); // tensor_array<f32[?,?,32,128]>
|
||||
|
||||
auto hidden_states = makePattern(); //
|
||||
auto hidden_states = makePattern("f32[?,?,?]"); //
|
||||
auto ShapeOf_485735 = makePattern<opset1::ShapeOf>({hidden_states}, {});
|
||||
auto Multiply_567524 = makePattern<opset1::Multiply>({ShapeOf_485735, {-1}}, {{"auto_broadcast", "numpy"}});
|
||||
auto Gather_377635 = makePattern<opset8::Gather>({Multiply_567524, {1}, 0}, {{"batch_dims", 0}});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "openvino/op/shape_of.hpp"
|
||||
#include "openvino/op/squeeze.hpp"
|
||||
#include "openvino/op/util/multi_subgraph_base.hpp"
|
||||
#include "openvino/op/util/op_types.hpp"
|
||||
#include "transformations/utils/utils.hpp"
|
||||
|
||||
namespace {
|
||||
|
|
@ -223,101 +222,7 @@ void optimize_value_usage(ov::Output<ov::Node>& output, STS_map& symbol_shape_so
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ov::Node>> topological_order(const std::shared_ptr<ov::Model>& m) {
|
||||
auto order = m->get_ordered_ops();
|
||||
|
||||
// step 1: split model into parameter related and parameter non-related ops
|
||||
const std::string op_depends_on_parameter = "topological_sort_op_depends_on";
|
||||
// values: true - parameter dependent; false otherwise
|
||||
for (const auto& op : order) {
|
||||
if (ov::as_type_ptr<ov::op::v0::Parameter>(op)) {
|
||||
op->get_rt_info()[op_depends_on_parameter] = true;
|
||||
} else if (ov::as_type_ptr<ov::op::v0::Constant>(op) || ov::as_type_ptr<ov::op::v0::ShapeOf>(op) ||
|
||||
ov::as_type_ptr<ov::op::v3::ShapeOf>(op) ||
|
||||
std::dynamic_pointer_cast<ov::op::util::VariableExtension>(op)) {
|
||||
op->get_rt_info()[op_depends_on_parameter] = false;
|
||||
} else { // deduce op type from inputs
|
||||
const auto& inputs = op->input_values();
|
||||
op->get_rt_info()[op_depends_on_parameter] =
|
||||
std::any_of(inputs.begin(),
|
||||
inputs.end(),
|
||||
[&op_depends_on_parameter](const ov::Output<ov::Node>& input) {
|
||||
return input.get_node_shared_ptr()->get_rt_info()[op_depends_on_parameter].as<bool>();
|
||||
});
|
||||
}
|
||||
}
|
||||
// step 2: starting from Result -- assign weight to ops:
|
||||
// if parameter dependant, weights is maximum of output indices plus one
|
||||
// else weights is maximum of output indices
|
||||
// this step doesn't assign weights to all the ops, this is intentional and will be used in the following step
|
||||
const std::string weight_rt_info_name = "topological_sort_weight";
|
||||
for (auto it = order.rbegin(); it != order.rend(); ++it) {
|
||||
const auto& op = *it;
|
||||
int64_t weight = 0;
|
||||
if (ov::as_type_ptr<ov::op::v0::Result>(op)) {
|
||||
op->get_rt_info()[weight_rt_info_name] = weight;
|
||||
} else {
|
||||
bool output_has_weight = false;
|
||||
for (const auto& output : op->outputs()) {
|
||||
for (const auto& input : output.get_target_inputs()) {
|
||||
const auto& output_op = input.get_node();
|
||||
const auto& rt_info = output_op->get_rt_info();
|
||||
if (!rt_info.count(weight_rt_info_name))
|
||||
continue;
|
||||
output_has_weight = true;
|
||||
auto output_weight = rt_info.at(weight_rt_info_name).as<int64_t>();
|
||||
weight = output_weight > weight ? output_weight : weight;
|
||||
}
|
||||
}
|
||||
if (output_has_weight) {
|
||||
if (op->get_rt_info()[op_depends_on_parameter].as<bool>()) {
|
||||
weight += 1;
|
||||
}
|
||||
op->get_rt_info()[weight_rt_info_name] = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
// step 3: make propagation for all the nodes:
|
||||
// if weight is already assigned -- skip operation
|
||||
// else operation weights is minimum of input indices
|
||||
// if all operation inputs have no weights -- this op is isolated and this algorithm doesn't make sense,
|
||||
// such cases are extremely rare and rather theoretical, to handle them we return original ov::Model op order
|
||||
std::map<int64_t, std::vector<std::shared_ptr<ov::Node>>> level_to_vector;
|
||||
for (const auto& op : order) {
|
||||
if (!op->get_rt_info().count(weight_rt_info_name)) {
|
||||
int64_t weight = std::numeric_limits<int64_t>::max();
|
||||
for (const auto& input : op->input_values()) {
|
||||
const auto& rt_info = input.get_node_shared_ptr()->get_rt_info();
|
||||
if (!rt_info.count(weight_rt_info_name))
|
||||
continue;
|
||||
auto input_weight = rt_info.at(weight_rt_info_name).as<int64_t>();
|
||||
weight = input_weight < weight ? input_weight : weight;
|
||||
}
|
||||
if (weight != std::numeric_limits<int64_t>::max())
|
||||
op->get_rt_info()[weight_rt_info_name] = weight;
|
||||
else
|
||||
return m->get_ordered_ops();
|
||||
}
|
||||
level_to_vector[op->get_rt_info().at(weight_rt_info_name).as<int64_t>()].push_back(op);
|
||||
}
|
||||
// finalization: descending order for levels and ops within level are ordered by get_ordered_ops
|
||||
std::vector<std::shared_ptr<ov::Node>> result;
|
||||
result.reserve(order.size());
|
||||
for (auto it = level_to_vector.rbegin(); it != level_to_vector.rend(); ++it) {
|
||||
const auto& item = *it;
|
||||
result.insert(result.end(), item.second.begin(), item.second.end());
|
||||
for (const auto& op : item.second) {
|
||||
op->get_rt_info().erase(weight_rt_info_name);
|
||||
op->get_rt_info().erase(op_depends_on_parameter);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void save_shape_sources(const std::shared_ptr<ov::Node>& op, STS_map& symbol_shape_source) {
|
||||
if (!ov::is_type<ov::op::v0::ShapeOf>(op) && !ov::is_type<ov::op::v3::ShapeOf>(op))
|
||||
return;
|
||||
const auto& output = op->input_value(0);
|
||||
void save_shape_sources(const ov::Output<ov::Node>& output, STS_map& symbol_shape_source) {
|
||||
for (const auto& d : output.get_partial_shape()) {
|
||||
if (d.is_static())
|
||||
continue;
|
||||
|
|
@ -335,7 +240,7 @@ bool ov::pass::OptimizeSymbolsUsedAsValues::run_on_model(const std::shared_ptr<o
|
|||
RUN_ON_FUNCTION_SCOPE(OptimizeSymbolsUsedAsValues);
|
||||
STS_map symbol_shape_source;
|
||||
STS_map symbol_value_source;
|
||||
for (const auto& op : topological_order(m)) {
|
||||
for (const auto& op : m->get_ordered_ops()) {
|
||||
// Result has output port which has shared (during validate_and_infer_type) tensor with input port.
|
||||
// Transformations may replace input of Result. After replacement and before Result::validate_and_infer_type --
|
||||
// output tensor of Result may contain inaccurate shape / symbols due to the sharing with tensor which may be
|
||||
|
|
@ -347,9 +252,10 @@ bool ov::pass::OptimizeSymbolsUsedAsValues::run_on_model(const std::shared_ptr<o
|
|||
// LTS maps aren't shared with sub-graphs because inner graph can not access outer graph for label sources
|
||||
ov::op::util::process_subgraph(*this, op);
|
||||
|
||||
for (auto& output : op->outputs())
|
||||
for (auto& output : op->outputs()) {
|
||||
optimize_value_usage(output, symbol_shape_source, symbol_value_source);
|
||||
save_shape_sources(op, symbol_shape_source);
|
||||
save_shape_sources(output, symbol_shape_source);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,16 +75,22 @@ TEST_F(TransformationTestsF, ApplySymbolEquivalence_Concat_Values) {
|
|||
auto input_2 = make_shared<v0::Parameter>(element::f32, PartialShape::dynamic(4));
|
||||
auto concat = make_shared<v0::Concat>(OutputVector{input_1, input_2}, -1);
|
||||
|
||||
auto shape = make_shared<v3::ShapeOf>(concat);
|
||||
auto gather = make_shared<v8::Gather>(shape,
|
||||
v0::Constant::create(element::i64, {1}, {-1}),
|
||||
v0::Constant::create(element::i64, {}, {0}));
|
||||
auto shape_1 = make_shared<v3::ShapeOf>(input_1);
|
||||
auto gather_1 = make_shared<v8::Gather>(shape_1,
|
||||
v0::Constant::create(element::i64, {1}, {3}),
|
||||
v0::Constant::create(element::i64, {}, {0}));
|
||||
|
||||
auto shape_2 = make_shared<v3::ShapeOf>(input_2);
|
||||
auto gather_2 = make_shared<v8::Gather>(shape_2,
|
||||
v0::Constant::create(element::i64, {1}, {3}),
|
||||
v0::Constant::create(element::i64, {}, {0}));
|
||||
|
||||
auto sum = make_shared<v1::Add>(gather_1, gather_2);
|
||||
|
||||
auto reshape = make_shared<v1::Reshape>(
|
||||
concat,
|
||||
make_shared<v0::Concat>(OutputVector{gather, v0::Constant::create(element::i64, {1}, {-1})}, 0),
|
||||
make_shared<v0::Concat>(OutputVector{sum, v0::Constant::create(element::i64, {1}, {-1})}, 0),
|
||||
false);
|
||||
|
||||
model_ref = make_shared<Model>(NodeVector{reshape}, ParameterVector{input_2, input_1});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@
|
|||
#include "openvino/op/equal.hpp"
|
||||
#include "openvino/op/exp.hpp"
|
||||
#include "openvino/op/greater.hpp"
|
||||
#include "openvino/op/matmul.hpp"
|
||||
#include "openvino/op/multiply.hpp"
|
||||
#include "openvino/op/non_max_suppression.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/reduce_sum.hpp"
|
||||
#include "openvino/op/relu.hpp"
|
||||
#include "openvino/op/sigmoid.hpp"
|
||||
#include "openvino/op/strided_slice.hpp"
|
||||
#include "openvino/op/subtract.hpp"
|
||||
#include "openvino/op/transpose.hpp"
|
||||
|
|
@ -1101,3 +1103,188 @@ TEST(pattern, wrap_type_multi_op) {
|
|||
ASSERT_FALSE(matcher->match(static_pointer_cast<Node>(c)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pattern, simple_model_and_pattern) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = std::make_shared<ov::op::v0::MatMul>(pattern::any_input(), pattern::any_input(), false, false);
|
||||
auto pattern_abs = std::make_shared<ov::op::v0::Abs>(pattern_mul->output(0));
|
||||
auto pattern_relu = std::make_shared<ov::op::v0::Relu>(pattern_abs->output(0));
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, simple_model_and_pattern_wrap_type) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, wrap_type_list) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
auto model_sig = std::make_shared<ov::op::v0::Sigmoid>(model_abs->output(0));
|
||||
auto model_result1 = std::make_shared<ov::op::v0::Result>(model_sig->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu, ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_sig));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_or) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a red branch
|
||||
auto red_pattern_add =
|
||||
ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto red_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({red_pattern_add->output(0)});
|
||||
auto red_pattern_sigmoid = ov::pass::pattern::wrap_type<ov::op::v0::Sigmoid>({red_pattern_relu->output(0)});
|
||||
|
||||
// Create a blue branch
|
||||
auto blue_pattern_mul =
|
||||
ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto blue_pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({blue_pattern_mul->output(0)});
|
||||
auto blue_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({blue_pattern_abs->output(0)});
|
||||
|
||||
// Create Or node
|
||||
auto pattern_or = std::make_shared<ov::pass::pattern::op::Or>(
|
||||
OutputVector{red_pattern_sigmoid->output(0), blue_pattern_relu->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_or, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_optional_middle) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern with an Optional node in the middle
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_sig_opt->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_optional_top) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern::any_input());
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern_sig_opt, pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_optional_root) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu);
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
|
@ -36,24 +36,36 @@ bool AclReduceExecutor::init(const ReduceAttrs& reduceAttrs,
|
|||
|
||||
this->reduceAttrs = reduceAttrs;
|
||||
|
||||
auto srcDims = srcDescs[0]->getShape().getStaticDims();
|
||||
auto dstDims = dstDescs[0]->getShape().getStaticDims();
|
||||
const auto& srcDims = srcDescs[0]->getShape().getStaticDims();
|
||||
const auto& dstDims = dstDescs[0]->getShape().getStaticDims();
|
||||
bool hasSrcNspcLayout = srcDescs[0]->hasLayoutType(LayoutType::nspc);
|
||||
bool hasDstNspcLayout = dstDescs[0]->hasLayoutType(LayoutType::nspc);
|
||||
auto srcShape = shapeCast(srcDims);
|
||||
auto dstShape = shapeCast(dstDims);
|
||||
if (hasSrcNspcLayout && hasDstNspcLayout) {
|
||||
changeLayoutToNH_C({&srcShape, &dstShape});
|
||||
}
|
||||
|
||||
TensorInfo srcTensorInfo = TensorInfo(shapeCast(srcDims), 1,
|
||||
TensorInfo srcTensorInfo = TensorInfo(srcShape, 1,
|
||||
precisionToAclDataType(srcDescs[0]->getPrecision()), getAclDataLayoutByMemoryDesc(srcDescs[0]));
|
||||
TensorInfo dstTensorInfo = TensorInfo(shapeCast(dstDims), 1,
|
||||
TensorInfo dstTensorInfo = TensorInfo(dstShape, 1,
|
||||
precisionToAclDataType(dstDescs[0]->getPrecision()), getAclDataLayoutByMemoryDesc(dstDescs[0]));
|
||||
|
||||
srcTensor.allocator()->init(srcTensorInfo);
|
||||
dstTensor.allocator()->init(dstTensorInfo);
|
||||
|
||||
std::function<std::unique_ptr<IFunction>(void)> exec_func;
|
||||
std::vector<int> castedAxes;
|
||||
for (size_t i = 0; i < reduceAttrs.axes.size(); ++i) {
|
||||
int axis = axisCast(reduceAttrs.axes[i], srcDims.size(), hasSrcNspcLayout ? NHWC_TO_NCHW : NO_LAYOUT_CONVERSION);
|
||||
if (hasSrcNspcLayout && axis == -1) return false;
|
||||
castedAxes.push_back(axis);
|
||||
}
|
||||
switch (reduceAttrs.operation) {
|
||||
case Algorithm::ReduceMean: {
|
||||
for (size_t i = 0; i < reduceAttrs.axes.size(); ++i) {
|
||||
auto axe = axisCast(reduceAttrs.axes[i], srcDims.size());
|
||||
auto pos = axisCast(i, reduceAttrs.axes.size());
|
||||
axesMean.set(pos, axe);
|
||||
axesMean.set(pos, castedAxes[i]);
|
||||
}
|
||||
Status reduceMeanStatus = NEReduceMean::validate(&srcTensorInfo, axesMean, reduceAttrs.keepDims, &dstTensorInfo);
|
||||
if (!reduceMeanStatus) {
|
||||
|
|
@ -71,15 +83,15 @@ bool AclReduceExecutor::init(const ReduceAttrs& reduceAttrs,
|
|||
case Algorithm::ReduceMin:
|
||||
case Algorithm::ReduceSum:
|
||||
case Algorithm::ReduceProd: {
|
||||
Status reductionOperationStatus = NEReductionOperation::validate(&srcTensorInfo, &dstTensorInfo, axisCast(reduceAttrs.axes[0], srcDims.size()),
|
||||
Status reductionOperationStatus = NEReductionOperation::validate(&srcTensorInfo, &dstTensorInfo, castedAxes[0],
|
||||
getAclReductionOperationByAlgorithm(reduceAttrs.operation), reduceAttrs.keepDims);
|
||||
if (!reductionOperationStatus) {
|
||||
DEBUG_LOG("NEReductionOperation validation with indices failed: ", reductionOperationStatus.error_description());
|
||||
return false;
|
||||
}
|
||||
exec_func = [this, srcDims]() -> std::unique_ptr<IFunction> {
|
||||
exec_func = [this, castedAxes]() -> std::unique_ptr<IFunction> {
|
||||
auto acl_op = std::make_unique<arm_compute::NEReductionOperation>();
|
||||
acl_op->configure(&srcTensor, &dstTensor, axisCast(this->reduceAttrs.axes[0], srcDims.size()),
|
||||
acl_op->configure(&srcTensor, &dstTensor, castedAxes[0],
|
||||
getAclReductionOperationByAlgorithm(this->reduceAttrs.operation), this->reduceAttrs.keepDims);
|
||||
return acl_op;
|
||||
};
|
||||
|
|
@ -103,4 +115,4 @@ void AclReduceExecutor::exec(const std::vector<MemoryCPtr>& src, const std::vect
|
|||
}
|
||||
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -67,8 +67,33 @@ inline arm_compute::TensorShape shapeCast(const VectorDims& dims) {
|
|||
return tensorShape;
|
||||
}
|
||||
|
||||
inline std::size_t axisCast(const std::size_t axis, const std::size_t shapeSize) {
|
||||
return shapeSize - axis - 1;
|
||||
enum ACLAxisCastMode {
|
||||
NO_LAYOUT_CONVERSION,
|
||||
NHWC_TO_NCHW,
|
||||
NCHW_TO_NHWC
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return reverted axis used in ACL. If axis cast mode is
|
||||
* @param axis axis that needs to be converted
|
||||
* @param shapeSize size of the shape, which axis needs to be converted
|
||||
* @param axisCastMode specifies whether layout conversion is required or not
|
||||
* @return reverted axis
|
||||
*/
|
||||
inline int axisCast(const std::size_t axis, const std::size_t shapeSize, ACLAxisCastMode axisCastMode = NO_LAYOUT_CONVERSION) {
|
||||
// CWHN (reverted NHWC) (0, 1, 2, 3) into WHCN (reverted NCHW) (1, 2, 0, 3)
|
||||
static std::vector<size_t> nhwcToNchw = {1, 2, 0, 3};
|
||||
// WHCN (reverted NCHW) (0, 1, 2, 3) into CWHN (reverted NHWC) (2, 0, 1, 3)
|
||||
static std::vector<size_t> nchwToNhwc = {2, 0, 1, 3};
|
||||
size_t revertedAxis = shapeSize - axis - 1;
|
||||
switch (axisCastMode) {
|
||||
case NHWC_TO_NCHW:
|
||||
return revertedAxis > 3 ? -1 : nhwcToNchw[revertedAxis];
|
||||
case NCHW_TO_NHWC:
|
||||
return revertedAxis > 3 ? -1 : nchwToNhwc[revertedAxis];
|
||||
default:
|
||||
return revertedAxis;
|
||||
}
|
||||
}
|
||||
|
||||
inline Dim vectorProduct(const VectorDims& vec, size_t size) {
|
||||
|
|
|
|||
|
|
@ -1992,8 +1992,7 @@ void Reduce::initSupportedPrimitiveDescriptors() {
|
|||
if (axis < 0)
|
||||
axis += static_cast<int>(getInputShapeAtPort(REDUCE_DATA).getRank());
|
||||
}
|
||||
// TODO: Per-channel layout is disabled due to accuracy issue in ACL Reduce Executor
|
||||
// pushDesc(LayoutType::nspc, LayoutType::nspc, input_prec, output_prec, undef, true);
|
||||
pushDesc(LayoutType::nspc, LayoutType::nspc, input_prec, output_prec, impl_desc_type::undef, true);
|
||||
pushDesc(LayoutType::ncsp, LayoutType::ncsp, input_prec, output_prec, impl_desc_type::undef, true);
|
||||
canUseAclExecutor = !supportedPrimitiveDescriptors.empty();
|
||||
if (canUseAclExecutor)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (C) 2020-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
#include "convert_reduce_no_keep_dims.hpp"
|
||||
|
||||
#include "openvino/core/rt_info.hpp"
|
||||
#include "openvino/opsets/opset8.hpp"
|
||||
|
||||
template <class T>
|
||||
ov::matcher_pass_callback ov::intel_cpu::ConvertReduceNoKeepDimsBase::convert_reduce() {
|
||||
return [&](ov::pass::pattern::Matcher& m) {
|
||||
auto reduce = std::dynamic_pointer_cast<T>(m.get_match_root());
|
||||
if (!reduce || reduce->get_keep_dims()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
reduce->set_keep_dims(true);
|
||||
const auto reduce_new = reduce->clone_with_new_inputs({reduce->input_value(0), reduce->input_value(1)});
|
||||
std::shared_ptr<ov::Node> squeeze = std::make_shared<ov::op::v0::Squeeze>(reduce_new, reduce->input_value(1));
|
||||
squeeze->set_friendly_name(reduce_new->get_friendly_name());
|
||||
ov::copy_runtime_info(reduce, {reduce_new, squeeze});
|
||||
ov::replace_node(reduce, squeeze);
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename ReductionType>
|
||||
ov::intel_cpu::ConvertReduction<ReductionType>::ConvertReduction() {
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(
|
||||
ov::pass::pattern::wrap_type<ReductionType>({ov::pass::pattern::any_input(),
|
||||
ov::pass::pattern::wrap_type<ov::opset8::Constant>()}), "ConvertReduction");
|
||||
register_matcher(m, convert_reduce<ReductionType>());
|
||||
}
|
||||
|
||||
template class ov::intel_cpu::ConvertReduction<ov::op::util::LogicalReductionKeepDims>;
|
||||
template class ov::intel_cpu::ConvertReduction<ov::op::util::ArithmeticReductionKeepDims>;
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (C) 2020-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/pass/pattern/op/wrap_type.hpp"
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "openvino/op/util/arithmetic_reductions_keep_dims.hpp"
|
||||
#include "openvino/op/util/logical_reduction_keep_dims.hpp"
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* ConvertReduceNoKeepDimsBase detects Reduce operations with keepDims = false.
|
||||
* Such Reduce operation is replaced with Reduce operation with keepDims = true and Squeeze
|
||||
* which removes undesired dimensions.
|
||||
*
|
||||
* Before:
|
||||
*
|
||||
* +--------------+ +-----------------+
|
||||
* | Data | | Axes tensor |
|
||||
* +-----------+--+ +-+---------------+
|
||||
* | |
|
||||
* +---------------------------+
|
||||
* | Reduce (keepDims = false) |
|
||||
* +---------------------------+
|
||||
*
|
||||
* After:
|
||||
*
|
||||
* +--------------+ +-----------------+
|
||||
* | Data | | Axes tensor |
|
||||
* +-----------+--+ +-+------------+--+
|
||||
* | | |
|
||||
* +---------------------------+ |
|
||||
* | Reduce (keepDims = true) | |
|
||||
* +-----------------------+---+ |
|
||||
* | |
|
||||
* +--------v------v-+
|
||||
* | Squeeze |
|
||||
* +-----------------+
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
|
||||
class ConvertReduceNoKeepDimsBase: public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("ConvertReduceNoKeepDims", "0");
|
||||
template <class T>
|
||||
ov::matcher_pass_callback convert_reduce();
|
||||
};
|
||||
|
||||
template <typename ReductionType>
|
||||
class ConvertReduction: public ConvertReduceNoKeepDimsBase {
|
||||
public:
|
||||
OPENVINO_RTTI("ConvertReduction", "0");
|
||||
ConvertReduction();
|
||||
};
|
||||
|
||||
|
||||
class ConvertReduceNoKeepDims: public ov::pass::GraphRewrite {
|
||||
public:
|
||||
OPENVINO_RTTI("ConvertReduceNoKeepDims", "0");
|
||||
ConvertReduceNoKeepDims() {
|
||||
add_matcher<ConvertReduction<ov::op::util::LogicalReductionKeepDims>>();
|
||||
add_matcher<ConvertReduction<ov::op::util::ArithmeticReductionKeepDims>>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
|
|
@ -122,6 +122,7 @@
|
|||
#include "transformations/cpu_opset/arm/pass/convert_group_conv1d.hpp"
|
||||
#include "transformations/cpu_opset/arm/pass/convert_reduce_multi_axis.hpp"
|
||||
#include "transformations/cpu_opset/arm/pass/mish_decomposition.hpp"
|
||||
#include "transformations/cpu_opset/arm/pass/convert_reduce_no_keep_dims.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/decompose_integer_divide.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/convert_fq_rnn_to_quantized_rnn.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/insert_convert_after_extension.hpp"
|
||||
|
|
@ -432,6 +433,7 @@ void Transformations::PreLpt(const std::vector<ov::element::Type>& defaultPrecis
|
|||
CPU_REGISTER_PASS_COMMON(manager, SwapConvertTranspose);
|
||||
CPU_REGISTER_PASS_X64(manager, ConvertToInteraction);
|
||||
CPU_REGISTER_PASS_X64(manager, ConvertInteractionInt8);
|
||||
CPU_REGISTER_PASS_ARM(manager, ConvertReduceNoKeepDims);
|
||||
CPU_REGISTER_PASS_ARM(manager, ConvertReduceMultiAxis);
|
||||
CPU_REGISTER_PASS_ARM32(manager, MishDecomposition);
|
||||
CPU_REGISTER_PASS_ARM(manager, ConvertConv1D);
|
||||
|
|
|
|||
|
|
@ -46,10 +46,7 @@ std::vector<std::vector<ov::test::InputShape>> inputShapes_SingleBatch = {
|
|||
|
||||
std::vector<CPUSpecificParams> cpuParams_4D = {
|
||||
CPUSpecificParams({nchw}, {nchw}, {}, {}),
|
||||
//NHWC layout is disabled on ARM due to accuracy issue: https://github.com/ARM-software/ComputeLibrary/issues/1044
|
||||
#if defined(OPENVINO_ARCH_X86) || defined(OPENVINO_ARCH_X86_64)
|
||||
CPUSpecificParams({nhwc}, {nhwc}, {}, {}),
|
||||
#endif
|
||||
};
|
||||
|
||||
/* ================================ 1.1 No fusion - Arithmetic ================================ */
|
||||
|
|
@ -160,4 +157,4 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
} // namespace Reduce
|
||||
} // namespace test
|
||||
} // namespace ov
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -141,6 +141,14 @@ const std::vector<LayerTestsDefinitions::ConvolutionTransformationParam> params
|
|||
false,
|
||||
"Convolution",
|
||||
"f32"
|
||||
},
|
||||
{
|
||||
{ 256ul, ov::Shape { 1, 1, 1, 1 }, { 0.f }, { 0.f }, { 0.f }, { 0.f } },
|
||||
false,
|
||||
{ 255ul, ov::Shape { 1, 1, 1, 1 }, { 0.f }, { 254.f }, { -12.7f }, { 12.7f } },
|
||||
false,
|
||||
"Convolution",
|
||||
"u8"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,26 @@ namespace snippets {
|
|||
|
||||
|
||||
namespace {
|
||||
std::vector<ov::PartialShape> input_shapes_4D{{2, 3, 5, 13}, {2, 3, 2, 4}, {1, 7, 1, 4}};
|
||||
std::vector<ov::PartialShape> input_shapes_3D{{3, 5, 13}, {3, 2, 4}, {7, 1, 4}};
|
||||
|
||||
const std::vector<InputShape> input_shapes_3D = {
|
||||
{{}, {{3, 5, 13}}},
|
||||
{{}, {{3, 2, 4}}},
|
||||
{{}, {{7, 1, 4}}},
|
||||
{{-1, -1, -1}, {{7, 1, 4}, {5, 7, 18}, {5, 7, 18}, {7, 1, 4}}},
|
||||
{{10, -1, -1}, {{10, 1, 4}, {10, 17, 18}, {10, 7, 18}, {10, 1, 4}}},
|
||||
{{10, -1, 15}, {{10, 1, 15}, {10, 1, 15}, {10, 7, 15}, {10, 15, 15}}},
|
||||
};
|
||||
|
||||
const std::vector<InputShape> input_shapes_4D = {
|
||||
{{}, {{2, 3, 5, 13}}},
|
||||
{{}, {{2, 3, 2, 4}}},
|
||||
{{}, {{1, 7, 1, 4}}},
|
||||
{{-1, -1, -1, -1}, {{1, 7, 1, 4}, {2, 3, 2, 4}, {8, 7, 1, 4}, {2, 3, 2, 4}, {1, 7, 1, 4}}},
|
||||
{{-1, 9, -1, -1}, {{1, 9, 1, 4}, {2, 9, 2, 4}, {8, 9, 1, 4}, {1, 9, 1, 4}, {1, 9, 1, 4}, {2, 9, 2, 4}}},
|
||||
{{-1, -1, -1, 5}, {{2, 8, 2, 5}, {2, 8, 2, 5}, {8, 2, 5, 5}, {1, 2, 5, 5}, {8, 3, 5, 5}}},
|
||||
{{-1, 9, -1, 5}, {{1, 9, 1, 5}, {2, 9, 2, 5}, {8, 9, 5, 5}, {1, 9, 5, 5}, {8, 9, 5, 5}}},
|
||||
{{2, -1, -1, -1}, {{2, 7, 1, 4}, {2, 3, 2, 4}, {2, 7, 1, 4}, {2, 3, 2, 4}, {2, 7, 1, 4}}},
|
||||
};
|
||||
|
||||
std::vector<std::vector<int32_t>> orders_4D{{0, 2, 3, 1}};
|
||||
std::vector<std::vector<int32_t>> orders_3D{{1, 2, 0}};
|
||||
|
|
@ -34,10 +52,23 @@ INSTANTIATE_TEST_SUITE_P(smoke_Snippets_Transpose_4D, Transpose,
|
|||
::testing::Values(ov::test::utils::DEVICE_CPU)),
|
||||
Transpose::getTestCaseName);
|
||||
|
||||
const std::vector<std::pair<InputShape, InputShape>> inputShapesPair = {
|
||||
{{{}, {{2, 31, 3, 5}}}, {{}, {{2, 3, 5, 31}}}},
|
||||
{{{-1, -1, -1, -1}, {{2, 31, 3, 5}, {2, 33, 4, 5}, {2, 33, 4, 5}}},
|
||||
{{-1, -1, -1, -1}, {{2, 3, 5, 31}, {2, 4, 5, 33}, {2, 4, 5, 33}}}},
|
||||
{{{-1, -1, -1, -1}, {{2, 31, 3, 5}, {2, 33, 4, 5}, {2, 33, 4, 5}}},
|
||||
{{-1, -1, 1, 1}, {{2, 3, 1, 1}, {2, 4, 1, 1}, {2, 4, 1, 1}}}},
|
||||
{{{-1, 33, -1, -1}, {{2, 33, 3, 5}, {2, 33, 4, 5}, {2, 33, 2, 5}}},
|
||||
{{-1, -1, 1, 1}, {{2, 3, 1, 1}, {2, 4, 1, 1}, {2, 2, 1, 1}}}},
|
||||
{{{-1, -1, -1, -1}, {{2, 16, 3, 5}, {2, 8, 4, 5}, {2, 4, 2, 5}}},
|
||||
{{-1, -1, -1, 1}, {{2, 3, 1, 1}, {2, 4, 1, 1}, {2, 2, 1, 1}}}},
|
||||
{{{-1, 18, -1, -1}, {{2, 18, 3, 5}, {2, 18, 4, 5}, {2, 18, 2, 6}}},
|
||||
{{-1, -1, -1, 18}, {{2, 3, 5, 18}, {2, 4, 5, 18}, {2, 2, 6, 18}}}},
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(smoke_Snippets_TransposeMul, TransposeMul,
|
||||
::testing::Combine(
|
||||
::testing::Values(ov::PartialShape {2, 31, 3, 5}),
|
||||
::testing::ValuesIn(std::vector<ov::PartialShape>{{2, 3, 5, 31}}),
|
||||
::testing::ValuesIn(inputShapesPair),
|
||||
::testing::Values(std::vector<int> {0, 2, 3, 1}),
|
||||
::testing::Values(1), // Transpose
|
||||
::testing::Values(1), // Tokenized Transpose
|
||||
|
|
|
|||
|
|
@ -12,13 +12,29 @@ namespace snippets {
|
|||
|
||||
namespace {
|
||||
|
||||
const std::vector<ov::Shape> inputShape = {
|
||||
ov::Shape{1, 128, 3, 16},
|
||||
const std::vector<std::vector<InputShape>> inputShape = {
|
||||
{{{}, {{1, 128, 3, 16}}}},
|
||||
{{{-1, -1, -1, -1}, {{1, 128, 3, 16}, {1, 64, 3, 8}, {1, 128, 3, 16}}}},
|
||||
{{{-1, 100, -1, -1}, {{1, 100, 3, 16}, {1, 100, 3, 8}, {1, 100, 2, 16}, {1, 100, 3, 8}}}},
|
||||
{{{-1, -1, -1, 16}, {{1, 128, 3, 16}, {1, 32, 3, 16}, {1, 32, 3, 16}, {1, 100, 2, 16}}}},
|
||||
};
|
||||
|
||||
const std::vector<std::vector<InputShape>> inputShapeWithEltwise = {
|
||||
{{{}, {{1, 128, 3, 16}}},
|
||||
{{}, {{1, 3, 16, 128}}}},
|
||||
{{{-1, -1, -1, -1}, {{1, 128, 3, 16}, {1, 64, 3, 8}, {1, 128, 3, 16}}},
|
||||
{{-1, -1, -1, -1}, {{1, 3, 16, 128}, {1, 3, 8, 64}, {1, 3, 16, 128}}}},
|
||||
{{{-1, 100, -1, -1}, {{1, 100, 3, 16}, {1, 100, 3, 8}, {1, 100, 2, 16}, {1, 100, 3, 8}}},
|
||||
{{-1, -1, -1, 100}, {{1, 1, 1, 100}, {1, 3, 8, 100}, {1, 2, 16, 100}, {1, 3, 8, 100}}}},
|
||||
{{{-1, 100, -1, 3}, {{1, 100, 3, 3}, {1, 100, 3, 3}, {1, 100, 2, 3}, {1, 100, 3, 3}}},
|
||||
{{-1, -1, -1, 100}, {{1, 1, 1, 100}, {1, 3, 3, 100}, {1, 2, 3, 100}, {1, 1, 1, 100}}}},
|
||||
{{{-1, -1, -1, 16}, {{1, 128, 3, 16}, {1, 32, 3, 16}, {1, 32, 3, 16}, {1, 100, 2, 16}}},
|
||||
{{-1, -1, 16, -1}, {{1, 3, 16, 128}, {1, 1, 16, 32}, {1, 3, 16, 32}, {1, 2, 16, 100}}}},
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(smoke_Snippets_TransposeSoftmax, TransposeSoftmax,
|
||||
::testing::Combine(
|
||||
::testing::Values(inputShape),
|
||||
::testing::ValuesIn(inputShape),
|
||||
::testing::Values(std::vector<int64_t>{0, 2, 3, 1}),
|
||||
::testing::Values(-1),
|
||||
::testing::Values(1),
|
||||
|
|
@ -28,7 +44,7 @@ INSTANTIATE_TEST_SUITE_P(smoke_Snippets_TransposeSoftmax, TransposeSoftmax,
|
|||
|
||||
INSTANTIATE_TEST_SUITE_P(smoke_Snippets_TransposeSoftmaxEltwise, TransposeSoftmaxEltwise,
|
||||
::testing::Combine(
|
||||
::testing::Values(inputShape),
|
||||
::testing::ValuesIn(inputShapeWithEltwise),
|
||||
::testing::Values(std::vector<int64_t>{0, 2, 3, 1}),
|
||||
::testing::Values(-1),
|
||||
::testing::Values(1),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <openvino/opsets/opset1.hpp>
|
||||
#include <transformations/cpu_opset/arm/pass/convert_reduce_no_keep_dims.hpp>
|
||||
#include "common_test_utils/ov_test_utils.hpp"
|
||||
|
||||
using namespace ov::intel_cpu;
|
||||
|
||||
template <class T>
|
||||
class ConvertReduceNoKeepDimsTest : public testing::Test {};
|
||||
|
||||
template <class T>
|
||||
static std::shared_ptr<ov::Model> createInitGraph(std::shared_ptr<ov::opset1::Parameter> param) {
|
||||
auto axes = ov::opset1::Constant::create(ov::element::i64, ov::Shape{2}, {0, 1});
|
||||
auto reduce = std::make_shared<T>(param, axes, false);
|
||||
return std::make_shared<ov::Model>(ov::NodeVector{ reduce }, ov::ParameterVector{ param });
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static std::shared_ptr<ov::Model> createRefGraph(std::shared_ptr<ov::opset1::Parameter> param) {
|
||||
auto axes = ov::opset1::Constant::create(ov::element::i64, ov::Shape{2}, {0, 1});
|
||||
auto reduce = std::make_shared<T>(param, axes, true);
|
||||
auto squeeze = std::make_shared<ov::opset1::Squeeze>(reduce, axes);
|
||||
return std::make_shared<ov::Model>(ov::NodeVector{ squeeze }, ov::ParameterVector{ param });
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static bool registerAndRunReducePass(std::shared_ptr<ov::Model> model) {
|
||||
ov::pass::Manager manager;
|
||||
if (std::is_base_of<ov::op::util::LogicalReductionKeepDims, T>::value) {
|
||||
manager.register_pass<ConvertReduction<ov::op::util::LogicalReductionKeepDims>>();
|
||||
} else if (std::is_base_of<ov::op::util::ArithmeticReductionKeepDims, T>::value) {
|
||||
manager.register_pass<ConvertReduction<ov::op::util::ArithmeticReductionKeepDims>>();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
manager.run_passes(model);
|
||||
return true;
|
||||
}
|
||||
|
||||
static ov::Shape static_param_shape = ov::Shape{2, 19, 2, 9};
|
||||
static ov::PartialShape dynamic_param_shape = ov::PartialShape{2, -1, 2, 9};
|
||||
|
||||
TYPED_TEST_SUITE_P(ConvertReduceNoKeepDimsTest);
|
||||
|
||||
TYPED_TEST_P(ConvertReduceNoKeepDimsTest, CheckConvertReduceTransformationIsAppliedForStaticShapes) {
|
||||
ov::element::Type_t dataType = std::is_base_of<ov::op::util::LogicalReductionKeepDims, TypeParam>::value ?
|
||||
ov::element::boolean : ov::element::f32;
|
||||
auto param = std::make_shared<ov::opset1::Parameter>(dataType, static_param_shape);
|
||||
auto model = createInitGraph<TypeParam>(param);
|
||||
auto model_ref = createRefGraph<TypeParam>(param);
|
||||
|
||||
if (!registerAndRunReducePass<TypeParam>(model)) {
|
||||
FAIL() << "Reduce pass is not registered.";
|
||||
}
|
||||
|
||||
auto res = compare_functions(model, model_ref);
|
||||
ASSERT_TRUE(res.first) << res.second;
|
||||
}
|
||||
|
||||
TYPED_TEST_P(ConvertReduceNoKeepDimsTest, CheckConvertReduceTransformationIsAppliedForDynaimcShapes) {
|
||||
ov::element::Type_t dataType = std::is_base_of<ov::op::util::LogicalReductionKeepDims, TypeParam>::value ?
|
||||
ov::element::boolean : ov::element::f32;
|
||||
auto param = std::make_shared<ov::opset1::Parameter>(dataType, dynamic_param_shape);
|
||||
auto model = createInitGraph<TypeParam>(param);
|
||||
auto model_ref = createRefGraph<TypeParam>(param);
|
||||
|
||||
if (!registerAndRunReducePass<TypeParam>(model)) {
|
||||
FAIL() << "Reduce pass is not registered.";
|
||||
}
|
||||
|
||||
auto res = compare_functions(model, model_ref);
|
||||
ASSERT_TRUE(res.first) << res.second;
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(ConvertReduceNoKeepDimsTest,
|
||||
CheckConvertReduceTransformationIsAppliedForStaticShapes,
|
||||
CheckConvertReduceTransformationIsAppliedForDynaimcShapes);
|
||||
|
||||
using reduceTypes = ::testing::Types<ov::opset1::ReduceMin,
|
||||
ov::opset1::ReduceMax,
|
||||
ov::opset1::ReduceSum,
|
||||
ov::opset1::ReduceProd,
|
||||
ov::opset1::ReduceMean,
|
||||
ov::opset1::ReduceLogicalAnd,
|
||||
ov::opset1::ReduceLogicalOr>;
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(ConvertReduce, ConvertReduceNoKeepDimsTest, reduceTypes);
|
||||
|
|
@ -282,6 +282,7 @@ private:
|
|||
void set_variables_state_info(const std::string& variable_id, const layout& variable_layout, ov::element::Type user_specified_type, const primitive* p);
|
||||
void dump_memory_pool(std::string dump_path, int64_t curr_iter);
|
||||
|
||||
std::vector<uint64_t> host_exec_times;
|
||||
#ifdef GPU_DEBUG_CONFIG
|
||||
int64_t iteration = 0;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public:
|
|||
int disable_onednn_opt_post_ops; // Disable onednn optimize post operators
|
||||
std::string dump_profiling_data; // Enables dump of extended performance profiling to specified dir
|
||||
int dump_profiling_data_per_iter; // Enables dump of extended performance profiling to specified dir for each iteration
|
||||
int host_time_profiling; // Enables measurement of scheduling time spend on the host
|
||||
std::string dump_graphs; // Dump optimized graph
|
||||
std::string dump_sources; // Dump opencl sources
|
||||
std::string dump_layers_path; // Enable dumping intermediate buffers and set the dest path
|
||||
|
|
|
|||
|
|
@ -433,6 +433,22 @@ network::network(program::ptr program, stream::ptr stream, uint16_t stream_id)
|
|||
: network(program, program->get_config(), stream, false, stream_id == 0) {}
|
||||
|
||||
network::~network() {
|
||||
GPU_DEBUG_IF(debug_configuration::get_instance()->host_time_profiling) {
|
||||
if (host_exec_times.size() >= 2) {
|
||||
double first = static_cast<double>(host_exec_times[0]);
|
||||
double avg = static_cast<double>(std::accumulate(host_exec_times.begin() + 1, host_exec_times.end(), (size_t)0, std::plus<size_t>()));
|
||||
avg /= (host_exec_times.size() - 1);
|
||||
std::string resolution = " us";
|
||||
if (avg > 1000.0) {
|
||||
resolution = " ms";
|
||||
avg /= 1000.0;
|
||||
first /= 1000.0;
|
||||
}
|
||||
GPU_DEBUG_COUT << "Network[" << net_id << "] First infer host time: " << first << resolution << std::endl;
|
||||
GPU_DEBUG_COUT << "Network[" << net_id << "] Infer avg host time: " << avg << resolution << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (_program != nullptr)
|
||||
_program->cancel_compilation_context();
|
||||
_memory_pool->clear_pool_for_network(net_id);
|
||||
|
|
@ -915,7 +931,12 @@ void network::add_to_exec_order(const primitive_id& id) {
|
|||
}
|
||||
|
||||
std::map<primitive_id, network_output> network::execute(const std::vector<event::ptr>& dependencies) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
execute_impl(dependencies);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
GPU_DEBUG_IF(debug_configuration::get_instance()->host_time_profiling)
|
||||
host_exec_times.push_back(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count());
|
||||
|
||||
auto output_ids = get_output_ids();
|
||||
std::map<primitive_id, network_output> result;
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@ KERNEL(sdpa_opt)(
|
|||
// If the number of partitions is greater than 1, save exm_sums and max_logits to the temporary buffers
|
||||
// Use single WI in the WG, since all the WIs have the same value
|
||||
if (num_of_partitions > 1 && sglid == 0) {
|
||||
for (uint i = 0; i < QK_MAX_NUMS_PER_SG; i++) {
|
||||
for (uint i = 0; i < QK_ITERS_END; i++) {
|
||||
if (target_seq_idx + sgid + (i * SUBGROUPS_PER_WG) < TARGET_SEQ_LEN) {
|
||||
const uint exp_sums_offset = b0_idx * (NUM_HEADS * TARGET_SEQ_LEN * num_of_partitions) +
|
||||
b1_idx * (TARGET_SEQ_LEN * num_of_partitions) +
|
||||
|
|
|
|||
|
|
@ -247,10 +247,11 @@ FullyConnected_bf_tiled::GetAutoTuneParams(const fully_connected_params& params,
|
|||
if (params.weights.GetDType() == WeightsType::UINT4 || params.weights.GetDType() == WeightsType::INT4) {
|
||||
if (!params.is_shape_agnostic && batch == 1) {
|
||||
// Tuning for Meteor Lake
|
||||
size_t ideal_num_threads = params.engineInfo.maxThreadsPerDevice * simd;
|
||||
if (output_f / 2 < ideal_num_threads * 0.8 && params.weights.GetLayout() == WeightsLayout::os_is_yx_osv32_isv2) {
|
||||
size_t min_num_threads = params.engineInfo.computeUnitsCount * simd;
|
||||
if (output_f / 2 < min_num_threads && params.weights.GetLayout() == WeightsLayout::os_is_yx_osv32_isv2) {
|
||||
GPU_DEBUG_TRACE_DETAIL << "FC bf tiled: Set ofm_tile 1. (output_f : " << output_f
|
||||
<< ", ideal threads : " << ideal_num_threads << ")" << std::endl;
|
||||
<< ", computeUnitsCount : " << params.engineInfo.computeUnitsCount
|
||||
<< " min_num_threads : " << min_num_threads << ")" << std::endl;
|
||||
return selector.Default(tune_params(1, 1, 4, 2, 1, 1, EXE_MODE_DEFAULT));
|
||||
} else {
|
||||
return selector.Default(tune_params(1, 2, 4, 2, 1, 1, EXE_MODE_DEFAULT));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "print_model_statistics.hpp"
|
||||
#include "intel_gpu/runtime/debug_configuration.hpp"
|
||||
#include "openvino/core/type.hpp"
|
||||
#include "openvino/op/util/multi_subgraph_base.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace ov {
|
||||
namespace intel_gpu {
|
||||
namespace {
|
||||
size_t collect_stats(const std::shared_ptr<ov::Model>& m, std::map<DiscreteTypeInfo, size_t>& ops_stat) {
|
||||
const std::vector<std::shared_ptr<ov::Node>> ops = m->get_ops();
|
||||
size_t total = ops.size();
|
||||
for (auto& op : ops) {
|
||||
const auto& tinfo = op->get_type_info();
|
||||
if (ops_stat.find(tinfo) == ops_stat.end()) {
|
||||
ops_stat[tinfo] = 0;
|
||||
}
|
||||
|
||||
ops_stat[tinfo]++;
|
||||
|
||||
if (auto subgraph_op = std::dynamic_pointer_cast<ov::op::util::MultiSubGraphOp>(op)) {
|
||||
for (const auto& subgraph : subgraph_op->get_functions()) {
|
||||
total += collect_stats(subgraph, ops_stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool PrintModelStatistics::run_on_model(const std::shared_ptr<ov::Model>& m) {
|
||||
std::map<DiscreteTypeInfo, size_t> ops_stat;
|
||||
size_t total = collect_stats(m, ops_stat);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Operations statistics:\n";
|
||||
for (auto& kv : ops_stat) {
|
||||
ss << "\t" << kv.first.version_id << "::" << kv.first.name << " " << kv.second << std::endl;
|
||||
}
|
||||
|
||||
ss << "\tTotal: " << total;
|
||||
|
||||
GPU_DEBUG_INFO << ss.str() << std::endl;;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace intel_gpu
|
||||
} // namespace ov
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/pass/pass.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace intel_gpu {
|
||||
|
||||
class PrintModelStatistics : public ov::pass::ModelPass {
|
||||
public:
|
||||
OPENVINO_RTTI("PrintModelStatistics", "0");
|
||||
PrintModelStatistics() = default;
|
||||
|
||||
bool run_on_model(const std::shared_ptr<ov::Model>& m) override;
|
||||
};
|
||||
|
||||
} // namespace intel_gpu
|
||||
} // namespace ov
|
||||
|
|
@ -61,6 +61,7 @@
|
|||
#include "plugin/transformations/kv_cache_fusion.hpp"
|
||||
#include "plugin/transformations/move_fc_reshape_to_weights.hpp"
|
||||
#include "plugin/transformations/bcast_and_pad_zp_buffers.hpp"
|
||||
#include "plugin/transformations/print_model_statistics.hpp"
|
||||
#include "plugin/transformations/swiglu_fusion.hpp"
|
||||
#include "plugin/transformations/transpose_fusion.hpp"
|
||||
#include "plugin/transformations/indirect_kv_cache.hpp"
|
||||
|
|
@ -340,11 +341,19 @@ void TransformationsPipeline::apply(std::shared_ptr<ov::Model> func) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// - The head size should be divisible by 16
|
||||
// - Head size should be static dim
|
||||
const auto head_size_dim = query_ps[query_ps.size() - 1];
|
||||
if (head_size_dim.is_dynamic())
|
||||
return false;
|
||||
|
||||
// - Head size should be 128 for any model type; or should be in the range of 64 to 256 for stateful LLMs because of performance reasons.
|
||||
// This limitations is recommended to prevent performance drop in models with small head size, such as SD,
|
||||
// until the SDPA operation is optimized for these cases
|
||||
const auto optimal_subgroup_size = 16;
|
||||
if (query_ps[query_ps.size() - 1].is_dynamic() ||
|
||||
query_ps[query_ps.size() - 1].get_length() != 128 ||
|
||||
query_ps[query_ps.size() - 1].get_length() % optimal_subgroup_size != 0) {
|
||||
const auto head_size = query_ps[query_ps.size() - 1].get_length();
|
||||
bool valid_head_size = head_size % optimal_subgroup_size == 0;
|
||||
valid_head_size &= (head_size == 128) || (func->get_variables().size() > 0 && head_size >= 64 && head_size <= 256);
|
||||
if (!valid_head_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -823,6 +832,9 @@ void TransformationsPipeline::apply(std::shared_ptr<ov::Model> func) {
|
|||
// This is supposed to be the last pass to ensure that we don't have name collisions until
|
||||
// GPU plugin stops using friendly names for program creation
|
||||
manager.register_pass<ov::pass::ResolveNameCollisions>(true);
|
||||
GPU_DEBUG_IF(cldnn::debug_configuration::get_instance()->verbose >= 1) {
|
||||
manager.register_pass<ov::intel_gpu::PrintModelStatistics>();
|
||||
}
|
||||
|
||||
manager.run_passes(func);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ static void print_help_messages() {
|
|||
"from one specific iteration by giving the same values for the start and end, and the open "
|
||||
"ended range is also available by range from given start to the last iteration as -1. e.g. "
|
||||
"OV_GPU_DumpProfilingDataIteration='10..-1'");
|
||||
message_list.emplace_back("OV_GPU_HostTimeProfiling", "Enable collecting of model enqueue time spent on the host");
|
||||
message_list.emplace_back("OV_GPU_DumpGraphs", "1) dump ngraph before and after transformation. 2) dump graph in model compiling."
|
||||
"3) dump graph in execution.");
|
||||
message_list.emplace_back("OV_GPU_DumpSources", "Dump opencl sources");
|
||||
|
|
@ -218,6 +219,7 @@ debug_configuration::debug_configuration()
|
|||
, disable_onednn_opt_post_ops(0)
|
||||
, dump_profiling_data(std::string(""))
|
||||
, dump_profiling_data_per_iter(0)
|
||||
, host_time_profiling(0)
|
||||
, dump_graphs(std::string())
|
||||
, dump_sources(std::string())
|
||||
, dump_layers_path(std::string())
|
||||
|
|
@ -266,6 +268,7 @@ debug_configuration::debug_configuration()
|
|||
get_gpu_debug_env_var("DisableOnednnOptPostOps", disable_onednn_opt_post_ops);
|
||||
get_gpu_debug_env_var("DumpProfilingData", dump_profiling_data);
|
||||
get_gpu_debug_env_var("DumpProfilingDataPerIter", dump_profiling_data_per_iter);
|
||||
get_gpu_debug_env_var("HostTimeProfiling", host_time_profiling);
|
||||
std::string dump_prof_data_iter_str;
|
||||
get_gpu_debug_env_var("DumpProfilingDataIteration", dump_prof_data_iter_str);
|
||||
get_gpu_debug_env_var("DryRunPath", dry_run_path);
|
||||
|
|
|
|||
|
|
@ -110,6 +110,14 @@ const std::vector<LayerTestsDefinitions::ConvolutionTransformationParam> params
|
|||
false,
|
||||
"Convolution",
|
||||
"FP32"
|
||||
},
|
||||
{
|
||||
{ 256ul, ov::Shape { 1, 1, 1, 1 }, { 0.f }, { 0.f }, { 0.f }, { 0.f } },
|
||||
false,
|
||||
{ 255ul, ov::Shape { 1, 1, 1, 1 }, { 0.f }, { 254.f }, { -12.7f }, { 12.7f } },
|
||||
false,
|
||||
"Convolution",
|
||||
"u8"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,20 +11,19 @@ namespace test {
|
|||
namespace snippets {
|
||||
|
||||
typedef std::tuple<
|
||||
ov::PartialShape, // Input 0 Shape
|
||||
std::vector<int>, // Transpose order
|
||||
size_t, // Expected num nodes
|
||||
size_t, // Expected num subgraphs
|
||||
std::string // Target Device
|
||||
InputShape, // Input 0 Shape
|
||||
std::vector<int>, // Transpose order
|
||||
size_t, // Expected num nodes
|
||||
size_t, // Expected num subgraphs
|
||||
std::string // Target Device
|
||||
> TransposeParams;
|
||||
|
||||
typedef std::tuple<
|
||||
ov::PartialShape, // Input 0 Shape
|
||||
ov::PartialShape, // Input 1 Shape
|
||||
std::vector<int>, // Transpose order
|
||||
size_t, // Expected num nodes
|
||||
size_t, // Expected num subgraphs
|
||||
std::string // Target Device
|
||||
std::pair<InputShape, InputShape>, // Input Shapes
|
||||
std::vector<int>, // Transpose order
|
||||
size_t, // Expected num nodes
|
||||
size_t, // Expected num subgraphs
|
||||
std::string // Target Device
|
||||
> TransposeMulParams;
|
||||
|
||||
class Transpose : public testing::WithParamInterface<ov::test::snippets::TransposeParams>,
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ namespace test {
|
|||
namespace snippets {
|
||||
|
||||
typedef std::tuple<
|
||||
std::vector<ov::Shape>, // Input shapes
|
||||
std::vector<int64_t>, // Transpose Order
|
||||
int64_t, // Softmax Axis
|
||||
size_t, // Expected num nodes
|
||||
size_t, // Expected num subgraphs
|
||||
std::string // Target Device
|
||||
std::vector<InputShape>, // Input shapes
|
||||
std::vector<int64_t>, // Transpose Order
|
||||
int64_t, // Softmax Axis
|
||||
size_t, // Expected num nodes
|
||||
size_t, // Expected num subgraphs
|
||||
std::string // Target Device
|
||||
> TransposeSoftmaxParams;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ namespace test {
|
|||
namespace snippets {
|
||||
|
||||
std::string Transpose::getTestCaseName(testing::TestParamInfo<ov::test::snippets::TransposeParams> obj) {
|
||||
ov::PartialShape inputShape;
|
||||
InputShape inputShapes;
|
||||
std::vector<int> order;
|
||||
std::string targetDevice;
|
||||
size_t num_nodes, num_subgraphs;
|
||||
std::tie(inputShape, order, num_nodes, num_subgraphs, targetDevice) = obj.param;
|
||||
std::tie(inputShapes, order, num_nodes, num_subgraphs, targetDevice) = obj.param;
|
||||
|
||||
std::ostringstream result;
|
||||
result << "IS=" << ov::test::utils::partialShape2str({inputShape}) << "_";
|
||||
result << "IS=" << inputShapes << "_";
|
||||
result << "Order=" << ov::test::utils::vec2str(order) << "_";
|
||||
result << "#N=" << num_nodes << "_";
|
||||
result << "#S=" << num_subgraphs << "_";
|
||||
|
|
@ -28,12 +28,12 @@ std::string Transpose::getTestCaseName(testing::TestParamInfo<ov::test::snippets
|
|||
}
|
||||
|
||||
void Transpose::SetUp() {
|
||||
ov::PartialShape inputShape;
|
||||
InputShape inputShape;
|
||||
std::vector<int> order;
|
||||
std::tie(inputShape, order, ref_num_nodes, ref_num_subgraphs, targetDevice) = this->GetParam();
|
||||
init_input_shapes({{{inputShape}, {inputShape.get_shape(), }}});
|
||||
init_input_shapes({inputShape});
|
||||
|
||||
auto f = ov::test::snippets::TransposeFunction({inputShape}, order);
|
||||
auto f = ov::test::snippets::TransposeFunction(inputDynamicShapes, order);
|
||||
function = f.getOriginal();
|
||||
if (!configuration.count("SNIPPETS_MODE")) {
|
||||
configuration.insert({"SNIPPETS_MODE", "IGNORE_CALLBACK"});
|
||||
|
|
@ -41,15 +41,15 @@ void Transpose::SetUp() {
|
|||
}
|
||||
|
||||
std::string TransposeMul::getTestCaseName(testing::TestParamInfo<ov::test::snippets::TransposeMulParams> obj) {
|
||||
std::vector<ov::PartialShape> inputShapes(2);
|
||||
std::pair<InputShape, InputShape> inputShapes;
|
||||
std::vector<int> order;
|
||||
std::string targetDevice;
|
||||
size_t num_nodes, num_subgraphs;
|
||||
std::tie(inputShapes[0], inputShapes[1], order, num_nodes, num_subgraphs, targetDevice) = obj.param;
|
||||
std::tie(inputShapes, order, num_nodes, num_subgraphs, targetDevice) = obj.param;
|
||||
|
||||
std::ostringstream result;
|
||||
for (int i = 0; i < inputShapes.size(); i++)
|
||||
result << "IS[" << i << "]=" << ov::test::utils::partialShape2str({inputShapes[i]}) << "_";
|
||||
result << "IS[0]=" << inputShapes.first << "_";
|
||||
result << "IS[1]=" << inputShapes.second << "_";
|
||||
result << "Order=" << ov::test::utils::vec2str(order) << "_";
|
||||
result << "#N=" << num_nodes << "_";
|
||||
result << "#S=" << num_subgraphs << "_";
|
||||
|
|
@ -58,11 +58,11 @@ std::string TransposeMul::getTestCaseName(testing::TestParamInfo<ov::test::snipp
|
|||
}
|
||||
|
||||
void TransposeMul::SetUp() {
|
||||
std::vector<ov::PartialShape> inputShapes(2);
|
||||
std::pair<InputShape, InputShape> inputShapes;
|
||||
std::vector<int> order;
|
||||
std::tie(inputShapes[0], inputShapes[1], order, ref_num_nodes, ref_num_subgraphs, targetDevice) = this->GetParam();
|
||||
init_input_shapes(static_partial_shapes_to_test_representation(inputShapes));
|
||||
auto f = ov::test::snippets::TransposeMulFunction(inputShapes, order);
|
||||
std::tie(inputShapes, order, ref_num_nodes, ref_num_subgraphs, targetDevice) = this->GetParam();
|
||||
init_input_shapes({inputShapes.first, inputShapes.second});
|
||||
auto f = ov::test::snippets::TransposeMulFunction(inputDynamicShapes, order);
|
||||
function = f.getOriginal();
|
||||
if (!configuration.count("SNIPPETS_MODE")) {
|
||||
configuration.insert({"SNIPPETS_MODE", "IGNORE_CALLBACK"});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace test {
|
|||
namespace snippets {
|
||||
|
||||
std::string TransposeSoftmax::getTestCaseName(testing::TestParamInfo<ov::test::snippets::TransposeSoftmaxParams> obj) {
|
||||
std::vector<ov::Shape> inputShapes;
|
||||
std::vector<InputShape> inputShapes;
|
||||
std::vector<int64_t> order;
|
||||
int axis;
|
||||
std::string targetDevice;
|
||||
|
|
@ -20,8 +20,13 @@ std::string TransposeSoftmax::getTestCaseName(testing::TestParamInfo<ov::test::s
|
|||
std::tie(inputShapes, order, axis, num_nodes, num_subgraphs, targetDevice) = obj.param;
|
||||
|
||||
std::ostringstream result;
|
||||
for (size_t i = 0; i < inputShapes.size(); ++i)
|
||||
result << "IS[" << i << "]=" << ov::test::utils::vec2str(inputShapes[i]) << "_";
|
||||
for (size_t i = 0; i < inputShapes.size(); ++i) {
|
||||
result << "IS[" << i<< "]=" << ov::test::utils::partialShape2str({inputShapes[i].first}) << "_";
|
||||
result << "TS[" << i<< "]=";
|
||||
for (const auto& shape : inputShapes[i].second) {
|
||||
result << "(" << ov::test::utils::vec2str(shape) << ")_";
|
||||
}
|
||||
}
|
||||
result << "TO=" << ov::test::utils::vec2str(order) << "_";
|
||||
result << "Axis=" << axis << "_";
|
||||
result << "#N=" << num_nodes << "_";
|
||||
|
|
@ -31,11 +36,11 @@ std::string TransposeSoftmax::getTestCaseName(testing::TestParamInfo<ov::test::s
|
|||
}
|
||||
|
||||
void TransposeSoftmax::SetUp() {
|
||||
std::vector<ov::Shape> inputShapes;
|
||||
std::vector<InputShape> inputShapes;
|
||||
std::vector<int64_t> order;
|
||||
int64_t axis;
|
||||
std::tie(inputShapes, order, axis, ref_num_nodes, ref_num_subgraphs, targetDevice) = this->GetParam();
|
||||
init_input_shapes(static_shapes_to_test_representation(inputShapes));
|
||||
init_input_shapes(inputShapes);
|
||||
|
||||
auto f = ov::test::snippets::TransposeSoftmaxFunction(inputDynamicShapes, order, axis);
|
||||
function = f.getOriginal();
|
||||
|
|
@ -46,11 +51,11 @@ void TransposeSoftmax::SetUp() {
|
|||
}
|
||||
|
||||
void TransposeSoftmaxEltwise::SetUp() {
|
||||
std::vector<ov::Shape> inputShapes;
|
||||
std::vector<InputShape> inputShapes;
|
||||
std::vector<int64_t> order;
|
||||
int64_t axis;
|
||||
std::tie(inputShapes, order, axis, ref_num_nodes, ref_num_subgraphs, targetDevice) = this->GetParam();
|
||||
init_input_shapes(static_shapes_to_test_representation(inputShapes));
|
||||
init_input_shapes(inputShapes);
|
||||
|
||||
auto f = ov::test::snippets::TransposeSoftmaxEltwiseFunction(inputDynamicShapes, order, axis);
|
||||
function = f.getOriginal();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ protected:
|
|||
class TransposeSoftmaxEltwiseFunction : public TransposeSoftmaxFunction {
|
||||
public:
|
||||
explicit TransposeSoftmaxEltwiseFunction(const std::vector<PartialShape>& inputShapes, const std::vector<int64_t>& order, const int64_t axis)
|
||||
: TransposeSoftmaxFunction(inputShapes, order, axis) {}
|
||||
: TransposeSoftmaxFunction(inputShapes, order, axis) {
|
||||
OPENVINO_ASSERT(input_shapes.size() == 2, "Got invalid number of input shapes");
|
||||
}
|
||||
protected:
|
||||
std::shared_ptr<ov::Model> initOriginal() const override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -78,11 +78,11 @@ std::shared_ptr<ov::Model> TransposeSoftmaxEltwiseFunction::initOriginal() const
|
|||
const auto transpose0Param = std::make_shared<ov::opset1::Parameter>(precision, input_shapes[0]);
|
||||
const auto transpose0Const = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{m_order.size()}, m_order);
|
||||
const auto transpose2 = std::make_shared<ov::op::v1::Transpose>(transpose0Param, transpose0Const);
|
||||
const auto mulConst = ov::test::utils::make_constant(ov::element::f32, transpose2->get_shape());
|
||||
const auto mul = std::make_shared<ov::op::v1::Multiply>(transpose2, mulConst);
|
||||
const auto mul1Param = std::make_shared<ov::opset1::Parameter>(precision, input_shapes[1]);
|
||||
const auto mul = std::make_shared<ov::op::v1::Multiply>(transpose2, mul1Param);
|
||||
const auto softMax = std::make_shared<ov::op::v8::Softmax>(mul, m_axis);
|
||||
const auto hswish = std::make_shared<ov::op::v4::HSwish>(softMax);
|
||||
return std::make_shared<ov::Model>(ov::NodeVector{hswish}, ov::ParameterVector{transpose0Param},
|
||||
return std::make_shared<ov::Model>(ov::NodeVector{hswish}, ov::ParameterVector{transpose0Param, mul1Param},
|
||||
"softmax_transpose");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -430,10 +430,6 @@ public:
|
|||
mvn_results /= tensor_size ? tensor_size : 1;
|
||||
if (!incorrect_values_abs.empty() && equal(1.f, topk_threshold) ||
|
||||
incorrect_values_abs.size() > static_cast<int>(std::floor(topk_threshold * tensor_size))) {
|
||||
#ifdef NDEBUG
|
||||
std::string msg = "[ COMPARATION ] COMPARATION IS FAILED!";
|
||||
msg += " Use DEBUG mode to print `incorrect_values_abs` and get detailed information!";
|
||||
#else
|
||||
std::string msg = "[ COMPARATION ] COMPARATION IS FAILED! incorrect elem counter: ";
|
||||
msg += std::to_string(incorrect_values_abs.size());
|
||||
msg += " among ";
|
||||
|
|
@ -441,11 +437,14 @@ public:
|
|||
msg += " shapes.";
|
||||
for (auto val : incorrect_values_abs) {
|
||||
std::cout << "\nExpected: " << val.expected_value << " Actual: " << val.actual_value
|
||||
<< " Coordinate: " << val.coordinate
|
||||
<< " Diff: " << std::fabs(val.expected_value - val.actual_value)
|
||||
<< " calculated_abs_threshold: " << val.threshold << " abs_threshold: " << abs_threshold
|
||||
<< " rel_threshold: " << rel_threshold << "\n";
|
||||
}
|
||||
#ifdef NDEBUG
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
throw std::runtime_error(msg);
|
||||
} else if (!less_or_equal(mvn_results, mvn_threshold)) {
|
||||
std::string msg = "[ COMPARATION ] COMPARATION IS FAILED due to MVN THRESHOLD: ";
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class TestHSVToRGB(CommonTFLayerTest):
|
|||
elif self.special_case == "Grayscale Image":
|
||||
images_shape = inputs_info['images:0']
|
||||
inputs_data = {}
|
||||
inputs_data['images:0'] = np.ones(images_shape).astype(self.input_type) * np.random.rand()
|
||||
inputs_data['images:0'] = np.broadcast_to([0, 0, 0.5], images_shape).astype(self.input_type)
|
||||
else:
|
||||
images_shape = inputs_info['images:0']
|
||||
inputs_data = {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue