[Transformations] ConvertDivide disabled transformation for divide ops from precision sensitive subgraphs (#9561)

* [Transformations] Skip ConvertDivide if Divide is on fp16 shapeOf subgraph

* lambda definition moved out of loop

* review fixes
This commit is contained in:
Vladislav Golubev 2022-01-13 10:32:44 +03:00 committed by GitHub
parent 282f37cdae
commit 456347e1b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 199 additions and 30 deletions

View File

@ -0,0 +1,28 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "openvino/pass/pass.hpp"
#include "transformations_visibility.hpp"
namespace ov {
namespace pass {
class TRANSFORMATIONS_API MarkPrecisionSensitiveDivides;
} // namespace pass
} // namespace ov
/**
* @ingroup ie_transformation_common_api
* @brief MarkPrecisionSensitiveDivides transformation marks the Divide fp16 layers
* inside the subgraph starting from precision-sensitive input and ending at
* the ShapeOf node as disabled for ConvertDivide transformation.
*/
class ov::pass::MarkPrecisionSensitiveDivides : public ModelPass {
public:
OPENVINO_RTTI("MarkPrecisionSensitiveDivides", "0");
bool run_on_model(const std::shared_ptr<ov::Model>& m) override;
};

View File

@ -0,0 +1,34 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "openvino/core/node.hpp"
#include "openvino/core/runtime_attribute.hpp"
#include "transformations_visibility.hpp"
namespace ov {
TRANSFORMATIONS_API void disable_divide_conversion(const std::shared_ptr<Node>& node);
TRANSFORMATIONS_API void enable_divide_conversion(const std::shared_ptr<Node>& node);
TRANSFORMATIONS_API bool divide_is_nonconvertible(const std::shared_ptr<Node>& node);
/**
* @ingroup ie_runtime_attr_api
* @brief NonconvertibleDivide class represents runtime info attribute that marks
* a Divide as prohibitted to transform it to power.
*/
class TRANSFORMATIONS_API NonconvertibleDivide : public RuntimeAttribute {
public:
OPENVINO_RTTI("nonconvertable_divide", "0");
NonconvertibleDivide() = default;
bool is_copyable() const override { return false; }
};
} // namespace ov

View File

@ -180,6 +180,10 @@ TRANSFORMATIONS_API std::shared_ptr<Node> clone_try_fold(const std::shared_ptr<N
TRANSFORMATIONS_API bool shapes_equal_except_dynamic_expected_batch(const ngraph::PartialShape& expected, const ngraph::PartialShape& actual);
TRANSFORMATIONS_API void visit_shape_path(const std::shared_ptr<ov::Node>& node,
std::unordered_set<std::shared_ptr<ov::Node>>& visited,
std::function<void(std::shared_ptr<ov::Node>)> func);
template <typename T, typename... Args>
std::shared_ptr<Node> make_try_fold(Args&&... args) {
auto unary_output_node = std::make_shared<T>(std::forward<Args>(args)...);

View File

@ -53,6 +53,7 @@
#include "transformations/common_optimizations/convert_compression_only_to_legacy.hpp"
#include <transformations/common_optimizations/transpose_reshape_elimination_for_matmul.hpp>
#include "transformations/common_optimizations/matmul_multiply_fusion.hpp"
#include "transformations/common_optimizations/mark_precision_sensitive_divides.hpp"
#include "transformations/op_conversions/bidirectional_sequences_decomposition.hpp"
#include "transformations/op_conversions/convert_pad_to_group_conv.hpp"
#include "transformations/op_conversions/convert_divide.hpp"
@ -117,6 +118,8 @@ bool ngraph::pass::CommonOptimizations::run_on_model(const std::shared_ptr<ngrap
// after support for FP16 IR is implemented
manager.register_pass<ov::pass::ConvertCompressedOnlyToLegacy>();
manager.register_pass<ov::pass::MarkPrecisionSensitiveDivides>();
// TODO: move to KMB
manager.register_pass<ngraph::pass::WeightsDequantizeToFakeQuantize>();

View File

@ -0,0 +1,47 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/common_optimizations/mark_precision_sensitive_divides.hpp"
#include <memory>
#include <vector>
#include "openvino/op/util/precision_sensitive_attribute.hpp"
#include "openvino/opsets/opset8.hpp"
#include "transformations/rt_info/nonconvertible_divide.hpp"
#include "transformations/utils/utils.hpp"
bool ov::pass::MarkPrecisionSensitiveDivides::run_on_model(const std::shared_ptr<ov::Model>& m) {
std::deque<std::shared_ptr<Node>> nodes;
std::unordered_set<std::shared_ptr<Node>> visited;
for (auto& r : m->get_results())
nodes.push_back(r);
for (auto& r : m->get_sinks())
nodes.emplace_back(r);
auto markup_func = [](std::shared_ptr<Node> node) {
if (ov::is_type<ov::opset8::Divide>(node) && node->get_output_element_type(0) == ngraph::element::f16) {
ov::disable_divide_conversion(node);
}
};
while (!nodes.empty()) {
auto curr_node = nodes.front();
nodes.pop_front();
if (visited.count(curr_node))
continue;
for (auto& input : curr_node->inputs()) {
if (ov::is_precision_sensitive(input))
ngraph::op::util::visit_shape_path(input.get_source_output().get_node_shared_ptr(), visited, markup_func);
}
visited.insert(curr_node);
for (auto& input_value : curr_node->input_values()) {
// continue searching
const auto& input_node = input_value.get_node_shared_ptr();
nodes.push_front(input_node);
}
}
return true;
}

View File

@ -17,33 +17,6 @@
using namespace std;
namespace {
void visit_shape_path(const shared_ptr<ov::Node>& node, unordered_set<shared_ptr<ov::Node>>& visited) {
if (!node)
return;
visited.insert(node);
deque<shared_ptr<ov::Node>> nodes{node};
while (!nodes.empty()) {
auto curr_node = nodes.front();
nodes.pop_front();
// Do not check if already visited
if (ov::is_type<ov::opset1::ShapeOf>(curr_node) || ov::is_type<ov::opset3::ShapeOf>(curr_node)) {
continue;
}
visited.insert(curr_node);
if (ov::is_type<ov::opset8::Constant>(curr_node)) {
ov::disable_fp16_compression(curr_node);
} else {
for (auto& input_value : curr_node->input_values()) {
// continue searching
const auto& input_node = input_value.get_node_shared_ptr();
nodes.push_front(input_node);
}
}
}
}
} // namespace
bool ov::pass::MarkPrecisionSensitiveSubgraphs::run_on_model(const std::shared_ptr<ov::Model>& f) {
deque<shared_ptr<Node>> nodes;
unordered_set<shared_ptr<Node>> visited;
@ -52,6 +25,12 @@ bool ov::pass::MarkPrecisionSensitiveSubgraphs::run_on_model(const std::shared_p
for (auto& r : f->get_sinks())
nodes.emplace_back(r);
auto markup_func = [](shared_ptr<Node> node) {
if (ov::is_type<ov::opset8::Constant>(node)) {
ov::disable_fp16_compression(node);
}
};
while (!nodes.empty()) {
auto curr_node = nodes.front();
nodes.pop_front();
@ -59,7 +38,7 @@ bool ov::pass::MarkPrecisionSensitiveSubgraphs::run_on_model(const std::shared_p
continue;
for (auto& input : curr_node->inputs()) {
if (ov::is_precision_sensitive(input))
visit_shape_path(input.get_source_output().get_node_shared_ptr(), visited);
ngraph::op::util::visit_shape_path(input.get_source_output().get_node_shared_ptr(), visited, markup_func);
}
visited.insert(curr_node);

View File

@ -14,6 +14,8 @@
#include <ngraph/validation_util.hpp>
#include <ngraph/log.hpp>
#include "transformations/rt_info/nonconvertible_divide.hpp"
NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertDivide, "ConvertDivide", 0);
NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertDivideWithConstant, "ConvertDivideWithConstant", 0);
@ -21,8 +23,8 @@ namespace {
bool convert_divide(std::shared_ptr<ngraph::Node> node) {
auto div = std::dynamic_pointer_cast<ngraph::opset1::Divide>(node);
// We can not apply this transformation in case with integer input data type
if (!div || div->get_input_element_type(0).is_integral()
|| div->get_input_element_type(1).is_integral()) {
if (!div || ov::divide_is_nonconvertible(div)
|| div->get_input_element_type(0).is_integral()) {
return false;
}

View File

@ -0,0 +1,20 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/rt_info/nonconvertible_divide.hpp"
void ov::disable_divide_conversion(const std::shared_ptr<Node>& node) {
auto& rt_info = node->get_rt_info();
rt_info[NonconvertibleDivide::get_type_info_static()] = NonconvertibleDivide{};
}
void ov::enable_divide_conversion(const std::shared_ptr<Node>& node) {
auto& rt_info = node->get_rt_info();
rt_info.erase(NonconvertibleDivide::get_type_info_static());
}
bool ov::divide_is_nonconvertible(const std::shared_ptr<Node>& node) {
const auto& rt_info = node->get_rt_info();
return rt_info.count(NonconvertibleDivide::get_type_info_static());
}

View File

@ -8,6 +8,7 @@
#include <functional>
#include <memory>
#include <ngraph/opsets/opset1.hpp>
#include <ngraph/op/broadcast.hpp>
#include <ngraph/op/constant.hpp>
#include <ngraph/op/reshape.hpp>
@ -177,6 +178,31 @@ bool shapes_equal_except_dynamic_expected_batch(const ngraph::PartialShape& expe
}
}
void visit_shape_path(const std::shared_ptr<ov::Node>& node,
std::unordered_set<std::shared_ptr<ov::Node>>& visited,
std::function<void(std::shared_ptr<ov::Node>)> func) {
if (!node)
return;
visited.insert(node);
std::deque<std::shared_ptr<ov::Node>> nodes{node};
while (!nodes.empty()) {
auto curr_node = nodes.front();
nodes.pop_front();
// Do not check if already visited
if (ngraph::is_type<ngraph::opset1::ShapeOf>(curr_node) || ngraph::is_type<ngraph::opset3::ShapeOf>(curr_node)) {
continue;
}
visited.insert(curr_node);
func(curr_node);
for (auto& input_value : curr_node->input_values()) {
// continue searching
const auto& input_node = input_value.get_node_shared_ptr();
nodes.push_front(input_node);
}
}
}
} // namespace util
} // namespace op
} // namespace ngraph

View File

@ -11,6 +11,7 @@
#include <ngraph/function.hpp>
#include <ngraph/opsets/opset1.hpp>
#include <transformations/op_conversions/convert_divide.hpp>
#include <transformations/common_optimizations/mark_precision_sensitive_divides.hpp>
#include <transformations/init_node_info.hpp>
#include <transformations/utils/utils.hpp>
#include <ngraph/pass/manager.hpp>
@ -122,3 +123,28 @@ TEST_F(TransformationTestsF, ConvertDivideWithConstantNegative) {
function_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{data1, data2});
}
}
TEST_F(TransformationTestsF, ConvertDivideFP16ShapeOfSubgraphNegative) {
{
auto data = std::make_shared<ngraph::opset1::Parameter>(ngraph::element::f16, ngraph::Shape{1, 3, 22, 22});
auto gather = ngraph::op::util::node_to_get_shape_value_of_indices_from_shape_source(data, {2, 3});
auto convert = std::make_shared<ngraph::opset1::Convert>(gather, ngraph::element::f16);
auto divide_constant = ngraph::opset1::Constant::create(ngraph::element::f16, ngraph::Shape{1}, {0.5});
auto divide = std::make_shared<ngraph::opset1::Divide>(convert, divide_constant);
auto convert_after = std::make_shared<ngraph::opset1::Convert>(divide, ngraph::element::i32);
ngraph::opset1::Interpolate::Attributes interp_attr;
interp_attr.antialias = false;
interp_attr.axes = {2, 3};
interp_attr.mode = "nearest";
interp_attr.pads_begin = {0, 0, 0, 0};
interp_attr.pads_end = {0, 0, 0, 0};
auto interpolate = std::make_shared<ngraph::opset1::Interpolate>(data, convert_after, interp_attr);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{interpolate}, ngraph::ParameterVector{data});
ov::pass::MarkPrecisionSensitiveDivides().run_on_model(function);
manager.register_pass<ngraph::pass::ConvertDivide>();
}
}