[network] Rework the backwarding
authorParichay Kapoor <pk.kapoor@samsung.com>
Thu, 3 Dec 2020 10:14:07 +0000 (19:14 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Thu, 10 Dec 2020 10:20:41 +0000 (19:20 +0900)
- remove forwarding from backwarding
backwarding should just do backwarding and no more
- moved backwarding back to neuralnetwork so that graph
does not has to care about how to backward etc.
Graph just provides iterators for iterating the graph
in reverse. Graph does not know that layers have backwarding etc.

Also this removes dependency of graph from optimizer.

V2:
Added comment fixes for the corresponding PR

**Self evaluation:**
1. Build test: [x]Passed [ ]Failed [ ]Skipped
2. Run test: [x]Passed [ ]Failed [ ]Skipped

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
Applications/ReinforcementLearning/DeepQ/jni/main.cpp
nntrainer/graph/network_graph.cpp
nntrainer/graph/network_graph.h
nntrainer/layers/layer_internal.h
nntrainer/models/neuralnet.cpp
nntrainer/models/neuralnet.h
nntrainer/tensor/weight.h
test/unittest/unittest_nntrainer_graph.cpp
test/unittest/unittest_nntrainer_layers.cpp
test/unittest/unittest_nntrainer_models.cpp

index 3e68dec..4baae82 100644 (file)
@@ -497,7 +497,8 @@ int main(int argc, char **argv) {
         nntrainer::Tensor in_tensor;
         try {
           in_tensor = nntrainer::Tensor(inbatch);
-          mainNet.backwarding({MAKE_SHARED_TENSOR(in_tensor)}, {Q}, iter);
+          mainNet.forwarding({MAKE_SHARED_TENSOR(in_tensor)}, {Q});
+          mainNet.backwarding({Q}, iter);
         } catch (...) {
           std::cerr << "Error during backwarding the network" << std::endl;
           return -1;
index dc4e6a0..534eed5 100644 (file)
@@ -528,30 +528,6 @@ sharedConstTensors NetworkGraph::forwarding(sharedConstTensors input) {
   return out;
 }
 
-void NetworkGraph::backwarding(sharedConstTensors output, int iteration) {
-
-  /** First layer in Sorted must a INPUT layer */
-  for (unsigned int i = Sorted.size() - 1; i > skip_non_trainable_layers + 1;
-       i--) {
-    LayerNode &layer_node = Sorted[i];
-    if (istrequal(layer_node.layer->getType(), nntrainer::LossLayer::type)) {
-      layer_node.layer->backwarding(output);
-    } else {
-      layer_node.layer->backwarding();
-    }
-    optimizer->apply_gradients(layer_node.layer->getWeightsRef(), iteration);
-  }
-
-  /** The last trainable layer need not calculate the derivatives */
-  // Order is matter here. 1. calcGradient 2.Derivative & Gradient
-  Sorted[skip_non_trainable_layers + 1].layer->calcGradient();
-#ifdef ENABLE_TEST
-  Sorted[skip_non_trainable_layers + 1].layer->calcDerivative();
-#endif
-  optimizer->apply_gradients(
-    Sorted[skip_non_trainable_layers + 1].layer->getWeightsRef(), iteration);
-}
-
 std::vector<TensorDim> NetworkGraph::getInputDimension() {
   return Sorted[0].layer->getInputDimension();
 }
index b40db74..822ab33 100644 (file)
@@ -191,19 +191,26 @@ public:
   sharedConstTensors forwarding(sharedConstTensors input);
 
   /**
-   * @brief     backwarding network graph
-   * @param[in] input data
-   * @param[in] iteration
+   * @brief     getter of ordered graph
+   * @retval    ordered LayerNode list
    */
-  void backwarding(sharedConstTensors input, int iteration);
+  const std::vector<LayerNode> &getSorted() { return Sorted; }
 
-  void setOptimizer(std::shared_ptr<Optimizer> opt) { optimizer = opt; }
+  /**
+   * @brief     get begin iterator for the backwarding
+   * @retval    const reverse iterator marking the begin of backwarding
+   */
+  std::vector<LayerNode>::const_reverse_iterator getBackwardingBeginIter() {
+    return Sorted.crbegin();
+  }
 
   /**
-   * @brief     getter of ordered graph
-   * @retval    ordered LayerNode list
+   * @brief     get end iterator for the backwarding
+   * @retval    const reverse iterator marking the end of backwarding
    */
-  std::vector<LayerNode> getSorted() { return Sorted; }
+  std::vector<LayerNode>::const_reverse_iterator getBackwardingEndIter() {
+    return Sorted.crend() - skip_non_trainable_layers;
+  }
 
   /**
    * @brief     getter of output dimension of graph
@@ -242,8 +249,6 @@ private:
     skip_non_trainable_layers; /**< denotes the number of non-trainable layers
                                   at the start of the graph */
 
-  std::shared_ptr<Optimizer> optimizer;
-
   /**
    * @brief Calculate the number of non-trainable layers at the start
    */
index fd7bec3..a3a0511 100644 (file)
@@ -133,6 +133,7 @@ public:
   /**
    * @brief     Apply the gradient for the layer
    * @param[in] iteration Iteration value for the Optimizer
+   * @note      This function is no-op if optimizer is nullptr
    */
   virtual void applyGradient(unsigned int iteration,
                              std::shared_ptr<Optimizer> optimizer) {
index 2a1204a..9700ace 100644 (file)
@@ -152,6 +152,13 @@ int NeuralNetwork::compile() {
 
   model_graph.topologicalSort();
 
+  auto &sorted = model_graph.getSorted();
+  if (sorted.empty() ||
+      !istrequal(sorted.back().layer->getType(), LossLayer::type)) {
+    ml_loge("last layer is not loss layer");
+    return ML_ERROR_INVALID_PARAMETER;
+  }
+
   compiled = true;
 
   return status;
@@ -234,8 +241,6 @@ int NeuralNetwork::initialize() {
 
   manager.initialize();
 
-  model_graph.setOptimizer(opt);
-
   initialized = true;
   return status;
 }
@@ -280,19 +285,37 @@ sharedConstTensors NeuralNetwork::forwarding(sharedConstTensors input,
  *            Call backwarding function of layer in reverse order
  *            No need to call at first Input Layer (No data to be updated)
  */
-void NeuralNetwork::backwarding(sharedConstTensors input,
-                                sharedConstTensors label, int iteration) {
-
-  if (model_graph.Sorted.empty() ||
-      !istrequal(model_graph.Sorted.back().layer->getType(), LossLayer::type)) {
-    throw std::invalid_argument("last layer is not loss layer");
-  }
-
-  forwarding(input, label);
-
-  sharedConstTensors output = label;
-
-  model_graph.backwarding(output, iteration);
+void NeuralNetwork::backwarding(sharedConstTensors label, int iteration) {
+
+  /**
+   * @note -2 as backwarding for input layer is not supported and
+   * last layer backwarding is run out of this loop
+   */
+  auto iter_begin = model_graph.getBackwardingBeginIter();
+  auto iter_end = model_graph.getBackwardingEndIter();
+  for (auto iter = iter_begin; iter != iter_end - 2; iter++) {
+    auto layer = iter->layer;
+    if (istrequal(layer->getType(), nntrainer::LossLayer::type)) {
+      layer->backwarding(label);
+    } else {
+      layer->backwarding();
+    }
+    opt->apply_gradients(layer->getWeightsRef(), iteration);
+  }
+
+  auto last_layer = (iter_end - 2)->layer;
+  /**
+   * The last trainable layer need not calculate the derivatives
+   * Do not change this order:
+   * 1. calcGradient
+   * 2. calcDerivative
+   * 3. applyGradient
+   */
+  last_layer->calcGradient();
+#ifdef ENABLE_TEST
+  last_layer->calcDerivative();
+#endif
+  opt->apply_gradients(last_layer->getWeightsRef(), iteration);
 }
 
 float NeuralNetwork::getLoss() {
@@ -507,7 +530,8 @@ int NeuralNetwork::train_run() {
       if (data_buffer->getDataFromBuffer(nntrainer::BufferType::BUF_TRAIN,
                                          in->getData(), label->getData())) {
         try {
-          backwarding({in}, {label}, iter++);
+          forwarding({in}, {label});
+          backwarding({label}, iter++);
         } catch (...) {
           data_buffer->clear(nntrainer::BufferType::BUF_TRAIN);
           ml_loge("Error: training error in #%d/%d.", epoch_idx, epochs);
index ed47bba..c4ca37f 100644 (file)
@@ -187,8 +187,7 @@ public:
    * @param[in] label List of Label Tensors for the model
    * @param[in] iteration Iteration Number for the optimizer
    */
-  void backwarding(sharedConstTensors input, sharedConstTensors label,
-                   int iteration);
+  void backwarding(sharedConstTensors label, int iteration);
 
   /**
    * @brief     save model and training parameters into file
index ea34858..284082a 100644 (file)
@@ -166,6 +166,7 @@ public:
    */
   void addOptimizerVariable(const TensorDim &dim) {
     opt_vars.emplace_back(dim);
+    // TODO: Move this out when an optimizer does not initialize with 0.
     opt_vars.back().setZero();
   }
 
index 4bb46d0..33f70e4 100644 (file)
@@ -107,7 +107,8 @@ TEST_P(nntrainerGraphTest, loadConfig) {
   for (int i = 0; i < batch; ++i)
     output.setValue(i, 0, 0, 3, 1.0);
 
-  NN.backwarding({MAKE_SHARED_TENSOR(input)}, {MAKE_SHARED_TENSOR(output)}, 1);
+  NN.forwarding({MAKE_SHARED_TENSOR(input)}, {MAKE_SHARED_TENSOR(output)});
+  NN.backwarding({MAKE_SHARED_TENSOR(output)}, 1);
 }
 
 static IniSection nw_base("model", "Type = NeuralNetwork | "
index 25f7b71..cd935e0 100644 (file)
@@ -168,8 +168,6 @@ protected:
     EXPECT_EQ(status, ML_ERROR_NONE);
 
     EXPECT_NO_THROW(opt->addOptimizerVariable(layer.getWeightsRef()));
-    // status = layer.setOptimizer(op);
-    // EXPECT_EQ(status, ML_ERROR_NONE);
 
     return status;
   }
index 46f0107..ecd65e6 100644 (file)
@@ -336,7 +336,7 @@ void GraphWatcher::compareFor(const std::string &reference,
       it->forward(iteration);
     }
 
-    nn.getNetworkGraph().backwarding(label, iteration);
+    nn.backwarding(label, iteration);
 
     for (auto it = nodes.rbegin(); it != nodes.rend() - 1; it++)
       it->backward(iteration);