RecurrentOutput::RecurrentOutput(const Connection &con) { set(con); };
} // namespace props
-RecurrentRealizer::RecurrentRealizer(
- const std::vector<std::string> &properties,
- const std::vector<std::string> &input_layers,
- const std::vector<std::string> &end_layers) :
- input_layers(input_layers.begin(), input_layers.end()),
- end_layers(end_layers),
- sequenced_return_layers(),
+RecurrentRealizer::RecurrentRealizer(const std::vector<std::string> &properties,
+ const std::vector<Connection> &input_conns,
+ const std::vector<Connection> &end_conns) :
+ input_layers(),
+ end_conns(end_conns),
+ sequenced_return_conns(),
recurrent_props(new PropTypes(
std::vector<props::RecurrentInput>(), std::vector<props::RecurrentOutput>(),
std::vector<props::AsSequence>(), props::UnrollFor(1))) {
auto left = loadProperties(properties, *recurrent_props);
- /// @note AsSequence must be identifier based (not connection based) for now
- /// consider A(layer) outputs a0, a1 connection and a0 needs return seq
- /// Then it is impossible to locate a0 and a1 with the same name unless we
- /// have some kind of multi,multiout identity layer. Until this is supported,
- /// AsSequenced stays as identifier based
+ /// @todo support AsSequence with index with identity layer
+ std::transform(input_conns.begin(), input_conns.end(),
+ std::inserter(this->input_layers, this->input_layers.begin()),
+ [](const Connection &c) { return c.getName(); });
auto &[inputs, outputs, as_sequence, unroll_for] = *recurrent_props;
"different size. input: "
<< inputs.size() << " output: " << outputs.size();
+ /// @todo Deal as sequence as proper connection with identity layer
NNTR_THROW_IF(!std::all_of(as_sequence.begin(), as_sequence.end(),
- [&end_layers](const std::string &seq) {
- return std::find(end_layers.begin(),
- end_layers.end(),
- seq) != end_layers.end();
+ [&end_conns](const Connection &seq) {
+ return std::find(end_conns.begin(),
+ end_conns.end(),
+ seq) != end_conns.end();
}),
std::invalid_argument)
<< "as_sequence property must be subset of end_layers";
std::unordered_set<std::string> check_seqs;
for (auto &name : as_sequence) {
- sequenced_return_layers.emplace(name.get());
+ sequenced_return_conns.emplace(name.get());
};
NNTR_THROW_IF(!left.empty(), std::invalid_argument)
* @todo support connection using node->remapConnection
*/
auto naive_output = [](const GraphRepresentation &reference_,
- const std::string &con, unsigned unroll_for) {
- auto target = con + "/" + std::to_string(unroll_for - 1);
+ const Connection &con, unsigned unroll_for) {
+ auto target = con.getName() + "/" + std::to_string(unroll_for - 1);
RemapRealizer r([target, con](std::string &name) {
if (name == target) {
- name = con;
+ name = con.getName();
}
});
*
*/
auto concat_output = [this](const GraphRepresentation &reference_,
- const std::string &con, unsigned unroll_for) {
+ const Connection &con, unsigned unroll_for) {
GraphRepresentation processed(reference_.begin(), reference_.end());
- std::vector<props::Name> names;
+ std::vector<props::RecurrentInput> conns;
for (unsigned int i = 0; i < unroll_for; ++i) {
- names.push_back(con + "/" + std::to_string(i));
+ conns.emplace_back(Connection{
+ con.getName() + "/" + std::to_string(i),
+ con.getIndex(),
+ });
}
/// @todo have axis in concat layer
+ /// @todo this has to be wrapped with identity layer as #1793
auto node = createLayerNode(
- "concat", {"name=" + con, "input_layers=" + to_string(names)});
+ "concat", {"name=" + con.getName(), "input_layers=" + to_string(conns)});
processed.push_back(std::move(node));
return processed;
/// @note below is inefficient way of processing nodes. consider optimize
/// below as needed by calling remap realizer only once
auto processed = reference_;
- for (auto &name : end_layers) {
- processed = sequenced_return_layers.count(name)
- ? concat_output(processed, name, unroll_for)
- : naive_output(processed, name, unroll_for);
+ for (auto &conn : end_conns) {
+ processed = sequenced_return_conns.count(conn)
+ ? concat_output(processed, conn, unroll_for)
+ : naive_output(processed, conn, unroll_for);
}
return processed;
* steps, where steps > 0
*
* @param properties
- * unroll_for = <int> // define timestep of unrolloing
+ * unroll_for = <int> // define timestep of unrolling
* return_sequences = <bool> // return sequences
* recurrent_inputs = <vector<std::string>> // start of the loop
* recurrent_ouptuts = <vector<std::string>> // end of the loop
- * @param input_layers input layer from outer side
- * @param end_layers end layers (output of the internal graph)
+ * @param input_conns input conns from outer side
+ * @param end_conns end connections (output of the internal graph)
*/
RecurrentRealizer(const std::vector<std::string> &properties,
- const std::vector<std::string> &input_layers,
- const std::vector<std::string> &end_layers);
+ const std::vector<Connection> &input_conns,
+ const std::vector<Connection> &end_conns);
/**
* @brief Construct a new Recurrent Realizer object
std::vector<props::AsSequence>, props::UnrollFor>;
std::unordered_set<std::string> input_layers; /**< external input layers */
- std::vector<std::string> end_layers; /**< final output layers id */
- std::unordered_set<std::string>
- sequenced_return_layers; /**< sequenced return layers, subset of end_layers
- */
+ std::vector<Connection> end_conns; /**< final output layers id */
+ std::unordered_set<Connection>
+ sequenced_return_conns; /**< sequenced return conns, subset of end_conns
+ */
std::unordered_map<Connection, Connection>
recurrent_info; /**< final output layers id */
std::unique_ptr<PropTypes> recurrent_props; /**< recurrent properties */
* @brief Construct a new Connection object from string representation
* string representation is format of {layer_name, idx};
*
- * @param string_representation string format of {layer_name}({idx})
+ * @param str_repr string format of {layer_name}({idx})
*/
- explicit Connection(const std::string &string_representation);
+ explicit Connection(const std::string &str_repr);
/**
* @brief Construct a new Connection object
* used in recurrent realizer
*
*/
-class AsSequence : public Name {
+class AsSequence : public Property<Connection> {
public:
static constexpr const char *key = "as_sequence";
- using prop_tag = str_prop_tag;
+ using prop_tag = connection_prop_tag;
};
/**
nodes.push_back(node->cloneConfiguration());
}
- auto normalize = [](const std::vector<std::string> &names) {
- std::vector<props::Name> prop_names(names.begin(), names.end());
- return std::vector<std::string>(prop_names.begin(), prop_names.end());
- };
-
- auto input_layers_ = normalize(input_layers);
- auto start_layers_ = normalize(start_layers);
- auto end_layers_ = normalize(end_layers);
-
auto start_conns =
std::vector<Connection>(start_layers.begin(), start_layers.end());
auto input_conns =
realizers.emplace_back(new PreviousInputRealizer(start_conns));
realizers.emplace_back(new SliceRealizer(start_conns, end_conns));
- if (!input_layers_.empty()) {
+ if (!input_conns.empty()) {
realizers.emplace_back(new InputRealizer(start_conns, input_conns));
}
if (type == ml::train::ReferenceLayersType::RECURRENT) {
realizers.emplace_back(
- new RecurrentRealizer(type_properties, input_layers_, end_layers_));
+ new RecurrentRealizer(type_properties, input_conns, end_conns));
}
if (!scope.empty()) {
realizers.emplace_back(
- new RemapRealizer([&scope, &input_layers_](std::string &name) {
- for (auto &i : input_layers_) {
- if (i == name) {
+ new RemapRealizer([&scope, &input_conns](std::string &name) {
+ for (auto &i : input_conns) {
+ if (i.getName() == name) {
return;
}
}
}
TEST(RecurrentRealizer, recurrent_no_return_sequence_p) {
+ using C = Connection;
RecurrentRealizer r(
{"unroll_for=3", "recurrent_input=fc_in", "recurrent_output=fc_out"},
- {"source"}, {"fc_out"});
+ {C("source")}, {C("fc_out")});
std::vector<LayerRepresentation> before = {
{"fully_connected", {"name=fc_in", "input_layers=source"}},
}
TEST(RecurrentRealizer, recurrent_return_sequence_single_p) {
-
+ using C = Connection;
RecurrentRealizer r({"unroll_for=3", "as_sequence=fc_out",
"recurrent_input=lstm", "recurrent_output=fc_out"},
- {"source"}, {"fc_out"});
+ {C("source")}, {C("fc_out")});
std::vector<LayerRepresentation> before = {
{"lstm", {"name=lstm", "input_layers=source"}},
}
TEST(RecurrentRealizer, recurrent_multi_inout_p) {
+ using C = Connection;
RecurrentRealizer r(
{
"unroll_for=3",
"recurrent_input=lstm,source3_dummy",
"recurrent_output=fc_out,output_dummy",
},
- {"source", "source2", "source3"}, {"fc_out"});
+ {C("source"), C("source2"), C("source3")}, {C("fc_out")});
/// @note for below graph,
/// 1. fc_out feds back to lstm
}
TEST(RecurrentRealizer, recurrent_multi_inout_return_seq_p) {
+ using C = Connection;
RecurrentRealizer r(
{
"unroll_for=3",
"as_sequence=fc_out",
"recurrent_output=fc_out,output_dummy",
},
- {"source", "source2", "source3"}, {"fc_out"});
+ {C("source"), C("source2"), C("source3")}, {C("fc_out")});
/// @note for below graph,
/// 1. fc_out feds back to lstm
}
TEST(RecurrentRealizer, recurrent_multi_inout_using_connection_return_seq_p) {
-
+ using C = Connection;
RecurrentRealizer r(
{
"unroll_for=3",
"recurrent_input=lstm,add(2)",
"recurrent_output=fc_out,split(1)",
},
- {"source", "source2", "source3"}, {"fc_out"});
+ {C("source"), C("source2"), C("source3")}, {C("fc_out")});
/// @note for below graph,
/// 1. fc_out feds back to lstm
}
TEST(RecurrentRealizer, recurrent_multi_inout_using_connection_p) {
+ using C = Connection;
RecurrentRealizer r(
{
"unroll_for=3",
"recurrent_input=lstm,add(2)",
"recurrent_output=fc_out,split(1)",
},
- {"source", "source2", "source3"}, {"fc_out"});
+ {C("source"), C("source2"), C("source3")}, {C("fc_out")});
/// @note for below graph,
/// 1. fc_out feds back to lstm