[nnc][utils][model_generator] Add converter from tree to tflite. (#2230)
authorИван Иванович Кулагин/AI Tools Lab /SRR/Engineer/삼성전자 <i.kulagin@samsung.com>
Wed, 21 Nov 2018 18:19:24 +0000 (21:19 +0300)
committerEfimov Alexander/AI Tools Lab/./Samsung Electronics <a.efimov@samsung.com>
Wed, 21 Nov 2018 18:19:24 +0000 (21:19 +0300)
Implemented converter from randomly generated Tree representation to TFLite model.

Signed-off-by: i-kulagin i.kulagin@samsung.com
contrib/nnc/utils/tflite_model_generator/ModelGenerator.cpp
contrib/nnc/utils/tflite_model_generator/RandomModelBuilder.h
contrib/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.cpp
contrib/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.h

index a6392d5..99ceac4 100644 (file)
 
 #include <iostream>
 #include <string>
+#include <memory>
+
+#include "Tree.h"
 
 #include "RandomModelBuilder.h"
 #include "TFLiteRandomModelBuilder.h"
 
 using namespace modelgen;
-
-static constexpr int maxNumLayers = 20;
+using namespace treebuilder;
 
 int main(int argc, const char* argv[]) {
-  std::random_device rd;
-  std::mt19937 gen(rd());
-  std::uniform_int_distribution<int> intRand(1, maxNumLayers);
+  std::unique_ptr<TreeBuilder> tree_builder(new TreeBuilder);
+  auto tree = tree_builder->buildTree();
 
   std::unique_ptr<RandomModelBuilder> builder(new TFLiteRandomModelBuilder);
-
-  if (builder->addInput()) {
-    std::cout << "Input was added" << std::endl;
-  }
-
-  auto n_layers = intRand(gen);
-  for (int i = 0; i < n_layers; i ++) {
-    builder->addLayer();
-  }
+  builder->convertTreeToModel(tree.get());
 
   std::unique_ptr<ModelSaver> saver = builder->createModelSaver();
-
   saver->saveModel();
 
   return 0;
index aa9ce3c..0fff191 100644 (file)
@@ -55,30 +55,18 @@ public:
                          std::numeric_limits<float>::max()),
                          intRand(static_cast<int32_t>(OpCodes::opFirst),
                                  static_cast<int32_t>(OpCodes::opLast)),
-                         operatorCounts{0},
-                         opCreators{
-                           [this](int input_tensor_id) { createLayerCONV_2D(input_tensor_id);},
-                         } {};
+                         operatorCounts{0} {
+    opCreators[static_cast<int>(OpCodes::opConv2d)] =
+            [this](treebuilder::Tree* t, treebuilder::Operation* op) {
+                createLayerCONV_2D(t, op);
+            };
+  };
 
   virtual ~RandomModelBuilder() = default;
 
-  /**
-   * @brief createLayerXXX are creator for operators.
-   * @param input_tensor_id is id of input tensor.
-   */
-  virtual void createLayerCONV_2D(int input_tensor_id) = 0;
+  virtual void convertTreeToModel(treebuilder::Tree* t) = 0;
 
   /**
-   * @brief addInput does add input tensor to model.
-   * @return status of adding.
-   */
-  virtual bool addInput() = 0;
-  /**
-   * @brief addLayer does add a new layer to model.
-   * @return status of adding.
-   */
-  virtual bool addLayer() = 0;
-  /**
    * @brief getModelSaver does create unique_ptr to ModelSaver.
    * @return unique_ptr to ModelSaver.
    */
@@ -92,20 +80,35 @@ protected:
 
   /**
    * @brief operatorCounts this array contains amount of used operators in generated model.
-   * @details For example: operatorCounts[Op_CONV_2D] -- amount of used 2D convolution operators.
+   * @details For example: operatorCounts[OpCodes::opConv2d] -- amount of used 2D convolution operators.
    */
   int operatorCounts[static_cast<int32_t>(OpCodes::opCount)];
 
   /**
    * @brief opCreators this array contains a lambda with call of method
    * for building specified operator.
-   * @details This array are used for convenient creation random operators,
-   * like follow: opCreators[rand()%OpCodes::OpCnt]
-   * For example: opCreators[Op_CONV_2D](0) -- will lead to call createLayerCONV_2D method.
+   * @details This array is used for convenient creation random operators,
+   * like follow: opCreators[OpCodes::opCount]
+   * For example: opCreators[OpCodes::opConv2d](0) -- will lead to call createLayerCONV_2D method.
    */
-  std::function<void(int)> opCreators[static_cast<int32_t>(OpCodes::opCount)];
-};
+  std::function<void(treebuilder::Tree*, treebuilder::Operation*)>
+      opCreators[static_cast<int32_t>(OpCodes::opCount)];
 
+  /**
+   * @Brief createInput does add input tensor to model.
+   */
+  virtual void createInput(treebuilder::Tree* t) = 0;
+  /**
+   * @brief addOperator does add a new layer to model.
+   */
+  virtual void addOperator(treebuilder::Tree* t, treebuilder::Operation* op) = 0;
+
+  /**
+   * @brief createLayerXXX are creator for operators.
+   * @param input_tensor_id is id of input tensor.
+   */
+  virtual void createLayerCONV_2D(treebuilder::Tree*, treebuilder::Operation*) = 0;
+};
 } // namespace modelgen
 
 #endif // RANDOM_MODELBUILDER_H
index 7a2d3f3..f098cfa 100644 (file)
@@ -55,81 +55,69 @@ TFLiteRandomModelBuilder::TFLiteRandomModelBuilder() : RandomModelBuilder(),
   std::fill_n(_mapOperatorCode, static_cast<long>(OpCodes::opCount), notInitialized);
 }
 
-std::unique_ptr<TensorT>
-        TFLiteRandomModelBuilder::createEmptyTensor(const std::vector<int32_t>& shape,
-                                                    const char* name) {
-  auto tensor_ptr = std::unique_ptr<TensorT>(new TensorT);
+void TFLiteRandomModelBuilder::convertTreeToModel(treebuilder::Tree* t) {
+  createInput(t);
 
-  tensor_ptr->type = tflite::TensorType_FLOAT32;
-  tensor_ptr->name = name;
-  tensor_ptr->shape = shape;
-  tensor_ptr->buffer = static_cast<uint32_t>(_model->buffers.size());
-  _model->buffers.push_back(std::unique_ptr<BufferT>(new BufferT));
+  for (auto& op : t->opList) {
+    addOperator(t, op.get());
+  }
+}
 
-  return tensor_ptr;
+std::unique_ptr<ModelSaver> TFLiteRandomModelBuilder::createModelSaver() {
+  return std::unique_ptr<ModelSaver>(new TFLiteModelSaver(std::move(_model)));
 }
 
-std::unique_ptr<TensorT>
-        TFLiteRandomModelBuilder::createTensorWthBuffer(const std::vector<int32_t>& shape,
-                                                        const char* name) {
-  auto tensor_ptr = createEmptyTensor(shape, name);
+/**
+ * @todo Add support several inputs.
+ */
+void TFLiteRandomModelBuilder::createInput(treebuilder::Tree* t) {
+  assert(_model->subgraphs.empty() && "Subgraph is already created");
 
-  size_t buffer_size = 1;
-  for (auto s : shape) {
-    buffer_size *= s;
-  }
-  buffer_size *= sizeof(float);
+  std::unique_ptr<SubGraphT> subgraph(new SubGraphT);
+  subgraph->inputs.push_back(0);
+  subgraph->outputs.push_back(0);
 
-  _model->buffers[tensor_ptr->buffer]->data.resize(buffer_size);
+  subgraph->tensors.push_back(createEmptyTensor(t->inputShapeTree, "input"));
+  _operandTree2tensor.push_back(0); // it is same as: push_pack(subgraph->tensors.size() - 1);
 
-  for (size_t i = 0; i < buffer_size; i += sizeof(float)) {
-    float val = floatRand(gen);
-    memcpy(_model->buffers[tensor_ptr->buffer]->data.data() + i, &val, sizeof(float));
-  }
-  return tensor_ptr;
+  _model->subgraphs.push_back(std::move(subgraph));
+  _model->description = "Random tflite model";
+  _model->version = 3;
 }
 
-std::unique_ptr<OperatorT> TFLiteRandomModelBuilder::createEmptyLayer(int opcode) {
-  auto operator_ptr = std::unique_ptr<OperatorT>(new OperatorT);
-  auto opcode_id = _mapOperatorCode[opcode];
-  auto tflite_opcode = internalOpCode2TFLiteOpCode[opcode];
+void TFLiteRandomModelBuilder::addOperator(treebuilder::Tree* t, treebuilder::Operation* op) {
+  assert(!_model->subgraphs.empty() && "Subgraph is not created");
 
-  if (opcode_id == notInitialized) {
-    auto opCodePtr = std::unique_ptr<OperatorCodeT>(new OperatorCodeT);
-    opCodePtr->builtin_code = tflite_opcode;
-    opCodePtr->custom_code = tflite::EnumNamesBuiltinOperator()[tflite_opcode];
-    opcode_id = _model->operator_codes.size();
-    _model->operator_codes.push_back(std::move(opCodePtr));
-    _mapOperatorCode[opcode] = opcode_id;
-  }
-  operator_ptr->opcode_index = opcode_id;
-  operatorCounts[opcode]++;
-
-  return operator_ptr;
+  std::cout << "Add operator [" << opNames[static_cast<int32_t>(op->opcode)] << "] on the level [ "
+            << op->levelOwner << " ]" << std::endl;
+  opCreators[static_cast<int32_t>(op->opcode)](t, op);
+  _model->subgraphs[0]->outputs[0] = (*_model->subgraphs[0]->operators.rbegin())->outputs[0];
 }
 
-void TFLiteRandomModelBuilder::createLayerCONV_2D(int input_tensor_id) {
+void TFLiteRandomModelBuilder::createLayerCONV_2D(treebuilder::Tree* t,
+                                                  treebuilder::Operation* op) {
   std::string output_name(opNames[static_cast<int32_t>(OpCodes::opConv2d)]);
   output_name += "_" + std::to_string(operatorCounts[static_cast<int32_t>(OpCodes::opConv2d)]);
-  auto operator_ptr = createEmptyLayer(static_cast<int32_t>(OpCodes::opConv2d));
+  auto operator_ptr = createEmptyOperator(op);
 
-  // @todo Random tensor's shape.
-  auto out_tensor_ptr = createEmptyTensor(_model->subgraphs[0]->tensors[input_tensor_id]->shape,
-                                          output_name.c_str());
-  auto kernel_ptr = createTensorWthBuffer({3, 1, 1, 3}, "Kernel");
-  auto bias_ptr = createTensorWthBuffer({3}, "bias");
+  auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
+  auto kernel_ptr = createTensorWthBuffer(op->kernelShape, "Kernel");
+  auto bias_ptr = createTensorWthBuffer({op->outputShape[3]}, "bias");
 
-  operator_ptr->inputs.push_back(input_tensor_id);
+  auto input_tensor_id = op->levelOwner == 0 ? _operandTree2tensor[0] :
+                                               _operandTree2tensor[t->inputCnt + op->inputs[0]];
 
+  operator_ptr->inputs.push_back(input_tensor_id);
   operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
   _model->subgraphs[0]->tensors.push_back(std::move(kernel_ptr));
   operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
   _model->subgraphs[0]->tensors.push_back(std::move(bias_ptr));
 
-  operator_ptr->outputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
+  auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
+  _operandTree2tensor.push_back(output_tensor_id);
+  operator_ptr->outputs.push_back(output_tensor_id);
   _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
 
-  // @todo Random activation function.
   operator_ptr->builtin_options.Set(tflite::Conv2DOptionsT());
   auto conv2D_opt = operator_ptr->builtin_options.AsConv2DOptions();
   conv2D_opt->stride_w = conv2D_opt->stride_h = 1;
@@ -139,43 +127,58 @@ void TFLiteRandomModelBuilder::createLayerCONV_2D(int input_tensor_id) {
   _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
 }
 
-bool TFLiteRandomModelBuilder::addInput() {
-  if (!_model->subgraphs.empty()) {
-    return false;
-  }
-
-  std::unique_ptr<SubGraphT> subgraph(new SubGraphT);
-  subgraph->inputs.push_back(static_cast<int32_t>(subgraph->tensors.size()));
-  subgraph->outputs.push_back(static_cast<int32_t>(subgraph->tensors.size()));
-  subgraph->tensors.push_back(createEmptyTensor({1, 224, 224, 3}, "input"));
+std::unique_ptr<TensorT>
+TFLiteRandomModelBuilder::createEmptyTensor(const std::vector<int32_t>& shape,
+                                            const char* name) {
+  auto tensor_ptr = std::unique_ptr<TensorT>(new TensorT);
 
-  _model->subgraphs.push_back(std::move(subgraph));
-  _model->description = "Random tflite model";
-  _model->version = 3;
+  tensor_ptr->type = tflite::TensorType_FLOAT32;
+  tensor_ptr->name = name;
+  tensor_ptr->shape = shape;
+  tensor_ptr->buffer = static_cast<uint32_t>(_model->buffers.size());
+  _model->buffers.push_back(std::unique_ptr<BufferT>(new BufferT));
 
-  return true;
+  return tensor_ptr;
 }
 
-bool TFLiteRandomModelBuilder::addLayer() {
+std::unique_ptr<TensorT>
+TFLiteRandomModelBuilder::createTensorWthBuffer(const std::vector<int32_t>& shape,
+                                                const char* name) {
+  auto tensor_ptr = createEmptyTensor(shape, name);
 
-  if (_model->subgraphs.empty()) {
-    return false;
+  size_t buffer_size = 1;
+  for (auto s : shape) {
+    buffer_size *= s;
   }
+  buffer_size *= sizeof(float);
 
-  auto input_id =
-          _model->subgraphs[0]->operators.size() == 0 ? _model->subgraphs[0]->inputs[0] :
-          _model->subgraphs[0]->operators[_model->subgraphs[0]->operators.size() - 1]->outputs[0];
+  _model->buffers[tensor_ptr->buffer]->data.resize(buffer_size);
+
+  for (size_t i = 0; i < buffer_size; i += sizeof(float)) {
+    float val = floatRand(gen);
+    memcpy(_model->buffers[tensor_ptr->buffer]->data.data() + i, &val, sizeof(float));
+  }
+  return tensor_ptr;
+}
 
-  auto op_id = intRand(gen);
+std::unique_ptr<OperatorT>
+TFLiteRandomModelBuilder::createEmptyOperator(treebuilder::Operation* op) {
+  auto operator_ptr = std::unique_ptr<OperatorT>(new OperatorT);
+  auto opcode_id = _mapOperatorCode[static_cast<int32_t>(op->opcode)];
+  auto tflite_opcode = internalOpCode2TFLiteOpCode[static_cast<int32_t>(op->opcode)];
 
-  std::cout << "Add layer: " << opNames[op_id] << std::endl;
-  opCreators[op_id](input_id);
-  _model->subgraphs[0]->outputs[0] = (*_model->subgraphs[0]->operators.rbegin())->outputs[0];
+  if (opcode_id == notInitialized) {
+    auto opcode_ptr = std::unique_ptr<OperatorCodeT>(new OperatorCodeT);
+    opcode_ptr->builtin_code = tflite_opcode;
+    opcode_ptr->custom_code = tflite::EnumNamesBuiltinOperator()[tflite_opcode];
+    opcode_id = static_cast<int32_t>(_model->operator_codes.size());
+    _model->operator_codes.push_back(std::move(opcode_ptr));
+    _mapOperatorCode[static_cast<int32_t>(op->opcode)] = opcode_id;
+  }
+  operator_ptr->opcode_index = static_cast<uint32_t>(opcode_id);
+  operatorCounts[static_cast<int32_t>(op->opcode)]++;
 
-  return true;
+  return operator_ptr;
 }
 
-std::unique_ptr<ModelSaver> TFLiteRandomModelBuilder::createModelSaver() {
-  return std::unique_ptr<ModelSaver>(new TFLiteModelSaver(std::move(_model)));
-}
 } // namespace modelgen
index 3670bc7..c617cd5 100644 (file)
@@ -20,6 +20,7 @@
 #include <vector>
 #include <memory>
 
+#include "Tree.h"
 #include "RandomModelBuilder.h"
 #include "schema_generated.h"
 
@@ -35,10 +36,9 @@ public:
   TFLiteModelSaver() = default;
   ~TFLiteModelSaver() override = default;
 
-  TFLiteModelSaver(std::unique_ptr<ModelT> &&m) : ModelSaver(), _model(std::move(m)) {}
+  TFLiteModelSaver(std::unique_ptr<ModelT>&& m) : ModelSaver(), _model(std::move(m)) {}
 
   void saveModel() override;
-
 private:
   flatbuffers::FlatBufferBuilder _flatBufferBuilder;
   std::unique_ptr<ModelT> _model;
@@ -53,13 +53,16 @@ public:
   TFLiteRandomModelBuilder();
   ~TFLiteRandomModelBuilder() override = default;
 
-  void createLayerCONV_2D(int input_tensor_id) override;
-
-  bool addInput() override;
-  bool addLayer() override;
+  void convertTreeToModel(treebuilder::Tree* t) override;
 
   std::unique_ptr<ModelSaver> createModelSaver() override;
-
+protected:
+  void createInput(treebuilder::Tree* t) override;
+  void addOperator(treebuilder::Tree* t, treebuilder::Operation* op) override;
+  /**
+   *  Operations:
+   */
+  void createLayerCONV_2D(treebuilder::Tree* t, treebuilder::Operation* op) override;
 private:
   /**
    * @brief createEmptyTensor does create tensor without buffer
@@ -80,19 +83,26 @@ private:
   std::unique_ptr<TensorT> createTensorWthBuffer(const std::vector<int32_t>& shape,
                                                  const char* name);
   /**
-   * @brief createEmptyLayer does create operator without tensors and
+   * @brief createEmptyOperator does create operator without tensors and
    * add operator to operators array of SubGraphT
    * @param opcode is operator's code.
    * @return unique_ptr to created operator.
    */
-  std::unique_ptr<OperatorT> createEmptyLayer(int opcode);
+  std::unique_ptr<OperatorT> createEmptyOperator(treebuilder::Operation* op);
 
   std::unique_ptr<ModelT> _model;
+
+  /**
+   * @details This vector contains a index of tensor (in subgraph tflite vector)
+   *          for output operand of tree's node `i`.
+   */
+  std::vector<int32_t> _operandTree2tensor;
+
   /**
    * @brief mapOperatorCode contains indexes to operator_codes array in ModelT.
    */
   long _mapOperatorCode[static_cast<int32_t>(OpCodes::opCount)];
 };
-} // namespace modegGen
+} // namespace modelgen
 
 #endif // TFLITEBUILDER_H