This patch updates recurrent realizer to suupporting multiple sequence with layer name not boolean
with `as_sequence` property
**Self evaluation:**
1. Build test: [X]Passed [ ]Failed [ ]Skipped
2. Run test: [X]Passed [ ]Failed [ ]Skipped
Signed-off-by: Jihoon Lee <jhoon.it.lee@samsung.com>
#include <util_func.h>
#include <zoneout_lstmcell.h>
+#include <iostream>
+
namespace nntrainer {
namespace props {
end_layers(end_layers),
recurrent_props(
new PropTypes(props::RecurrentInput(), props::RecurrentOutput(),
- props::ReturnSequences(false), props::UnrollFor(1))) {
+ std::vector<props::AsSequence>(), props::UnrollFor(1))) {
auto left = loadProperties(properties, *recurrent_props);
auto throw_if_empty = [](auto &&prop) {
<< getPropKey(prop);
};
- throw_if_empty(std::get<0>(*recurrent_props));
- throw_if_empty(std::get<1>(*recurrent_props));
- throw_if_empty(std::get<2>(*recurrent_props));
- throw_if_empty(std::get<3>(*recurrent_props));
+ /// @todo check input, output number matches
+ /// @todo check if as sequence is subset of recurrent output
+ /// @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
+ throw_if_empty(std::get<0>(*recurrent_props)); // input
+ throw_if_empty(std::get<1>(*recurrent_props)); // ouput
+ throw_if_empty(std::get<3>(*recurrent_props)); // unroll for
NNTR_THROW_IF(!left.empty(), std::invalid_argument)
<< "There is unparesed properties";
}
* @todo support connection using node->remapConnection
*/
auto naive_output = [](const GraphRepresentation &reference_,
- unsigned unroll_for) {
- /// last output's index is removed so that it can be directly an output
- auto suffix = "/" + std::to_string(unroll_for - 1);
- RemapRealizer r([suffix](std::string &name) {
- if (endswith(name, suffix)) {
- auto pos = name.find_last_of('/');
- if (pos != std::string::npos) {
- name = name.substr(0, pos);
- }
+ const std::string &con, unsigned unroll_for) {
+ auto target = con + "/" + std::to_string(unroll_for - 1);
+ RemapRealizer r([target, con](std::string &name) {
+ std::cout << name << " vs " << target << '\n';
+ if (name == target) {
+ std::cout << "matched, setting to con: " << con << '\n';
+ name = con;
}
});
*
*/
auto concat_output = [this](const GraphRepresentation &reference_,
- unsigned unroll_for) {
+ const std::string &con, unsigned unroll_for) {
GraphRepresentation processed(reference_.begin(), reference_.end());
for (auto &end : end_layers) {
auto step3_connect_output =
[this, naive_output, concat_output](const GraphRepresentation &reference_,
unsigned unroll_for) {
- bool return_sequence =
- std::get<props::ReturnSequences>(*recurrent_props).get();
- /// @todo return_sequence will become a sequenced_output_layers
- return return_sequence ? concat_output(reference_, unroll_for)
- : naive_output(reference_, unroll_for);
+ auto sequenced_layers =
+ std::get<std::vector<props::AsSequence>>(*recurrent_props);
+
+ std::unordered_set<std::string> check_seqs;
+ for (auto &name : sequenced_layers) {
+ check_seqs.emplace(name.get());
+ };
+
+ /// @note below is inefficient way of processing nodes consider optimize
+ /// below as needed by calling remap realizer only once
+ std::vector<props::RecurrentOutput> output_conns = {
+ std::get<props::RecurrentOutput>(*recurrent_props)};
+
+ auto processed = reference_;
+ for (auto &name : output_conns) {
+ processed = check_seqs.count(name)
+ ? concat_output(processed, name, unroll_for)
+ : naive_output(processed, name, unroll_for);
+ }
+
+ return processed;
};
auto unroll_for = std::get<props::UnrollFor>(*recurrent_props).get();
namespace props {
class UnrollFor;
-class ReturnSequences;
+class AsSequence;
class OutputLayer;
class RecurrentInput;
class RecurrentOutput;
GraphRepresentation realize(const GraphRepresentation &reference) override;
private:
- using PropTypes = std::tuple<props::RecurrentInput, props::RecurrentOutput,
- props::ReturnSequences, props::UnrollFor>;
+ using PropTypes =
+ std::tuple<props::RecurrentInput, props::RecurrentOutput,
+ 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 */
};
/**
+ * @brief Identifiers to locate a connection which should be returned as whole
+ * used in recurrent realizer
+ *
+ */
+class AsSequence : public Name {
+public:
+ static constexpr const char *key = "as_sequence";
+ using prop_tag = str_prop_tag;
+};
+
+/**
* @brief Number of class
* @todo deprecate this
*/
TEST(RecurrentRealizer, recurrent_no_return_sequence_p) {
- RecurrentRealizer r({"unroll_for=3", "return_sequences=false",
- "recurrent_input=fc_in", "recurrent_output=fc_out"},
- {"source"}, {"fc_out"});
+ RecurrentRealizer r(
+ {"unroll_for=3", "recurrent_input=fc_in", "recurrent_output=fc_out"},
+ {"source"}, {"fc_out"});
std::vector<LayerRepresentation> before = {
{"fully_connected", {"name=fc_in", "input_layers=source"}},
{"fully_connected",
{"name=fc_out/1", "input_layers=fc_in/1", "shared_from=fc_out/0"}},
{"fully_connected",
- {"name=fc_in", "input_layers=fc_out/1", "shared_from=fc_in/0"}},
+ {"name=fc_in/2", "input_layers=fc_out/1", "shared_from=fc_in/0"}},
{"fully_connected",
- {"name=fc_out", "input_layers=fc_in", "shared_from=fc_out/0"}},
+ {"name=fc_out", "input_layers=fc_in/2", "shared_from=fc_out/0"}},
};
realizeAndEqual(r, before, expected);
}
-TEST(DISABLED_RecurrentRealizer, recurrent_return_sequence_single_p) {
+TEST(RecurrentRealizer, recurrent_return_sequence_single_p) {
- RecurrentRealizer r({"unroll_for=3", "return_sequences=fc_out",
+ RecurrentRealizer r({"unroll_for=3", "as_sequence=fc_out",
"recurrent_input=lstm", "recurrent_output=fc_out"},
{"source"}, {"fc_out"});
ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=false",
"recurrent_input=a1",
"recurrent_output=a2",
});
ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=false",
"recurrent_input=a1",
"recurrent_output=a2",
});
ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a1",
"recurrent_input=a1",
"recurrent_output=a1",
});
ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a2",
"recurrent_input=a1",
"recurrent_output=a2",
});
ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a1",
"recurrent_input=a1",
"recurrent_output=a1",
});
ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a2",
"recurrent_input=a1",
"recurrent_output=a2",
});
{"a1"}, ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a1",
"recurrent_input=a1",
"recurrent_output=a1",
});
{"a2"}, ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a2",
"recurrent_input=a1",
"recurrent_output=a2",
});
{"a1"}, ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a1",
"recurrent_input=a1",
"recurrent_output=a1",
});
{"a2"}, ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=2",
- "return_sequences=true",
+ "as_sequence=a2",
"recurrent_input=a1",
"recurrent_output=a2",
});
{"fc2"}, ml::train::ReferenceLayersType::RECURRENT,
{
"unroll_for=3",
- "return_sequences=true",
+ "as_sequence=fc2",
"recurrent_input=fc1",
"recurrent_output=fc2",
});