Loop operation: IE IR Reader update (#2766)

* ie_ir_reader update, Loop implementation update

* fix ie ir reader
This commit is contained in:
Ivan Tikhonov 2020-10-26 19:30:14 +03:00 committed by GitHub
parent de686fce39
commit 735c8747b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 461 additions and 21 deletions

View File

@ -668,8 +668,8 @@ std::shared_ptr<ngraph::Node>
V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &inputs, const pugi::xml_node &node,
std::istream &binStream,
const V10Parser::GenericLayerParams &layerParsePrms,
std::shared_ptr<ngraph::op::util::SubGraphOp> tensor_iterator) {
tensor_iterator->set_friendly_name(GetStrAttr(node, "name"));
std::shared_ptr<ngraph::op::util::SubGraphOp> subgraph_op) {
subgraph_op->set_friendly_name(GetStrAttr(node, "name"));
auto body_node = node.child("body");
if (body_node.empty()) {
@ -696,12 +696,12 @@ V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &input
// Disabled reshape for generic operations in the TI body
::ngraph::op::GenericIE::DisableReshape noReshape(ngraph_function);
auto body = std::make_shared<ngraph::Function>(result_nodes, parameter_nodes);
tensor_iterator->set_function(body);
subgraph_op->set_function(body);
// Parse PortMap: inputs
std::map<uint64_t, pugi::xml_node> input_map;
FOREACH_CHILD(_input, node.child("port_map"), "input") {
int64_t ext_port_id = GetUIntAttr(_input, "external_port_id");
int64_t ext_port_id = GetInt64Attr(_input, "external_port_id");
input_map[ext_port_id] = _input;
}
@ -709,7 +709,8 @@ V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &input
for (const auto& input : input_map) {
auto &_input = input.second;
auto axis_attr = _input.attribute("axis");
size_t ti_input_index = GetUIntAttr(_input, "external_port_id");
auto purpose = GetStrAttr(_input, "purpose", "");
int64_t ti_input_index = GetInt64Attr(_input, "external_port_id");
size_t body_parameter_index = GetUIntAttr(_input, "internal_layer_id");
auto body_param = std::find_if(parameter_nodes.begin(), parameter_nodes.end(),
@ -721,7 +722,8 @@ V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &input
THROW_IE_EXCEPTION << "PortMap input parsing error. Body parameter with id = " << body_parameter_index
<< " not found.";
}
if (ti_input_index >= inputs.size())
if (ti_input_index >= static_cast<int64_t>(inputs.size()))
THROW_IE_EXCEPTION << "TensorIterator " << layerParsePrms.name << " has incorrect number of inputs!";
// if axis is set, then slicing is enabled. Create ngraph::TensorIterator::SlicedInput.
@ -731,7 +733,7 @@ V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &input
int64_t stride = GetInt64Attr(_input, "stride", 1);
int64_t end = GetInt64Attr(_input, "end", -1);
int64_t part_size = GetInt64Attr(_input, "part_size", 1);
tensor_iterator->set_sliced_input(*body_param, inputs[ti_input_index], start, stride, part_size, end, axis);
subgraph_op->set_sliced_input(*body_param, inputs.at(ti_input_index), start, stride, part_size, end, axis);
is_sliced_input_exists = true;
} else {
// otherwise find corresponding back edge and create ngraph::TensorIterator::MergedInput
@ -752,28 +754,40 @@ V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &input
<< " not found.";
}
tensor_iterator->set_merged_input(*body_param, inputs[ti_input_index], *body_result);
subgraph_op->set_merged_input(*body_param, inputs.at(ti_input_index), *body_result);
is_back_edge_exist = true;
break;
}
}
if (!is_back_edge_exist) {
tensor_iterator->set_invariant_input(*body_param, inputs[ti_input_index]);
// ti_input_index = -1 means that Parameter of the body is not connected to inputs of TensorIterator
// and is used only for internal needs.
if (!is_back_edge_exist && ti_input_index >= 0) {
subgraph_op->set_invariant_input(*body_param, inputs.at(ti_input_index));
}
if (purpose == "current_iteration") {
auto loop = std::dynamic_pointer_cast<ngraph::opset5::Loop>(subgraph_op);
if (!loop)
THROW_IE_EXCEPTION << "PortMap output parsing error. Purpose attribute is available only for Loop operation.";
loop->set_special_body_ports(ngraph::opset5::Loop::SpecialBodyPorts{ngraph_function->get_parameter_index(*body_param),
-1});
}
}
}
// Parse PortMap: outputs
std::map<uint32_t, pugi::xml_node> output_map;
std::map<int64_t, pugi::xml_node> output_map;
FOREACH_CHILD(_output, node.child("port_map"), "output") {
uint32_t ext_port_id = GetUIntAttr(_output, "external_port_id");
int64_t ext_port_id = GetInt64Attr(_output, "external_port_id");
output_map[ext_port_id] = _output;
}
int i = 0;
for (const auto& output : output_map) {
auto& _output = output.second;
auto axis_attr = _output.attribute("axis");
auto purpose = GetStrAttr(_output, "purpose", "");
size_t body_result_index = GetUIntAttr(_output, "internal_layer_id");
auto body_result =
@ -788,25 +802,38 @@ V10Parser::LayerBaseCreator::fillSubGraphLayer(const ngraph::OutputVector &input
// if axis is set, then concatenation is enabled. Create ngraph::TensorIterator::ConcatOutput.
if (!axis_attr.empty()) {
uint32_t axis = GetUIntAttr(_output, "axis");
int64_t axis = GetInt64Attr(_output, "axis");
int64_t start = GetInt64Attr(_output, "start", 0);
int64_t stride = GetInt64Attr(_output, "stride", 1);
int64_t end = GetInt64Attr(_output, "end", -1);
int64_t part_size = GetInt64Attr(_output, "part_size", 1);
tensor_iterator->get_concatenated_slices(*body_result, start, stride, part_size, end, axis);
subgraph_op->get_concatenated_slices(*body_result, start, stride, part_size, end, axis);
if (!is_sliced_input_exists) {
if (auto ti = std::dynamic_pointer_cast<ngraph::op::TensorIterator>(tensor_iterator))
ti->set_num_iterations((std::abs(end - start)) / part_size);
if (auto ti = std::dynamic_pointer_cast<ngraph::op::TensorIterator>(subgraph_op))
// for Loop op we just skip this call
if (ti)
ti->set_num_iterations((std::abs(end - start)) / part_size);
}
} else if (purpose == "execution_condition") {
auto loop = std::dynamic_pointer_cast<ngraph::opset5::Loop>(subgraph_op);
if (!loop)
THROW_IE_EXCEPTION << "PortMap output parsing error. Purpose attribute is available only for Loop operation.";
loop->set_special_body_ports(ngraph::opset5::Loop::SpecialBodyPorts{loop->get_special_body_ports().current_iteration_input_idx,
ngraph_function->get_result_index(*body_result)});
// if external_port_id < 0,
// it means that this body result isn't connected to the Loop output and is used only for internal needs.
if (output.first >= 0) {
subgraph_op->get_iter_value(*body_result, -1);
}
} else {
// otherwise create ngraph::TensorIterator::BodyOutput. -1 means last iteration.
tensor_iterator->get_iter_value(*body_result, -1);
subgraph_op->get_iter_value(*body_result, -1);
}
}
tensor_iterator->validate_and_infer_types();
return tensor_iterator;
subgraph_op->validate_and_infer_types();
return subgraph_op;
}
@ -824,7 +851,7 @@ template <>
std::shared_ptr<ngraph::Node> V10Parser::LayerCreator<ngraph::opset5::Loop>::createLayer(
const ngraph::OutputVector& inputs, const pugi::xml_node& node, std::istream& binStream,
const GenericLayerParams& layerParsePrms) {
auto loop = std::make_shared<ngraph::opset5::Loop>();
auto loop = std::make_shared<ngraph::opset5::Loop>(inputs[0], inputs[1]);
return fillSubGraphLayer(inputs, node, binStream, layerParsePrms, loop);
}

View File

@ -106,7 +106,7 @@ private:
static std::shared_ptr<ngraph::Node> fillSubGraphLayer(const ngraph::OutputVector& inputs, const pugi::xml_node& node,
std::istream& binStream,
const GenericLayerParams& layerParsePrms,
std::shared_ptr<ngraph::op::util::SubGraphOp> sub_graph_node);
std::shared_ptr<ngraph::op::util::SubGraphOp> subgraph_op);
explicit LayerBaseCreator(const std::string& type): type(type) {}
std::string getType() {
return type;

View File

@ -0,0 +1,383 @@
// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <string>
#include "ngraph_reader_tests.hpp"
#include "common_test_utils/data_utils.hpp"
TEST_F(NGraphReaderTests, ReadLoopNetwork) {
std::string model = R"V0G0N(
<net name="yolov3-10" version="10">
<layers>
<layer id="0" name="TFNodes/yolo_evaluation_layer_1/arange_4__77_trip_cnt/placeholder_port_0" type="Parameter" version="opset1">
<data element_type="f32" shape=""/>
<output>
<port id="0" precision="FP32"/>
</output>
</layer>
<layer id="1" name="TFNodes/yolo_evaluation_layer_1/arange_4__77_trip_cnt" type="Convert" version="opset1">
<data destination_type="i64"/>
<input>
<port id="0"/>
</input>
<output>
<port id="1" precision="I64"/>
</output>
</layer>
<layer id="2" name="TFNodes/yolo_evaluation_layer_1/arange__271_cond/Output_0/Data__const" type="Parameter" version="opset1">
<data element_type="boolean" offset="0" shape="" size="1"/>
<output>
<port id="1" precision="BOOL"/>
</output>
</layer>
<layer id="3" name="TFNodes/yolo_evaluation_layer_1/arange_5/start:0/Output_0/Data__const" type="Parameter" version="opset1">
<data element_type="i32" offset="1" shape="" size="4"/>
<output>
<port id="1" precision="I32"/>
</output>
</layer>
<layer id="4" name="TFNodes/yolo_evaluation_layer_1/arange_5/delta:0/Output_0/Data__const" type="Parameter" version="opset1">
<data element_type="i32" offset="5" shape="" size="4"/>
<output>
<port id="1" precision="I32"/>
</output>
</layer>
<layer id="5" name="TFNodes/yolo_evaluation_layer_1/arange_4__77_loop" type="Loop" version="opset5">
<input>
<port id="0"/>
<port id="1"/>
<port id="2"/>
<port id="3"/>
</input>
<output>
<port id="4" precision="I32">
<dim>1</dim>
</port>
</output>
<port_map>
<input external_port_id="3" internal_layer_id="6"/>
<input external_port_id="1" internal_layer_id="0"/>
<input external_port_id="2" internal_layer_id="2"/>
<output external_port_id="-1" internal_layer_id="1" purpose="execution_condition"/>
<output axis="0" external_port_id="4" internal_layer_id="5"/>
</port_map>
<back_edges>
<edge from-layer="1" from-port="0" to-layer="0" to-port="0"/>
<edge from-layer="8" from-port="0" to-layer="2" to-port="0"/>
</back_edges>
<body>
<layers>
<layer id="0" name="cond" type="Parameter" version="opset1">
<data element_type="boolean" shape=""/>
<output>
<port id="0" precision="BOOL"/>
</output>
</layer>
<layer id="1" name="Identity__82/sink_port_0" type="Result" version="opset1">
<data order="0"/>
<input>
<port id="0"/>
</input>
</layer>
<layer id="2" name="prev" type="Parameter" version="opset1">
<data element_type="i32" shape=""/>
<output>
<port id="0" precision="I32"/>
</output>
</layer>
<layer id="3" name="11_input_port_1/value/Output_0/Data__const" type="Const" version="opset1">
<data element_type="i64" offset="0" shape="1" size="8"/>
<output>
<port id="1" precision="I64">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="4" name="11" type="Unsqueeze" version="opset1">
<input>
<port id="0"/>
<port id="1">
<dim>1</dim>
</port>
</input>
<output>
<port id="2" precision="I32">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="5" name="Identity__84/sink_port_0" type="Result" version="opset1">
<data order="2"/>
<input>
<port id="0">
<dim>1</dim>
</port>
</input>
</layer>
<layer id="6" name="TFNodes/yolo_evaluation_layer_1/arange_5/delta:0" type="Parameter" version="opset1">
<data element_type="i32" shape=""/>
<output>
<port id="0" precision="I32"/>
</output>
</layer>
<layer id="7" name="add__83" type="Add" version="opset1">
<input>
<port id="0"/>
<port id="1"/>
</input>
<output>
<port id="2" precision="I32"/>
</output>
</layer>
<layer id="8" name="add__83/sink_port_0" type="Result" version="opset1">
<data order="1"/>
<input>
<port id="0"/>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0"/>
<edge from-layer="2" from-port="0" to-layer="4" to-port="0"/>
<edge from-layer="3" from-port="1" to-layer="4" to-port="1"/>
<edge from-layer="4" from-port="2" to-layer="5" to-port="0"/>
<edge from-layer="2" from-port="0" to-layer="7" to-port="0"/>
<edge from-layer="6" from-port="0" to-layer="7" to-port="1"/>
<edge from-layer="7" from-port="2" to-layer="8" to-port="0"/>
</edges>
</body>
</layer>
<layer id="8" name="TFNodes/yolo_evaluation_layer_1/Reshape_13/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>1</dim>
<dim>1</dim>
<dim>1</dim>
<dim>1</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0"/>
<edge from-layer="1" from-port="1" to-layer="5" to-port="0"/>
<edge from-layer="2" from-port="1" to-layer="5" to-port="1"/>
<edge from-layer="3" from-port="1" to-layer="5" to-port="2"/>
<edge from-layer="4" from-port="1" to-layer="5" to-port="3"/>
<edge from-layer="5" from-port="4" to-layer="8" to-port="0"/>
</edges>
</net>
)V0G0N";
Core reader;
Blob::Ptr weights;
weights = make_shared_blob<uint8_t>(TensorDesc(Precision::U8, {8}, Layout::C));
weights->allocate();
weights->buffer().as<int64_t*>()[0] = 0;
EXPECT_NO_THROW(reader.ReadNetwork(model, weights));
}
TEST_F(NGraphReaderTests, ReadLoopNetwork_2) {
std::string model = R"V0G0N(
<?xml version="1.0" ?>
<net name="loop_2d_add" version="10">
<layers>
<layer id="0" name="trip_count/Output_0/Data__const" type="Parameter" version="opset1">
<data element_type="i64" offset="0" shape="1" size="8"/>
<output>
<port id="1" precision="I64">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="1" name="a_final5/execution_cond/Output_0/Data__const" type="Parameter" version="opset1">
<data element_type="boolean" offset="8" shape="" size="1"/>
<output>
<port id="1" precision="BOOL"/>
</output>
</layer>
<layer id="2" name="a_init" type="Parameter" version="opset1">
<data element_type="f32" shape="1,2"/>
<output>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="3" name="a_final5" type="Loop" version="opset5">
<input>
<port id="0">
<dim>1</dim>
</port>
<port id="1"/>
<port id="2">
<dim>1</dim>
<dim>2</dim>
</port>
</input>
<output>
<port id="3" precision="FP32">
<dim>1</dim>
<dim>2</dim>
</port>
<port id="4" precision="FP32">
<dim>3</dim>
<dim>1</dim>
<dim>2</dim>
</port>
</output>
<port_map>
<input external_port_id="1" internal_layer_id="0"/>
<input external_port_id="2" internal_layer_id="2"/>
<output external_port_id="-1" internal_layer_id="1" purpose="execution_condition"/>
<output external_port_id="3" internal_layer_id="5"/>
<output axis="0" external_port_id="4" internal_layer_id="8"/>
</port_map>
<back_edges>
<edge from-layer="1" from-port="0" to-layer="0" to-port="0"/>
<edge from-layer="5" from-port="0" to-layer="2" to-port="0"/>
</back_edges>
<body>
<layers>
<layer id="0" name="cond_in" type="Parameter" version="opset1">
<data element_type="boolean" shape=""/>
<output>
<port id="0" precision="BOOL"/>
</output>
</layer>
<layer id="1" name="cond_identity/sink_port_0" type="Result" version="opset1">
<input>
<port id="0"/>
</input>
</layer>
<layer id="2" name="a_in" type="Parameter" version="opset1">
<data element_type="f32" shape="1,2"/>
<output>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="3" name="b/Output_0/Data__const" type="Const" version="opset1">
<data element_type="f32" offset="0" shape="1,2" size="8"/>
<output>
<port id="1" precision="FP32">
<dim>1</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="4" name="loop_body_add" type="Add" version="opset1">
<input>
<port id="0">
<dim>1</dim>
<dim>2</dim>
</port>
<port id="1">
<dim>1</dim>
<dim>2</dim>
</port>
</input>
<output>
<port id="2" precision="FP32">
<dim>1</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="5" name="loop_body_add/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>1</dim>
<dim>2</dim>
</port>
</input>
</layer>
<layer id="6" name="11_input_port_1/value/Output_0/Data__const" type="Const" version="opset1">
<data element_type="i64" offset="0" shape="1" size="8"/>
<output>
<port id="1" precision="I64">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="7" name="11" type="Unsqueeze" version="opset1">
<input>
<port id="0">
<dim>1</dim>
<dim>2</dim>
</port>
<port id="1">
<dim>1</dim>
</port>
</input>
<output>
<port id="2" precision="FP32">
<dim>3</dim>
<dim>1</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="8" name="output_accumulator/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>3</dim>
<dim>1</dim>
<dim>2</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0"/>
<edge from-layer="2" from-port="0" to-layer="4" to-port="0"/>
<edge from-layer="3" from-port="1" to-layer="4" to-port="1"/>
<edge from-layer="4" from-port="2" to-layer="5" to-port="0"/>
<edge from-layer="4" from-port="2" to-layer="7" to-port="0"/>
<edge from-layer="6" from-port="1" to-layer="7" to-port="1"/>
<edge from-layer="7" from-port="2" to-layer="8" to-port="0"/>
</edges>
</body>
</layer>
<layer id="6" name="a_final/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>1</dim>
<dim>2</dim>
</port>
</input>
</layer>
<layer id="9" name="a_values/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>3</dim>
<dim>1</dim>
<dim>2</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="1" to-layer="3" to-port="0"/>
<edge from-layer="1" from-port="1" to-layer="3" to-port="1"/>
<edge from-layer="2" from-port="0" to-layer="3" to-port="2"/>
<edge from-layer="3" from-port="3" to-layer="6" to-port="0"/>
<edge from-layer="3" from-port="4" to-layer="9" to-port="0"/>
</edges>
</net>
)V0G0N";
Core reader;
Blob::Ptr weights;
weights = make_shared_blob<uint8_t>(TensorDesc(Precision::U8, {8}, Layout::C));
weights->allocate();
weights->buffer().as<float*>()[0] = 0;
weights->buffer().as<float*>()[1] = 0;
EXPECT_NO_THROW(reader.ReadNetwork(model, weights));
}

View File

@ -111,6 +111,36 @@ void op::v5::Loop::validate_and_infer_types()
m_num_iterations = 1; // condition_always_false, do_while mode
}
}
else if (const auto& cond_param = std::dynamic_pointer_cast<const ngraph::opset5::Parameter>(
body_execution_condition.get_node_shared_ptr()))
{
// Const(true or false) -> Loop (body: Parameter -> execution_condition output)
for (const auto& desc : get_input_descriptions())
{
if (m_body->get_parameters().at(desc->m_body_parameter_index) == cond_param)
{
if (const auto& cond_value =
std::dynamic_pointer_cast<const ngraph::opset5::Constant>(
input_value(desc->m_input_index).get_node_shared_ptr()))
{
auto val = cond_value->cast_vector<bool>();
NODE_VALIDATION_CHECK(
this,
val.size() == 1,
"The number of values in the Condition constant is greater than 1");
if (val[0])
{
condition_always_true = true;
}
else
{
m_num_iterations = 1; // condition_always_false, do_while mode
}
}
}
}
}
const auto& trip_count = input_value(0);
const auto& trip_count_rank = trip_count.get_partial_shape().rank();