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:
parent
c58bf68bc8
commit
18caac366d
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user