[layer] Enable tflite layer for V2
authorParichay Kapoor <kparichay@gmail.com>
Mon, 12 Jul 2021 03:03:34 +0000 (12:03 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Thu, 22 Jul 2021 11:47:24 +0000 (20:47 +0900)
This patch enables tflite layer for V2 design.

Signed-off-by: Parichay Kapoor <kparichay@gmail.com>
nntrainer/layers/tflite_layer.cpp
nntrainer/layers/tflite_layer.h
test/unittest/layers/meson.build
test/unittest/layers/unittest_layers_split.cpp
test/unittest/layers/unittest_layers_tflite.cpp [new file with mode: 0644]

index 3d1aa31..26023e8 100644 (file)
@@ -10,6 +10,7 @@
  * @bug    No known bugs except for NYI items
  */
 
+#include <layer_internal.h>
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
 #include <tflite_layer.h>
@@ -39,73 +40,85 @@ void TfLiteLayer::setDimensions(const std::vector<int> &tensor_idx_list,
   }
 }
 
-int TfLiteLayer::initialize(Manager &manager) {
+void TfLiteLayer::finalize(InitLayerContext &context) {
   tflite::ops::builtin::BuiltinOpResolver resolver;
 
   model = tflite::FlatBufferModel::BuildFromFile(modelfile.c_str());
   if (!model)
-    return ML_ERROR_INVALID_PARAMETER;
+    throw std::invalid_argument("Failed to build tflite model");
 
   tflite::InterpreterBuilder(*model.get(), resolver)(&interpreter);
   if (!interpreter)
-    return ML_ERROR_INVALID_PARAMETER;
+    throw std::invalid_argument("Failed to build tflite interpreter");
 
   if (interpreter->AllocateTensors() != kTfLiteOk)
     throw std::runtime_error("Failed to allocate tensors!");
 
   std::vector<TensorDim> dims;
   setDimensions(interpreter->inputs(), dims, false);
-  setDimensions(interpreter->outputs(), output_dim, true);
+  const std::vector<TensorDim> &input_dims = context.getInputDimensions();
 
-  if (input_dim.size() && input_dim[0].getTensorDim(0) != 0) {
-    if (dims.size() != input_dim.size())
-      throw std::invalid_argument(
-        "Provided number of input dimensions mismatch");
+  if (dims.size() != input_dims.size())
+    throw std::invalid_argument("Provided number of input dimensions mismatch");
 
-    for (size_t idx = 0; idx < dims.size(); idx++) {
-      if (dims[idx] != input_dim[idx])
-        throw std::invalid_argument("Input dimensions mismatch");
-    }
-  } else {
-    input_dim.resize(dims.size());
-    std::copy(dims.begin(), dims.end(), input_dim.begin());
+  for (size_t idx = 0; idx < dims.size(); idx++) {
+    if (dims[idx] != input_dims[idx])
+      throw std::invalid_argument("Input dimensions mismatch");
   }
 
-  return ML_ERROR_NONE;
+  std::vector<TensorDim> output_dims;
+  setDimensions(interpreter->outputs(), output_dims, true);
+  context.setOutputDimensions(output_dims);
+}
+
+void TfLiteLayer::setProperty(const std::vector<std::string> &values) {
+  /// @todo: deprecate this in favor of loadProperties
+  for (unsigned int i = 0; i < values.size(); ++i) {
+    std::string key;
+    std::string value;
+    std::stringstream ss;
+
+    if (getKeyValue(values[i], key, value) != ML_ERROR_NONE) {
+      throw std::invalid_argument("Error parsing the property: " + values[i]);
+    }
+
+    if (value.empty()) {
+      ss << "value is empty: key: " << key << ", value: " << value;
+      throw std::invalid_argument(ss.str());
+    }
+
+    /// @note this calls derived setProperty if available
+    setProperty(key, value);
+  }
 }
 
-void TfLiteLayer::setProperty(const PropertyType type,
+void TfLiteLayer::setProperty(const std::string &type_str,
                               const std::string &value) {
+  using PropertyType = LayerV1::PropertyType;
+  LayerV1::PropertyType type =
+    static_cast<LayerV1::PropertyType>(parseLayerProperty(type_str));
+
   switch (type) {
   case PropertyType::modelfile: {
-    if (!value.empty())
-      modelfile = value;
+    modelfile = value;
   } break;
   default:
-    LayerV1::setProperty(type, value);
-    break;
+    std::string msg = "[TfLiteLayer] Unknown Layer Property Key for value " +
+                      std::string(value);
+    throw exception::not_supported(msg);
   }
 }
 
-void TfLiteLayer::forwarding(bool training) {
-#ifdef DEBUG
-  if (net_input.size() != input_dim.size())
-    throw std::invalid_argument("Provided number of input dimensions mismatch");
-
-  for (unsigned int idx = 0; idx < input_dim.size(); idx++) {
-    if (net_input[idx]->getDim() != input_dim[idx])
-      throw std::invalid_argument("Input dimensions mismatch");
-  }
-#endif
+void TfLiteLayer::forwarding(RunLayerContext &context, bool training) {
   auto in_indices = interpreter->inputs();
-  for (size_t idx = 0; idx < net_input.size(); idx++)
+  for (size_t idx = 0; idx < in_indices.size(); idx++)
     interpreter->tensor(in_indices[idx])->data.raw =
-      (char *)net_input[idx]->getVariableRef().getData();
+      reinterpret_cast<char *>(context.getInput(idx).getData());
 
   auto out_indices = interpreter->outputs();
   for (size_t idx = 0; idx < out_indices.size(); idx++) {
     interpreter->tensor(out_indices[idx])->data.raw =
-      reinterpret_cast<char *>(net_hidden[idx]->getVariableRef().getData());
+      reinterpret_cast<char *>(context.getOutput(idx).getData());
   }
 
   int status = interpreter->Invoke();
@@ -129,14 +142,7 @@ void TfLiteLayer::forwarding(bool training) {
 #endif
 }
 
-void TfLiteLayer::copy(std::shared_ptr<LayerV1> l) {
-  LayerV1::copy(l);
-
-  std::shared_ptr<TfLiteLayer> from = std::static_pointer_cast<TfLiteLayer>(l);
-  this->modelfile = from->modelfile;
-}
-
-void TfLiteLayer::calcDerivative() {
+void TfLiteLayer::calcDerivative(RunLayerContext &context) {
   throw exception::not_supported(
     "calcDerivative is not supported for tflite layer");
 }
index afc90b3..1d30b16 100644 (file)
@@ -15,8 +15,7 @@
 #define __TENSORFLOW_LITE_H__
 #ifdef __cplusplus
 
-#include <layer_internal.h>
-#include <tensor.h>
+#include <layer_devel.h>
 
 #include <tensorflow/contrib/lite/interpreter.h>
 #include <tensorflow/contrib/lite/kernels/register.h>
@@ -28,13 +27,13 @@ namespace nntrainer {
  * @class   TfLiteLayer
  * @brief   Tensorflow Lite layer
  */
-class TfLiteLayer : public LayerV1 {
+class TfLiteLayer : public Layer {
 public:
   /**
    * @brief     Constructor of NNStreamer Layer
    */
   TfLiteLayer(std::string model = "") :
-    LayerV1(),
+    Layer(),
     modelfile(model),
     interpreter(nullptr),
     model(nullptr) {}
@@ -45,43 +44,35 @@ public:
   ~TfLiteLayer() = default;
 
   /**
-   * @copydoc Layer::forwarding(bool training)
+   * @copydoc Layer::finalize(InitLayerContext &context)
    */
-  void forwarding(bool training = true) override;
+  void finalize(InitLayerContext &context) override;
 
   /**
-   * @copydoc Layer::calcDerivative()
+   * @copydoc Layer::forwarding(RunLayerContext &context, bool training)
    */
-  void calcDerivative() override;
+  void forwarding(RunLayerContext &context, bool training) override;
 
   /**
-   * @copydoc Layer::copy(std::shared_ptr<layer> l)
+   * @copydoc Layer::calcDerivative(RunLayerContext &context)
    */
-  void copy(std::shared_ptr<LayerV1> l) override;
-
-  /**
-   * @copydoc Layer::initialize()
-   */
-  int initialize(Manager &manager) override;
-
-  /**
-   * @copydoc bool supportBackwarding() const
-   */
-  bool supportBackwarding() const override { return false; };
+  void calcDerivative(RunLayerContext &context) override;
 
   /**
    * @copydoc Layer::getType()
    */
   const std::string getType() const override { return TfLiteLayer::type; };
 
-  using LayerV1::setProperty;
+  /**
+   * @copydoc Layer::supportBackwarding()
+   */
+  bool supportBackwarding() const { return false; }
 
   /**
    * @copydoc Layer::setProperty(const PropertyType type, const std::string
    * &value)
    */
-  void setProperty(const PropertyType type,
-                   const std::string &value = "") override;
+  void setProperty(const std::vector<std::string> &values) override;
 
   inline static const std::string type = "backbone_tflite";
 
@@ -99,6 +90,16 @@ private:
    */
   void setDimensions(const std::vector<int> &tensor_idx_list,
                      std::vector<TensorDim> &dim, bool is_output);
+
+  /**
+   * @brief setProperty by type and value separated
+   * @param[in] type property type to be passed
+   * @param[in] value value to be passed
+   * @exception exception::not_supported     when property type is not valid for
+   * the particular layer
+   * @exception std::invalid_argument invalid argument
+   */
+  void setProperty(const std::string &type_str, const std::string &value);
 };
 
 } // namespace nntrainer
index 36df886..e0b5215 100644 (file)
@@ -19,6 +19,7 @@ test_target = [
   'unittest_layers_split.cpp',
   'unittest_layers_embedding.cpp',
   'unittest_layers_concat.cpp',
+  'unittest_layers_tflite.cpp',
 ]
 
 exe = executable(
index d603788..d8e93e3 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Copyright (C) 2021 Parichay Kapoor <pk.kapoor@samsung.com>
  *
- * @file unittest_layers_fully_connected.cpp
+ * @file unittest_layers_split.cpp
  * @date 12 June 2021
  * @brief Split Layer Test
  * @see        https://github.com/nnstreamer/nntrainer
diff --git a/test/unittest/layers/unittest_layers_tflite.cpp b/test/unittest/layers/unittest_layers_tflite.cpp
new file mode 100644 (file)
index 0000000..34f7d71
--- /dev/null
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2021 Parichay Kapoor <pk.kapoor@samsung.com>
+ *
+ * @file unittest_layers_tflite.cpp
+ * @date 12 June 2021
+ * @brief TfLite Layer Test
+ * @see        https://github.com/nnstreamer/nntrainer
+ * @author Parichay Kapoor <pk.kapoor@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+#include <tuple>
+
+#include <gtest/gtest.h>
+
+#include <layers_common_tests.h>
+#include <tflite_layer.h>
+
+auto semantic_tflite = LayerSemanticsParamType(
+  nntrainer::createLayer<nntrainer::TfLiteLayer>, nntrainer::TfLiteLayer::type,
+  {"modelfile=../test/test_models/models/add.tflite"}, {}, 0, false);
+
+INSTANTIATE_TEST_CASE_P(TfLite, LayerSemantics,
+                        ::testing::Values(semantic_tflite));