From: Jihoon Lee Date: Thu, 16 Dec 2021 13:54:18 +0000 (+0900) Subject: [Realizer] Change input connection semantics X-Git-Tag: accepted/tizen/unified/20220323.062643~67 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0fc3264ad5fea90b8cd95db0a5afc53b9e2b08ee;p=platform%2Fcore%2Fml%2Fnntrainer.git [Realizer] Change input connection semantics Input connection semantic changed to be more intuitive by mapping connection one to one **Self evaluation:** 1. Build test: [X]Passed [ ]Failed [ ]Skipped 2. Run test: [X]Passed [ ]Failed [ ]Skipped Signed-off-by: Jihoon Lee --- diff --git a/nntrainer/compiler/input_realizer.cpp b/nntrainer/compiler/input_realizer.cpp index a842c78..e0ba2c2 100644 --- a/nntrainer/compiler/input_realizer.cpp +++ b/nntrainer/compiler/input_realizer.cpp @@ -9,19 +9,24 @@ * @author Jihoon Lee * @bug No known bugs except for NYI items */ +#include #include #include #include #include #include +#include #include namespace nntrainer { -InputRealizer::InputRealizer(const std::vector &start_layers, - const std::vector &input_layers) : - start_layers(start_layers), - input_layers(input_layers) {} +InputRealizer::InputRealizer(const std::vector &start_conns, + const std::vector &input_conns) : + start_conns(start_conns), + input_conns(input_conns) { + NNTR_THROW_IF(start_conns.size() != input_conns.size(), std::invalid_argument) + << "start connection size is not same input_conns size"; +} InputRealizer::~InputRealizer() {} @@ -34,38 +39,24 @@ InputRealizer::realize(const GraphRepresentation &reference) { std::inserter(existing_nodes, existing_nodes.end()), [](auto &node) { return std::pair(node->getName(), node.get()); }); - /// if start_layer is empty, it's not a hard error but likely to be wrong if - /// there is two inputs - ml_logw("trying to realize without start_layer specified, if there is more " - "than two inputs, sort order make setting graph not determinated"); - - auto get_next_input_ref = [input_ref_iter = input_layers.begin(), - this]() mutable { - NNTR_THROW_IF(input_ref_iter == input_layers.end(), std::invalid_argument) - << "there is no more input layers"; - return input_ref_iter++; - }; - - for (auto &start_name : start_layers) { - auto node = existing_nodes.at(start_name); - - auto num_input = node->getNumInputConnections(); + for (unsigned i = 0u, sz = start_conns.size(); i < sz; ++i) { + const auto &sc = start_conns[i]; + const auto &ic = input_conns[i]; + auto node = existing_nodes.at(sc.getName()); - if (num_input == 0) { - // case1. There is no input layers presented -> push single input - node->setProperty({"input_layers=" + *get_next_input_ref()}); + auto num_connection = node->getNumInputConnections(); + if (num_connection == 0) { + NNTR_THROW_IF(sc.getIndex() != 0, std::invalid_argument) + << "start connection: " << sc.toString() + << " not defined and num connection of that node is empty, although " + "start connection of index zero is allowed"; + node->setProperty({"input_layers=" + ic.toString()}); } else { - /// case2. There is multiple input layers -> substitute orphaned node - /// Orphaned node probably is being created from slicing or it is also a - /// possible scenario that the graph in the first place is designed to - /// have a orphaned node. In the latter case, the graph was non-compilable - /// from the first time. - for (auto i = 0u; i < num_input; ++i) { - auto name = node->getInputConnectionName(i); - if (!existing_nodes.count(name)) { - node->setInputConnectionName(i, *get_next_input_ref()); - } - } + NNTR_THROW_IF(sc.getIndex() >= num_connection, std::invalid_argument) + << "start connection: " << sc.toString() + << " not defined, num connection: " << num_connection; + node->setInputConnectionName(sc.getIndex(), ic.getName()); + node->setInputConnectionIndex(sc.getIndex(), ic.getIndex()); } } diff --git a/nntrainer/compiler/input_realizer.h b/nntrainer/compiler/input_realizer.h index 530a7e6..0825be6 100644 --- a/nntrainer/compiler/input_realizer.h +++ b/nntrainer/compiler/input_realizer.h @@ -16,18 +16,17 @@ #include #include +#include #include namespace nntrainer { /** * @brief Graph realizer class which remaps input from start -> input layers - * @note This class find orphaned identifer in order from start_layers and - * change the identifier to input_layers. If start_layers does not have any - * input layers, push single input identifier, if start_layers have - * input_layers, check if the given input layer exists starting from the first - * input layers, if not exist, change to the given input layer in order. In case - * of start_layer contains n input_layers to be replaced. + * @note This class overwrites input conns to the location of start conns. + * This requires each start location have the slot for input connection. + * @note When number of input connection == 0 for a start connection of index + * zero, this pushes input connection to the slot * */ class InputRealizer final : public GraphRealizer { @@ -35,11 +34,11 @@ public: /** * @brief Construct a new Input Realizer object * - * @param start_layers start layers - * @param input_layers input layers + * @param start_conns start layers + * @param input_conns input layers */ - InputRealizer(const std::vector &start_layers, - const std::vector &input_layers); + InputRealizer(const std::vector &start_conns, + const std::vector &input_conns); /** * @brief Destroy the Graph Realizer object @@ -57,8 +56,8 @@ public: GraphRepresentation realize(const GraphRepresentation &reference) override; private: - std::vector start_layers; - std::vector input_layers; + std::vector start_conns; + std::vector input_conns; }; } // namespace nntrainer diff --git a/nntrainer/models/neuralnet.cpp b/nntrainer/models/neuralnet.cpp index 59fc886..36b9fcc 100644 --- a/nntrainer/models/neuralnet.cpp +++ b/nntrainer/models/neuralnet.cpp @@ -945,7 +945,8 @@ void NeuralNetwork::addWithReferenceLayers( auto start_conns = std::vector(start_layers.begin(), start_layers.end()); - + auto input_conns = + std::vector(input_layers.begin(), input_layers.end()); auto end_conns = std::vector(end_layers.begin(), end_layers.end()); @@ -955,7 +956,7 @@ void NeuralNetwork::addWithReferenceLayers( realizers.emplace_back(new SliceRealizer(start_conns, end_conns)); if (!input_layers_.empty()) { - realizers.emplace_back(new InputRealizer(start_layers_, input_layers_)); + realizers.emplace_back(new InputRealizer(start_conns, input_conns)); } if (type == ml::train::ReferenceLayersType::RECURRENT) { diff --git a/test/unittest/compiler/unittest_realizer.cpp b/test/unittest/compiler/unittest_realizer.cpp index 5385e47..4fa973f 100644 --- a/test/unittest/compiler/unittest_realizer.cpp +++ b/test/unittest/compiler/unittest_realizer.cpp @@ -514,28 +514,110 @@ TEST(SliceRealizer, slice_02_p) { realizeAndEqual(r, before, after); } -TEST(InputRealizer, remap_p) { +TEST(InputRealizer, input_p) { std::vector before = { - {"fully_connected", {"name=fc1"}}, // no input_layers specified - {"fully_connected", - {"name=fc2", "input_layers=none1,fc1"}}, // single orphaned node - {"fully_connected", - {"name=fc3", "input_layers=none2,fc2,none3"}}, // multi orphaned node + {"fully_connected", {"name=fc1"}}, + {"fully_connected", {"name=fc2", "input_layers=none1,fc1"}}, + {"fully_connected", {"name=fc3", "input_layers=none2,fc2,none3"}}, }; std::vector after = { - {"fully_connected", - {"name=fc1", "input_layers=in1"}}, // no input_layers specified - {"fully_connected", - {"name=fc2", "input_layers=in2,fc1"}}, // single orphaned node - {"fully_connected", - {"name=fc3", "input_layers=in3,fc2,in4"}}, // multi orphaned node + {"fully_connected", {"name=fc1", "input_layers=in1(0)"}}, + {"fully_connected", {"name=fc2", "input_layers=in2,fc1"}}, + {"fully_connected", {"name=fc3", "input_layers=in3(3),fc2,in4"}}, }; - InputRealizer r({"fc1", "fc2", "fc3"}, {"in1", "in2", "in3", "in4"}); + using C = Connection; + InputRealizer r( + { + C("fc1"), + C("fc2(0)"), + C("fc3(0)"), + C("fc3(2)"), + }, + { + C("in1(0)"), + C("in2"), + C("in3(3)"), + C("in4"), + }); realizeAndEqual(r, before, after); } +TEST(InputRealizer, input_start_num_not_match_n) { + using C = Connection; + EXPECT_ANY_THROW(InputRealizer r( + { + C("fc1"), + }, + { + C("in1(0)"), + C("in2"), + C("in3(3)"), + C("in4"), + })); +} + +TEST(InputRealizer, start_empty_conn_not_defined_n) { + std::vector before = { + {"fully_connected", {"name=fc1"}}, + {"fully_connected", {"name=fc2", "input_layers=none1,fc1"}}, + {"fully_connected", {"name=fc3", "input_layers=none2,fc2,none3"}}, + }; + + std::vector after = { + {"fully_connected", {"name=fc1", "input_layers=in1(0)"}}, + {"fully_connected", {"name=fc2", "input_layers=in2,fc1"}}, + {"fully_connected", {"name=fc3", "input_layers=in3(3),fc2,in4"}}, + }; + + using C = Connection; + InputRealizer r( + { + C("fc1(2)"), /**< connection not defined, although fc1(0) is allowed */ + C("fc2(0)"), + C("fc3(0)"), + C("fc3(2)"), + }, + { + C("in1(0)"), + C("in2"), + C("in3(3)"), + C("in4"), + }); + EXPECT_ANY_THROW(realizeAndEqual(r, before, after)); +} + +TEST(InputRealizer, intermediate_conn_not_defined_n) { + std::vector before = { + {"fully_connected", {"name=fc1"}}, + {"fully_connected", {"name=fc2", "input_layers=none1,fc1"}}, + {"fully_connected", {"name=fc3", "input_layers=none2,fc2,none3"}}, + }; + + std::vector after = { + {"fully_connected", {"name=fc1", "input_layers=in1(0)"}}, + {"fully_connected", {"name=fc2", "input_layers=in2,fc1"}}, + {"fully_connected", {"name=fc3", "input_layers=in3(3),fc2,in4"}}, + }; + + using C = Connection; + InputRealizer r( + { + C("fc1"), + C("fc2(4)"), /**< connection not defined */ + C("fc3(0)"), + C("fc3(2)"), + }, + { + C("in1(0)"), + C("in2"), + C("in3(3)"), + C("in4"), + }); + EXPECT_ANY_THROW(realizeAndEqual(r, before, after)); +} + TEST(PreviousInputRealizer, previous_p) { { /// realization without identifying custom input std::vector before = {