[split/test] Added unittests for split layer
authorParichay Kapoor <pk.kapoor@samsung.com>
Mon, 31 May 2021 09:44:43 +0000 (18:44 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Tue, 1 Jun 2021 04:57:50 +0000 (13:57 +0900)
Added unittests for split layer covering initialization,
forwarding and backwarding. Corresponding bug fixes are
also added.
This has revealed an issue with concat layer #1226
which will be resolved soon.

Also add split layer to android build

Resolves #1208

**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>
api/ccapi/include/layer.h
jni/Android.mk
nntrainer/app_context.cpp
nntrainer/layers/concat_layer.h
nntrainer/layers/layer_factory.cpp
nntrainer/layers/split_layer.cpp
nntrainer/layers/split_layer.h
test/unittest/unittest_nntrainer_layers.cpp

index e115a0d..9f9f963 100644 (file)
@@ -52,6 +52,7 @@ enum LayerType {
   LAYER_EMBEDDING,            /** Embedding Layer type */
   LAYER_RNN,                  /** RNN Layer type */
   LAYER_LSTM,                 /** LSTM Layer type */
+  LAYER_SPLIT,                /** Splite Layer type */
   LAYER_TIME_DIST,            /** Time Distributed Layer type */
   LAYER_UNKNOWN = ML_TRAIN_LAYER_TYPE_UNKNOWN /** Unknown */
 };
index 439b999..813834e 100644 (file)
@@ -122,6 +122,7 @@ NNTRAINER_SRCS := $(NNTRAINER_ROOT)/nntrainer/models/neuralnet.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/layers/lstm.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/layers/time_dist.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/layers/acti_func.cpp \
+                  $(NNTRAINER_ROOT)/nntrainer/layers/split_layer.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/layers/common_properties.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/graph/network_graph.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/graph/graph_core.cpp \
index 47b2ea6..86ae145 100644 (file)
@@ -47,6 +47,7 @@
 #include <preprocess_flip_layer.h>
 #include <preprocess_translate_layer.h>
 #include <rnn.h>
+#include <split_layer.h>
 #include <time_dist.h>
 
 #ifdef ENABLE_TFLITE_BACKBONE
@@ -236,16 +237,14 @@ static void add_default_object(AppContext &ac) {
 #endif
   ac.registerFactory(nntrainer::createLayer<EmbeddingLayer>,
                      EmbeddingLayer::type, LayerType::LAYER_EMBEDDING);
-
   ac.registerFactory(nntrainer::createLayer<RNNLayer>, RNNLayer::type,
                      LayerType::LAYER_RNN);
-
   ac.registerFactory(nntrainer::createLayer<LSTMLayer>, LSTMLayer::type,
                      LayerType::LAYER_LSTM);
-
   ac.registerFactory(nntrainer::createLayer<TimeDistLayer>, TimeDistLayer::type,
                      LayerType::LAYER_TIME_DIST);
-
+  ac.registerFactory(nntrainer::createLayer<SplitLayer>, SplitLayer::type,
+                     LayerType::LAYER_SPLIT);
   ac.registerFactory(AppContext::unknownFactory<nntrainer::Layer>, "unknown",
                      LayerType::LAYER_UNKNOWN);
 }
index 3742d0e..eb4bcfb 100644 (file)
@@ -81,6 +81,8 @@ public:
    */
   void calcDerivative() override;
 
+  using Layer::setProperty;
+
   /**
    * @copydoc Layer::setProperty(const PropertyType type, const std::string
    * &value)
index 1edfcd8..f46789f 100644 (file)
@@ -27,6 +27,7 @@
 #include <parse_util.h>
 #include <pooling2d_layer.h>
 #include <rnn.h>
+#include <split_layer.h>
 #include <time_dist.h>
 
 #ifdef ENABLE_TFLITE_BACKBONE
@@ -77,6 +78,8 @@ const std::string layerGetStrType(const LayerType &type) {
     return EmbeddingLayer::type;
   case LayerType::LAYER_TIME_DIST:
     return TimeDistLayer::type;
+  case LayerType::LAYER_SPLIT:
+    return SplitLayer::type;
   case LayerType::LAYER_UNKNOWN:
     /** fallthrough intended */
   default:
index 8aaff07..72710ce 100644 (file)
@@ -101,7 +101,7 @@ void SplitLayer::forwarding(bool training) {
   input_.reshape(input_reshape_helper);
 
   for (unsigned int idx = 0; idx < getNumOutputs(); idx++) {
-    Tensor &output_ = net_hidden[0]->getVariableRef();
+    Tensor &output_ = net_hidden[idx]->getVariableRef();
     output_.reshape(output_reshape_helper);
 
     for (unsigned int batch = 0; batch < input_.batch(); batch++) {
@@ -109,7 +109,7 @@ void SplitLayer::forwarding(bool training) {
         input_.getAddress(batch, 0, idx, 0), input_reshape_helper.width(),
         {1, 1, 1, input_reshape_helper.width()});
       Tensor dest_tensor = Tensor::Map(
-        output_.getAddress(batch, 0, idx, 0), output_reshape_helper.width(),
+        output_.getAddress(batch, 0, 0, 0), output_reshape_helper.width(),
         {1, 1, 1, output_reshape_helper.width()});
       dest_tensor.copy(source_tensor);
     }
@@ -126,7 +126,7 @@ void SplitLayer::calcDerivative() {
   input_.reshape(input_reshape_helper);
 
   for (unsigned int idx = 0; idx < getNumOutputs(); idx++) {
-    Tensor &output_ = net_hidden[0]->getGradientRef();
+    Tensor &output_ = net_hidden[idx]->getGradientRef();
     output_.reshape(output_reshape_helper);
 
     for (unsigned int batch = 0; batch < input_.batch(); batch++) {
@@ -134,7 +134,7 @@ void SplitLayer::calcDerivative() {
                                        input_reshape_helper.width(),
                                        {1, 1, 1, input_reshape_helper.width()});
       const Tensor source_tensor = Tensor::Map(
-        output_.getAddress(batch, 0, idx, 0), output_reshape_helper.width(),
+        output_.getAddress(batch, 0, 0, 0), output_reshape_helper.width(),
         {1, 1, 1, output_reshape_helper.width()});
       dest_tensor.copy(source_tensor);
     }
index a3c8048..ff91798 100644 (file)
@@ -32,12 +32,9 @@ public:
    * @brief     Constructor of Split Layer
    */
   template <typename... Args>
-  SplitLayer(unsigned int num_output_, unsigned int split_dim = 1,
-             Args... args) :
+  SplitLayer(unsigned int split_dim = 1, Args... args) :
     Layer(args...),
-    split_dimension(split_dim) {
-    setNumOutputs(num_output_);
-  }
+    split_dimension(split_dim) {}
 
   /**
    * @brief     Destructor of Split Layer
@@ -86,6 +83,8 @@ public:
    */
   void calcDerivative() override;
 
+  using Layer::setProperty;
+
   /**
    * @copydoc Layer::setProperty(const PropertyType type, const std::string
    * &value)
index f13d3f2..2cdd1a9 100644 (file)
@@ -17,6 +17,7 @@
 #include <activation_layer.h>
 #include <addition_layer.h>
 #include <bn_layer.h>
+#include <concat_layer.h>
 #include <conv2d_layer.h>
 #include <embedding.h>
 #include <fc_layer.h>
@@ -34,6 +35,7 @@
 #include <preprocess_flip_layer.h>
 #include <preprocess_translate_layer.h>
 #include <rnn.h>
+#include <split_layer.h>
 #include <tensor_dim.h>
 #include <util_func.h>
 
@@ -2499,6 +2501,139 @@ TEST_F(nntrainer_LSTMLayer, backwarding_01_p) {
 }
 
 /**
+ * @brief nntainer split Layer for test
+ */
+class nntrainer_SplitLayer
+  : public nntrainer_abstractLayer<nntrainer::SplitLayer> {
+
+protected:
+  typedef nntrainer_abstractLayer<nntrainer::SplitLayer> super;
+
+  virtual void prepareLayer() {
+    int status = setProperty("input_shape=9:8:7:6");
+    EXPECT_EQ(status, ML_ERROR_NONE);
+  }
+
+  nntrainer::Tensor result;
+};
+
+/**
+ * @brief Split Layer
+ */
+TEST_F(nntrainer_SplitLayer, init_01_p) {
+  nntrainer::TensorDim out_dim_expect;
+  layer.setBatch(9);
+
+  for (unsigned int idx = 1; idx < nntrainer::MAXDIM; idx++) {
+    std::stringstream ss;
+    ss << "split_dimension=" << idx;
+    EXPECT_EQ(ML_ERROR_NONE, layer.setProperty({ss.str()}));
+    ss.clear();
+    layer.initialize(manager);
+
+    auto in_dim = layer.getInputDimension();
+    auto out_dim = layer.getOutputDimension();
+
+    int val_at_split_dim = in_dim[0].getTensorDim(idx);
+    EXPECT_EQ(out_dim.size(), val_at_split_dim);
+
+    out_dim_expect = in_dim[0];
+    out_dim_expect.setTensorDim(idx, 1);
+
+    for (auto const &out_d : out_dim) {
+      EXPECT_EQ(out_dim_expect, out_d);
+    }
+  }
+}
+
+/**
+ * @brief Split Layer
+ */
+TEST_F(nntrainer_SplitLayer, init_02_n) {
+  nntrainer::TensorDim out_dim_expect;
+  layer.setBatch(9);
+  layer.setProperty({"split_dimension=0"});
+  EXPECT_EQ(ML_ERROR_INVALID_PARAMETER, layer.initialize(manager));
+}
+
+/**
+ * @brief Split Layer
+ */
+TEST_F(nntrainer_SplitLayer, init_03_n) {
+  nntrainer::TensorDim out_dim_expect;
+  layer.setBatch(9);
+  layer.setProperty({"split_dimension=5"});
+  EXPECT_EQ(ML_ERROR_INVALID_PARAMETER, layer.initialize(manager));
+  layer.setProperty({"split_dimension=8"});
+  EXPECT_EQ(ML_ERROR_INVALID_PARAMETER, layer.initialize(manager));
+}
+
+/**
+ * @brief Split + Concat Layer
+ */
+TEST_F(nntrainer_SplitLayer, forwarding_backwarding_01_p) {
+  nntrainer::ConcatLayer concat;
+  nntrainer::TensorDim out_dim_expect;
+  layer.setBatch(9);
+  concat.setBatch(9);
+
+  /// enable till nntrainer::MAXDIM once #1227 is resolved
+  for (unsigned int idx = 1; idx < 2; idx++) {
+    std::stringstream ss;
+    ss << "num_inputs=" << idx;
+    EXPECT_EQ(ML_ERROR_NONE, concat.setProperty({ss.str()}));
+    ss.str(std::string());
+
+    ss << "split_dimension=" << idx;
+    layer.setProperty({ss.str()});
+    EXPECT_EQ(ML_ERROR_NONE, layer.setProperty({ss.str()}));
+
+    auto in_dim = layer.getInputDimension();
+    auto out_dim = layer.getOutputDimension();
+    unsigned int val_at_split_dim = in_dim[0].getTensorDim(idx);
+    out_dim_expect = in_dim[0];
+    out_dim_expect.setTensorDim(idx, 1);
+
+    for (unsigned int ni = 0; ni < val_at_split_dim; ni++)
+      concat.setInputDimension(
+        std::vector<nntrainer::TensorDim>(val_at_split_dim, out_dim_expect));
+
+    nntrainer::Manager manager;
+    manager.setInferenceInOutMemoryOptimization(false);
+
+    layer.initialize(manager);
+    concat.initialize(manager);
+
+    layer.setInputBuffers(manager.trackLayerInputs(
+      layer.getType(), layer.getName(), layer.getInputDimension()));
+    layer.setOutputBuffers(manager.trackLayerOutputs(
+      layer.getType(), layer.getName(), layer.getOutputDimension()));
+    concat.setInputBuffers(layer.getOutputRef());
+    concat.setOutputBuffers(manager.trackLayerOutputs(
+      concat.getType(), concat.getName(), concat.getOutputDimension()));
+
+    manager.initializeTensors(true);
+    manager.allocateTensors();
+
+    nntrainer::Tensor input(in_dim[0]);
+    nntrainer::Tensor derivative(in_dim[0]);
+    input.setRandUniform();
+    derivative.setRandUniform();
+
+    auto split_outs = layer.forwarding_with_val({MAKE_SHARED_TENSOR(input)});
+    auto joined_inputs = concat.forwarding_with_val(split_outs);
+
+    EXPECT_EQ(input, *joined_inputs[0].get());
+
+    auto split_derv =
+      concat.backwarding_with_val({MAKE_SHARED_TENSOR(derivative)});
+    auto joined_deriv = layer.backwarding_with_val(split_derv);
+
+    EXPECT_EQ(*joined_deriv[0].get(), derivative);
+  }
+}
+
+/**
  * @brief Main gtest
  */
 int main(int argc, char **argv) {