Commit 1ce31a49 authored by Tomasz Socha's avatar Tomasz Socha Committed by Michał Karzyński

[SPEC] Add v1:Convolution operator (#3636)

parent ac4676ff
...@@ -25,9 +25,468 @@ ...@@ -25,9 +25,468 @@
using namespace std; using namespace std;
using namespace ngraph; using namespace ngraph;
constexpr NodeTypeInfo op::Convolution::type_info; // *** Convolution OP SET 1 ***
constexpr NodeTypeInfo op::v1::Convolution::type_info;
op::Convolution::Convolution(const Output<Node>& data_batch, op::v1::Convolution::Convolution(const Output<Node>& data_batch,
const Output<Node>& filters,
const Strides& strides,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end,
const Strides& dilations,
const PadType& auto_pad)
: Op({data_batch, filters})
, m_strides(strides)
, m_dilations(dilations)
, m_pads_begin(pads_begin)
, m_pads_end(pads_end)
, m_auto_pad(auto_pad)
{
constructor_validate_and_infer_types();
}
void op::v1::Convolution::validate_and_infer_types()
{
const PartialShape& data_batch_shape = get_input_partial_shape(0);
element::Type data_batch_et = get_input_element_type(0);
const PartialShape& filters_shape = get_input_partial_shape(1);
element::Type filters_et = get_input_element_type(1);
if (m_strides.size() == 0)
{
m_strides = conv_default_strides(this, data_batch_shape, filters_shape);
}
if (m_dilations.size() == 0)
{
m_dilations = conv_default_strides(this, data_batch_shape, filters_shape);
}
if (m_pads_begin.size() == 0)
{
m_pads_begin = conv_default_padding(this, data_batch_shape, filters_shape);
}
if (m_pads_end.size() == 0)
{
m_pads_end = conv_default_padding(this, data_batch_shape, filters_shape);
}
if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER)
{
if (data_batch_shape.is_static() && filters_shape.is_static())
{
m_pads_begin.clear();
m_pads_end.clear();
auto filter_shape = filters_shape.to_shape();
filter_shape.erase(filter_shape.begin(), filter_shape.begin() + 2); // Remove {O,I}
infer_auto_padding(data_batch_shape.to_shape(),
filter_shape,
m_strides,
m_dilations,
m_auto_pad,
m_pads_end,
m_pads_begin);
}
}
element::Type result_et;
PartialShape result_shape;
NODE_VALIDATION_CHECK(
this,
element::Type::merge(result_et, data_batch_et, filters_et),
"Element types for data batch and filters do not match (data batch element type: ",
data_batch_et,
", filters element type: ",
filters_et,
").");
result_shape =
infer_convolution_forward(this,
data_batch_shape,
Strides(static_cast<size_t>(data_batch_shape.rank()) - 2, 1),
m_pads_begin,
m_pads_end,
filters_shape,
m_strides,
m_dilations);
set_output_type(0, result_et, result_shape);
}
shared_ptr<Node> op::v1::Convolution::copy_with_new_args(const NodeVector& new_args) const
{
check_new_args_count(this, new_args);
return make_shared<v1::Convolution>(new_args.at(0),
new_args.at(1),
m_strides,
m_pads_begin,
m_pads_end,
m_dilations,
m_auto_pad);
}
void op::v1::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas)
{
auto delta = deltas.at(0);
auto x = input_value(0);
const auto x_shape = x.get_shape();
auto f = input_value(1);
const auto f_shape = f.get_shape();
adjoints.add_delta(x,
make_shared<op::v1::ConvolutionBackpropData>(
x_shape, f, delta, m_strides, m_dilations, m_pads_begin, m_pads_end));
adjoints.add_delta(f,
make_shared<op::v1::ConvolutionBackpropFilters>(
x, f_shape, delta, m_strides, m_dilations, m_pads_begin, m_pads_end));
}
constexpr NodeTypeInfo op::v1::ConvolutionBackpropData::type_info;
shared_ptr<Node> op::v1::Convolution::get_default_value() const
{
return ngraph::make_constant_from_string("0", get_element_type(), get_shape());
}
op::v1::ConvolutionBackpropData::ConvolutionBackpropData(const Shape& data_batch_shape,
const Output<Node>& filters,
const Output<Node>& output_delta,
const Strides& strides,
const Strides& dilations,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end)
: Op({filters, output_delta})
, m_data_batch_shape(data_batch_shape)
, m_strides(strides)
, m_dilations(dilations)
, m_pads_begin(pads_begin)
, m_pads_end(pads_end)
{
constructor_validate_and_infer_types();
}
void op::v1::ConvolutionBackpropData::validate_and_infer_types()
{
// Backprop to data is itself convolution, with inputs/outputs/attributes transmogrified as
// follows.
//
// Forward Backward
// "N" axis for data batch 0 0
// "C" axis for data batch 1 1
// "Co" axis for filters 0 0
// "Ci" axis for filters 1 1
// "N" axis for output 0 0
// "C" axis for output 1 1
// Data batch x delta
// Data batch shape S_x S_o
// Filters f reverse(f) [on spatial axes]
// Filters shape S_f S_f
// Window movement strides q_x p_x
// Window dilation strides p_f p_f
// Padding below a_x (S_f - 1)p_f - a_x
// Padding above b_x (S_f - 1)p_f +
// + ((a_x + (S_x - 1)p_x + b_x - (S_f - 1)p_f)
// % q_x)
// - b_x
// Output shape S_o S_x
//
// To _validate_, we simply need to check/infer the output shape of the forward convolution,
// then check to make sure that the incoming delta has the same shape as the forward output.
const PartialShape& filters_shape = get_input_partial_shape(0);
element::Type filters_et = get_input_element_type(0);
const PartialShape& delta_shape = get_input_partial_shape(1);
element::Type delta_et = get_input_element_type(1);
element::Type forward_result_et;
PartialShape forward_result_shape;
NODE_VALIDATION_CHECK(
this,
element::Type::merge(forward_result_et, delta_et, filters_et),
"Element types for data batch and filters do not match (data batch element type: ",
delta_et,
", filters element type: ",
filters_et,
").");
forward_result_shape = infer_convolution_forward(this,
m_data_batch_shape,
Strides(m_data_batch_shape.size() - 2, 0),
m_pads_begin,
m_pads_end,
filters_shape,
m_strides,
m_dilations);
NODE_VALIDATION_CHECK(this,
forward_result_shape.compatible(delta_shape),
"Inferred forward output shape (",
forward_result_shape,
") does not match shape of ",
"delta (",
delta_shape,
").");
set_output_type(0, forward_result_et, m_data_batch_shape);
}
void op::v1::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints,
const NodeVector& deltas)
{
auto delta = deltas.at(0);
auto x = input_value(1);
const auto x_shape = x.get_shape();
auto f = input_value(0);
const auto f_shape = f.get_shape();
auto data_conv = make_shared<op::v1::Convolution>(
delta, f, m_strides, m_pads_begin, m_pads_end, m_dilations);
adjoints.add_delta(x, data_conv);
Strides strides = m_dilations;
CoordinateDiff pads_begin;
CoordinateDiff pads_end;
const Shape& filters_shape = get_input_shape(0);
for (size_t i = 0; i < f_shape.size() - 2; i++)
{
ptrdiff_t pads_begin_backward =
(static_cast<ptrdiff_t>(filters_shape[i + 2]) - 1) - m_pads_begin[i];
pads_begin.push_back(pads_begin_backward);
ptrdiff_t pads_end_backward =
(static_cast<ptrdiff_t>(filters_shape[i + 2]) - 1) * m_dilations[i] +
((m_pads_begin[i] + ((m_data_batch_shape[i + 2]) - 1) * m_strides[i] + m_pads_end[i] -
(static_cast<ptrdiff_t>(filters_shape[i + 2]) - 1) * m_dilations[i]) %
m_strides[i]) -
m_pads_end[i];
pads_end.push_back(pads_end_backward -
(pads_begin_backward + (x_shape[i + 2] - 1) * m_strides[i] +
pads_end_backward - (f_shape[i + 2] - 1) * m_dilations[i]) %
m_strides[i]);
}
auto swap_NC = [](const Output<Node>& n) {
AxisVector ax_order = ngraph::get_default_order(n.get_shape());
ax_order[0] = 1;
ax_order[1] = 0;
auto new_shape = n.get_shape();
new_shape[0] = n.get_shape()[1];
new_shape[1] = n.get_shape()[0];
return make_shared<op::Reshape>(n, ax_order, new_shape);
};
delta = swap_NC(delta);
x = swap_NC(x);
shared_ptr<Node> filter_deconv_bprop = make_shared<op::v1::Convolution>(
x, delta, strides, pads_begin, pads_end, Strides(x.get_shape().size() - 2, 1));
AxisSet axes;
for (size_t i = 2; i < filter_deconv_bprop->get_shape().size(); ++i)
{
axes.insert(i);
}
filter_deconv_bprop = make_shared<ngraph::op::Reverse>(filter_deconv_bprop, axes);
adjoints.add_delta(f, filter_deconv_bprop);
}
shared_ptr<Node>
op::v1::ConvolutionBackpropData::copy_with_new_args(const NodeVector& new_args) const
{
check_new_args_count(this, new_args);
return make_shared<v1::ConvolutionBackpropData>(m_data_batch_shape,
new_args.at(0),
new_args.at(1),
m_strides,
m_dilations,
m_pads_begin,
m_pads_end);
}
CoordinateDiff op::v1::ConvolutionBackpropData::compute_backward_delta_out_pad_below() const
{
auto& in_shape = get_data_batch_shape();
auto& filter_dilation = get_dilations();
auto& filter_shape = get_input_shape(0);
auto& in_pad_below = get_pads_begin();
size_t spatial_dim_count = static_cast<size_t>(in_shape.size()) - 2;
CoordinateDiff backward_delta_out_pad_below;
backward_delta_out_pad_below.resize(spatial_dim_count);
for (size_t i = 0; i < spatial_dim_count; i++)
{
backward_delta_out_pad_below[i] =
(static_cast<ptrdiff_t>(filter_shape[i + 2]) - 1) * filter_dilation[i] -
in_pad_below[i];
}
return backward_delta_out_pad_below;
}
CoordinateDiff op::v1::ConvolutionBackpropData::compute_backward_delta_out_pad_above() const
{
auto& in_shape = get_data_batch_shape();
auto& filter_dilation = get_dilations();
auto& filter_shape = get_input_shape(0);
auto& in_pad_below = get_pads_begin();
auto& in_pad_above = get_pads_end();
auto& stride = get_strides();
size_t spatial_dim_count = static_cast<size_t>(in_shape.size()) - 2;
CoordinateDiff backward_delta_out_pad_above;
backward_delta_out_pad_above.resize(spatial_dim_count);
for (size_t i = 0; i < spatial_dim_count; i++)
{
backward_delta_out_pad_above[i] =
(static_cast<ptrdiff_t>(filter_shape[i + 2]) - 1) * filter_dilation[i] +
((in_pad_below[i] + ((in_shape[i + 2]) - 1) + in_pad_above[i] -
(static_cast<ptrdiff_t>(filter_shape[i + 2]) - 1) * filter_dilation[i]) %
stride[i]) -
in_pad_above[i];
}
return backward_delta_out_pad_above;
}
constexpr NodeTypeInfo op::v1::ConvolutionBackpropFilters::type_info;
op::v1::ConvolutionBackpropFilters::ConvolutionBackpropFilters(const Output<Node>& data_batch,
const Shape& filters_shape,
const Output<Node>& output_delta,
const Strides& strides,
const Strides& dilations,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end)
: Op({data_batch, output_delta})
, m_filters_shape(filters_shape)
, m_strides(strides)
, m_dilations(dilations)
, m_pads_begin(pads_begin)
, m_pads_end(pads_end)
{
constructor_validate_and_infer_types();
}
void op::v1::ConvolutionBackpropFilters::validate_and_infer_types()
{
// Backprop to filters is itself convolution, with inputs/outputs/attributes transmogrified as
// follows.
//
// Forward Backward
// "N" axis for data batch 0 1
// "C" axis for data batch 1 0
// "Co" axis for filters 0 0
// "Ci" axis for filters 1 1
// "N" axis for output 0 1
// "C" axis for output 1 0
// Data batch x x
// Data batch shape S_x S_x
// Filters f delta
// Filters shape S_f S_f
// Window movement strides q_x p_f
// Window dilation strides p_f q_x
// Padding below a_x a_x
// Padding above b_x b_x - (a_x + (S_x - 1)p_x + b_x - (S_f - 1)p_f) % q_x
// Output shape S_o S_f
//
// To _validate_, we simply need to check/infer the output shape of the forward convolution,
// then check to make sure that the incoming delta has the same shape as the forward output.
//
// We will also compute and store the various parameters in the "backward" column above, since
// some backends need them. (TODO(amprocte): Is it just because of the way the reference works
// that this stuff is needed? If so, we can probably get rid of it and have conv_backprop
// reference kernels that do the calculations of the backward parameters internally, or supply
// utility functions to do it.)
const PartialShape& data_batch_shape = get_input_partial_shape(0);
element::Type data_batch_et = get_input_element_type(0);
const PartialShape& delta_shape = get_input_partial_shape(1);
element::Type delta_et = get_input_element_type(1);
element::Type forward_result_et;
PartialShape forward_result_shape;
NODE_VALIDATION_CHECK(
this,
element::Type::merge(forward_result_et, data_batch_et, delta_et),
"Element types for data batch and filters do not match (data batch element type: ",
data_batch_et,
", filters element type: ",
delta_et,
").");
forward_result_shape =
infer_convolution_forward(this,
data_batch_shape,
Strides(static_cast<size_t>(data_batch_shape.rank()) - 2, 1),
m_pads_begin,
m_pads_end,
m_filters_shape,
m_strides,
m_dilations);
NODE_VALIDATION_CHECK(this,
forward_result_shape.compatible(delta_shape),
"Inferred forward output shape (",
forward_result_shape,
") does not match shape of ",
"delta (",
delta_shape,
").");
set_output_type(0, forward_result_et, m_filters_shape);
}
shared_ptr<Node>
op::v1::ConvolutionBackpropFilters::copy_with_new_args(const NodeVector& new_args) const
{
check_new_args_count(this, new_args);
return make_shared<v1::ConvolutionBackpropFilters>(new_args.at(0),
m_filters_shape,
new_args.at(1),
m_strides,
m_dilations,
m_pads_begin,
m_pads_end);
}
CoordinateDiff op::v1::ConvolutionBackpropFilters::compute_backward_in_pad_above() const
{
const auto& in_shape = get_input_shape(0);
const auto& out_shape = get_input_shape(1);
const auto& filter_shape = get_filters_shape();
const auto& in_pad_above = get_pads_end();
const auto& in_pad_below = get_pads_begin();
const auto& filter_dilation = get_dilations();
const auto& stride = get_strides();
size_t spatial_dim_count = static_cast<size_t>(out_shape.size()) - 2;
CoordinateDiff backward_in_pad_above;
backward_in_pad_above.resize(spatial_dim_count);
for (size_t i = 0; i < spatial_dim_count; i++)
{
backward_in_pad_above[i] =
in_pad_above[i] -
(in_pad_below[i] + (static_cast<ptrdiff_t>(in_shape[i + 2]) - 1) + in_pad_above[i] -
(filter_shape[i + 2] - 1) * filter_dilation[i]) %
stride[i];
}
return backward_in_pad_above;
}
// *** Convolution OP SET 0 ***
constexpr NodeTypeInfo op::v0::Convolution::type_info;
op::v0::Convolution::Convolution(const Output<Node>& data_batch,
const Output<Node>& filters, const Output<Node>& filters,
const Strides& window_movement_strides, const Strides& window_movement_strides,
const Strides& window_dilation_strides, const Strides& window_dilation_strides,
...@@ -46,7 +505,7 @@ op::Convolution::Convolution(const Output<Node>& data_batch, ...@@ -46,7 +505,7 @@ op::Convolution::Convolution(const Output<Node>& data_batch,
constructor_validate_and_infer_types(); constructor_validate_and_infer_types();
} }
void op::Convolution::validate_and_infer_types() void op::v0::Convolution::validate_and_infer_types()
{ {
const PartialShape& data_batch_shape = get_input_partial_shape(0); const PartialShape& data_batch_shape = get_input_partial_shape(0);
element::Type data_batch_et = get_input_element_type(0); element::Type data_batch_et = get_input_element_type(0);
...@@ -121,7 +580,7 @@ void op::Convolution::validate_and_infer_types() ...@@ -121,7 +580,7 @@ void op::Convolution::validate_and_infer_types()
set_output_type(0, result_et, result_shape); set_output_type(0, result_et, result_shape);
} }
op::Convolution::Convolution(const Output<Node>& data_batch, op::v0::Convolution::Convolution(const Output<Node>& data_batch,
const Output<Node>& filters, const Output<Node>& filters,
const Strides& window_movement_strides, const Strides& window_movement_strides,
const Strides& window_dilation_strides, const Strides& window_dilation_strides,
...@@ -137,7 +596,7 @@ op::Convolution::Convolution(const Output<Node>& data_batch, ...@@ -137,7 +596,7 @@ op::Convolution::Convolution(const Output<Node>& data_batch,
{ {
} }
op::Convolution::Convolution(const Output<Node>& data_batch, op::v0::Convolution::Convolution(const Output<Node>& data_batch,
const Output<Node>& filters, const Output<Node>& filters,
const Strides& window_movement_strides, const Strides& window_movement_strides,
const Strides& window_dilation_strides) const Strides& window_dilation_strides)
...@@ -150,7 +609,7 @@ op::Convolution::Convolution(const Output<Node>& data_batch, ...@@ -150,7 +609,7 @@ op::Convolution::Convolution(const Output<Node>& data_batch,
{ {
} }
op::Convolution::Convolution(const Output<Node>& data_batch, op::v0::Convolution::Convolution(const Output<Node>& data_batch,
const Output<Node>& filters, const Output<Node>& filters,
const Strides& window_movement_strides) const Strides& window_movement_strides)
: Convolution(data_batch, : Convolution(data_batch,
...@@ -162,15 +621,15 @@ op::Convolution::Convolution(const Output<Node>& data_batch, ...@@ -162,15 +621,15 @@ op::Convolution::Convolution(const Output<Node>& data_batch,
{ {
} }
op::Convolution::Convolution(const Output<Node>& data_batch, const Output<Node>& filters) op::v0::Convolution::Convolution(const Output<Node>& data_batch, const Output<Node>& filters)
: Convolution(data_batch, filters, Strides(), Strides(), CoordinateDiff(), CoordinateDiff()) : Convolution(data_batch, filters, Strides(), Strides(), CoordinateDiff(), CoordinateDiff())
{ {
} }
shared_ptr<Node> op::Convolution::copy_with_new_args(const NodeVector& new_args) const shared_ptr<Node> op::v0::Convolution::copy_with_new_args(const NodeVector& new_args) const
{ {
check_new_args_count(this, new_args); check_new_args_count(this, new_args);
return make_shared<Convolution>(new_args.at(0), return make_shared<v0::Convolution>(new_args.at(0),
new_args.at(1), new_args.at(1),
m_window_movement_strides, m_window_movement_strides,
m_window_dilation_strides, m_window_dilation_strides,
...@@ -180,7 +639,7 @@ shared_ptr<Node> op::Convolution::copy_with_new_args(const NodeVector& new_args) ...@@ -180,7 +639,7 @@ shared_ptr<Node> op::Convolution::copy_with_new_args(const NodeVector& new_args)
m_pad_type); m_pad_type);
} }
void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas) void op::v0::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas)
{ {
auto delta = deltas.at(0); auto delta = deltas.at(0);
...@@ -191,7 +650,7 @@ void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const Node ...@@ -191,7 +650,7 @@ void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const Node
const auto f_shape = f.get_shape(); const auto f_shape = f.get_shape();
adjoints.add_delta(x, adjoints.add_delta(x,
make_shared<op::ConvolutionBackpropData>(x_shape, make_shared<op::v0::ConvolutionBackpropData>(x_shape,
f, f,
delta, delta,
m_window_movement_strides, m_window_movement_strides,
...@@ -201,7 +660,7 @@ void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const Node ...@@ -201,7 +660,7 @@ void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const Node
m_data_dilation_strides)); m_data_dilation_strides));
adjoints.add_delta(f, adjoints.add_delta(f,
make_shared<op::ConvolutionBackpropFilters>(x, make_shared<op::v0::ConvolutionBackpropFilters>(x,
f_shape, f_shape,
delta, delta,
m_window_movement_strides, m_window_movement_strides,
...@@ -211,13 +670,14 @@ void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const Node ...@@ -211,13 +670,14 @@ void op::Convolution::generate_adjoints(autodiff::Adjoints& adjoints, const Node
m_data_dilation_strides)); m_data_dilation_strides));
} }
constexpr NodeTypeInfo op::ConvolutionBackpropData::type_info; constexpr NodeTypeInfo op::v0::ConvolutionBackpropData::type_info;
shared_ptr<Node> op::Convolution::get_default_value() const shared_ptr<Node> op::v0::Convolution::get_default_value() const
{ {
return ngraph::make_constant_from_string("0", get_element_type(), get_shape()); return ngraph::make_constant_from_string("0", get_element_type(), get_shape());
} }
op::ConvolutionBackpropData::ConvolutionBackpropData(const Shape& data_batch_shape, op::v0::ConvolutionBackpropData::ConvolutionBackpropData(
const Shape& data_batch_shape,
const Output<Node>& filters, const Output<Node>& filters,
const Output<Node>& output_delta, const Output<Node>& output_delta,
const Strides& window_movement_strides_forward, const Strides& window_movement_strides_forward,
...@@ -236,7 +696,7 @@ op::ConvolutionBackpropData::ConvolutionBackpropData(const Shape& data_batch_sha ...@@ -236,7 +696,7 @@ op::ConvolutionBackpropData::ConvolutionBackpropData(const Shape& data_batch_sha
constructor_validate_and_infer_types(); constructor_validate_and_infer_types();
} }
void op::ConvolutionBackpropData::validate_and_infer_types() void op::v0::ConvolutionBackpropData::validate_and_infer_types()
{ {
// Backprop to data is itself convolution, with inputs/outputs/attributes transmogrified as // Backprop to data is itself convolution, with inputs/outputs/attributes transmogrified as
// follows. // follows.
...@@ -302,7 +762,7 @@ void op::ConvolutionBackpropData::validate_and_infer_types() ...@@ -302,7 +762,7 @@ void op::ConvolutionBackpropData::validate_and_infer_types()
set_output_type(0, forward_result_et, m_data_batch_shape); set_output_type(0, forward_result_et, m_data_batch_shape);
} }
void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints, void op::v0::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints,
const NodeVector& deltas) const NodeVector& deltas)
{ {
auto delta = deltas.at(0); auto delta = deltas.at(0);
...@@ -313,7 +773,7 @@ void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints ...@@ -313,7 +773,7 @@ void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints
auto f = input_value(0); auto f = input_value(0);
const auto f_shape = f.get_shape(); const auto f_shape = f.get_shape();
auto data_conv = make_shared<op::Convolution>(delta, auto data_conv = make_shared<op::v0::Convolution>(delta,
f, f,
m_window_movement_strides_forward, m_window_movement_strides_forward,
m_window_dilation_strides_forward, m_window_dilation_strides_forward,
...@@ -369,7 +829,7 @@ void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints ...@@ -369,7 +829,7 @@ void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints
delta = swap_NC(delta); delta = swap_NC(delta);
x = swap_NC(x); x = swap_NC(x);
shared_ptr<Node> filter_deconv_bprop = make_shared<op::Convolution>(x, shared_ptr<Node> filter_deconv_bprop = make_shared<op::v0::Convolution>(x,
delta, delta,
window_movement_strides, window_movement_strides,
window_dilation_strides, window_dilation_strides,
...@@ -385,10 +845,11 @@ void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints ...@@ -385,10 +845,11 @@ void op::ConvolutionBackpropData::generate_adjoints(autodiff::Adjoints& adjoints
adjoints.add_delta(f, filter_deconv_bprop); adjoints.add_delta(f, filter_deconv_bprop);
} }
shared_ptr<Node> op::ConvolutionBackpropData::copy_with_new_args(const NodeVector& new_args) const shared_ptr<Node>
op::v0::ConvolutionBackpropData::copy_with_new_args(const NodeVector& new_args) const
{ {
check_new_args_count(this, new_args); check_new_args_count(this, new_args);
return make_shared<ConvolutionBackpropData>(m_data_batch_shape, return make_shared<v0::ConvolutionBackpropData>(m_data_batch_shape,
new_args.at(0), new_args.at(0),
new_args.at(1), new_args.at(1),
m_window_movement_strides_forward, m_window_movement_strides_forward,
...@@ -398,7 +859,7 @@ shared_ptr<Node> op::ConvolutionBackpropData::copy_with_new_args(const NodeVecto ...@@ -398,7 +859,7 @@ shared_ptr<Node> op::ConvolutionBackpropData::copy_with_new_args(const NodeVecto
m_data_dilation_strides_forward); m_data_dilation_strides_forward);
} }
CoordinateDiff op::ConvolutionBackpropData::compute_backward_delta_out_pad_below() const CoordinateDiff op::v0::ConvolutionBackpropData::compute_backward_delta_out_pad_below() const
{ {
auto& in_shape = get_data_batch_shape(); auto& in_shape = get_data_batch_shape();
auto& filter_dilation = get_window_dilation_strides_forward(); auto& filter_dilation = get_window_dilation_strides_forward();
...@@ -418,7 +879,7 @@ CoordinateDiff op::ConvolutionBackpropData::compute_backward_delta_out_pad_below ...@@ -418,7 +879,7 @@ CoordinateDiff op::ConvolutionBackpropData::compute_backward_delta_out_pad_below
return backward_delta_out_pad_below; return backward_delta_out_pad_below;
} }
CoordinateDiff op::ConvolutionBackpropData::compute_backward_delta_out_pad_above() const CoordinateDiff op::v0::ConvolutionBackpropData::compute_backward_delta_out_pad_above() const
{ {
auto& in_shape = get_data_batch_shape(); auto& in_shape = get_data_batch_shape();
auto& filter_dilation = get_window_dilation_strides_forward(); auto& filter_dilation = get_window_dilation_strides_forward();
...@@ -444,9 +905,9 @@ CoordinateDiff op::ConvolutionBackpropData::compute_backward_delta_out_pad_above ...@@ -444,9 +905,9 @@ CoordinateDiff op::ConvolutionBackpropData::compute_backward_delta_out_pad_above
return backward_delta_out_pad_above; return backward_delta_out_pad_above;
} }
constexpr NodeTypeInfo op::ConvolutionBackpropFilters::type_info; constexpr NodeTypeInfo op::v0::ConvolutionBackpropFilters::type_info;
op::ConvolutionBackpropFilters::ConvolutionBackpropFilters( op::v0::ConvolutionBackpropFilters::ConvolutionBackpropFilters(
const Output<Node>& data_batch, const Output<Node>& data_batch,
const Shape& filters_shape, const Shape& filters_shape,
const Output<Node>& output_delta, const Output<Node>& output_delta,
...@@ -466,7 +927,7 @@ op::ConvolutionBackpropFilters::ConvolutionBackpropFilters( ...@@ -466,7 +927,7 @@ op::ConvolutionBackpropFilters::ConvolutionBackpropFilters(
constructor_validate_and_infer_types(); constructor_validate_and_infer_types();
} }
void op::ConvolutionBackpropFilters::validate_and_infer_types() void op::v0::ConvolutionBackpropFilters::validate_and_infer_types()
{ {
// Backprop to filters is itself convolution, with inputs/outputs/attributes transmogrified as // Backprop to filters is itself convolution, with inputs/outputs/attributes transmogrified as
// follows. // follows.
...@@ -537,10 +998,10 @@ void op::ConvolutionBackpropFilters::validate_and_infer_types() ...@@ -537,10 +998,10 @@ void op::ConvolutionBackpropFilters::validate_and_infer_types()
} }
shared_ptr<Node> shared_ptr<Node>
op::ConvolutionBackpropFilters::copy_with_new_args(const NodeVector& new_args) const op::v0::ConvolutionBackpropFilters::copy_with_new_args(const NodeVector& new_args) const
{ {
check_new_args_count(this, new_args); check_new_args_count(this, new_args);
return make_shared<ConvolutionBackpropFilters>(new_args.at(0), return make_shared<v0::ConvolutionBackpropFilters>(new_args.at(0),
m_filters_shape, m_filters_shape,
new_args.at(1), new_args.at(1),
m_window_movement_strides_forward, m_window_movement_strides_forward,
...@@ -550,7 +1011,7 @@ shared_ptr<Node> ...@@ -550,7 +1011,7 @@ shared_ptr<Node>
m_data_dilation_strides_forward); m_data_dilation_strides_forward);
} }
CoordinateDiff op::ConvolutionBackpropFilters::compute_backward_in_pad_above() const CoordinateDiff op::v0::ConvolutionBackpropFilters::compute_backward_in_pad_above() const
{ {
const auto& in_shape = get_input_shape(0); const auto& in_shape = get_input_shape(0);
const auto& out_shape = get_input_shape(1); const auto& out_shape = get_input_shape(1);
......
...@@ -23,6 +23,204 @@ ...@@ -23,6 +23,204 @@
namespace ngraph namespace ngraph
{ {
namespace op namespace op
{
namespace v1
{
/// \brief Batched convolution operation, with optional window dilation and stride.
///
class Convolution : public Op
{
public:
NGRAPH_API
static constexpr NodeTypeInfo type_info{"Convolution", 1};
const NodeTypeInfo& get_type_info() const override { return type_info; }
/// \brief Constructs a batched convolution operation.
Convolution() = default;
/// \brief Constructs a batched convolution operation.
///
/// \param data_batch The node producing the input data batch tensor.<br>
/// `[N, C_IN, D1, ... Df]`
/// \param filters The node producing the filters tensor.<br>
/// `[C_OUT, C_IN, F1, ... Ff]`
/// \param strides The strides.<br>
/// `[f]`
/// \param dilations The dilations.<br>
/// `[f]`
/// \param pads_begin The beginning of padding shape.<br>
/// `[f]`
/// \param pads_end The end of padding shape.<br>
/// `[f]`
/// \param auto_pad The pad type for automatically computing padding sizes.<br>
/// `[f]`
///
/// Output `[N, C_OUT, R1, ... Rf]`
///
Convolution(const Output<Node>& data_batch,
const Output<Node>& filters,
const Strides& strides,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end,
const Strides& dilations,
const PadType& auto_pad = PadType::EXPLICIT);
size_t get_version() const override { return 1; }
void validate_and_infer_types() override;
virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override;
void generate_adjoints(autodiff::Adjoints& adjoints,
const NodeVector& deltas) override;
/// \return The strides.
const Strides& get_strides() const { return m_strides; }
void set_strides(const Strides& strides) { m_strides = strides; }
/// \return The dilations.
const Strides& get_dilations() const { return m_dilations; }
void set_dilations(const Strides& dilations) { m_dilations = dilations; }
/// \return The padding-below sizes (possibly negative).
const CoordinateDiff& get_pads_begin() const { return m_pads_begin; }
void set_pads_begin(const CoordinateDiff& pads_begin) { m_pads_begin = pads_begin; }
/// \return The padding-above sizes (possibly negative).
const CoordinateDiff& get_pads_end() const { return m_pads_end; }
void set_adding_above(const CoordinateDiff& pads_end) { m_pads_end = pads_end; }
/// \return The pad type for convolution.
const PadType& get_auto_pad() const { return m_auto_pad; }
void set_auto_pad(const PadType& auto_pad) { m_auto_pad = auto_pad; }
/// \return The default value for Convolution.
virtual std::shared_ptr<Node> get_default_value() const override;
protected:
Strides m_strides;
Strides m_dilations;
CoordinateDiff m_pads_begin;
CoordinateDiff m_pads_end;
PadType m_auto_pad;
};
/// \brief Data batch backprop for batched convolution operation.
class ConvolutionBackpropData : public Op
{
public:
NGRAPH_API
static constexpr NodeTypeInfo type_info{"ConvolutionBackpropData", 1};
const NodeTypeInfo& get_type_info() const override { return type_info; }
/// \brief Constructs a batched-convolution data batch-backprop operation.
ConvolutionBackpropData() = default;
/// \brief Constructs a batched-convolution data batch-backprop operation.
///
/// \param data_batch_shape The shape of the data batch from forward-prop.
/// \param filters The node producing the filters from forward-prop.
/// \param output_delta The node producing output delta.
/// \param strides The strides from forward-prop.
/// \param dilations The dilations from forward-prop.
/// \param pads_begin The padding-below sizes from forward-prop.
/// \param pads_end The padding-above sizes from forward-prop.
ConvolutionBackpropData(const Shape& data_batch_shape,
const Output<Node>& filters,
const Output<Node>& output_delta,
const Strides& strides,
const Strides& dilations,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end);
size_t get_version() const override { return 1; }
void validate_and_infer_types() override;
void generate_adjoints(autodiff::Adjoints& adjoints,
const NodeVector& deltas) override;
virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override;
/// \return The data batch shape.
const Shape& get_data_batch_shape() const { return m_data_batch_shape; }
void set_data_batch_shape(const Shape& data_batch_shape)
{
m_data_batch_shape = data_batch_shape;
}
/// \return The strides from the forward prop.
const Strides& get_strides() const { return m_strides; }
void set_strides(const Strides& strides) { m_strides = strides; }
/// \return The dilations from the forward prop.
const Strides& get_dilations() const { return m_dilations; }
void set_dilations(const Strides& dilations) { m_dilations = dilations; }
/// \return The padding-below sizes (possibly negative) from the forward prop.
const CoordinateDiff& get_pads_begin() const { return m_pads_begin; }
void set_pads_begin(const CoordinateDiff& pads_begin) { m_pads_begin = pads_begin; }
/// \return The padding-above sizes (possibly negative) from the forward prop.
const CoordinateDiff& get_pads_end() const { return m_pads_end; }
void set_pads_end(const CoordinateDiff& pads_end) { m_pads_end = pads_end; }
// Compute the pad_above values to be used if in a convolution
CoordinateDiff compute_backward_delta_out_pad_above() const;
CoordinateDiff compute_backward_delta_out_pad_below() const;
protected:
Shape m_data_batch_shape;
Strides m_strides;
Strides m_dilations;
CoordinateDiff m_pads_begin;
CoordinateDiff m_pads_end;
};
/// \brief Filters backprop for batched convolution operation.
class ConvolutionBackpropFilters : public Op
{
public:
NGRAPH_API
static constexpr NodeTypeInfo type_info{"ConvolutionBackpropFilters", 1};
const NodeTypeInfo& get_type_info() const override { return type_info; }
/// \brief Constructs a batched-convolution filter-backprop operation.
ConvolutionBackpropFilters() = default;
/// \brief Constructs a batched-convolution filter-backprop operation.
///
/// \param data_batch The tensor producing the data batch from forward-prop.
/// \param filters_shape The shape of the filters from forward-prop.
/// \param output_delta The node producing output delta.
/// \param strides The strides from forward-prop.
/// \param dilations The dilations from forward-prop.
/// \param pads_begin The padding-below sizes from forward-prop.
/// \param pads_end The padding-above sizes from forward-prop.
ConvolutionBackpropFilters(const Output<Node>& data_batch,
const Shape& filters_shape,
const Output<Node>& output_delta,
const Strides& strides,
const Strides& dilations,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end);
size_t get_version() const override { return 1; }
void validate_and_infer_types() override;
virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override;
/// \return The filters tensor shape.
const Shape& get_filters_shape() const { return m_filters_shape; }
/// \return The strides from the forward prop.
const Strides& get_strides() const { return m_strides; }
void set_strides(const Strides& strides) { m_strides = strides; }
/// \return The dilations from the forward prop.
const Strides& get_dilations() const { return m_dilations; }
void set_dilations(const Strides& dilations) { m_dilations = dilations; }
/// \return The padding-below sizes (possibly negative) from the forward prop.
const CoordinateDiff& get_pads_begin() const { return m_pads_begin; }
void set_pads_begin(const CoordinateDiff& pads_begin) { m_pads_begin = pads_begin; }
/// \return The padding-above sizes (possibly negative) from the forward prop.
const CoordinateDiff& get_pads_end() const { return m_pads_end; }
void set_pads_end(const CoordinateDiff& pads_end) { m_pads_end = pads_end; }
// Compute the pad_above value to be used if in a convolution
CoordinateDiff compute_backward_in_pad_above() const;
protected:
Shape m_filters_shape;
Strides m_strides;
Strides m_dilations;
CoordinateDiff m_pads_begin;
CoordinateDiff m_pads_end;
};
} // namespace v1
namespace v0
{ {
/// \brief Batched convolution operation, with optional window dilation and stride. /// \brief Batched convolution operation, with optional window dilation and stride.
/// ///
...@@ -64,7 +262,8 @@ namespace ngraph ...@@ -64,7 +262,8 @@ namespace ngraph
const Strides& data_dilation_strides, const Strides& data_dilation_strides,
const PadType& pad_type = PadType::EXPLICIT); const PadType& pad_type = PadType::EXPLICIT);
/// \brief Constructs a batched convolution operation with no data dilation (i.e., all /// \brief Constructs a batched convolution operation with no data dilation (i.e.,
/// all
/// data dilation strides are 1). /// data dilation strides are 1).
/// ///
/// \param data_batch The node producing the input data batch tensor.<br> /// \param data_batch The node producing the input data batch tensor.<br>
...@@ -89,7 +288,8 @@ namespace ngraph ...@@ -89,7 +288,8 @@ namespace ngraph
const CoordinateDiff& padding_below, const CoordinateDiff& padding_below,
const CoordinateDiff& padding_above); const CoordinateDiff& padding_above);
/// \brief Constructs a batched convolution operation with no padding or data dilation /// \brief Constructs a batched convolution operation with no padding or data
/// dilation
/// (i.e., padding above and below are 0 everywhere, and all data dilation /// (i.e., padding above and below are 0 everywhere, and all data dilation
/// strides are 1). /// strides are 1).
/// ///
...@@ -109,7 +309,8 @@ namespace ngraph ...@@ -109,7 +309,8 @@ namespace ngraph
const Strides& window_movement_strides, const Strides& window_movement_strides,
const Strides& window_dilation_strides); const Strides& window_dilation_strides);
/// \brief Constructs a batched convolution operation with no window dilation, padding, /// \brief Constructs a batched convolution operation with no window dilation,
/// padding,
/// or data dilation (i.e., padding above and below are 0 everywhere, and all /// or data dilation (i.e., padding above and below are 0 everywhere, and all
/// window/data dilation strides are 1). /// window/data dilation strides are 1).
/// ///
...@@ -143,16 +344,23 @@ namespace ngraph ...@@ -143,16 +344,23 @@ namespace ngraph
virtual std::shared_ptr<Node> virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override; copy_with_new_args(const NodeVector& new_args) const override;
void generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas) override; void generate_adjoints(autodiff::Adjoints& adjoints,
const NodeVector& deltas) override;
/// \return The window movement strides. /// \return The window movement strides.
const Strides& get_window_movement_strides() const { return m_window_movement_strides; } const Strides& get_window_movement_strides() const
{
return m_window_movement_strides;
}
void set_window_movement_strides(const Strides& window_movement_strides) void set_window_movement_strides(const Strides& window_movement_strides)
{ {
m_window_movement_strides = window_movement_strides; m_window_movement_strides = window_movement_strides;
} }
/// \return The window dilation strides. /// \return The window dilation strides.
const Strides& get_window_dilation_strides() const { return m_window_dilation_strides; } const Strides& get_window_dilation_strides() const
{
return m_window_dilation_strides;
}
void set_window_dilation_strides(const Strides& window_dilation_strides) void set_window_dilation_strides(const Strides& window_dilation_strides)
{ {
m_window_dilation_strides = window_dilation_strides; m_window_dilation_strides = window_dilation_strides;
...@@ -210,7 +418,8 @@ namespace ngraph ...@@ -210,7 +418,8 @@ namespace ngraph
/// forward-prop. /// forward-prop.
/// \param padding_below_forward The padding-below sizes from forward-prop. /// \param padding_below_forward The padding-below sizes from forward-prop.
/// \param padding_above_forward The padding-above sizes from forward-prop. /// \param padding_above_forward The padding-above sizes from forward-prop.
/// \param data_dilation_strides_forward The data dilation strides from forward-prop. /// \param data_dilation_strides_forward The data dilation strides from
/// forward-prop.
ConvolutionBackpropData(const Shape& data_batch_shape, ConvolutionBackpropData(const Shape& data_batch_shape,
const Output<Node>& filters, const Output<Node>& filters,
const Output<Node>& output_delta, const Output<Node>& output_delta,
...@@ -222,7 +431,8 @@ namespace ngraph ...@@ -222,7 +431,8 @@ namespace ngraph
void validate_and_infer_types() override; void validate_and_infer_types() override;
void generate_adjoints(autodiff::Adjoints& adjoints, const NodeVector& deltas) override; void generate_adjoints(autodiff::Adjoints& adjoints,
const NodeVector& deltas) override;
virtual std::shared_ptr<Node> virtual std::shared_ptr<Node>
copy_with_new_args(const NodeVector& new_args) const override; copy_with_new_args(const NodeVector& new_args) const override;
...@@ -237,7 +447,8 @@ namespace ngraph ...@@ -237,7 +447,8 @@ namespace ngraph
{ {
return m_window_movement_strides_forward; return m_window_movement_strides_forward;
} }
void set_window_movement_strides_forward(const Strides& window_movement_strides_forward) void set_window_movement_strides_forward(
const Strides& window_movement_strides_forward)
{ {
m_window_movement_strides_forward = window_movement_strides_forward; m_window_movement_strides_forward = window_movement_strides_forward;
} }
...@@ -246,7 +457,8 @@ namespace ngraph ...@@ -246,7 +457,8 @@ namespace ngraph
{ {
return m_window_dilation_strides_forward; return m_window_dilation_strides_forward;
} }
void set_window_dilation_strides_forward(const Strides& window_dilation_strides_forward) void set_window_dilation_strides_forward(
const Strides& window_dilation_strides_forward)
{ {
m_window_dilation_strides_forward = window_dilation_strides_forward; m_window_dilation_strides_forward = window_dilation_strides_forward;
} }
...@@ -306,12 +518,11 @@ namespace ngraph ...@@ -306,12 +518,11 @@ namespace ngraph
/// \param filters_shape The shape of the filters from forward-prop. /// \param filters_shape The shape of the filters from forward-prop.
/// \param output_delta The node producing output delta. /// \param output_delta The node producing output delta.
/// \param window_movement_strides_forward The window movement strides from /// \param window_movement_strides_forward The window movement strides from
/// forward-prop. /// forward-prop. \param window_dilation_strides_forward The window dilation strides
/// \param window_dilation_strides_forward The window dilation strides from /// from forward-prop. \param padding_below_forward The padding-below sizes from
/// forward-prop. /// forward-prop. \param padding_above_forward The padding-above sizes from
/// \param padding_below_forward The padding-below sizes from forward-prop. /// forward-prop. \param data_dilation_strides_forward The data dilation strides
/// \param padding_above_forward The padding-above sizes from forward-prop. /// from forward-prop.
/// \param data_dilation_strides_forward The data dilation strides from forward-prop.
ConvolutionBackpropFilters(const Output<Node>& data_batch, ConvolutionBackpropFilters(const Output<Node>& data_batch,
const Shape& filters_shape, const Shape& filters_shape,
const Output<Node>& output_delta, const Output<Node>& output_delta,
...@@ -333,7 +544,8 @@ namespace ngraph ...@@ -333,7 +544,8 @@ namespace ngraph
{ {
return m_window_movement_strides_forward; return m_window_movement_strides_forward;
} }
void set_window_movement_strides_forward(const Strides& window_movement_strides_forward) void set_window_movement_strides_forward(
const Strides& window_movement_strides_forward)
{ {
m_window_movement_strides_forward = window_movement_strides_forward; m_window_movement_strides_forward = window_movement_strides_forward;
} }
...@@ -342,7 +554,8 @@ namespace ngraph ...@@ -342,7 +554,8 @@ namespace ngraph
{ {
return m_window_dilation_strides_forward; return m_window_dilation_strides_forward;
} }
void set_window_dilation_strides_forward(const Strides& window_dilation_strides_forward) void set_window_dilation_strides_forward(
const Strides& window_dilation_strides_forward)
{ {
m_window_dilation_strides_forward = window_dilation_strides_forward; m_window_dilation_strides_forward = window_dilation_strides_forward;
} }
...@@ -386,11 +599,13 @@ namespace ngraph ...@@ -386,11 +599,13 @@ namespace ngraph
Strides m_data_dilation_strides_forward; Strides m_data_dilation_strides_forward;
}; };
} // namespace v0
namespace util namespace util
{ {
// This is a legacy function, retained because the CPU backend uses it for now. // This is a legacy function, retained because the CPU backend uses it for now.
// TODO: Update CPU backend to use the new stuff in validation_util.hpp, and remove // TODO: Update CPU backend to use the new stuff in validation_util.hpp, and remove this
// this function. // function.
Shape infer_convolution_output_shape(const Node* node, Shape infer_convolution_output_shape(const Node* node,
const Shape& data_batch_shape, const Shape& data_batch_shape,
const Shape& filters_shape, const Shape& filters_shape,
...@@ -405,6 +620,10 @@ namespace ngraph ...@@ -405,6 +620,10 @@ namespace ngraph
size_t output_channel_axis_filters, size_t output_channel_axis_filters,
size_t batch_axis_result, size_t batch_axis_result,
size_t output_channel_axis_result); size_t output_channel_axis_result);
} } // namespace util
}
} using v0::Convolution;
using v0::ConvolutionBackpropData;
using v0::ConvolutionBackpropFilters;
} // namespace op
} // namespace ngraph
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "ngraph/graph_util.hpp" #include "ngraph/graph_util.hpp"
#include "ngraph/op/avg_pool.hpp" #include "ngraph/op/avg_pool.hpp"
#include "ngraph/op/constant.hpp" #include "ngraph/op/constant.hpp"
#include "ngraph/op/convolution.hpp"
#include "ngraph/op/gather.hpp" #include "ngraph/op/gather.hpp"
#include "ngraph/op/get_output_element.hpp" #include "ngraph/op/get_output_element.hpp"
#include "ngraph/op/max_pool.hpp" #include "ngraph/op/max_pool.hpp"
...@@ -131,6 +132,107 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node) ...@@ -131,6 +132,107 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node)
modified = true; modified = true;
break; break;
} }
case OP_TYPEID::Convolution:
{
auto tmp = dynamic_cast<const op::v0::Convolution*>(node.get());
auto strides = tmp->get_window_movement_strides();
auto dilations = tmp->get_window_dilation_strides();
auto pads_begin = tmp->get_padding_below();
auto pads_end = tmp->get_padding_above();
auto data_dilation_strides = tmp->get_data_dilation_strides();
auto auto_pad = tmp->get_pad_type();
bool is_dds_valid = true;
for (auto value : data_dilation_strides)
{
is_dds_valid = is_dds_valid && (value == 1);
}
NGRAPH_CHECK(is_dds_valid,
"Unable to convert Convolution:0 to Convolution:1 with data dilation strides "
"other than `1`. Node: ",
*node);
auto replacement_node = make_shared<op::v1::Convolution>(node->input(0).get_source_output(),
node->input(1).get_source_output(),
strides,
pads_begin,
pads_end,
dilations,
auto_pad);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::ConvolutionBackpropData:
{
auto tmp = dynamic_cast<const op::v0::ConvolutionBackpropData*>(node.get());
auto data_batch_shape = tmp->get_data_batch_shape();
auto strides = tmp->get_window_movement_strides_forward();
auto dilations = tmp->get_window_dilation_strides_forward();
auto pads_begin = tmp->get_padding_below_forward();
auto pads_end = tmp->get_padding_above_forward();
auto data_dilation_strides = tmp->get_data_dilation_strides_forward();
bool is_dds_valid = true;
for (auto value : data_dilation_strides)
{
is_dds_valid = is_dds_valid && (value == 1);
}
NGRAPH_CHECK(is_dds_valid,
"Unable to convert ConvolutionBackpropData:0 to ConvolutionBackpropData:1 "
"with data dilation strides "
"other than `1`. Node: ",
*node);
auto replacement_node =
make_shared<op::v1::ConvolutionBackpropData>(data_batch_shape,
node->input(0).get_source_output(),
node->input(1).get_source_output(),
strides,
dilations,
pads_begin,
pads_end);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::ConvolutionBackpropFilters:
{
auto tmp = dynamic_cast<const op::v0::ConvolutionBackpropFilters*>(node.get());
auto filters_shape = tmp->get_filters_shape();
auto strides = tmp->get_window_movement_strides_forward();
auto dilations = tmp->get_window_dilation_strides_forward();
auto pads_begin = tmp->get_padding_below_forward();
auto pads_end = tmp->get_padding_above_forward();
auto data_dilation_strides = tmp->get_data_dilation_strides_forward();
bool is_dds_valid = true;
for (auto value : data_dilation_strides)
{
is_dds_valid = is_dds_valid && (value == 1);
}
NGRAPH_CHECK(
is_dds_valid,
"Unable to convert ConvolutionBackpropFilters:0 to ConvolutionBackpropFilters:1 "
"with data dilation strides "
"other than `1`. Node: ",
*node);
auto replacement_node =
make_shared<op::v1::ConvolutionBackpropFilters>(node->input(0).get_source_output(),
filters_shape,
node->input(1).get_source_output(),
strides,
dilations,
pads_begin,
pads_end);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Gather: case OP_TYPEID::Gather:
{ {
auto tmp = dynamic_cast<const op::v0::Gather*>(node.get()); auto tmp = dynamic_cast<const op::v0::Gather*>(node.get());
...@@ -143,6 +245,60 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node) ...@@ -143,6 +245,60 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node)
modified = true; modified = true;
break; break;
} }
case OP_TYPEID::Product:
{
bool keep_dims = false;
auto replacement_node = make_shared<op::v1::ReduceProd>(
node->input(0).get_source_output(), node->input(1).get_source_output(), keep_dims);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Sum:
{
bool keep_dims = false;
auto replacement_node = make_shared<op::v1::ReduceSum>(
node->input(0).get_source_output(), node->input(1).get_source_output(), keep_dims);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Pad:
{
auto tmp = dynamic_cast<const op::v0::Pad*>(node.get());
auto padding_below = tmp->get_padding_below();
auto pads_begin_node =
make_shared<op::Constant>(element::i64, Shape{padding_below.size()}, padding_below);
auto padding_above = tmp->get_padding_above();
auto pads_end_node =
make_shared<op::Constant>(element::i64, Shape{padding_above.size()}, padding_above);
auto replacement_node = make_shared<op::v1::Pad>(node->input(0).get_source_output(),
pads_begin_node,
pads_end_node,
node->input(1).get_source_output(),
tmp->get_pad_mode());
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Softmax:
{
auto tmp = dynamic_cast<const op::v0::Softmax*>(node.get());
AxisSet axes = tmp->get_axes();
NGRAPH_CHECK(
axes.size() == 1,
"Unable to convert Softmax:0 to Softmax:1 with zero or more than one axis. Node: ",
*node);
auto replacement_node =
make_shared<op::v1::Softmax>(node->input(0).get_source_output(), axes.to_vector()[0]);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::MaxPool: case OP_TYPEID::MaxPool:
{ {
auto tmp = dynamic_cast<const op::v0::MaxPool*>(node.get()); auto tmp = dynamic_cast<const op::v0::MaxPool*>(node.get());
...@@ -200,44 +356,6 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node) ...@@ -200,44 +356,6 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node)
modified = true; modified = true;
break; break;
} }
case OP_TYPEID::Product:
{
bool keep_dims = false;
auto replacement_node = make_shared<op::v1::ReduceProd>(
node->input(0).get_source_output(), node->input(1).get_source_output(), keep_dims);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Sum:
{
bool keep_dims = false;
auto replacement_node = make_shared<op::v1::ReduceSum>(
node->input(0).get_source_output(), node->input(1).get_source_output(), keep_dims);
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Pad:
{
auto tmp = dynamic_cast<const op::v0::Pad*>(node.get());
auto padding_below = tmp->get_padding_below();
auto pads_begin_node =
make_shared<op::Constant>(element::i64, Shape{padding_below.size()}, padding_below);
auto padding_above = tmp->get_padding_above();
auto pads_end_node =
make_shared<op::Constant>(element::i64, Shape{padding_above.size()}, padding_above);
auto replacement_node = make_shared<op::v1::Pad>(node->input(0).get_source_output(),
pads_begin_node,
pads_end_node,
node->input(1).get_source_output(),
tmp->get_pad_mode());
replace_node(node, replacement_node);
modified = true;
break;
}
case OP_TYPEID::Reverse: case OP_TYPEID::Reverse:
{ {
// creates a Constant node from the v0::Reverse reversed_axes attribute // creates a Constant node from the v0::Reverse reversed_axes attribute
...@@ -257,24 +375,9 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node) ...@@ -257,24 +375,9 @@ bool pass::Opset1Upgrade::run_on_node(shared_ptr<Node> node)
break; break;
} }
case OP_TYPEID::Softmax:
{
auto tmp = dynamic_cast<const op::v0::Softmax*>(node.get());
AxisSet axes = tmp->get_axes();
NGRAPH_CHECK(
axes.size() == 1,
"Unable to convert Softmax:0 to Softmax:1 with zero or more than one axis. Node: ",
*node);
auto replacement_node =
make_shared<op::v1::Softmax>(node->input(0).get_source_output(), axes.to_vector()[0]);
replace_node(node, replacement_node);
modified = true;
break;
}
default: break; default: break;
} }
#if defined(__clang__) #if defined(__clang__)
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif #endif
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ngraph/code_writer.hpp" #include "ngraph/code_writer.hpp"
#include "ngraph/node.hpp" #include "ngraph/node.hpp"
#include "ngraph/op/avg_pool.hpp" #include "ngraph/op/avg_pool.hpp"
#include "ngraph/op/convolution.hpp"
#include "ngraph/op/gather.hpp" #include "ngraph/op/gather.hpp"
#include "ngraph/op/max_pool.hpp" #include "ngraph/op/max_pool.hpp"
#include "ngraph/op/pad.hpp" #include "ngraph/op/pad.hpp"
...@@ -109,10 +110,7 @@ namespace ngraph ...@@ -109,10 +110,7 @@ namespace ngraph
class QuantizedConvolution; class QuantizedConvolution;
class GroupConvolution; class GroupConvolution;
class GroupConvolutionBias; class GroupConvolutionBias;
class Convolution;
class ConvolutionBackpropFilters;
class DeconvolutionBias; class DeconvolutionBias;
class ConvolutionBackpropData;
class QuantizedConvolutionBias; class QuantizedConvolutionBias;
class QuantizedConvolutionBiasAdd; class QuantizedConvolutionBiasAdd;
class QuantizedConvolutionBiasSignedAdd; class QuantizedConvolutionBiasSignedAdd;
......
...@@ -964,6 +964,8 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -964,6 +964,8 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
break; break;
} }
case OP_TYPEID::Convolution: case OP_TYPEID::Convolution:
{
if (op_version == 0)
{ {
auto window_movement_strides = auto window_movement_strides =
node_js.at("window_movement_strides").get<vector<size_t>>(); node_js.at("window_movement_strides").get<vector<size_t>>();
...@@ -988,7 +990,7 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -988,7 +990,7 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
if (data_dilation_strides.empty()) if (data_dilation_strides.empty())
{ {
node = make_shared<op::Convolution>(args[0], node = make_shared<op::v0::Convolution>(args[0],
args[1], args[1],
window_movement_strides, window_movement_strides,
window_dilation_strides, window_dilation_strides,
...@@ -997,8 +999,8 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -997,8 +999,8 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
} }
else else
{ {
node = node = make_shared<op::v0::Convolution>(
make_shared<op::Convolution>(args[0], args[0],
args[1], args[1],
window_movement_strides, window_movement_strides,
window_dilation_strides, window_dilation_strides,
...@@ -1007,9 +1009,24 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -1007,9 +1009,24 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
data_dilation_strides.get<std::vector<size_t>>(), data_dilation_strides.get<std::vector<size_t>>(),
pad_type); pad_type);
} }
}
if (op_version == 1)
{
auto strides = node_js.at("strides").get<vector<size_t>>();
auto dilations = node_js.at("dilations").get<vector<size_t>>();
auto pads_begin = node_js.at("pads_begin").get<vector<std::ptrdiff_t>>();
auto pads_end = node_js.at("pads_end").get<vector<std::ptrdiff_t>>();
op::PadType auto_pad = read_pad_type(node_js);
node = make_shared<op::v1::Convolution>(
args[0], args[1], strides, pads_begin, pads_end, dilations, auto_pad);
}
break; break;
} }
case OP_TYPEID::ConvolutionBackpropData: case OP_TYPEID::ConvolutionBackpropData:
{
if (op_version == 0)
{ {
auto data_batch_shape = node_js.at("data_batch_shape").get<vector<size_t>>(); auto data_batch_shape = node_js.at("data_batch_shape").get<vector<size_t>>();
auto window_movement_strides_forward = auto window_movement_strides_forward =
...@@ -1022,7 +1039,7 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -1022,7 +1039,7 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
node_js.at("padding_above_forward").get<vector<std::ptrdiff_t>>(); node_js.at("padding_above_forward").get<vector<std::ptrdiff_t>>();
auto data_dilation_strides_forward = auto data_dilation_strides_forward =
node_js.at("data_dilation_strides_forward").get<vector<size_t>>(); node_js.at("data_dilation_strides_forward").get<vector<size_t>>();
node = make_shared<op::ConvolutionBackpropData>(data_batch_shape, node = make_shared<op::v0::ConvolutionBackpropData>(data_batch_shape,
args[0], args[0],
args[1], args[1],
window_movement_strides_forward, window_movement_strides_forward,
...@@ -1030,9 +1047,22 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -1030,9 +1047,22 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
padding_below_forward, padding_below_forward,
padding_above_forward, padding_above_forward,
data_dilation_strides_forward); data_dilation_strides_forward);
}
if (op_version == 1)
{
auto data_batch_shape = node_js.at("data_batch_shape").get<vector<size_t>>();
auto strides = node_js.at("strides").get<vector<size_t>>();
auto dilations = node_js.at("dilations").get<vector<size_t>>();
auto pads_begin = node_js.at("pads_begin").get<vector<std::ptrdiff_t>>();
auto pads_end = node_js.at("pads_end").get<vector<std::ptrdiff_t>>();
node = make_shared<op::v1::ConvolutionBackpropData>(
data_batch_shape, args[0], args[1], strides, dilations, pads_begin, pads_end);
}
break; break;
} }
case OP_TYPEID::ConvolutionBackpropFilters: case OP_TYPEID::ConvolutionBackpropFilters:
{
if (op_version == 0)
{ {
auto filters_shape = node_js.at("filters_shape").get<vector<size_t>>(); auto filters_shape = node_js.at("filters_shape").get<vector<size_t>>();
auto window_movement_strides_forward = auto window_movement_strides_forward =
...@@ -1045,7 +1075,8 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -1045,7 +1075,8 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
node_js.at("padding_above_forward").get<vector<std::ptrdiff_t>>(); node_js.at("padding_above_forward").get<vector<std::ptrdiff_t>>();
auto data_dilation_strides_forward = auto data_dilation_strides_forward =
node_js.at("data_dilation_strides_forward").get<vector<size_t>>(); node_js.at("data_dilation_strides_forward").get<vector<size_t>>();
node = make_shared<op::ConvolutionBackpropFilters>(args[0], node =
make_shared<op::v0::ConvolutionBackpropFilters>(args[0],
filters_shape, filters_shape,
args[1], args[1],
window_movement_strides_forward, window_movement_strides_forward,
...@@ -1053,6 +1084,17 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js) ...@@ -1053,6 +1084,17 @@ shared_ptr<Node> JSONDeserializer::deserialize_node(json node_js)
padding_below_forward, padding_below_forward,
padding_above_forward, padding_above_forward,
data_dilation_strides_forward); data_dilation_strides_forward);
}
if (op_version == 1)
{
auto filters_shape = node_js.at("filters_shape").get<vector<size_t>>();
auto strides = node_js.at("strides").get<vector<size_t>>();
auto dilations = node_js.at("dilations").get<vector<size_t>>();
auto pads_begin = node_js.at("pads_begin").get<vector<std::ptrdiff_t>>();
auto pads_end = node_js.at("pads_end").get<vector<std::ptrdiff_t>>();
node = make_shared<op::v1::ConvolutionBackpropFilters>(
args[0], filters_shape, args[1], strides, dilations, pads_begin, pads_end);
}
break; break;
} }
case OP_TYPEID::ConvolutionBias: case OP_TYPEID::ConvolutionBias:
...@@ -2483,35 +2525,71 @@ json JSONSerializer::serialize_node(const Node& n) ...@@ -2483,35 +2525,71 @@ json JSONSerializer::serialize_node(const Node& n)
} }
case OP_TYPEID::Convolution: case OP_TYPEID::Convolution:
{ {
auto tmp = dynamic_cast<const op::Convolution*>(&n); if (op_version == 0)
{
auto tmp = dynamic_cast<const op::v0::Convolution*>(&n);
node["window_movement_strides"] = tmp->get_window_movement_strides(); node["window_movement_strides"] = tmp->get_window_movement_strides();
node["window_dilation_strides"] = tmp->get_window_dilation_strides(); node["window_dilation_strides"] = tmp->get_window_dilation_strides();
node["padding_below"] = tmp->get_padding_below(); node["padding_below"] = tmp->get_padding_below();
node["padding_above"] = tmp->get_padding_above(); node["padding_above"] = tmp->get_padding_above();
node["data_dilation_strides"] = tmp->get_data_dilation_strides(); node["data_dilation_strides"] = tmp->get_data_dilation_strides();
node["pad_type"] = tmp->get_pad_type(); node["pad_type"] = tmp->get_pad_type();
}
if (op_version == 1)
{
auto tmp = dynamic_cast<const op::v1::Convolution*>(&n);
node["strides"] = tmp->get_strides();
node["dilations"] = tmp->get_dilations();
node["pads_begin"] = tmp->get_pads_begin();
node["pads_end"] = tmp->get_pads_end();
node["auto_pad"] = tmp->get_auto_pad();
}
break; break;
} }
case OP_TYPEID::ConvolutionBackpropData: case OP_TYPEID::ConvolutionBackpropData:
{ {
auto tmp = dynamic_cast<const op::ConvolutionBackpropData*>(&n); if (op_version == 0)
{
auto tmp = dynamic_cast<const op::v0::ConvolutionBackpropData*>(&n);
node["data_batch_shape"] = tmp->get_data_batch_shape(); node["data_batch_shape"] = tmp->get_data_batch_shape();
node["window_movement_strides_forward"] = tmp->get_window_movement_strides_forward(); node["window_movement_strides_forward"] = tmp->get_window_movement_strides_forward();
node["window_dilation_strides_forward"] = tmp->get_window_dilation_strides_forward(); node["window_dilation_strides_forward"] = tmp->get_window_dilation_strides_forward();
node["padding_below_forward"] = tmp->get_padding_below_forward(); node["padding_below_forward"] = tmp->get_padding_below_forward();
node["padding_above_forward"] = tmp->get_padding_above_forward(); node["padding_above_forward"] = tmp->get_padding_above_forward();
node["data_dilation_strides_forward"] = tmp->get_data_dilation_strides_forward(); node["data_dilation_strides_forward"] = tmp->get_data_dilation_strides_forward();
}
if (op_version == 1)
{
auto tmp = dynamic_cast<const op::v1::ConvolutionBackpropData*>(&n);
node["data_batch_shape"] = tmp->get_data_batch_shape();
node["strides"] = tmp->get_strides();
node["dilations"] = tmp->get_dilations();
node["pads_begin"] = tmp->get_pads_begin();
node["pads_end"] = tmp->get_pads_end();
}
break; break;
} }
case OP_TYPEID::ConvolutionBackpropFilters: case OP_TYPEID::ConvolutionBackpropFilters:
{ {
auto tmp = dynamic_cast<const op::ConvolutionBackpropFilters*>(&n); if (op_version == 0)
{
auto tmp = dynamic_cast<const op::v0::ConvolutionBackpropFilters*>(&n);
node["filters_shape"] = tmp->get_filters_shape(); node["filters_shape"] = tmp->get_filters_shape();
node["window_movement_strides_forward"] = tmp->get_window_movement_strides_forward(); node["window_movement_strides_forward"] = tmp->get_window_movement_strides_forward();
node["window_dilation_strides_forward"] = tmp->get_window_dilation_strides_forward(); node["window_dilation_strides_forward"] = tmp->get_window_dilation_strides_forward();
node["padding_below_forward"] = tmp->get_padding_below_forward(); node["padding_below_forward"] = tmp->get_padding_below_forward();
node["padding_above_forward"] = tmp->get_padding_above_forward(); node["padding_above_forward"] = tmp->get_padding_above_forward();
node["data_dilation_strides_forward"] = tmp->get_data_dilation_strides_forward(); node["data_dilation_strides_forward"] = tmp->get_data_dilation_strides_forward();
}
if (op_version == 1)
{
auto tmp = dynamic_cast<const op::v1::ConvolutionBackpropFilters*>(&n);
node["filters_shape"] = tmp->get_filters_shape();
node["strides"] = tmp->get_strides();
node["dilations"] = tmp->get_dilations();
node["pads_begin"] = tmp->get_pads_begin();
node["pads_end"] = tmp->get_pads_end();
}
break; break;
} }
case OP_TYPEID::ConvolutionBias: case OP_TYPEID::ConvolutionBias:
......
...@@ -69,13 +69,14 @@ set(SRC ...@@ -69,13 +69,14 @@ set(SRC
node_input_output.cpp node_input_output.cpp
nop_elimination.cpp nop_elimination.cpp
op.cpp op.cpp
opset_pass/convolution_opset_pass.cpp
opset_pass/gather_opset_pass.cpp opset_pass/gather_opset_pass.cpp
opset_pass/pad_opset_pass.cpp opset_pass/pad_opset_pass.cpp
opset_pass/poolings_opset_pass.cpp
opset_pass/product_opset_pass.cpp opset_pass/product_opset_pass.cpp
opset_pass/reverse_opset_pass.cpp opset_pass/reverse_opset_pass.cpp
opset_pass/softmax_opset_pass.cpp opset_pass/softmax_opset_pass.cpp
opset_pass/sum_opset_pass.cpp opset_pass/sum_opset_pass.cpp
opset_pass/poolings_opset_pass.cpp
partial_shape.cpp partial_shape.cpp
pass.cpp pass.cpp
pass_liveness.cpp pass_liveness.cpp
......
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ngraph/ngraph.hpp"
#include "ngraph/pass/manager.hpp"
#include "ngraph/pass/opset1_upgrade.hpp"
#include "util/test_control.hpp"
#include "util/type_prop.hpp"
using namespace std;
using namespace ngraph;
TEST(upgrade_pass, opset1_convolution_pass)
{
auto data = make_shared<op::Parameter>(element::f32, Shape{1, 3, 6, 9});
auto filters = make_shared<op::Parameter>(element::f32, Shape{1, 3, 3, 3});
CoordinateDiff pads_begin{0, 0};
CoordinateDiff pads_end{0, 0};
Strides strides{1, 1};
Strides dilations{1, 1};
Strides data_dilations_strides{1, 1};
op::PadType pad_type = op::PadType::EXPLICIT;
auto convolution_v0 = make_shared<op::v0::Convolution>(
data, filters, strides, dilations, pads_begin, pads_end, data_dilations_strides, pad_type);
auto result = make_shared<op::Result>(convolution_v0);
auto f = make_shared<Function>(ResultVector{result}, ParameterVector{data, filters});
ngraph::pass::Manager pass_manager;
pass_manager.register_pass<pass::Opset1Upgrade>();
pass_manager.run_passes(f);
auto convolution_s1_result = f->get_results().at(0);
auto node = convolution_s1_result->input(0).get_source_output().get_node_shared_ptr();
auto convolution_v1_node = static_pointer_cast<op::v1::Convolution>(node);
EXPECT_EQ(convolution_v1_node->description(), "Convolution");
EXPECT_EQ(convolution_v1_node->get_version(), 1);
EXPECT_EQ(convolution_v1_node->get_pads_begin(), pads_begin);
EXPECT_EQ(convolution_v1_node->get_pads_end(), pads_end);
EXPECT_EQ(convolution_v1_node->get_strides(), strides);
EXPECT_EQ(convolution_v1_node->get_auto_pad(), pad_type);
EXPECT_EQ(convolution_v1_node->get_dilations(), dilations);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment