ConvertPadToGroupConvolution support Pad12 with positive indexes (#18301)

* fix transformations

* clang fix

* fix unit tests - check the both Pad versions

* add unit tests checking negative padding

---------

Co-authored-by: Ivan Tikhonov <ivan.tikhonov@intel.com>
This commit is contained in:
Evgeny Kotov 2023-07-05 09:25:32 +02:00 committed by GitHub
parent c58bf68bc8
commit 18caac366d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 196 additions and 62 deletions

View File

@ -24,6 +24,7 @@ class TRANSFORMATIONS_API ConvertPadToGroupConvolution;
* 1. PadMode must be Constant and value is equal to 0
* 2. Padding must be applied only for spatial dimensions
* 3. Input shape rank must be static and greater than 3
* 4. Padding values must be non-negative
*/
class ov::pass::ConvertPadToGroupConvolution : public ov::pass::MatcherPass {

View File

@ -8,6 +8,7 @@
#include <ngraph/pattern/op/pattern.hpp>
#include <ngraph/pattern/op/wrap_type.hpp>
#include <ngraph/rt_info.hpp>
#include <openvino/op/util/pad_base.hpp>
#include <openvino/opsets/opset4.hpp>
#include <vector>
@ -15,10 +16,10 @@
ov::pass::ConvertPadToGroupConvolution::ConvertPadToGroupConvolution() {
MATCHER_SCOPE(ConvertPadToGroupConvolution);
auto neg = ngraph::pattern::wrap_type<opset4::Pad>(pattern::has_static_dim(1));
auto neg = ngraph::pattern::wrap_type<op::util::PadBase>(pattern::has_static_dim(1));
matcher_pass_callback callback = [](pattern::Matcher& m) {
auto pad = std::dynamic_pointer_cast<ov::opset4::Pad>(m.get_match_root());
auto pad = std::dynamic_pointer_cast<ov::op::util::PadBase>(m.get_match_root());
if (!pad) {
return false;
}
@ -57,6 +58,15 @@ ov::pass::ConvertPadToGroupConvolution::ConvertPadToGroupConvolution() {
return false;
}
// check that Pad has non-negative values
auto pred = [](int64_t a) {
return a < 0;
};
if (std::any_of(pad_begin.begin(), pad_begin.end(), pred) ||
std::any_of(pad_begin.begin(), pad_begin.end(), pred)) {
return false;
}
// Check that not spatial dimension are not padded
if (std::any_of(pad_begin.begin(),
pad_begin.begin() + 2,

View File

@ -5,9 +5,7 @@
#include <gtest/gtest.h>
#include <memory>
#include <ngraph/function.hpp>
#include <ngraph/opsets/opset4.hpp>
#include <ngraph/pass/manager.hpp>
#include <openvino/op/pad.hpp>
#include <queue>
#include <string>
#include <transformations/init_node_info.hpp>
@ -15,94 +13,219 @@
#include <transformations/utils/utils.hpp>
#include "common_test_utils/ngraph_test_utils.hpp"
#include "openvino/core/model.hpp"
#include "openvino/opsets/opset12.hpp"
#include "openvino/pass/manager.hpp"
using namespace testing;
using namespace ngraph;
using namespace ov;
using namespace ov::opset12;
TEST_F(TransformationTestsF, ConvertPadToConv) {
using NodePtr = std::shared_ptr<ov::Node>;
class IPadFactory {
public:
explicit IPadFactory(const std::string& type_name) : type_name_(type_name) {}
virtual ~IPadFactory() = default;
virtual std::shared_ptr<ov::Node> create(const Output<Node>& arg,
const Output<Node>& pads_begin,
const Output<Node>& pads_end,
ov::op::PadMode pad_mode) const = 0;
virtual std::shared_ptr<ov::Node> create(const Output<Node>& arg,
const Output<Node>& pads_begin,
const Output<Node>& pads_end,
const Output<Node>& arg_pad_value,
ov::op::PadMode pad_mode) const = 0;
const std::string& getTypeName() const {
return type_name_;
}
private:
const std::string type_name_;
};
using PadFactoryPtr = std::shared_ptr<IPadFactory>;
template <typename PadT>
class PadFactory : public IPadFactory {
public:
explicit PadFactory(const std::string& type_name) : IPadFactory(type_name) {}
NodePtr create(const Output<Node>& arg,
const Output<Node>& pads_begin,
const Output<Node>& pads_end,
ov::op::PadMode pad_mode) const override {
return std::make_shared<PadT>(arg, pads_begin, pads_end, pad_mode);
}
NodePtr create(const Output<Node>& arg,
const Output<Node>& pads_begin,
const Output<Node>& pads_end,
const Output<Node>& arg_pad_value,
ov::op::PadMode pad_mode) const override {
return std::make_shared<PadT>(arg, pads_begin, pads_end, arg_pad_value, pad_mode);
}
};
template <typename PadT>
PadFactoryPtr CreatePadFactory(const std::string& type_name) {
return std::make_shared<PadFactory<PadT>>(type_name);
}
#undef CREATE_PAD_FACTORY
#define CREATE_PAD_FACTORY(type_name, type_str) CreatePadFactory<type_name>(type_str)
std::vector<PadFactoryPtr> pad_factories = {CREATE_PAD_FACTORY(ov::op::v1::Pad, "op_v1_Pad"),
CREATE_PAD_FACTORY(ov::op::v12::Pad, "op_v12_Pad")};
struct ITestModelFactory {
explicit ITestModelFactory(const std::string& a_test_name) : test_name(a_test_name) {}
virtual ~ITestModelFactory() = default;
virtual void setup(PadFactoryPtr pad_factory, ov::pass::Manager& manager) = 0;
std::string test_name;
std::shared_ptr<ov::Model> function;
std::shared_ptr<ov::Model> function_ref;
};
using TestModelFactoryPtr = std::shared_ptr<ITestModelFactory>;
using TestParams = std::tuple<PadFactoryPtr, TestModelFactoryPtr>;
class PadTestFixture : public ::testing::WithParamInterface<TestParams>, public TransformationTestsF {
public:
static std::string get_test_name(const ::testing::TestParamInfo<TestParams>& obj) {
PadFactoryPtr pad_factory;
TestModelFactoryPtr model_factory;
std::tie(pad_factory, model_factory) = obj.param;
std::ostringstream test_name;
test_name << "pad_factory=" << pad_factory->getTypeName() << "/";
test_name << "model_factory=" << model_factory->test_name;
return test_name.str();
}
};
TEST_P(PadTestFixture, CompareFunctions) {
PadFactoryPtr pad_factory;
TestModelFactoryPtr model_factory;
std::tie(pad_factory, model_factory) = this->GetParam();
model_factory->setup(pad_factory, manager);
model = model_factory->function;
model_ref = model_factory->function_ref;
if (!model_ref)
model_ref = model->clone();
}
#define TEST_BODY(TestName) \
struct TestName : public ITestModelFactory { \
TestName() : ITestModelFactory(#TestName) {} \
void setup(PadFactoryPtr pad_factory, ov::pass::Manager& manager) override; \
}; \
void TestName::setup(PadFactoryPtr pad_factory, ov::pass::Manager& manager)
TEST_BODY(ConvertPadToConv) {
{
auto input = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = opset4::Constant::create(element::f32, Shape{}, {0});
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = Constant::create(element::f32, Shape{}, {0});
auto pad_mode = op::PadMode::CONSTANT;
auto pad = std::make_shared<opset4::Pad>(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Function>(NodeVector{pad}, ParameterVector{input});
auto pad = pad_factory->create(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Model>(NodeVector{pad}, ParameterVector{input});
manager.register_pass<ov::pass::ConvertPadToGroupConvolution>();
}
{
auto input = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 3, 64, 64});
auto weights = opset4::Constant::create(element::f32, Shape{3, 1, 1, 1, 1}, {1});
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto weights = Constant::create(element::f32, Shape{3, 1, 1, 1, 1}, {1});
Strides stride{1, 1};
CoordinateDiff pad_begin{1, 0}, pad_end{0, 1};
auto conv = std::make_shared<opset4::GroupConvolution>(input, weights, stride, pad_begin, pad_end, stride);
auto conv = std::make_shared<GroupConvolution>(input, weights, stride, pad_begin, pad_end, stride);
function_ref = std::make_shared<Function>(NodeVector{conv}, ParameterVector{input});
function_ref = std::make_shared<Model>(NodeVector{conv}, ParameterVector{input});
}
}
TEST_F(TransformationTestsF, ConvertPadToConvNeg1) {
auto get_function = []() -> std::shared_ptr<Function> {
auto input = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = opset4::Constant::create(element::i64, Shape{4}, {1, 0, 1, 0}); // Batch dim padding
auto pad_end = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = opset4::Constant::create(element::f32, Shape{}, {0});
TEST_BODY(NegativeConvertPadToConv) {
{
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = Constant::create(element::i64, Shape{4}, {0, 0, -1, 0});
auto pad_end = Constant::create(element::i64, Shape{4}, {0, 0, 0, -1});
auto pad_value = Constant::create(element::f32, Shape{}, {0});
auto pad_mode = op::PadMode::CONSTANT;
auto pad = std::make_shared<opset4::Pad>(input, pad_begin, pad_end, pad_value, pad_mode);
return std::make_shared<Function>(NodeVector{pad}, ParameterVector{input});
};
function = get_function();
function_ref = get_function();
auto pad = pad_factory->create(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Model>(NodeVector{pad}, ParameterVector{input});
}
manager.register_pass<ov::pass::ConvertPadToGroupConvolution>();
}
TEST_F(TransformationTestsF, ConvertPadToConvNeg2) {
auto get_function = []() -> std::shared_ptr<Function> {
auto input = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = opset4::Constant::create(element::i64, Shape{4}, {0, 1, 0, 1}); // Channel dim padding
auto pad_value = opset4::Constant::create(element::f32, Shape{}, {0});
TEST_BODY(ConvertPadToConvNeg1) {
{
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = Constant::create(element::i64, Shape{4}, {1, 0, 1, 0}); // Batch dim padding
auto pad_end = Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = Constant::create(element::f32, Shape{}, {0});
auto pad_mode = op::PadMode::CONSTANT;
auto pad = std::make_shared<opset4::Pad>(input, pad_begin, pad_end, pad_value, pad_mode);
return std::make_shared<Function>(NodeVector{pad}, ParameterVector{input});
};
auto pad = pad_factory->create(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Model>(NodeVector{pad}, ParameterVector{input});
}
function = get_function();
function_ref = get_function();
manager.register_pass<ov::pass::ConvertPadToGroupConvolution>();
}
TEST_F(TransformationTestsF, ConvertPadToConvNeg3) {
auto get_function = []() -> std::shared_ptr<Function> {
auto input = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = opset4::Constant::create(element::f32, Shape{}, {0});
TEST_BODY(ConvertPadToConvNeg2) {
{
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = Constant::create(element::i64, Shape{4}, {0, 1, 0, 1}); // Channel dim padding
auto pad_value = Constant::create(element::f32, Shape{}, {0});
auto pad_mode = op::PadMode::CONSTANT;
auto pad = pad_factory->create(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Model>(NodeVector{pad}, ParameterVector{input});
}
manager.register_pass<ov::pass::ConvertPadToGroupConvolution>();
}
TEST_BODY(ConvertPadToConvNeg3) {
{
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = Constant::create(element::f32, Shape{}, {0});
auto pad_mode = op::PadMode::SYMMETRIC; // Unsupported mode
auto pad = std::make_shared<opset4::Pad>(input, pad_begin, pad_end, pad_value, pad_mode);
return std::make_shared<Function>(NodeVector{pad}, ParameterVector{input});
};
auto pad = pad_factory->create(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Model>(NodeVector{pad}, ParameterVector{input});
}
function = get_function();
function_ref = get_function();
manager.register_pass<ov::pass::ConvertPadToGroupConvolution>();
}
TEST_F(TransformationTestsF, ConvertPadToConvNeg4) {
auto get_function = []() -> std::shared_ptr<Function> {
auto input = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = opset4::Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = opset4::Constant::create(element::f32, Shape{}, {1.}); // Unsupported value
TEST_BODY(ConvertPadToConvNeg4) {
{
auto input = std::make_shared<Parameter>(element::f32, Shape{1, 3, 64, 64});
auto pad_begin = Constant::create(element::i64, Shape{4}, {0, 0, 1, 0});
auto pad_end = Constant::create(element::i64, Shape{4}, {0, 0, 0, 1});
auto pad_value = Constant::create(element::f32, Shape{}, {1.}); // Unsupported value
auto pad_mode = op::PadMode::CONSTANT;
auto pad = std::make_shared<opset4::Pad>(input, pad_begin, pad_end, pad_value, pad_mode);
return std::make_shared<Function>(NodeVector{pad}, ParameterVector{input});
};
auto pad = pad_factory->create(input, pad_begin, pad_end, pad_value, pad_mode);
function = std::make_shared<Model>(NodeVector{pad}, ParameterVector{input});
}
function = get_function();
function_ref = get_function();
manager.register_pass<ov::pass::ConvertPadToGroupConvolution>();
}
#undef CREATE_MODEL_FACTORY
#define CREATE_MODEL_FACTORY(type_name) std::make_shared<type_name>()
std::vector<TestModelFactoryPtr> model_factories = {CREATE_MODEL_FACTORY(ConvertPadToConv),
CREATE_MODEL_FACTORY(ConvertPadToConvNeg1),
CREATE_MODEL_FACTORY(ConvertPadToConvNeg2),
CREATE_MODEL_FACTORY(ConvertPadToConvNeg3),
CREATE_MODEL_FACTORY(ConvertPadToConvNeg4),
CREATE_MODEL_FACTORY(NegativeConvertPadToConv)};
INSTANTIATE_TEST_SUITE_P(ConvertPadToGroupConvolutionTestSuite,
PadTestFixture,
::testing::Combine(::testing::ValuesIn(pad_factories), ::testing::ValuesIn(model_factories)),
PadTestFixture::get_test_name);