467 lines
18 KiB
C++
467 lines
18 KiB
C++
//*****************************************************************************
|
|
// Copyright 2017-2021 Intel Corporation
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//*****************************************************************************
|
|
|
|
#include "ngraph/op/convolution.hpp"
|
|
#include "itt.hpp"
|
|
#include "ngraph/axis_vector.hpp"
|
|
#include "ngraph/coordinate_diff.hpp"
|
|
#include "ngraph/op/reshape.hpp"
|
|
#include "ngraph/util.hpp"
|
|
#include "ngraph/validation_util.hpp"
|
|
|
|
using namespace std;
|
|
using namespace ngraph;
|
|
|
|
// *** Convolution OP SET 1 ***
|
|
NGRAPH_RTTI_DEFINITION(op::v1::Convolution, "Convolution", 1);
|
|
|
|
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();
|
|
}
|
|
|
|
bool op::v1::Convolution::visit_attributes(AttributeVisitor& visitor)
|
|
{
|
|
NGRAPH_OP_SCOPE(v1_Convolution_visit_attributes);
|
|
visitor.on_attribute("strides", m_strides);
|
|
visitor.on_attribute("dilations", m_dilations);
|
|
visitor.on_attribute("pads_begin", m_pads_begin);
|
|
visitor.on_attribute("pads_end", m_pads_end);
|
|
visitor.on_attribute("auto_pad", m_auto_pad);
|
|
return true;
|
|
}
|
|
|
|
void op::v1::Convolution::validate_and_infer_types()
|
|
{
|
|
NGRAPH_OP_SCOPE(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);
|
|
|
|
PartialShape result_shape = PartialShape::dynamic();
|
|
if (data_batch_shape.rank().is_static())
|
|
{
|
|
result_shape =
|
|
std::vector<Dimension>(data_batch_shape.rank().get_length(), Dimension::dynamic());
|
|
|
|
if (data_batch_shape.rank().get_length() > 1)
|
|
{
|
|
result_shape[0] = data_batch_shape[0]; // batch size
|
|
}
|
|
if (filters_shape.rank().is_static() && filters_shape.rank().get_length() > 1)
|
|
{
|
|
result_shape[1] = filters_shape[0]; // filter channel size
|
|
}
|
|
}
|
|
|
|
element::Type result_et;
|
|
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,
|
|
").");
|
|
|
|
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_auto_pad == PadType::VALID)
|
|
{
|
|
m_pads_begin = conv_default_padding(this, data_batch_shape, filters_shape);
|
|
}
|
|
|
|
if (m_pads_end.size() == 0 || m_auto_pad == PadType::VALID)
|
|
{
|
|
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)
|
|
{
|
|
bool auto_padding_applied = false;
|
|
if (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}
|
|
auto_padding_applied = try_apply_auto_padding(data_batch_shape,
|
|
filter_shape,
|
|
m_strides,
|
|
m_dilations,
|
|
m_auto_pad,
|
|
m_pads_end,
|
|
m_pads_begin);
|
|
}
|
|
if (!auto_padding_applied)
|
|
{
|
|
set_output_type(0, result_et, result_shape);
|
|
return;
|
|
}
|
|
}
|
|
|
|
result_shape = infer_convolution_forward(this,
|
|
data_batch_shape,
|
|
Strides(m_strides.size(), 1), // dummy data dilations
|
|
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::clone_with_new_inputs(const OutputVector& new_args) const
|
|
{
|
|
NGRAPH_OP_SCOPE(v1_Convolution_clone_with_new_inputs);
|
|
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);
|
|
}
|
|
|
|
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 Output<Node>& data,
|
|
const Output<Node>& filters,
|
|
const Output<Node>& output_shape,
|
|
const Strides& strides,
|
|
const CoordinateDiff& pads_begin,
|
|
const CoordinateDiff& pads_end,
|
|
const Strides& dilations,
|
|
const PadType& auto_pad,
|
|
const CoordinateDiff& output_padding)
|
|
: Op({data, filters, output_shape})
|
|
, m_strides(strides)
|
|
, m_dilations(dilations)
|
|
, m_pads_begin(pads_begin)
|
|
, m_pads_end(pads_end)
|
|
, m_auto_pad(auto_pad)
|
|
, m_output_padding(output_padding)
|
|
{
|
|
constructor_validate_and_infer_types();
|
|
}
|
|
|
|
bool op::v1::ConvolutionBackpropData::visit_attributes(AttributeVisitor& visitor)
|
|
{
|
|
NGRAPH_OP_SCOPE(v1_ConvolutionBackpropData_visit_attributes);
|
|
visitor.on_attribute("strides", m_strides);
|
|
visitor.on_attribute("dilations", m_dilations);
|
|
visitor.on_attribute("pads_begin", m_pads_begin);
|
|
visitor.on_attribute("pads_end", m_pads_end);
|
|
visitor.on_attribute("auto_pad", m_auto_pad);
|
|
visitor.on_attribute("output_padding", m_output_padding);
|
|
return true;
|
|
}
|
|
|
|
op::v1::ConvolutionBackpropData::ConvolutionBackpropData(const Output<Node>& data,
|
|
const Output<Node>& filters,
|
|
const Strides& strides,
|
|
const CoordinateDiff& pads_begin,
|
|
const CoordinateDiff& pads_end,
|
|
const Strides& dilations,
|
|
const PadType& auto_pad,
|
|
const CoordinateDiff& output_padding)
|
|
: Op({data, filters})
|
|
, m_strides(strides)
|
|
, m_dilations(dilations)
|
|
, m_pads_begin(pads_begin)
|
|
, m_pads_end(pads_end)
|
|
, m_auto_pad(auto_pad)
|
|
, m_output_padding(output_padding)
|
|
{
|
|
constructor_validate_and_infer_types();
|
|
}
|
|
|
|
bool op::v1::ConvolutionBackpropData::is_dynamic() const
|
|
{
|
|
bool is_dynamic = Node::is_dynamic();
|
|
if (inputs().size() == 3 && !is_dynamic)
|
|
{
|
|
return !has_and_set_equal_bounds(input_value(2));
|
|
}
|
|
return is_dynamic;
|
|
}
|
|
|
|
const PartialShape op::v1::ConvolutionBackpropData::get_output_shape() const
|
|
{
|
|
auto data_pshape = get_input_partial_shape(0);
|
|
|
|
PartialShape shape;
|
|
if (data_pshape.rank().is_static())
|
|
{
|
|
shape = PartialShape{vector<Dimension>(data_pshape.rank().get_length() - 2)};
|
|
}
|
|
else
|
|
{
|
|
shape = PartialShape{vector<Dimension>(m_strides.size())};
|
|
}
|
|
bool is_output_shape_present = inputs().size() == 3;
|
|
if (is_output_shape_present)
|
|
{
|
|
if (auto const_op = get_constant_from_source(input_value(2)))
|
|
{
|
|
shape = const_op->get_shape_val();
|
|
}
|
|
else
|
|
{
|
|
shape = PartialShape::dynamic();
|
|
}
|
|
}
|
|
return shape;
|
|
}
|
|
|
|
void op::v1::ConvolutionBackpropData::set_output_shape(const Shape& shape)
|
|
{
|
|
this->input(2).replace_source_output(
|
|
op::Constant::create(this->get_input_element_type(2), Shape{shape.size()}, shape)
|
|
->output(0));
|
|
}
|
|
|
|
void op::v1::ConvolutionBackpropData::infer_conv_backprop_output_spatial_shape(
|
|
const vector<Dimension>& input_data_shape,
|
|
const vector<Dimension>& filters_shape,
|
|
const Strides& strides,
|
|
const Strides& dilations,
|
|
const CoordinateDiff& pads_begin,
|
|
const CoordinateDiff& pads_end,
|
|
const CoordinateDiff& output_padding,
|
|
vector<Dimension>& output_spatial_shape)
|
|
{
|
|
size_t num_spatial_dims = input_data_shape.size();
|
|
NODE_VALIDATION_CHECK(
|
|
this,
|
|
filters_shape.size() == num_spatial_dims && strides.size() == num_spatial_dims &&
|
|
dilations.size() == num_spatial_dims && pads_begin.size() == num_spatial_dims &&
|
|
pads_end.size() == num_spatial_dims && output_padding.size() == num_spatial_dims);
|
|
|
|
for (size_t i = 0; i < num_spatial_dims; ++i)
|
|
{
|
|
if (input_data_shape[i].is_static() && filters_shape[i].is_static())
|
|
{
|
|
int64_t val = strides[i] * (input_data_shape[i].get_length() - 1) +
|
|
dilations[i] * (filters_shape[i].get_length() - 1) + 1 - pads_begin[i] -
|
|
pads_end[i] + output_padding[i];
|
|
output_spatial_shape.push_back(val);
|
|
}
|
|
else
|
|
{
|
|
output_spatial_shape.push_back(Dimension::dynamic());
|
|
}
|
|
}
|
|
}
|
|
|
|
void op::v1::ConvolutionBackpropData::validate_and_infer_types()
|
|
{
|
|
NGRAPH_OP_SCOPE(v1_ConvolutionBackpropData_validate_and_infer_types);
|
|
auto data_pshape = get_input_partial_shape(0);
|
|
element::Type delta_et = get_input_element_type(0);
|
|
const PartialShape& filters_pshape = get_input_partial_shape(1);
|
|
element::Type filters_et = get_input_element_type(1);
|
|
|
|
bool is_output_shape_present = inputs().size() == 3;
|
|
PartialShape output_pshape = get_output_shape();
|
|
|
|
element::Type result_et;
|
|
NODE_VALIDATION_CHECK(
|
|
this,
|
|
element::Type::merge(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,
|
|
").");
|
|
|
|
if (data_pshape.rank().is_static() && filters_pshape.rank().is_static())
|
|
{
|
|
if (m_pads_begin.size() == 0)
|
|
{
|
|
m_pads_begin = conv_default_padding(this, data_pshape, filters_pshape);
|
|
}
|
|
if (m_pads_end.size() == 0)
|
|
{
|
|
m_pads_end = conv_default_padding(this, data_pshape, filters_pshape);
|
|
}
|
|
if (m_output_padding.size() == 0)
|
|
{
|
|
m_output_padding = conv_default_padding(this, data_pshape, filters_pshape);
|
|
}
|
|
if (m_strides.size() == 0)
|
|
{
|
|
m_strides = conv_default_strides(this, data_pshape, filters_pshape);
|
|
}
|
|
if (m_dilations.size() == 0)
|
|
{
|
|
m_dilations = conv_default_strides(this, data_pshape, filters_pshape);
|
|
}
|
|
|
|
const auto num_spatial_dims = data_pshape.rank().get_length() - 2;
|
|
|
|
NODE_VALIDATION_CHECK(this,
|
|
m_strides.size() == num_spatial_dims,
|
|
"Strides should be defined for all and only spatial features.");
|
|
|
|
NODE_VALIDATION_CHECK(this,
|
|
m_dilations.size() == num_spatial_dims,
|
|
"Dilations should be defined for all and only spatial features.");
|
|
|
|
NODE_VALIDATION_CHECK(this,
|
|
m_output_padding.size() == num_spatial_dims,
|
|
"Output padding should be defined for all and only "
|
|
"spatial features.");
|
|
}
|
|
|
|
PartialShape result_shape;
|
|
if (is_output_shape_present)
|
|
{
|
|
if (output_pshape.is_static() && filters_pshape.is_static() && data_pshape.is_static())
|
|
{
|
|
Shape output_shape = output_pshape.to_shape();
|
|
const Shape data_shape = data_pshape.to_shape();
|
|
const Shape filters_shape = filters_pshape.to_shape();
|
|
const size_t num_spatial_dims = data_shape.size() - 2;
|
|
|
|
NODE_VALIDATION_CHECK(this,
|
|
output_shape.size() == num_spatial_dims,
|
|
"Output shape should be specified only and for "
|
|
"all spatial dimensions.");
|
|
|
|
// If auto_pad has one of following mode we infer paddings. Otherwise in
|
|
// EXPLICIT auto_pad mode we use what is provided.
|
|
if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER)
|
|
{
|
|
opset1::infer_conv_backprop_auto_padding(
|
|
Shape{std::next(data_shape.begin(), 2), std::end(data_shape)},
|
|
Shape{std::next(filters_shape.begin(), 2), std::end(filters_shape)},
|
|
output_shape,
|
|
m_strides,
|
|
m_dilations,
|
|
m_auto_pad,
|
|
m_output_padding,
|
|
m_pads_begin,
|
|
m_pads_end);
|
|
}
|
|
|
|
// C_OUTPUT
|
|
output_shape.insert(output_shape.begin(), filters_shape.at(1));
|
|
// N
|
|
output_shape.insert(output_shape.begin(), data_shape.at(0));
|
|
output_pshape = output_shape;
|
|
}
|
|
set_input_is_relevant_to_shape(2);
|
|
}
|
|
// Deduce output shape from input spatial shape, strides, dilations, output padding
|
|
// and padding values.
|
|
else
|
|
{
|
|
if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER ||
|
|
m_auto_pad == PadType::VALID)
|
|
{
|
|
m_pads_begin.assign(m_pads_begin.size(), 0);
|
|
m_pads_end.assign(m_pads_end.size(), 0);
|
|
}
|
|
|
|
if (data_pshape.rank().is_static() && filters_pshape.is_static())
|
|
{
|
|
vector<Dimension> data_shape{data_pshape}, filters_shape{filters_pshape}, output_shape;
|
|
|
|
infer_conv_backprop_output_spatial_shape(
|
|
vector<Dimension>{std::next(data_shape.begin(), 2), std::end(data_shape)},
|
|
vector<Dimension>{std::next(filters_shape.begin(), 2), std::end(filters_shape)},
|
|
m_strides,
|
|
m_dilations,
|
|
m_pads_begin,
|
|
m_pads_end,
|
|
m_output_padding,
|
|
output_shape);
|
|
|
|
// C_OUTPUT
|
|
output_shape.insert(output_shape.begin(), filters_shape.at(1));
|
|
// N
|
|
output_shape.insert(output_shape.begin(), data_shape.at(0));
|
|
output_pshape = PartialShape{output_shape};
|
|
}
|
|
else
|
|
{
|
|
output_pshape = PartialShape::dynamic(data_pshape.rank());
|
|
}
|
|
}
|
|
|
|
set_input_is_relevant_to_shape(0);
|
|
set_input_is_relevant_to_shape(1);
|
|
set_output_type(0, result_et, output_pshape);
|
|
}
|
|
|
|
shared_ptr<Node>
|
|
op::v1::ConvolutionBackpropData::clone_with_new_inputs(const OutputVector& new_args) const
|
|
{
|
|
NGRAPH_OP_SCOPE(v1_ConvolutionBackpropData_clone_with_new_inputs);
|
|
check_new_args_count(this, new_args);
|
|
if (new_args.size() == 3)
|
|
{
|
|
return make_shared<v1::ConvolutionBackpropData>(new_args.at(0),
|
|
new_args.at(1),
|
|
new_args.at(2),
|
|
m_strides,
|
|
m_pads_begin,
|
|
m_pads_end,
|
|
m_dilations,
|
|
m_auto_pad,
|
|
m_output_padding);
|
|
}
|
|
else
|
|
{
|
|
return make_shared<v1::ConvolutionBackpropData>(new_args.at(0),
|
|
new_args.at(1),
|
|
m_strides,
|
|
m_pads_begin,
|
|
m_pads_end,
|
|
m_dilations,
|
|
m_auto_pad,
|
|
m_output_padding);
|
|
}
|
|
}
|