Skip to content

Commit 97493d8

Browse files
committed
initial take on groupconv support
1 parent 618232a commit 97493d8

File tree

13 files changed

+291
-105
lines changed

13 files changed

+291
-105
lines changed

src/common/transformations/include/ov_ops/convolution.hpp

+22-9
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,40 @@ class TRANSFORMATIONS_API Convolution : public ov::op::util::ConvolutionFwdPropB
2020

2121
Convolution() = default;
2222

23-
Convolution(const Output<Node>& data_batch,
24-
const Output<Node>& filters,
25-
const Strides& strides,
26-
const CoordinateDiff& pads_begin,
27-
const CoordinateDiff& pads_end,
28-
const Strides& dilations,
29-
const PadType& auto_pad = PadType::EXPLICIT);
30-
3123
Convolution(const Output<Node>& data_batch,
3224
const Output<Node>& filters,
3325
const Output<Node>& bias,
3426
const Strides& strides,
3527
const CoordinateDiff& pads_begin,
3628
const CoordinateDiff& pads_end,
3729
const Strides& dilations,
38-
const PadType& auto_pad = PadType::EXPLICIT);
30+
const int64_t& groups,
31+
const PadType& auto_pad,
32+
const element::Type& output_type);
3933

4034
void validate_and_infer_types() override;
4135
bool visit_attributes(AttributeVisitor& visitor) override;
4236

4337
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
38+
39+
bool has_groups() const;
40+
int64_t get_groups() const;
41+
42+
bool is_asymmetric() const;
43+
44+
struct Args {
45+
static constexpr const size_t INPUT = 0;
46+
static constexpr const size_t WEIGHTS = 1;
47+
static constexpr const size_t BIAS = 2;
48+
static constexpr const size_t AZP = 3;
49+
static constexpr const size_t WZP = 4;
50+
static constexpr const size_t COMPENSATION = 5;
51+
};
52+
53+
protected:
54+
int64_t m_groups = -1; // negative value means no groups
55+
bool m_asymmetric = false;
56+
ov::element::Type m_output_type = ov::element::dynamic;
4457
};
4558

4659
} // namespace internal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (C) 2018-2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#pragma once
6+
7+
#include "openvino/pass/matcher_pass.hpp"
8+
#include "transformations_visibility.hpp"
9+
10+
namespace ov {
11+
namespace pass {
12+
13+
class TRANSFORMATIONS_API ConvertGroupConvolutionToConvolutionInternal;
14+
15+
} // namespace pass
16+
} // namespace ov
17+
18+
class ov::pass::ConvertGroupConvolutionToConvolutionInternal : public ov::pass::MatcherPass {
19+
public:
20+
OPENVINO_MATCHER_PASS_RTTI("ConvertGroupConvolutionToConvolutionInternal");
21+
ConvertGroupConvolutionToConvolutionInternal();
22+
};

src/common/transformations/src/ov_ops/convolution.cpp

+29-29
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,33 @@
44

55
#include "ov_ops/convolution.hpp"
66

7-
#include "convolution_shape_inference.hpp"
7+
#include "internal_convolution_shape_inference.hpp"
88
#include "itt.hpp"
99
#include "openvino/op/util/precision_sensitive_attribute.hpp"
1010

1111
using namespace std;
1212

1313
namespace ov {
14-
op::internal::Convolution::Convolution(const Output<Node>& data_batch,
15-
const Output<Node>& filters,
16-
const Strides& strides,
17-
const CoordinateDiff& pads_begin,
18-
const CoordinateDiff& pads_end,
19-
const Strides& dilations,
20-
const PadType& auto_pad)
21-
: op::util::ConvolutionFwdPropBase({data_batch, filters}, strides, pads_begin, pads_end, dilations, auto_pad) {
22-
constructor_validate_and_infer_types();
23-
}
24-
2514
op::internal::Convolution::Convolution(const Output<Node>& data_batch,
2615
const Output<Node>& filters,
2716
const Output<Node>& bias,
2817
const Strides& strides,
2918
const CoordinateDiff& pads_begin,
3019
const CoordinateDiff& pads_end,
3120
const Strides& dilations,
32-
const PadType& auto_pad)
33-
: op::util::ConvolutionFwdPropBase({data_batch, filters, bias},
34-
strides,
35-
pads_begin,
36-
pads_end,
37-
dilations,
38-
auto_pad) {
21+
const int64_t& groups,
22+
const PadType& auto_pad,
23+
const element::Type& output_type)
24+
: op::util::ConvolutionFwdPropBase(
25+
bias.get_node() ? OutputVector{data_batch, filters, bias} : OutputVector{data_batch, filters},
26+
strides,
27+
pads_begin,
28+
pads_end,
29+
dilations,
30+
auto_pad),
31+
m_groups(groups),
32+
m_asymmetric(false),
33+
m_output_type(output_type) {
3934
constructor_validate_and_infer_types();
4035
}
4136

@@ -79,23 +74,28 @@ void op::internal::Convolution::validate_and_infer_types() {
7974

8075
shared_ptr<Node> op::internal::Convolution::clone_with_new_inputs(const OutputVector& new_args) const {
8176
check_new_args_count(this, new_args);
82-
if (new_args.size() == 2) {
83-
return make_shared<internal::Convolution>(new_args.at(0),
84-
new_args.at(1),
85-
m_strides,
86-
m_pads_begin,
87-
m_pads_end,
88-
m_dilations,
89-
m_auto_pad);
90-
}
9177
return make_shared<internal::Convolution>(new_args.at(0),
9278
new_args.at(1),
9379
new_args.at(2),
9480
m_strides,
9581
m_pads_begin,
9682
m_pads_end,
9783
m_dilations,
98-
m_auto_pad);
84+
m_groups,
85+
m_auto_pad,
86+
m_output_type);
87+
}
88+
89+
bool op::internal::Convolution::has_groups() const {
90+
return m_groups > 0;
91+
}
92+
93+
int64_t op::internal::Convolution::get_groups() const {
94+
return m_groups;
95+
}
96+
97+
bool op::internal::Convolution::is_asymmetric() const {
98+
return m_asymmetric;
9999
}
100100

101101
} // namespace ov

src/common/transformations/src/transformations/op_conversions/convert_convolution_to_convolution_internal.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
// SPDX-License-Identifier: Apache-2.0
33
//
44

5+
#include "transformations/op_conversions/convert_convolution_to_convolution_internal.hpp"
6+
57
#include "itt.hpp"
68
#include "openvino/core/rt_info.hpp"
79
#include "openvino/op/convolution.hpp"
810
#include "openvino/pass/pattern/op/wrap_type.hpp"
911
#include "ov_ops/convolution.hpp"
10-
#include "transformations/op_conversions/convert_convolution_to_convolution_internal.hpp"
1112

1213
using namespace ov;
1314

@@ -24,11 +25,14 @@ ov::pass::ConvertConvolutionToConvolutionInternal::ConvertConvolutionToConvoluti
2425

2526
auto new_conv = std::make_shared<ov::op::internal::Convolution>(conv->input_value(0),
2627
conv->input_value(1),
28+
Output<Node>(),
2729
conv->get_strides(),
2830
conv->get_pads_begin(),
2931
conv->get_pads_end(),
3032
conv->get_dilations(),
31-
conv->get_auto_pad());
33+
-1,
34+
conv->get_auto_pad(),
35+
conv->get_output_element_type(0));
3236

3337
new_conv->set_friendly_name(conv->get_friendly_name());
3438
copy_runtime_info(conv, new_conv);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (C) 2018-2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#include "transformations/op_conversions/convert_group_convolution_to_convolution_internal.hpp"
6+
7+
#include "itt.hpp"
8+
#include "openvino/core/rt_info.hpp"
9+
#include "openvino/op/group_conv.hpp"
10+
#include "openvino/pass/pattern/op/wrap_type.hpp"
11+
#include "ov_ops/convolution.hpp"
12+
13+
using namespace ov;
14+
15+
ov::pass::ConvertGroupConvolutionToConvolutionInternal::ConvertGroupConvolutionToConvolutionInternal() {
16+
MATCHER_SCOPE(ConvertGroupConvolutionToConvolutionInternal);
17+
18+
auto GroupConvolution = pattern::wrap_type<ov::op::v1::GroupConvolution>();
19+
20+
matcher_pass_callback gconv_callback = [](pattern::Matcher& m) {
21+
auto gconv = ov::as_type_ptr<ov::op::v1::GroupConvolution>(m.get_match_root());
22+
if (!gconv) {
23+
return false;
24+
}
25+
26+
int64_t groups = -1;
27+
auto weights_shape = gconv->get_input_partial_shape(1);
28+
if (weights_shape[0].is_dynamic()) {
29+
return false;
30+
}
31+
groups = weights_shape[0].get_length();
32+
33+
auto new_conv = std::make_shared<ov::op::internal::Convolution>(gconv->input_value(0),
34+
gconv->input_value(1),
35+
Output<Node>(),
36+
gconv->get_strides(),
37+
gconv->get_pads_begin(),
38+
gconv->get_pads_end(),
39+
gconv->get_dilations(),
40+
groups,
41+
gconv->get_auto_pad(),
42+
gconv->get_output_element_type(0));
43+
44+
new_conv->set_friendly_name(gconv->get_friendly_name());
45+
copy_runtime_info(gconv, new_conv);
46+
replace_node(gconv, new_conv);
47+
return true;
48+
};
49+
50+
auto gconv_m = std::make_shared<pattern::Matcher>(GroupConvolution, matcher_name);
51+
this->register_matcher(gconv_m, gconv_callback);
52+
}

src/common/transformations/src/transformations/op_conversions/convolution_bias_fusion.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ ov::pass::ConvolutionBiasFusion::ConvolutionBiasFusion() {
110110
conv->get_pads_begin(),
111111
conv->get_pads_end(),
112112
conv->get_dilations(),
113-
conv->get_auto_pad());
113+
conv->get_groups(),
114+
conv->get_auto_pad(),
115+
conv->get_output_element_type(0));
114116

115117
new_ops.push_back(new_conv);
116118

src/core/shape_inference/include/convolution_shape_inference.hpp

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ template <class TOp,
1515
class TShape,
1616
class TRShape = result_shape_t<TShape>,
1717
typename std::enable_if<std::is_same<TOp, v1::Convolution>::value ||
18-
std::is_same<TOp, internal::Convolution>::value ||
1918
std::is_same<TOp, v1::BinaryConvolution>::value>::type* = nullptr>
2019
std::vector<TRShape> shape_infer(const TOp* op,
2120
const std::vector<TShape>& input_shapes,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (C) 2018-2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
#pragma once
5+
6+
#include "convolution_shape_inference_util.hpp"
7+
#include "openvino/op/convolution.hpp"
8+
#include "ov_ops/convolution.hpp"
9+
#include "utils.hpp"
10+
11+
namespace ov {
12+
namespace op {
13+
14+
template <class TOp,
15+
class TShape,
16+
class TRShape = result_shape_t<TShape>,
17+
typename std::enable_if<std::is_same<TOp, internal::Convolution>::value>::type* = nullptr>
18+
std::vector<TRShape> shape_infer(const TOp* op,
19+
const std::vector<TShape>& input_shapes,
20+
CoordinateDiff& pads_begin,
21+
CoordinateDiff& pads_end) {
22+
NODE_VALIDATION_CHECK(op, input_shapes.size() >= 2);
23+
using namespace ov::util;
24+
25+
const auto num_spatial = convolution::calculate_num_spatial(op, input_shapes);
26+
27+
auto output_shapes = std::vector<TRShape>(1);
28+
auto& output_shape = output_shapes[0];
29+
if (num_spatial != util::num_spatial_undefined) {
30+
const auto& data_shape = input_shapes[0];
31+
const auto& filters_shape = input_shapes[1];
32+
const auto data_rank = data_shape.rank();
33+
const auto filters_rank = filters_shape.rank();
34+
35+
if (op->get_groups() > 1) {
36+
convolution::resize_empty_padding(num_spatial, pads_begin, pads_end);
37+
if (is_attr_validation_required(op)) {
38+
convolution::validate::data_shape(op, data_shape);
39+
40+
NODE_VALIDATION_CHECK(op,
41+
data_rank.compatible(filters_rank - 1),
42+
"Data batch and filters rank do not match (data batch shape: ",
43+
data_shape,
44+
", filters shape: ",
45+
filters_shape,
46+
").");
47+
48+
convolution::validate::common_attributes(op, num_spatial, pads_begin, pads_end);
49+
}
50+
convolution::apply_padding(op, data_shape, filters_shape, pads_begin, pads_end);
51+
52+
output_shape.reserve(util::spatial_dim_offset + num_spatial);
53+
output_shape.emplace_back(data_rank.is_static() ? data_shape[0] : dim::inf_bound);
54+
55+
if (filters_rank.is_static()) {
56+
auto groups = filters_shape[0];
57+
58+
if (data_rank.is_static() && filters_shape[2].is_static()) {
59+
NODE_VALIDATION_CHECK(
60+
op,
61+
groups.merge(groups, groups, (data_shape[1] / filters_shape[2].get_length())),
62+
"Input channels dimension of data batch is incompatible with filter groups or input channels.");
63+
}
64+
65+
groups *= filters_shape[1];
66+
output_shape.push_back(std::move(groups));
67+
} else {
68+
output_shape.emplace_back(dim::inf_bound);
69+
}
70+
} else {
71+
convolution::resize_empty_padding(num_spatial, pads_begin, pads_end);
72+
convolution::validate::filter_shape(op, filters_shape, data_shape);
73+
if (is_attr_validation_required(op)) {
74+
convolution::validate::data_shape(op, data_shape);
75+
convolution::validate::common_attributes(op, num_spatial, pads_begin, pads_end);
76+
}
77+
convolution::apply_padding(op, data_shape, filters_shape, pads_begin, pads_end);
78+
79+
output_shape.reserve(util::spatial_dim_offset + num_spatial);
80+
output_shape.emplace_back(data_rank.is_static() ? data_shape[0] : dim::inf_bound);
81+
output_shape.emplace_back(filters_rank.is_static() ? filters_shape[0] : dim::inf_bound);
82+
}
83+
84+
convolution::append_spatial_shape(op, data_shape, filters_shape, pads_begin, pads_end, output_shape);
85+
} else {
86+
output_shape = PartialShape::dynamic();
87+
}
88+
89+
return output_shapes;
90+
}
91+
92+
} // namespace op
93+
} // namespace ov

src/plugins/intel_cpu/src/graph_optimizer.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,7 @@ void GraphOptimizer::FuseConvolutionMatMulDeconvAndBias(Graph& graph) {
328328
}
329329

330330
if (!deconv) {
331-
return (node->getType() == Type::MatMul ||
332-
(node->getType() == Type::Convolution && node->getAlgorithm() == Algorithm::ConvolutionGrouped)) &&
333-
node->getParentEdges().size() == 2;
331+
return node->getType() == Type::MatMul && node->getParentEdges().size() == 2;
334332
}
335333
return deconv->canFuseBias();
336334
};

src/plugins/intel_cpu/src/nodes/conv.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,10 @@ Convolution::Convolution(const std::shared_ptr<ov::Node>& op, const GraphContext
272272
auto internalConvolutionOp = ov::as_type_ptr<ov::op::internal::Convolution>(op);
273273

274274
if (internalConvolutionOp) {
275-
algorithm = Algorithm::ConvolutionBiased;
276-
withBiases = true;
277-
groupNum = 1;
278-
isGrouped = false;
275+
withBiases = internalConvolutionOp->inputs().size() > 2;
276+
isGrouped = internalConvolutionOp->get_groups() > 1;
277+
groupNum = isGrouped ? internalConvolutionOp->input_value(1).get_shape()[0] : 1;
278+
algorithm = isGrouped ? Algorithm::ConvolutionGrouped : Algorithm::ConvolutionBiased;
279279

280280
weightDims = internalConvolutionOp->input_value(1).get_shape();
281281

src/plugins/intel_cpu/src/shape_inference/shape_inference.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include "gru_cell_shape_inference.hpp"
6666
#include "gru_sequence_shape_inference.hpp"
6767
#include "i420_shape_inference.hpp"
68+
#include "internal_convolution_shape_inference.hpp"
6869
#include "interpolate_shape_inference.hpp"
6970
#include "inverse_shape_inference.hpp"
7071
#include "irdft_shape_inference.hpp"

0 commit comments

Comments
 (0)